ubuntu下使用docker、socket、yolov5进行图像处理数据交互记录
概述:主要实现了在宿主机上通过8000端口传递一张图像给docker镜像,然后镜像中处理后,通过8001端口回传处理后的图像给宿主机。
第一章、构建镜像
一、dockerfile文件
1.拉取ubuntu20.04镜像
基于ubuntu20.04进行构建,拉取ubuntu20.04的指令如下:
sudo docker pull ubuntu:20.04
2.编写dockerfile文件
dockerfile写入如下:
# Dockerfile# Base images 基础镜像FROM ubuntu:20.04# MAINTAINER 维护者信息
maintainer chenjun_1241370589@qq.com
# 设置超时
ENV PIP_DEFAULT_TIMEOUT=100
# 设置环境变量以避免手动选择时区
ENV DEBIAN_FRONTEND=noninteractive#RUN 执行以下命令
RUN apt update
RUN apt install python3 python3-pip -y
RUN apt-get install nano
# 安装必要的系统依赖项
RUN apt-get update && apt-get install -y \libglib2.0-0 \libsm6 \libxext6 \libxrender-dev
RUN pip3 install --upgrade pip setuptools
RUN pip3 cache purge
RUN pip3 install opencv-python==4.5.5.62 -i https://pypi.tuna.tsinghua.edu.cn/simple
# 安装 OpenCV
RUN apt-get update && apt-get install -y \ libgl1-mesa-glx # 安装 OpenGL 库
# 其他依赖和设置RUN pip3 install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN apt install libgl1-mesa-glx
RUN mkdir -p /data/code/#拷贝文件至工作文件夹
COPY test.py /data/code/test.py#工作目录
WORKDIR /data/code/#容器启动时执行的命令
CMD ["python3","test.py"]
二、编写docker内运行文件
1.新建test.py文件
test.py文件将被拷贝进镜像中,作为镜像内部运行文件。写入如下:
import cv2 # 导入OpenCV库
import numpy as np # 导入NumPy库
import socket # 导入socket库
import struct # 导入struct库
import timedef receive_image():server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP/IP套接字server_socket.bind(('0.0.0.0', 8000)) # 绑定到本地地址和端口8000server_socket.listen(1) # 监听连接print("Waiting for connection...") # 打印等待连接信息client_socket, addr = server_socket.accept() # 接受连接print(f"Connected to {addr}") # 打印连接地址data = b"" # 初始化数据变量payload_size = struct.calcsize("L") # 获取消息大小的字节数while len(data) < payload_size: # 接收消息大小data += client_socket.recv(4096)packed_msg_size = data[:payload_size] # 获取打包的消息大小data = data[payload_size:] # 剩余数据msg_size = struct.unpack("L", packed_msg_size)[0] # 解包消息大小while len(data) < msg_size: # 接收完整消息data += client_socket.recv(4096)frame_data = data[:msg_size] # 获取图像数据frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR) # 解码图像client_socket.close() # 关闭客户端套接字server_socket.close() # 关闭服务器套接字return frame # 返回图像def process_image(image):height, width = image.shape[:2] # 获取图像高度和宽度new_height, new_width = height // 2, width // 2 # 计算新高度和宽度print("Process_image ok !")return cv2.resize(image, (new_width, new_height)) # 调整图像大小def send_image(image):client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP/IP套接字for _ in range(5): # 尝试5次连接try:client_socket.connect(('172.17.0.1', 8001)) # 连接到指定地址和端口breakexcept ConnectionRefusedError:print("Connection refused, retrying...")time.sleep(2) # 等待2秒后重试else:print("Failed to connect after 5 attempts")return_, img_encoded = cv2.imencode('.jpg', image) # 编码图像为JPEG格式data = img_encoded.tobytes() # 转换为字节数据client_socket.sendall(struct.pack("L", len(data)) + data) # 发送数据client_socket.close() # 关闭客户端套接字if __name__ == "__main__":while True:# 接收图像received_image = receive_image()# 处理图像processed_image = process_image(received_image)# 发送处理后的图像send_image(processed_image)
其中client_socket.connect((‘172.17.0.1’, 8001)) # 连接到指定地址和端口这一行中的ip需要在宿主机运行指令:
ip addr show docker0
将inet后的ip填入代码中。
三、构建镜像
宿主机终端输入:
sudo docker build -t python_socket2 . -f Dockerfile
其中“python_socket2”是我自己创建的名字,自行修改。
第二章、宿主机代码
一、交互代码
在宿主机中写入test1.py代码,这里图像处理部分只对图像进行尺寸减半的操作,代码如下:
import cv2
import numpy as np
import socket
import structdef send_image(image_path):# 读取图像image = cv2.imread(image_path)if image is None:print(f"无法读取图像: {image_path}")return# 创建TCP/IP套接字client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client_socket.connect(('localhost', 8000)) # 连接到Docker容器的8000端口# 编码图像为JPEG格式_, img_encoded = cv2.imencode('.jpg', image)data = img_encoded.tobytes()# 发送数据client_socket.sendall(struct.pack("L", len(data)) + data)client_socket.close()print("图像已发送")def receive_processed_image():# 创建TCP/IP套接字server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.bind(('0.0.0.0', 8001)) # 绑定到本地地址和端口8001server_socket.listen(1)print("等待接收处理后的图像...")client_socket, addr = server_socket.accept()print(f"已连接到 {addr}")data = b""payload_size = struct.calcsize("L")while len(data) < payload_size:data += client_socket.recv(4096)packed_msg_size = data[:payload_size]data = data[payload_size:]msg_size = struct.unpack("L", packed_msg_size)[0]while len(data) < msg_size:data += client_socket.recv(4096)frame_data = data[:msg_size]# 解码图像frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR)client_socket.close()server_socket.close()return frameif __name__ == "__main__":image_path = "/home/cj/work/Docker/Yolov5DnnImage/bus.jpg" # 替换为实际的图像路径send_image(image_path)processed_image = receive_processed_image()# 显示处理后的图像#cv2.imshow("Processed Image", processed_image)#cv2.waitKey(0)#cv2.destroyAllWindows()# 保存处理后的图像cv2.imwrite("processed_image.jpg", processed_image)print("处理后的图像已保存为 processed_image.jpg")
图像输入地址自行修改。
第三章、交互使用
一、docker端
在宿主机终端输入:
sudo docker run -p 8000:8000 -i -t python_socket2:latest
这里的“python_socket2:latest”修改成自己的镜像。
查看镜像宿主机终端输入:
sudo docker images
二、宿主机端
运行test1.py文件
同时在docker端也会有输出:
在宿主机当前工作目录下也会生成对应的图像。
第四章、增加yolov5目标检测
一、宿主机文件摆放
因为需要更新dockerfile文件,因此需要留意文件摆放。本文中将需要用的文件都摆放在项目目录下。
目录中,bus.jpg是等待传输和处理的图像,classes.txt是目标检测类别文件,Dockerfile是进行构建文件,processed_image.jpg是宿主机传递给镜像处理后传输回来的图像,test.py是镜像中的主函数代码,test1.py是宿主机端的主函数代码,yolo.py是yolov5调用opencv的dnn模块进行推理的代码,yolov5s.onnx是模型全重文件。
二、各文件内容
1.classes.txt文件
person
bicycle
car
motorbike
aeroplane
bus
train
truck
boat
traffic light
fire hydrant
stop sign
parking meter
bench
bird
cat
dog
horse
sheep
cow
elephant
bear
zebra
giraffe
backpack
umbrella
handbag
tie
suitcase
frisbee
skis
snowboard
sports ball
kite
baseball bat
baseball glove
skateboard
surfboard
tennis racket
bottle
wine glass
cup
fork
knife
spoon
bowl
banana
apple
sandwich
orange
broccoli
carrot
hot dog
pizza
donut
cake
chair
sofa
pottedplant
bed
diningtable
toilet
tvmonitor
laptop
mouse
remote
keyboard
cell phone
microwave
oven
toaster
sink
refrigerator
book
clock
vase
scissors
teddy bear
hair drier
toothbrush
2.Dockerfile文件
主要是通过COPY命令,将一些必要的文件拷贝到镜像中
# Dockerfile# Base images 基础镜像FROM ubuntu:20.04# MAINTAINER 维护者信息
maintainer chenjun_1241370589@qq.com
# 设置超时
ENV PIP_DEFAULT_TIMEOUT=100
# 设置环境变量以避免手动选择时区
ENV DEBIAN_FRONTEND=noninteractive#RUN 执行以下命令
RUN apt update
RUN apt install python3 python3-pip -y
RUN apt-get install nano
# 安装必要的系统依赖项
RUN apt-get update && apt-get install -y \libglib2.0-0 \libsm6 \libxext6 \libxrender-dev
RUN pip3 install --upgrade pip setuptools
RUN pip3 cache purge
RUN pip3 install opencv-python==4.5.5.62 -i https://pypi.tuna.tsinghua.edu.cn/simple
# 安装 OpenCV
RUN apt-get update && apt-get install -y \ libgl1-mesa-glx # 安装 OpenGL 库
# 其他依赖和设置RUN pip3 install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
RUN apt install libgl1-mesa-glx
RUN mkdir -p /data/code/#拷贝文件至工作文件夹
COPY test.py /data/code/test.py
COPY yolo.py /data/code/yolo.py
COPY classes.txt /data/code/classes.txt
COPY yolov5s.onnx /data/code/yolov5s.onnx#工作目录
WORKDIR /data/code/#容器启动时执行的命令
CMD ["python3","test.py"]
3.test.py文件
主要增加Yolov5Dnn函数
import cv2 # 导入OpenCV库
import numpy as np # 导入NumPy库
import socket # 导入socket库
import struct # 导入struct库
import time
from yolo import build_model,detect,load_classes,wrap_detection,format_yolov5
def receive_image():server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP/IP套接字server_socket.bind(('0.0.0.0', 8000)) # 绑定到本地地址和端口8000server_socket.listen(1) # 监听连接print("Waiting for connection...") # 打印等待连接信息client_socket, addr = server_socket.accept() # 接受连接print(f"Connected to {addr}") # 打印连接地址data = b"" # 初始化数据变量payload_size = struct.calcsize("L") # 获取消息大小的字节数while len(data) < payload_size: # 接收消息大小data += client_socket.recv(4096)packed_msg_size = data[:payload_size] # 获取打包的消息大小data = data[payload_size:] # 剩余数据msg_size = struct.unpack("L", packed_msg_size)[0] # 解包消息大小while len(data) < msg_size: # 接收完整消息data += client_socket.recv(4096)frame_data = data[:msg_size] # 获取图像数据frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR) # 解码图像client_socket.close() # 关闭客户端套接字server_socket.close() # 关闭服务器套接字return frame # 返回图像def process_image(image):height, width = image.shape[:2] # 获取图像高度和宽度new_height, new_width = height // 2, width // 2 # 计算新高度和宽度print("Process_image ok !")return cv2.resize(image, (new_width, new_height)) # 调整图像大小def send_image(image):client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP/IP套接字for _ in range(5): # 尝试5次连接try:client_socket.connect(('172.17.0.1', 8001)) # 连接到指定地址和端口breakexcept ConnectionRefusedError:print("Connection refused, retrying...")time.sleep(2) # 等待2秒后重试else:print("Failed to connect after 5 attempts")return_, img_encoded = cv2.imencode('.jpg', image) # 编码图像为JPEG格式data = img_encoded.tobytes() # 转换为字节数据client_socket.sendall(struct.pack("L", len(data)) + data) # 发送数据client_socket.close() # 关闭客户端套接字def Yolov5Dnn(image):print("Yolo detect ")class_list = load_classes()colors = [(255, 255, 0), (0, 255, 0), (0, 255, 255), (255, 0, 0)]is_cuda = 0net = build_model(is_cuda)inputImage = format_yolov5(image)outs = detect(inputImage, net)class_ids, confidences, boxes = wrap_detection(inputImage, outs[0])for (classid, confidence, box) in zip(class_ids, confidences, boxes):color = colors[int(classid) % len(colors)]cv2.rectangle(image, box, color, 2)cv2.rectangle(image, (box[0], box[1] - 20), (box[0] + box[2], box[1]), color, -1)cv2.putText(image, class_list[classid], (box[0], box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, .5, (0, 0, 0))return imageif __name__ == "__main__":while True:# 接收图像received_image = receive_image()# 处理图像#processed_image = process_image(received_image)processed_image = Yolov5Dnn(received_image)# 发送处理后的图像send_image(processed_image)
4.test1.py文件
这里和前面的代码是一样的
import cv2
import numpy as np
import socket
import structdef send_image(image_path):# 读取图像image = cv2.imread(image_path)if image is None:print(f"无法读取图像: {image_path}")return# 创建TCP/IP套接字client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client_socket.connect(('localhost', 8000)) # 连接到Docker容器的8000端口# 编码图像为JPEG格式_, img_encoded = cv2.imencode('.jpg', image)data = img_encoded.tobytes()# 发送数据client_socket.sendall(struct.pack("L", len(data)) + data)client_socket.close()print("图像已发送")def receive_processed_image():# 创建TCP/IP套接字server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server_socket.bind(('0.0.0.0', 8001)) # 绑定到本地地址和端口8001server_socket.listen(1)print("等待接收处理后的图像...")client_socket, addr = server_socket.accept()print(f"已连接到 {addr}")data = b""payload_size = struct.calcsize("L")while len(data) < payload_size:data += client_socket.recv(4096)packed_msg_size = data[:payload_size]data = data[payload_size:]msg_size = struct.unpack("L", packed_msg_size)[0]while len(data) < msg_size:data += client_socket.recv(4096)frame_data = data[:msg_size]# 解码图像frame = cv2.imdecode(np.frombuffer(frame_data, dtype=np.uint8), cv2.IMREAD_COLOR)client_socket.close()server_socket.close()return frameif __name__ == "__main__":image_path = "/home/cj/work/Docker/Yolov5DnnImage/bus.jpg" # 替换为实际的图像路径send_image(image_path)processed_image = receive_processed_image()# 显示处理后的图像#cv2.imshow("Processed Image", processed_image)#cv2.waitKey(0)#cv2.destroyAllWindows()# 保存处理后的图像cv2.imwrite("processed_image.jpg", processed_image)print("处理后的图像已保存为 processed_image.jpg")
5.yolo.py文件
这个文件包含的是yolov5调用opencv的dnn模块进行目标检测的一些函数,内容如下:
import cv2
import time
import sys
import numpy as npdef build_model(is_cuda):net = cv2.dnn.readNet("./yolov5s.onnx")if is_cuda:print("Attempty to use CUDA")net.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)net.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA_FP16)else:print("Running on CPU")net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)return netINPUT_WIDTH = 640
INPUT_HEIGHT = 640
SCORE_THRESHOLD = 0.2
NMS_THRESHOLD = 0.4
CONFIDENCE_THRESHOLD = 0.4def detect(image, net):blob = cv2.dnn.blobFromImage(image, 1/255.0, (INPUT_WIDTH, INPUT_HEIGHT), swapRB=True, crop=False)net.setInput(blob)preds = net.forward()return predsdef load_classes():class_list = []with open("./classes.txt", "r") as f:class_list = [cname.strip() for cname in f.readlines()]return class_listdef wrap_detection(input_image, output_data):class_ids = []confidences = []boxes = []rows = output_data.shape[0]image_width, image_height, _ = input_image.shapex_factor = image_width / INPUT_WIDTHy_factor = image_height / INPUT_HEIGHTfor r in range(rows):row = output_data[r]confidence = row[4]if confidence >= 0.4:classes_scores = row[5:]_, _, _, max_indx = cv2.minMaxLoc(classes_scores)class_id = max_indx[1]if (classes_scores[class_id] > .25):confidences.append(confidence)class_ids.append(class_id)x, y, w, h = row[0].item(), row[1].item(), row[2].item(), row[3].item() left = int((x - 0.5 * w) * x_factor)top = int((y - 0.5 * h) * y_factor)width = int(w * x_factor)height = int(h * y_factor)box = np.array([left, top, width, height])boxes.append(box)indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.25, 0.45) result_class_ids = []result_confidences = []result_boxes = []for i in indexes:result_confidences.append(confidences[i])result_class_ids.append(class_ids[i])result_boxes.append(boxes[i])return result_class_ids, result_confidences, result_boxesdef format_yolov5(frame):row, col, _ = frame.shape_max = max(col, row)result = np.zeros((_max, _max, 3), np.uint8)result[0:row, 0:col] = framereturn result
三、更新并运行docker镜像
1.更新镜像
在文件目录下打开终端,输入:
sudo docker build -t python_socket2 . -f Dockerfile
等待构建完成。
2.运行镜像
终端输入:
sudo docker run -p 8000:8000 -i -t python_socket2:latest
宿主机端,运行test1.py文件。
3.查看结果
文件夹下出现processed_image.jpg,可以看到检测结果。