本文共 3083 字,大约阅读时间需要 10 分钟。
之前尝试过使用UDP进行图像传输,而UDP协议要求包小于64K,对于较大的图像,需要使用分片压缩的方式进行传输,操作较复杂,同时不能保证图片的每一部分都能够正确传输。详见:,
TCP对于传输的数据大小没有限制,同时TCP在发送失败时还有重传机制,可以保证传输的可靠性,所以本文将使用TCP协议来进行图像的实时传输。
TCP连接过程见后面的程序,一般服务端创建一个套接字,绑定本地IP,开启监听,然后客户端也创建一个套接字,连接服务端就可以了,详见后面的代码。直接介绍数据传输流程,如下图:
由于TCP是以字节流的形式发送数据的,不能预知数据的大小,所以客户端在发送图像数据之前,需要先发送数据长度等信息。同时为了防止粘包(服务端接收到的数据会先缓存在缓冲区,在接收一次数据后,如果不及时处理,下一次接收到的数据也会送到缓冲区。由于这些数据都是字节流形式的,这样两次接收到的数据就会黏在一起,无法分开),客户端在发送完数据长度信息后,不能马上发送图像数据,需要等待服务端返回的应答信号。客户端接收到应答信号后,就可以开始发送图像字节流数据了。服务端完成图像数据接收后,还要返回给客户端一个应答信号,通知客户端开始下一帧图像的传输
与前两篇文章不同,本文使用Python来实现主要功能(因为方便)。
#-*- coding: UTF-8 -*- import socketimport cv2import numpy as npHOST = ''PORT = 8080ADDRESS = (HOST, PORT)# 创建一个套接字tcpServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 绑定本地iptcpServer.bind(ADDRESS)# 开始监听tcpServer.listen(5)while True: print("等待连接……") client_socket, client_address = tcpServer.accept() print("连接成功!") try: while True: # 接收标志数据 data = client_socket.recv(1024) if data: # 通知客户端“已收到标志数据,可以发送图像数据” client_socket.send(b"ok") # 处理标志数据 flag = data.decode().split(",") # 图像字节流数据的总长度 total = int(flag[0]) # 接收到的数据计数 cnt = 0 # 存放接收到的数据 img_bytes = b"" while cnt < total: # 当接收到的数据少于数据总长度时,则循环接收图像数据,直到接收完毕 data = client_socket.recv(256000) img_bytes += data cnt += len(data) print("receive:" + str(cnt) + "/" + flag[0]) # 通知客户端“已经接收完毕,可以开始下一帧图像的传输” client_socket.send(b"ok") # 解析接收到的字节流数据,并显示图像 img = np.asarray(bytearray(img_bytes), dtype="uint8") img = cv2.imdecode(img, cv2.IMREAD_COLOR) cv2.imshow("img", img) cv2.waitKey(1) else: print("已断开!") break finally: client_socket.close()
#-*- coding: UTF-8 -*- import cv2import timeimport socket# 服务端ip地址HOST = '192.168.0.100'# 服务端端口号PORT = 8080ADDRESS = (HOST, PORT)# 创建一个套接字tcpClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 连接远程iptcpClient.connect(ADDRESS)cap = cv2.VideoCapture(0)while True: # 计时 start = time.perf_counter() # 读取图像 ref, cv_image = cap.read() # 压缩图像 img_encode = cv2.imencode('.jpg', cv_image, [cv2.IMWRITE_JPEG_QUALITY, 99])[1] # 转换为字节流 bytedata = img_encode.tostring() # 标志数据,包括待发送的字节流长度等数据,用‘,’隔开 flag_data = (str(len(bytedata))).encode() + ",".encode() + " ".encode() tcpClient.send(flag_data) # 接收服务端的应答 data = tcpClient.recv(1024) if ("ok" == data.decode()): # 服务端已经收到标志数据,开始发送图像字节流数据 tcpClient.send(bytedata) # 接收服务端的应答 data = tcpClient.recv(1024) if ("ok" == data.decode()): # 计算发送完成的延时 print("延时:" + str(int((time.perf_counter() - start) * 1000)) + "ms")
注:如果是Python3.7及以下版本,则将time.perf_counter()
改为time.clock()
即可
转载地址:http://nmvgn.baihongyu.com/