一. 前言
本文主要实现了远程开发板摄像头画面实时传输回本地电脑进而达到视频监控功能。主要分为开发板客户端和电脑服务端的两部分代码讲解。
本文使用的是米尔的Remi Pi开发板,摄像头是米尔配套的MY-CAM003M,开发板Python环境为3.8,电脑Python3.9
效果展示如下:
电脑端画面显示
OpenCV远程监控
按下k键图片保存在本地
二. 代码展示
1. 开发板客户端代码
import socket
import struct
import time
import traceback
import cv2
import numpy as np
class Client(object):
"""客户端"""
def __init__(self, addr_port=('192.168.43.242', 11000)):
# 连接的服务器的地址
self.addr_port = addr_port
# 创建套接字
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 地址端口可以复用
self.client.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 分辨率
self.resolution = (320, 240)
def connect(self):
"""链接服务器"""
try:
self.client.connect(self.addr_port)
print('连接服务器成功')
return True
except Exception as e:
traceback.print_exc() # 打印原始的异常信息
print('连接失败')
return False
def send2server(self, fps=60):
"""读摄像头数据 发送给服务器"""
camera = cv2.VideoCapture('/dev/video0') # 使用OpenCV原生的VideoCapture打开摄像头
print('isOpened:', camera.isOpened())
if not camera.isOpened():
print("无法打开摄像头")
return
# Calculate the time to sleep between frames to achieve the desired FPS
delay = 1.0 / fps
while camera.isOpened():
try:
start_time = time.time()
# Capture frame-by-frame
ret, frame = camera.read()
if not ret:
print("无法读取摄像头帧")
break
# Resize and compress the frame
frame = cv2.resize(frame, self.resolution)
ret, img = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 50]) # Reduced quality for faster transmission
# Convert to numpy array and then to binary data
img_code = np.array(img)
img = img_code.tobytes()
# Get the data length
length = len(img)
# Pack the data and send it
all_data = struct.pack('ihh', length, self.resolution[0], self.resolution[1]) + img
self.client.send(all_data)
# Calculate the processing time and sleep to maintain the frame rate
elapsed_time = time.time() - start_time
if elapsed_time < delay:
time.sleep(delay - elapsed_time)
except Exception as e:
camera.release() # 释放摄像头
traceback.print_exc()
break
camera.release()
if __name__ == '__main__':
client = Client()
if client.connect():
client.send2server(fps=20) # Set the desired FPS (e.g., 20 FPS)
(1)自己设置要连接的服务端ip和port
addr_port=('192.168.43.242', 11000)
(2)自己设置合适的分辨率(我这里和摄像头初始化文件v4l2-init.h保持一致)
self.resolution = (320, 240)
(3)使用cv2.VideoCapture函数打开摄像头设备,预先查看自己摄像头节点
camera = cv2.VideoCapture('/dev/video0')
(4)自行调整画质
ret, img = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 50])
(5)自行设置分辨率
client.send2server(fps=20)
以上关键的参数直接影响了画面的清晰度以及流畅度,请大家根据自己的设备仔细调整,往往需要不断tradeoff才能达到最佳的效果
1.帧率(Frame Rate)
- 定义:帧率是指每秒钟显示的图像帧数,通常以FPS(Frames Per Second)为单位。
- 清晰度的影响:帧率对图像的清晰度影响较小,主要影响的是运动中的画面质量。在低帧率下,快速移动的物体可能会产生运动模糊或跳帧现象。
- 流畅度的影响:帧率直接影响视频的流畅度。较高的帧率(如30FPS或60FPS)可以提供更流畅的视觉体验,使得视频播放时的动作更自然顺滑。较低的帧率(如15FPS或更低)会导致视频显得卡顿或不连贯。
2. 分辨率(Resolution)
- 定义:分辨率是指图像的像素宽度和高度,常见的分辨率有640x480(VGA)、1280x720(HD)、1920x1080(Full HD)等。
- 清晰度的影响:分辨率对图像的清晰度有直接影响。较高的分辨率意味着更多的像素数,可以呈现更细腻的画面细节。因此,分辨率越高,图像的清晰度就越高。
- 流畅度的影响:较高的分辨率需要更多的计算资源进行处理和传输,可能会影响视频的流畅度。如果硬件或网络带宽不足,高分辨率视频可能会导致播放时卡顿或延迟。
3. 画质(Quality)
- 定义:画质通常与图像压缩程度和编码质量相关,影响图像的细节、颜色和整体视觉效果。
- 清晰度的影响:较高的画质意味着更少的压缩损失,可以保留更多的图像细节和真实感。例如,在JPEG压缩中,较低的压缩率(高画质)会使图像更清晰,而较高的压缩率(低画质)会导致图像模糊、出现伪影等问题。
- 流畅度的影响:较高的画质通常意味着更大的数据量,这可能会增加传输和处理的负担,进而影响视频的流畅度。在低带宽或低性能设备上,高画质的视频可能会导致播放不流畅。
除此之外,硬件性能和网络带宽也是影响视频传输的重要因素。
2. 电脑服务端代码
import socket
import struct
import threading
import traceback
import cv2
import numpy as np
import os
class Server:
def __init__(self):
# 设置tcp服务端的socket
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置重复使用
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定地址和端口
self.server.bind(('192.168.43.242', 11000))
# 设置被动监听
self.server.listen(128)
def run(self):
while True:
print('等待客户端连接')
# 等待客户端连接
client, addr = self.server.accept()
print(f"客户端已连接: {addr}")
ProcessClient(client).start()
class ProcessClient(threading.Thread):
def __init__(self, client):
super().__init__()
self.client = client
# 初始化图片保存的编号
self.i = 0
path = "E:/sample"
os.makedirs(path, exist_ok=True) # 如果文件夹不存在,创建它
# 获取现有图片的最大序号
file_list = [int(f.split('.')[0]) for f in os.listdir(path) if f.endswith('.jpg')]
if file_list:
self.i = max(file_list) + 1
def run(self):
while True:
try:
# 接收图片的长度和分辨率
data = self.client.recv(8)
if not data:
break
length, width, height = struct.unpack('ihh', data)
# 接收完整的图像数据
img_data = b'' # 存放最终的图片数据
while length > 0:
temp_data = self.client.recv(length)
if not temp_data:
break
img_data += temp_data
length -= len(temp_data)
# 把二进制数据还原
img_array = np.frombuffer(img_data, dtype='uint8')
# 还原成图像
image = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
if image is None:
print("解码失败,接收到的图像数据可能有误。")
continue
# 显示图像
cv2.imshow('capture', image)
# 保存图像
k = cv2.waitKey(1)
if k == ord('k'):
cv2.imwrite(f"E:/sample/{self.i}.jpg", image) # 存储路径
print(f"保存图片: {self.i}.jpg")
self.i += 1
if k == ord('q'):
break
except Exception as e:
print(f"发生错误: {e}")
traceback.print_exc()
break
# 断开连接时关闭窗口
self.client.close()
cv2.destroyAllWindows()
if __name__ == '__main__':
server = Server()
server.run()
一些参数解释同上,同时按下k键可以将图片保存到指定路径,按下q键退出。
需要注意的是,需要先运行电脑服务端的程序等待连接,再运行开发板程序,顺序不能颠倒!
三. 后序
本次尝试是笔者第一次使用OpenCV实现视频传输的功能,难免有疏忽理解不到位之处欢迎大家交流指正,在查资料的过程中主要参考了下面这位老哥的文章
https://blog.csdn.net/weixin_47460769/article/details/116300354
除此之外还看到了几篇写的不错的文章放在下面
https://blog.csdn.net/rosen_er/article/details/119716012
https://gitee.com/indecisive/Linux_QT