【基于xilinx Zynq7000的PYNQ框架项目】03 Socket实现开发板和电脑的实时视频传输并存储图片

03 socket实现实时视频流传输并保存

  • 前言
  • 一、服务器端代码
  • 二、客户端代码
  • 三、运行
  • 总结


前言


主机摄像头获取实时视频流,需要传输到开发板上并实时保存更新,才能使用开发板上的人脸识别模型进行人脸检测。本篇文章通过 socket 实现主机与 ZYNQ_MINI 开发板之间的数据传输。

一、服务器端代码


承接本项目系列的上一篇文章 【基于Xilinx Zynq7000的PYNQ框架项目】02 PYNQ镜像制作 ,使用 PYNQ 提供的 jupyter notebook 来写服务器端代码(意思是开发板作为服务器端):

import socket
import struct
import threading
import cv2
import numpy
import os


class Server:
    def __init__(self):
        # 设置tcp服务端的socket
        self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 设置重复使用
        self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 绑定地址和端口,此处的IP地址为开发板的IP,端口号可以随意设置,只要不跟已占用的端口号冲突
        self.server.bind(('192.168.137.80', 11000))
        # 设d置被动监听
        self.server.listen(128)
    def run(self):
        while True:
        	# 等待客户端连接
            print('等待客户端连接')
            client, addr = self.server.accept()
            ProcessClient(client).start()

class ProcessClient(threading.Thread):
    def __init__(self, client):
        super().__init__()
        self.client = client
		# 以下内容作用是根据文件夹的图片序号规定'i'的初值,方便下一次保存图片时使用
        self.i=0        
        path = "home/xilinx/jupyter_notebooks/VideoFlows"  # 设置传输过来的视频流在开发板上的存储路径 
        file_list = []
        for file in file_list:
            file = int(file.split('.')[0])
            file_list.append(file)
        if len(file_list) == 0:
            self.i = 0
        else:
            file_list.sort()
            self.i = file_list[-1] + 1
    def run(self):
        while True: 
            data = self.client.recv(8)  # 此处的接收大小 " 8 " 是跟客户端(电脑)传输的数据包大小对应的
            if not data:
                break
            length, width, height = struct.unpack('ihh', data)  # 图片的长、宽、高
            # 接收二进制形式的图片数据
            imgg = b''  
            while length:
                temp_size = self.client.recv(length)
                length -= len(temp_size)
                imgg += temp_size
            # 还原二进制图片数据成一维数组形式
            data = numpy.fromstring(imgg, dtype='uint8')
            # 最终还原成矩阵形式
            image = cv2.imdecode(data, cv2.IMREAD_UNCHANGED)
            # 存储每帧图片,以10张为一组进行循环存储,编号为 0 到 9
            cv2.imwrite(r"/home/xilinx/jupyter_notebooks/VideoFlows/" + str(self.i) + ".jpg", image)
            self.i = self.i + 1
            if self.i == 10:
                self.i = 0
            # time.sleep(5)


if __name__ == '__main__':
    server = Server()
    server.run()
    

注释已经写得比较详细,这里就不再讲述代码了。


二、客户端代码


自己的笔记本电脑上写客户端代码:

import socket
import struct
import time
import traceback
import cv2
import numpy
import sys


class Client(object):

    def __init__(self, addr_port=('192.168.137.80', 11000)):
        # 连接的服务器的地址和端口号
        self.addr_port = addr_port
        # 创建套接字
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 地址端口可以复用
        self.client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        # 视频的分辨率
        self.resolution = (640, 480)
        
    def connect(self):

        try:
            self.client.connect(self.addr_port)
            return True
            
        except Exception as e:
            traceback.print_exc()  # 打印原始的异常信息
            print('连接失败')
            return False
            
    def send2server(self):
        camera = cv2.VideoCapture(0)  # 获取摄像头
        print('isOpened:', camera.isOpened())
        while camera.isOpened():
            try:
                # 读取摄像头的数据
                ret, frame = camera.read()
                # 对每一帧图片做大小的处理和压缩
                frame = cv2.resize(frame, self.resolution)
                # cv2.imencode(图片后缀名,原图片的数据,图片质量 可选范围为0-100 )
                # 图片质量需合理设置,太大传得慢,太小画面不清晰  
                ret, img = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 20])
                print(img)
                print('-------------')
                print(img.tobytes())
                # 转换为numpy格式数据
                img_code = numpy.array(img)
                # 转为二进制数据
                img = img_code.tobytes()
                # 获取数据长度
                length = len(img)
                # 发送的数据  大小 宽 高 图片数据
                # 数据打包变为二进制
                # pack方法参数1 指定打包数据的数据大小  i 4字节 h代表2字节
                # 【ihh】数据包的大小,i代表4字节,h代表2字节;【length】数据长度;【】后两个为长、宽;【img】图片数据
                all_data = struct.pack('ihh', length, self.resolution[0], self.resolution[1]) + img
                self.client.send(all_data)
                # 传输间隔需合理设置,太小时延太高,太大帧数太低
                time.sleep(0.5)
            except:
                camera.release()  # 释放摄像头
                traceback.print_exc()
                return


if __name__ == '__main__':
    client = Client()
    if client.connect():
        client.send2server()

注释已经写得比较详细,这里就不再讲述代码了。


三、运行


在 MobaXterm 串口终端上运行服务器端代码 server.py :

服务器端
在笔记本电脑上运行客户端代码 main.py ,显示的打印内容如下:

【基于xilinx Zynq7000的PYNQ框架项目】03 Socket实现开发板和电脑的实时视频传输并存储图片_第1张图片
在MobaXterm 上打开自己设置的视频流存放目录(在 server.py 代码里输入的那个路径):

【基于xilinx Zynq7000的PYNQ框架项目】03 Socket实现开发板和电脑的实时视频传输并存储图片_第2张图片
可以看到各图片的最后修改时间会实时发生变化 ,说明成功接收到笔记本电脑的视频流。所谓视频流,其实就是足够多的图片在足够短的时间内播放。


总结


本文章实现了将笔记本摄像头视频流传输到开发板上,开发板接收并存储到本地目录下。下一步就是导入每帧图片进行人脸检测啦~

有不清楚的地方,欢迎大家在评论区交流~

您的关注、点赞与收藏,是我持续创作优质博客的最大动力!

本项目系列文章:

【基于Xilinx ZYNQ7000的PYNQ框架项目】01人脸识别项目介绍与展示

【基于Xilinx Zynq7000的PYNQ框架项目】02 PYNQ镜像制作

【基于Xilinx ZYNQ7000的PYNQ框架项目】04开发板上运行人脸识别模型

【基于Xilinx ZYNQ7000的PYNQ框架项目】05使用Overlay库和python父子进程实现开发板按键控制LED灯流水或熄灭

更多优质博客:小黄能吃辣的CSDN主页


你可能感兴趣的:(嵌入式硬件,单片机,tcp/ip,opencv,ubuntu,课程设计,音视频)