往期文章:
公众号:
【以下是本期正文】:
一、问题描述

我们使用Python和OpenCV可以对USB摄像头进行非常便捷的编程,在之前的文章种我们介绍过用几行代码就可以完成对摄像头的拉流和展示:
cap = cv2.VideoCapture(0) # 打开默认摄像头while True:ret, frame = cap.read() # 读取一帧视频if not ret:print("Failed to grab frame")break#可对视频帧进行图像算法处理cv2.imshow('Video Stream', frame) #显示帧# 按'q'键退出循环if cv2.waitKey(1) & 0xFF == ord('q'):break# 清理资源cap.release()cv2.destroyAllWindows()
但在实际使用中我们有时会碰到如下的问题:
同一个USB摄像头,在Windows下使用相机应用打开是1920*1080的清晰大图:

但Linux下使用上述代码得到可能是640*480甚至是更小尺寸的画面模糊的图:

取图质量的差异会给我们的功能实现带来问题,那么为什么会出现这种情况呢?该怎么解决呢?
二、问题原因
出现这种差异的根本原因在于操作系统对UVC摄像头的默认视频格式协商策略不同。
在Windows系统中,通常由厂商驱动(如Logitech Capture、Microsoft WDM)默认启用MJPEG或H.264压缩格式,这些格式在高分辨率下占用带宽更低,可稳定支持1920×1080@30fps。
在Linux系统中,内核的uvcvideo驱动默认优先选择未压缩的YUYV格式,该格式在1920×1080分辨率下需要高达约622 MB/s的USB带宽,远超普通USB 2.0接口(理论上限480 Mbps)的承载能力,因此驱动自动降级至640×480以保证流式传输稳定。
三、解决方案
我们可以通过强制设置为MJPEG格式来改变获得视频帧的画质。在Linux下,通过v4l2-ctl工具手动指定像素格式为MJPG并设置分辨率,即可获得与Windows一致的高清输出。
具体的操作步骤:
1、确认摄像头设备节点
使用shell指令:
ls /dev/video*通常为/dev/video0 或 /dev/video1。
2、查看摄像头支持的格式与分辨率
使用shell指令:
v4l2-ctl --device /dev/video0 --list-formats-ext输出中应包含类似如下条目(关键字段):
Index : 1Type : Video CapturePixel Format: 'MJPG' (compressed)Name : Motion-JPEGSize: Discrete 1920x1080Interval: Discrete 0.033s (30.000 fps)
3、查看格式为支持的高分辨率图像
使用如下shell指令设置分辨率为支持的1920×1080,格式为MJPG:
v4l2-ctl --device /dev/video0 --set-fmt-video=width=1920,height=1080,pixelformat=MJPG4、验证设置是否生效
使用shell指令:
v4l2-ctl --device /dev/video0 --get-fmt-video输出应显示:
Format Video Capture:Width/Height: 1920/1080Pixel Format: 'MJPG'
5、在代码中使用需显式指定后端和格式
在实际代码中若使用Python + OpenCV,需显式指定后端和格式:
import cv2 as cvcap = cv.VideoCapture(0, cv.CAP_V4L2)cap.set(cv.CAP_PROP_FRAME_WIDTH, 1920)cap.set(cv.CAP_PROP_FRAME_HEIGHT, 1080)cap.set(cv.CAP_PROP_FOURCC, cv.VideoWriter_fourcc('M', 'J', 'P', 'G'))while cap.isOpened():ret, frame = cap.read()cv.imshow("video", frame)if cv.waitKey(1) & 0xFF == ord('q'): # 按下 'q' 键退出循环breaktime.sleep(0.04)cap.release() # 关闭摄像头cv.destroyAllWindows() # 销毁窗口
