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黑魔法Descriptor描述符的实例解析
Jun 02 Python
windows上安装Anaconda和python的教程详解
Mar 28 Python
Python简单实现的代理服务器端口映射功能示例
Apr 08 Python
Python FTP两个文件夹间的同步实例代码
May 25 Python
使用 Visual Studio Code(VSCode)搭建简单的Python+Django开发环境的方法步骤
Dec 17 Python
把django中admin后台界面的英文修改为中文显示的方法
Jul 26 Python
python web框架 django wsgi原理解析
Aug 20 Python
Python循环实现n的全排列功能
Sep 16 Python
win10环境下配置vscode python开发环境的教程详解
Oct 16 Python
python将数组n等分的实例
Dec 02 Python
python中安装django模块的方法
Mar 12 Python
Python实战之疫苗研发情况可视化
May 18 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根据IP判断地区名信息的示例代码
2014/03/03 PHP
php实现RSA加密类实例
2015/03/26 PHP
PHPExcel简单读取excel文件示例
2016/05/26 PHP
Yii框架表单提交验证功能分析
2017/01/07 PHP
javascript编程起步(第二课)
2007/02/27 Javascript
防止网站内容被拷贝的一些方法与优缺点好处与坏处分析
2007/11/30 Javascript
利用jQuery的$.event.fix函数统一浏览器event事件处理
2009/12/21 Javascript
jQuery+jqmodal弹出窗口实现代码分明
2010/06/14 Javascript
浅析XMLHttpRequest的缓存问题
2013/12/13 Javascript
jquery实现九宫格大转盘抽奖
2015/11/13 Javascript
三种Node.js写文件的方式
2016/03/08 Javascript
AngularJS基础 ng-show 指令简单示例
2016/08/03 Javascript
BootStrap整体框架之基础布局组件
2016/12/15 Javascript
详解nodejs通过响应回写的方式渲染页面资源
2018/04/07 NodeJs
VUE.js实现动态设置输入框disabled属性
2019/10/28 Javascript
稍微学一下Vue的数据响应式(Vue2及Vue3区别)
2019/11/21 Javascript
js屏蔽F12审查元素,禁止修改页面代码等实现代码
2020/10/02 Javascript
Python实现抢购IPhone手机
2018/02/07 Python
举例讲解Python常用模块
2019/03/08 Python
Python使用py2neo操作图数据库neo4j的方法详解
2020/01/13 Python
Python selenium自动化测试模型图解
2020/04/15 Python
Python如何爬取b站热门视频并导入Excel
2020/08/10 Python
Joules美国官网:出色的英国风格
2017/10/30 全球购物
Dune London官网:英国著名奢华鞋履品牌
2017/11/30 全球购物
迪士尼英国官方商店:shopDisney UK
2019/09/21 全球购物
你懂得怎么写自荐信吗?
2013/12/27 职场文书
应届毕业生个人求职自荐信
2014/01/06 职场文书
家长会标语
2014/06/24 职场文书
我们的节日春节活动方案
2014/08/22 职场文书
个人查摆剖析材料
2014/10/16 职场文书
钢琴师观后感
2015/06/12 职场文书
标枪加油稿
2015/07/22 职场文书
《正比例》教学反思
2016/02/23 职场文书
Python list去重且保持原顺序不变的方法
2021/04/03 Python
TensorFlow中tf.batch_matmul()的用法
2021/06/02 Python
python自动化操作之动态验证码、滑动验证码的降噪和识别
2021/08/30 Python