ubuntu下使用docker、socket、yolov5进行图像处理数据交互记录

ubuntu下使用docker、socket、yolov5进行图像处理数据交互记录

概述:主要实现了在宿主机上通过8000端口传递一张图像给docker镜像,然后镜像中处理后,通过8001端口回传处理后的图像给宿主机。

第一章、构建镜像

一、dockerfile文件

1.拉取ubuntu20.04镜像

基于ubuntu20.04进行构建,拉取ubuntu20.04的指令如下:

python 复制代码
sudo docker pull ubuntu:20.04

2.编写dockerfile文件

dockerfile写入如下:

python 复制代码
# 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文件将被拷贝进镜像中,作为镜像内部运行文件。写入如下:

python 复制代码
import cv2  # 导入OpenCV库
import numpy as np  # 导入NumPy库
import socket  # 导入socket库
import struct  # 导入struct库
import time

def receive_image():
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建TCP/IP套接字
    server_socket.bind(('0.0.0.0', 8000))  # 绑定到本地地址和端口8000
    server_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))  # 连接到指定地址和端口
            break
        except 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需要在宿主机运行指令:

python 复制代码
ip addr show docker0

将inet后的ip填入代码中。

三、构建镜像

宿主机终端输入:

python 复制代码
sudo docker build -t python_socket2 . -f Dockerfile

其中"python_socket2"是我自己创建的名字,自行修改。

第二章、宿主机代码

一、交互代码

在宿主机中写入test1.py代码,这里图像处理部分只对图像进行尺寸减半的操作,代码如下:

python 复制代码
import cv2
import numpy as np
import socket
import struct

def 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))  # 绑定到本地地址和端口8001
    server_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 frame

if __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端

在宿主机终端输入:

python 复制代码
sudo docker run -p 8000:8000 -i -t python_socket2:latest

这里的"python_socket2:latest"修改成自己的镜像。

查看镜像宿主机终端输入:

python 复制代码
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文件

python 复制代码
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命令,将一些必要的文件拷贝到镜像中

python 复制代码
# 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函数

python 复制代码
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))  # 绑定到本地地址和端口8000
    server_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))  # 连接到指定地址和端口
            break
        except 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 = 0
    net = 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 image

if __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文件

这里和前面的代码是一样的

python 复制代码
import cv2
import numpy as np
import socket
import struct

def 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))  # 绑定到本地地址和端口8001
    server_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 frame

if __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模块进行目标检测的一些函数,内容如下:

python 复制代码
import cv2
import time
import sys
import numpy as np

def 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 net

INPUT_WIDTH = 640
INPUT_HEIGHT = 640
SCORE_THRESHOLD = 0.2
NMS_THRESHOLD = 0.4
CONFIDENCE_THRESHOLD = 0.4

def 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 preds


def load_classes():
    class_list = []
    with open("./classes.txt", "r") as f:
        class_list = [cname.strip() for cname in f.readlines()]
    return class_list



def wrap_detection(input_image, output_data):
    class_ids = []
    confidences = []
    boxes = []

    rows = output_data.shape[0]

    image_width, image_height, _ = input_image.shape

    x_factor = image_width / INPUT_WIDTH
    y_factor =  image_height / INPUT_HEIGHT

    for 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_boxes

def format_yolov5(frame):

    row, col, _ = frame.shape
    _max = max(col, row)
    result = np.zeros((_max, _max, 3), np.uint8)
    result[0:row, 0:col] = frame
    return result
三、更新并运行docker镜像

1.更新镜像

在文件目录下打开终端,输入:

python 复制代码
sudo docker build -t python_socket2 . -f Dockerfile

等待构建完成。
2.运行镜像

终端输入:

python 复制代码
sudo docker run -p 8000:8000 -i -t python_socket2:latest

宿主机端,运行test1.py文件。

3.查看结果

文件夹下出现processed_image.jpg,可以看到检测结果。

相关推荐
编程、小哥哥15 分钟前
在 Docker 中部署 Jenkins,并完成项目的构建和发布
servlet·docker·jenkins
~央千澈~2 小时前
优雅草央千澈-关于蓝湖如何快速的标注交互原型是如何使用的-如何使用蓝湖设计交互原型和整个软件项目的流程逻辑-实践项目详细说明
ui·交互·蓝湖
云上的阿七2 小时前
云计算中的容器技术(如Docker)是什么?
docker·容器·云计算
杨浦老苏2 小时前
开源PDF翻译工具PDFMathTranslate
人工智能·docker·ai·pdf·群晖·翻译
dessler3 小时前
Docker-如何启动docker
运维·docker·云原生·容器·eureka
zhy295633 小时前
【DOCKER】基于DOCKER的服务之DUFS
运维·docker·容器·dufs
丰色木夕4 小时前
Ubuntu下的tcl/tk编程快速入门
ubuntu·tcl·tk
大妞4 小时前
ubuntu20.04 install vscode[ROS]
ubuntu
蜜獾云4 小时前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
ac.char5 小时前
在 Ubuntu 下使用 Tauri 打包 EXE 应用
linux·运维·ubuntu