大家好,又见面了,我是你们的朋友全栈君。
一、前言
最近想写一个实时的视频传输程序,然后上网找了很久没有找到合适的
我想用OpenCV 进行图像采集,然后用pygame 将视频信号转化为可通过UDP 网络传输的字符流,然后到达终端后再通过pygame 对字符流进行解析,进而将图像显示出来
之所以使用UDP 传输而不是TCP 传输,是因为UDP 在视频传输方面拥有快速、无需连接等优点,适合密集传送大量信息的场合
但UDP 传输有一个问题,就是一次传输量有限,这就直接导致后续传输的视频信号需要进行一定压缩
所以我就开始自己捣鼓了 ?
本文内容若有不懂可查阅如下教程:
Windows 安装pygame 模块
树莓派 与 Xbox360手柄 基于pygame 的一次邂逅
树莓派Camera 的使用
【手把手教学—超简单】树莓派安装OpenCV 3
Python + OpenCV 学习笔记(二)>>> 加载视频流
Python >>> UDP 网络编程
我是用树莓派作为视频采集端,然后笔记本作为信号接收端的
注意:PC端需关闭防火墙后方可成功接收数据!
二、开始
1)整体思路
- 在发送端使用OpenCV 打开摄像头采集视频帧信号;
- 将视频帧信号输出为
jpg
图片文件; - pygame 模块将该
jpg
图片转化为字符流; - 将该字符流通过UDP 传输到接收端;
- 接收端同样使用pygame 对字符流进行解析,最终即可获得图像信息。
2)摄像头采集视频帧
import cv2 as cv
capture = cv.VideoCapture(0)
while True:
# 获得图像帧
ret, frame = capture.read()
# 图像翻转
frame = cv.flip(frame, -1)
# 将图像尺寸缩小,便于传输
frame = cv.resize(frame, (160, 120))
3)将图像帧输出为图片
cv.imwrite("test.jpg", frame)
4)将图片转化为字符流
# 加载图片
Img = pygame.image.load('/home/pi/test.jpg')
# 图片转化字符串
string = pygame.image.tostring(Img, "RGB")
5)通过UDP 传输字符流
我创建了一个进程用于传输数据:
def mythread(sock, data, addr):
sock.sendto(data, addr)
print("已发送 " + str(len(data)) + " bytes")
sock.close()
# socket.SOCK_DGRAM 代表是使用UDP协议
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
t = threading.Thread(target=mythread, args=(sock, string, ('192.168.0.196', 9999)))
t.start()
注意:当发送密集数据时,如图像数据等。需要控制图像大小(例如160*120),不然发不出去。
6)接收端接收数据并解析
# 创建连接
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
s.bind(('192.168.0.196', 9999))
# 接收数据
data, addr = s.recvfrom(60000)
# 字符串再转化为图片
img = pygame.image.frombuffer(data, (180, 160), "RGB")
7)将图像显示出来
# 窗口内设置白色底色和图片,并更新显示
gameDisplay.fill(WHITE)
gameDisplay.blit(img, (0, 0))
pygame.display.update()
三、完整程序
1)发送端
#coding:utf-8
import pygame
import cv2 as cv
from picamera import PiCamera
import time
import threading
import socket
pygame.init()
# 传输视频信号的UDP 连接进程
def mythread(sock, data, addr):
sock.sendto(data, addr)
print("已发送 " + str(len(data)) + " bytes")
sock.close()
def main():
print("begin")
start = time.clock()
capture = cv.VideoCapture(0)
while True:
ret, frame = capture.read()
frame = cv.flip(frame, -1)
frame = cv.resize(frame, (160, 120))
cv.imwrite("test.jpg", frame)
# 加载图片
Img = pygame.image.load('/home/pi/test.jpg')
# 图片转化字符串
string = pygame.image.tostring(Img, "RGB")
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
t = threading.Thread(target=mythread, args=(sock, string, ('192.168.0.196', 9999)))
t.start()
if cv.waitKey(10) & 0xff == ord('q'):
break
main()
cv.destroyAllWindows()
2)接收端
#coding=utf-8
import socket
import time
import threading
import pygame
# 颜色
WHITE = (255,255,255)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 128)
BLACK = ( 0, 0, 0)
# 创建连接
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
s.bind(('192.168.0.196', 9999))
print('Waiting for connection...')
pygame.init()
# 设置标题
pygame.display.set_caption('UDP 视频传输')
# 开启窗口
display_width = 300
display_height = 200
gameDisplay = pygame.display.set_mode((display_width,display_height))
# 判断是否结束pygame
gamefinish = False
# 帧率
frame_rate = 0
# 通过字体文件获得字体对象
fontObj = pygame.font.Font('C:/Windows/Fonts/comici.ttf', 20)
# 配置要显示的文字
textSurfaceObj = fontObj.render('FPS', True, BLACK, WHITE)
# 获得要显示的对象的rect
textRectObj = textSurfaceObj.get_rect()
# 设置显示对象的坐标
textRectObj.center = (200, 10)
# 用于视频显示delay
clock = pygame.time.Clock()
# 用于帧率计算
start = time.perf_counter()
while not gamefinish:
for event in pygame.event.get():
if event.type == pygame.QUIT:
gamefinish = True
print(event)
# 无数据接收则线程挂起,等待数据
data, addr = s.recvfrom(60000)
frame_rate = frame_rate + 1
if time.perf_counter() - start > 1:
start = time.perf_counter()
# 配置要显示的文字
textSurfaceObj = fontObj.render(("FPS: " + str(frame_rate)), True, BLACK, WHITE)
frame_rate = 0
print('Received from %s:%s.' % addr)
# 字符串再转化为图片
img = pygame.image.frombuffer(data, (160, 120), "RGB")
# 窗口内设置白色底色和图片,并更新显示
gameDisplay.fill(WHITE)
gameDisplay.blit(img, (0, 0))
gameDisplay.blit(textSurfaceObj, textRectObj)
pygame.display.update()
clock.tick(60)
pygame.quit()
四、结果
在实际测试中,我发现160*120
的分辨率对网络要求较高,而当分辨率降为80*60
时则效果好一点(帧率基本在30左右):
当然,这样的话视频窗口显得很小。
我们可以在接收端对图像进行放大:
(成像质量下降)
# 指定size 放大
img = pygame.transform.scale(img, (160, 120))
# 直接放大两倍
img = pygame.transform.scale2x(img)
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/128978.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...