如何在Qt5中通过OpenCV视频捕获获取树莓派摄像头的最新帧
树莓派CSI摄像头OpenCV读取旧帧问题解决方案
根因
树莓派CSI摄像头默认使用的V4L2驱动底层会预存2-3帧数据到内核缓冲队列,OpenCV默认的VideoCapture逻辑会优先读取队列中的旧帧,和USB摄像头的驱动缓冲逻辑存在差异,仅设置CAP_PROP_BUFFERSIZE为1无法完全清空驱动层预存的旧帧。
修复步骤
- 打开VideoCapture时主动指定V4L2后端,避免OpenCV自动匹配到GStreamer等其他后端导致缓冲参数不生效
- 打开成功后第一时间设置缓冲区大小为1,不要先执行read操作
- 设置缓冲完成后连续读取3次帧并丢弃,清空驱动层预存的旧帧
- 实时性要求极高的场景,每次获取最新帧前都先连续丢弃2帧,再读取的就是当前最新帧
修改后完整代码
#include <opencv2/videoio.hpp> // 其他项目依赖头文件 QScopedPointer<cv::VideoCapture> videoCapture_; cv::Mat CaptureImage; // 摄像头初始化逻辑 videoCapture_.reset(new cv::VideoCapture(0, cv::CAP_V4L2)); // 明确指定V4L2后端 if (videoCapture_->isOpened()) { // 打开后第一时间设置缓冲区大小为1 videoCapture_->set(cv::CAP_PROP_BUFFERSIZE, 1); // 可按需设置摄像头帧率,根据硬件实际支持参数调整 videoCapture_->set(cv::CAP_PROP_FPS, 30); // 清空驱动层预存的旧帧 cv::Mat temp; for(int i = 0; i < 3; i++) { videoCapture_->read(temp); } // 此时读取到的即为最新帧 videoCapture_->read(CaptureImage); } // 循环取帧场景下的实时读帧逻辑 void getLatestFrame() { // 每次取最新帧前先丢2帧清空缓冲 cv::Mat temp; for(int i = 0; i < 2; i++) { videoCapture_->read(temp); } videoCapture_->read(CaptureImage); // 后续帧处理逻辑 }
注意:树莓派Bullseye及以上版本系统默认使用libcamera驱动,需要先执行
sudo raspi-config,在Interface Options中开启Legacy Camera支持并重启,否则V4L2后端无法正常识别CSI摄像头。
内容的提问来源于stack exchange,提问作者G Balaji
相关产品推荐
相关产品推荐

