这个星期,我在 linux 环境驱动一个 usb 连接的 4K 800 万像素的摄像头,图像处理的代码倒是挺简单的,就是打开摄像头获取无损的帧图像比较费劲。
当然,开始之前我使用过 v4l2-ctl
命令查过最高支持的分辨率就是 3264 x 2448
,
v4l2-ctl --list-formats-ext -d /dev/video0
所以为了获得最高分辨率的帧图像,我就将帧的尺寸设置为了上面的比例数值。
首先,我是使用 opencv 去加载摄像头的,正常的代码也就是下面几行:
import cv2
capture = cv2.VideoCapture(-1)
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 3264)
capture.set(cv2.CAP_PROP_FRAME_Height, 2448)
while True:
is_success, frame = capture.read()
if is_success:
cv2.namedWindow('frame', cv2.WINDOW_NORMAL)
cv2.imshow('frame', frame)
capture.release()
cv2.destroyAllWindows()
结果出现了下面的 timeout 的错误:
global cap_v4l.cpp:1119 tryIoctl VIDEOIO(V4L2:/dev/video0): select() timeout
权宜之计:MJPG 编码
这时候我去网上寻找解决方案,如果是将编码的格式设定成 MJPG 的话,就是加上下面这句话:
capture.set(6, cv2.VideoWriter.fourcc('M', 'J', 'P', 'G'))
就可以正常的捕获帧,输出视频了,但是这已经是经过压缩的图像,我想要的是无损的 YUYV 格式的图像,这就很难受了。我试过将分辨率降低,降到某个程度,确实也是能正常捕获的。
但是我的目的是:高分辨率无损 YUYV 格式的帧图像
另辟蹊径:ffplay 测试
我也尝试使用过下面语句来直接调用 video4linux2 捕获视频:
ffplay -f video4linux2 -input_format mjpeg -framerate 30 -video_size 3264*1836 /dev/video0
ffplay -f video4linux2 -input_format yuyv422 -video_size 2564*1836 /dev/video0
ffplay -f video4linux2 -input_format yuyv422 -video_size 3264*1836 /dev/video0
发现,第一和第二句是可以正常捕获的,但是第三句还是不行,应该就是 v4l2 的问题了。
amcap
百思不得其解,没有理由啊,为什么会出现 timeout 的,然后就去问厂家,厂家给我一个 amcap.exe 文件,我在 window 上使用了其中的拍照功能,虽然可能要等个 1 秒左右,但是人家确实给我输出了一张无损的 YUYV 格式的图片。
额,但是这什么也说明不了啊,opencv 的在 linux 和 window 和摄像头的交互接口完全不一样,linux 是通过 video4linux2, 而 window 是通过 DirectShow,我感觉一顿操作下来,一无所获。
唯一知道的是,问题估计出在 v4l2 上面,要么就是缓存区太小了,要么就是 usb 接口的带宽太小了,要么就是 timeout 的时间设置得太短了。
总结
感觉现在也没啥特别好的解决方案,如果不要无损图片的话,编码成 MJPG 也不失为一种方法,起码帧图像可以正常的采集,分辨率也能够满足要求,就是经过了压缩。可能后面会按照 v4l2 的流程,自己写一段 c++ 的代码来捕获帧图像吧。