Python树莓派学习笔记之UDP传输视频帧操作详解


Posted in Python onNovember 15, 2019

本文实例讲述了Python树莓派学习笔记之UDP传输视频帧操作。分享给大家供大家参考,具体如下:

因为我在自己笔记本电脑上没能成功安装OpenCV-Contrib模块,因此不能使用人脸识别等高级功能,不过已经在树莓派上安装成功了,所以我想实现把树莓派上采集的视频帧传输到PC的功能,这样可以省去给树莓派配显示屏的麻烦,而且以后可能可以用在远程监控上。

1 UDP还是TCP

首先考虑用哪种传输方式,平常TCP用的非常多,但是像视频帧这种数据用TCP不是太合适,因为视频数据的传输最先要考虑的是速度而不是准确性,视频帧的数据量很大,帧间隔也非常短,需要尽量保证传输速度,同时丢失一些数据是无所谓的。TCP需要维护连接、保证数据包正确,会耗费一些时间,因此应该使用UDP,就像所有参考书上说的,UDP不在乎是否建立连接,也不管数据是否能被准确接收,只关心能否把数据发送出去而已。

在Python的socket代码中也可直观地看到UDP的特点,对于发送方,我们通过server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)创建UDP套接字对象,然后执行server.connect((HOST,PORT)) 指定发送方地址,但其实connect函数直接就返回了,不像TCP中的客户端会等待连接成功,接着就可直接在套接字对象上调用send函数发送数据了,这个过程根本没确立连接。

2 图像传输中的编解码

但是用UDP传输图像有一个很关键的问题需要考虑,就是图像的大小。根据UDP协议,单个包的数据大小最大只能65507个字节(去掉包头),而一般直接从摄像头采集的图像帧的大小比这个数要大得多,以我的逻辑C270为例,单幅图像的大小为480X640X3个字节,远大于65507,因此一个包是没法发送完的。解决方法有两种,一种是把图像拆成几次进行发送,相应的接收端用个循环多次接收,这种方法可以完整地接收数据,但是速度肯定受到影响,而且可能要添加一些自定义规则,徒增麻烦;另一种方法就是发送前先对图像进行编码压缩,接收后再解码,清晰度会有所下降,但是可以保持速度上的优势,这种方式比较合适。

OpenCV中的imencode和imdecode方法可分别用于图像的编码和解码。imencode根据指定的标识将图像数据编码并存入缓存区,函数原型为cv2.imencode(ext, img[, params]) → retval, buf,ext为文件扩展名,指定了存储格式,如'.jpg';img为需要编码的图像数据; params为指定的编码标识,其形式为paramId_1, paramValue_1, paramId_2, paramValue_2, ... ,对于jpg格式,可以指定标识为CV_IMWRITE_JPEG_QUALITY ,其对应的值在0到100之间,表示了压缩质量,值越大压缩率越大,编码后的数据量越小,但解码后的图像质量也越差。

imdecode从缓存区读取图像数据,通过指定标识,可以实现指定的解码格式。imdecode的函数原型为cv2.imdecode(buf, flags) → retval ,其中flags指定图像的读取类型,实际上就是指定了以多少深度多少通道读取图像,比如CV_LOAD_IMAGE_ANYDEPTH(即整数2)表示单个通道,深度不变的灰度图;CV_LOAD_IMAGE_COLOR(即整数1)表示3通道、8位深度的彩色图。

3 树莓派程序

结合套接字对象和编解码函数,就可以编写发送端的代码了,不过还有一个需要注意的地方是发送和接收的数据格式问题,套接字的发送和接收都是字节流,或者说是byte数组,发送数据时需要以字节流格式发送,接收数据后需要把字节流类型转换成合适的数据类型。

从摄像头获取的图像是480X640X3的numpy.ndarray类型,通过imencode编码,得到?X1的numpy.ndarray对象,经测试,这个对象可以直接发送出去;在接收端,获得的是byte数组,这个数组直接做imdecode的参数会报错,经调试,发现还需要把数组转换成numpy.ndarray类型。树莓派作为发送端,其Python代码如下:

import cv2
import numpy
import socket
import struct
HOST='192.168.1.122'
PORT=9999
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #socket对象
server.connect((HOST,PORT))
print('now starting to send frames...')
capture=cv2.VideoCapture(0) #VideoCapture对象,可获取摄像头设备的数据
try:
  while True:
    success,frame=capture.read()
    while not success and frame is None:
      success,frame=capture.read() #获取视频帧
  result,imgencode=cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,50]) #编码
  server.sendall(struct.pack('i',imgencode.shape[0])) #发送编码后的字节长度,这个值不是固定的
  server.sendall(imgencode) #发送视频帧数据
  print('have sent one frame')
except Exception as e:
  print(e)
  server.sendall(struct.pack('c',1)) #发送关闭消息
  capture.release()
  server.close()

在代码中,首先把编码后的字节长度发送了过去,目的是让接收端可以进行简单的校验,并且接收端可以据此判断是否应该关闭程序,相应的,自定义单字节的1为关闭消息。

4 PC端程序

自己的电脑作为接收端,为了解码数据,需要把原始字节流转成numpy.ndarray对象,代码如下:

import cv2
import numpy
import socket
import struct
HOST='192.168.191.122'
PORT=9999
buffSize=65535
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #创建socket对象
server.bind((HOST,PORT))
print('now waiting for frames...')
while True:
  data,address=server.recvfrom(buffSize) #先接收的是字节长度
  if len(data)==1 and data[0]==1: #如果收到关闭消息则停止程序
    server.close()
    cv2.destroyAllWindows()
    exit()
  if len(data)!=4: #进行简单的校验,长度值是int类型,占四个字节
    length=0
  else:
    length=struct.unpack('i',data)[0] #长度值
  data,address=server.recvfrom(buffSize) #接收编码图像数据
  if length!=len(data): #进行简单的校验
    continue
  data=numpy.array(bytearray(data)) #格式转换
  imgdecode=cv2.imdecode(data,1) #解码
  print('have received one frame')
  cv2.imshow('frames',imgdecode) #窗口显示
  if cv2.waitKey(1)==27: #按下“ESC”退出
    break
server.close()
cv2.destroyAllWindows()

5 测试

因为我树莓派上的OpenCV只关联了Python2,因此以python2 UDP_Frame_Send.py 的命令启动发送程序(接好摄像头);电脑上,在开始菜单中输入cmd进入Windows的控制台,进入程序文件目录,输入python UDP_Frame_Recv.py启动接收程序,结果表明可以比较流畅地窗口显示,不过有几个问题,一是在树莓派上,程序有时候打不开摄像头,需要重启几次程序,二是在电脑上,recvfrom这个函数是阻塞式的,在Windows系统的控制台中似乎没办法用键盘中断强制从这个函数退出,所以如果发送端出错接收端的程序就没法正常退出了,对此可以用TCP&UDP调试助手手动发送单个字节的1来终止程序。

更多关于Python相关内容可查看本站专题:《Python Socket编程技巧总结》、《Python数据结构与算法教程》、《Python函数使用技巧总结》、《Python字符串操作技巧汇总》、《Python入门与进阶经典教程》及《Python文件与目录操作技巧汇总》

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python的条件语句与运算符优先级详解
Oct 13 Python
详解Python编程中基本的数学计算使用
Feb 04 Python
windows下ipython的安装与使用详解
Oct 20 Python
Python实现拷贝/删除文件夹的方法详解
Aug 29 Python
[原创]Python入门教程5. 字典基本操作【定义、运算、常用函数】
Nov 01 Python
python定间隔取点(np.linspace)的实现
Nov 27 Python
python实现图片二值化及灰度处理方式
Dec 07 Python
python中的逆序遍历实例
Dec 25 Python
Python 解码Base64 得到码流格式文本实例
Jan 09 Python
安装python3.7编译器后如何正确安装opnecv的方法详解
Jun 16 Python
Pytorch 如何实现LSTM时间序列预测
May 17 Python
关于Python使用turtle库画任意图的问题
Apr 01 Python
Python numpy数组转置与轴变换
Nov 15 #Python
python修改文件内容的3种方法详解
Nov 15 #Python
Python实现基于socket的udp传输与接收功能详解
Nov 15 #Python
python根据文本生成词云图代码实例
Nov 15 #Python
解决django后台管理界面添加中文内容乱码问题
Nov 15 #Python
python中的TCP(传输控制协议)用法实例分析
Nov 15 #Python
Django admin禁用编辑链接和添加删除操作详解
Nov 15 #Python
You might like
PHP4中session登录页面的应用
2008/07/25 PHP
php 数学运算验证码实现代码
2009/10/11 PHP
PHP中遍历stdclass object的实现代码
2011/06/09 PHP
for循环连续求和、九九乘法表代码
2012/02/20 PHP
让ThinkPHP支持大小写url地址访问的方法
2014/10/31 PHP
PHP未登录自动跳转到登录页面
2016/12/21 PHP
如何实现JS函数的重载
2006/09/22 Javascript
JavaScript Object的extend是一个常用的功能
2009/12/02 Javascript
js,jquery滚动/跳转页面到指定位置的实现思路
2014/06/03 Javascript
js遍历子节点子元素附属性及方法
2014/08/19 Javascript
IE浏览器下PNG相关功能
2015/07/05 Javascript
纯javascript判断查询日期是否为有效日期
2015/08/24 Javascript
jQuery操作DOM_动力节点Java学院整理
2017/07/04 jQuery
Vue利用路由钩子token过期后跳转到登录页的实例
2017/10/26 Javascript
js form表单input框限制20个字符,10个汉字代码实例
2019/04/12 Javascript
解决layer弹出层msg的文字不显示的问题
2019/09/11 Javascript
vue自动添加浏览器兼容前后缀操作
2020/08/13 Javascript
VUE前端从后台请求过来的数据进行转换数据结构操作
2020/11/11 Javascript
Python批量按比例缩小图片脚本分享
2015/05/21 Python
对python-3-print重定向输出的几种方法总结
2018/05/11 Python
详解Django-restframework 之频率源码分析
2019/02/27 Python
python实现图片九宫格分割
2021/03/07 Python
Python for循环与getitem的关系详解
2020/01/02 Python
使用Python和百度语音识别生成视频字幕的实现
2020/04/09 Python
用python对excel查重
2020/12/07 Python
HTML5重塑Web世界它将如何改变互联网
2012/12/17 HTML / CSS
Chicco婴儿用品美国官网:汽车座椅、婴儿推车、高脚椅等
2018/11/05 全球购物
德国BA保镖药房中文网:Bodyguard Apotheke
2021/03/09 全球购物
球队口号
2014/06/18 职场文书
总经理助理岗位职责范本
2014/07/20 职场文书
内勤岗位职责
2015/02/10 职场文书
大学生求职信怎么写
2015/03/19 职场文书
2015年秋学期师德师风建设工作总结
2015/10/23 职场文书
小学英语听课心得体会
2016/01/14 职场文书
低端且暴利的线上线下创业项目分享
2019/09/03 职场文书
python实现A*寻路算法
2021/06/13 Python