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 相关文章推荐
CentOS下使用yum安装python-pip失败的完美解决方法
Aug 16 Python
浅谈用Python实现一个大数据搜索引擎
Nov 28 Python
python基础之包的导入和__init__.py的介绍
Jan 08 Python
python pandas dataframe 按列或者按行合并的方法
Apr 12 Python
Python自然语言处理 NLTK 库用法入门教程【经典】
Jun 26 Python
Django安装配置mysql的方法步骤
Oct 15 Python
对DataFrame数据中的重复行,利用groupby累加合并的方法详解
Jan 30 Python
详解python配置虚拟环境
Apr 08 Python
python 随机生成10位数密码的实现代码
Jun 27 Python
python爬虫开发之urllib模块详细使用方法与实例全解
Mar 09 Python
opencv python 图片读取与显示图片窗口未响应问题的解决
Apr 24 Python
将不规则的Python多维数组拉平到一维的方法实现
Jan 11 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
Dedecms V3.1 生成HTML速度的优化办法
2007/03/18 PHP
CodeIgniter安全相关设置汇总
2014/07/03 PHP
PHP开发框架kohana中处理ajax请求的例子
2014/07/14 PHP
PHP开启opcache提升代码性能
2015/04/26 PHP
UTF-8正则表达式如何匹配汉字
2015/08/03 PHP
PHPExcel简单读取excel文件示例
2016/05/26 PHP
thinkPHP5框架实现多数据库连接,跨数据连接查询操作示例
2019/05/29 PHP
一个不错的应用,用于提交获取文章内容,不推荐用
2007/03/03 Javascript
静态图片的十一种滤镜效果--不支持Ie7及非IE浏览器。
2007/03/06 Javascript
JSON 客户端和服务器端的格式转换
2009/08/27 Javascript
基于Jquery实现的一个图片滚动切换
2012/06/21 Javascript
javascript的渐进增强与平稳退化浅谈
2013/11/12 Javascript
多个jquery.datatable共存,checkbox全选异常的快速解决方法
2013/12/10 Javascript
JqueryMobile动态生成listView并实现刷新的两种方法
2014/03/05 Javascript
纯javascript实现简单下拉刷新功能
2015/03/13 Javascript
Bootstrap的modal拖动效果
2016/12/25 Javascript
jQuery Validate 数组 全部验证问题
2017/01/12 Javascript
JS简单获取当前日期和农历日期的方法
2017/04/17 Javascript
javascript中new Array()和var arr=[]用法区别
2017/12/01 Javascript
vue嵌套路由与404重定向实现方法分析
2018/05/04 Javascript
vue删除html内容的标签样式实例
2018/09/13 Javascript
vue-cli2.0转3.0之项目搭建的详细步骤
2018/12/11 Javascript
vue-cli随机生成port源码的方法
2019/09/02 Javascript
[02:02:38]VG vs Mineski Supermajor 败者组 BO3 第一场 6.6
2018/06/07 DOTA
python实现获取序列中最小的几个元素
2014/09/25 Python
Python实现中文数字转换为阿拉伯数字的方法示例
2017/05/26 Python
一行python实现树形结构的方法
2019/08/09 Python
重写django的model下的objects模型管理器方式
2020/05/15 Python
Pyinstaller加密打包应用的示例代码
2020/06/11 Python
Python如何爬取b站热门视频并导入Excel
2020/08/10 Python
Mistine官方海外旗舰店:泰国国民彩妆品牌
2016/12/28 全球购物
意大利会呼吸的鞋:Geox健乐士
2017/02/12 全球购物
波兰多品牌运动商店:StreetStyle24.pl
2020/09/22 全球购物
一个独生女的故事观后感
2015/06/04 职场文书
优秀班干部主要事迹材料
2015/11/04 职场文书
分析SQL窗口函数之排名窗口函数
2022/04/21 Oracle