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 11 Python
Python中自定义函数的教程
Apr 27 Python
python处理二进制数据的方法
Jun 03 Python
Python简单实现子网掩码转换的方法
Apr 13 Python
Django自定义用户登录认证示例代码
Jun 30 Python
pytorch构建多模型实例
Jan 15 Python
python导入库的具体方法
Jun 18 Python
python Scrapy爬虫框架的使用
Jan 21 Python
Python爬取你好李焕英豆瓣短评生成词云的示例代码
Feb 24 Python
Django实现翻页的示例代码
May 24 Python
python not运算符的实例用法
Jun 30 Python
分享提高 Python 代码的可读性的技巧
Mar 03 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
十天学会php之第五天
2006/10/09 PHP
Linux下从零开始安装配置Nginx服务器+PHP开发环境
2015/12/21 PHP
php中请求url的五种方法总结
2017/07/13 PHP
php实现的统计字数函数定义与使用示例
2017/07/26 PHP
一个不错的js html页面倒计时可精确到秒
2014/10/22 Javascript
js实现进度条的方法
2015/02/13 Javascript
BootStrap 图标icon符号图标glyphicons不正常显示的快速解决办法
2016/12/08 Javascript
angularjs中回车键触发某一事件的方法
2017/04/24 Javascript
zTree节点文字过多的处理方法
2017/11/24 Javascript
vue-router3.0版本中 router.push 不能刷新页面的问题
2018/05/10 Javascript
解决Layui当中的导航条动态添加后渲染失败的问题
2019/09/25 Javascript
Weex开发之WEEX-EROS开发踩坑(小结)
2019/10/16 Javascript
javascript实现打砖块小游戏(附完整源码)
2020/09/18 Javascript
vue+elementUI动态增加表单项并添加验证的代码详解
2020/12/17 Vue.js
[17:36]VG战队纪录片
2014/08/21 DOTA
[03:45]Newbee战队出征西雅图 决战2016国际邀请赛
2016/08/02 DOTA
Python实现1-9数组形成的结果为100的所有运算式的示例
2017/11/03 Python
使用 Visual Studio Code(VSCode)搭建简单的Python+Django开发环境的方法步骤
2018/12/17 Python
PyQt5实现暗黑风格的计时器
2019/07/29 Python
python已协程方式处理任务实现过程
2019/12/27 Python
使用 PyTorch 实现 MLP 并在 MNIST 数据集上验证方式
2020/01/08 Python
使用 Python 处理3万多条数据只要几秒钟
2020/01/19 Python
python实现word文档批量转成自定义格式的excel文档的思路及实例代码
2020/02/21 Python
Python ArgumentParse的subparser用法说明
2020/04/20 Python
python 输入字符串生成所有有效的IP地址(LeetCode 93号题)
2020/10/15 Python
HTML5实现视频弹幕功能
2019/08/09 HTML / CSS
澳大利亚购买最佳炊具品牌网站:Cookware Brands
2019/02/16 全球购物
美国便宜的横幅和标志印刷在线:Best of Signs
2019/05/29 全球购物
机械设计制造专业个人求职信
2013/09/25 职场文书
运动会广播稿50字
2014/01/26 职场文书
关于迟到的检讨书
2014/01/26 职场文书
2014年应届大学生毕业自我鉴定
2014/01/31 职场文书
个人对照检查材料
2014/02/12 职场文书
助人为乐道德模范事迹材料
2014/08/16 职场文书
行政申诉状范文
2015/05/20 职场文书
vue实现滑动解锁功能
2022/03/03 Vue.js