OpenCV VideoCapture读取图像序列后,复制帧显示异常如何解决?
问题分析:OpenCV手动复制图像帧到新Mat时显示异常
我尝试使用OpenCV的VideoCapture类从指定文件夹中顺序读取图像,图像读取功能正常,可正确查看读取图像(imgSrc)的视频流。但当我通过嵌套for循环将每一帧复制到新的Mat对象(imgDst)时,新图像与原图像不符,显示出现异常。请问我在操作中存在哪些错误导致了该问题?
原始代码
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv; using namespace std; void help(char** argv) { cout << "\nThis program gets you started reading a sequence of images using cv::VideoCapture.\n" << "Image sequences are a common way to distribute video data sets for computer vision.\n" << "Usage: " << argv[0] << " <path to the first image in the sequence>\n" << "example: " << argv[0] << " right%%02d.jpg\n" << "q,Q,esc -- quit\n" << "\tThis is a starter sample, to get you up and going in a copy paste fashion\n" << endl; } int main(int argc, char** argv) { if(argc != 2) { help(argv); return 1; } string arg = argv[1]; VideoCapture sequence(arg); if (!sequence.isOpened()) { cerr << "Failed to open Image Sequence!\n" << endl; return 1; } Mat imgSrc; // source image for(;;) { sequence >> imgSrc; if(imgSrc.empty()) { cout << "End of Sequence" << endl; break; } Mat imgDst = cv::Mat::zeros(cv::Size(imgSrc.size().width,imgSrc.size().height),CV_16U); // Copying the elements of imgSrc to imgDst uint16_t* imgSrcPtr; uint16_t* imgDstPtr; for(int i = 0; i < imgSrc.rows; i++) { imgDstPtr = imgDst.ptr<uint16_t>(i); imgSrcPtr = imgSrc.ptr<uint16_t>(i); for(int j= 0; j < imgSrc.cols; j++) imgDstPtr[j] = imgSrcPtr[j]; } namedWindow("Source image ",WINDOW_AUTOSIZE ); namedWindow("Destination image ",WINDOW_AUTOSIZE ); imshow("Destination image ", imgDst); waitKey(0; return 0; } }
核心错误分析
1. 图像数据类型不匹配(最关键原因)
从VideoCapture读取的图像imgSrc默认是8位无符号整数类型(CV_8U),但你创建的imgDst是16位无符号整数类型(CV_16U)。
你用uint16_t*指针去访问imgSrc的内存,会导致严重的内存读取错误:
- CV_8U每个像素占1字节,而uint16_t是2字节,所以
imgSrcPtr[j]会一次性读取imgSrc中两个相邻的像素字节,将其拼接成一个16位值,完全扭曲了原始图像数据。 - 这种类型不匹配会直接导致
imgDst的像素值和原图像毫无关联,显示自然异常。
2. 程序提前终止,无法处理多帧
你的waitKey(0)和return 0写在了帧处理的循环内部:
waitKey(0)会阻塞程序直到你按下任意键,但紧接着的return 0会直接结束整个程序,导致你只能看到第一帧的错误结果,根本不会处理后续图像序列。- 另外
waitKey(0)后面还少了一个闭合括号,属于语法错误,编译时应该会报错。
3. 不必要的重复窗口创建
你在每一次帧循环里都调用namedWindow创建窗口,虽然OpenCV会自动处理重复创建的情况,但这是冗余操作,应该把窗口创建放在循环之前,只执行一次。
4. 手动指针复制的风险
手动操作指针进行像素复制容易出错,比如类型不匹配、内存越界等,OpenCV本身提供了更安全高效的复制方法,没必要手动写嵌套循环。
修正后的代码示例
下面是修复所有问题后的代码:
#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> using namespace cv; using namespace std; void help(char** argv) { cout << "\nThis program gets you started reading a sequence of images using cv::VideoCapture.\n" << "Image sequences are a common way to distribute video data sets for computer vision.\n" << "Usage: " << argv[0] << " <path to the first image in the sequence>\n" << "example: " << argv[0] << " right%%02d.jpg\n" << "q,Q,esc -- quit\n" << "\tThis is a starter sample, to get you up and going in a copy paste fashion\n" << endl; } int main(int argc, char** argv) { if(argc != 2) { help(argv); return 1; } string arg = argv[1]; VideoCapture sequence(arg); if (!sequence.isOpened()) { cerr << "Failed to open Image Sequence!\n" << endl; return 1; } // 提前创建窗口,只执行一次 namedWindow("Source image ", WINDOW_AUTOSIZE); namedWindow("Destination image ", WINDOW_AUTOSIZE); Mat imgSrc; // source image for(;;) { sequence >> imgSrc; if(imgSrc.empty()) { cout << "End of Sequence" << endl; break; } // 方案1:创建和源图像同类型的目标图像,直接复制 Mat imgDst; imgSrc.copyTo(imgDst); // 使用OpenCV内置的复制函数,安全高效 // 如果你确实需要将图像转换为CV_16U类型,可以用下面的方案: // Mat imgDst; // imgSrc.convertTo(imgDst, CV_16U); // 将8位图像转换为16位,自动缩放像素值(0-255 -> 0-65535) // 显示图像 imshow("Source image ", imgSrc); imshow("Destination image ", imgDst); // 等待30ms,按q/esc退出 char key = waitKey(30); if(key == 'q' || key == 'Q' || key == 27) { break; } } return 0; }
关键修复点说明
- 匹配数据类型:要么让
imgDst和imgSrc类型一致(用copyTo),要么显式转换类型(用convertTo),避免指针类型不匹配的问题。 - 调整程序流程:把
waitKey放在循环内,设置合适的等待时间(比如30ms),并添加退出逻辑;将return 0移到循环结束后。 - 移除冗余操作:窗口创建移到循环外,只执行一次。
- 使用OpenCV内置函数:
copyTo和convertTo比手动指针操作更安全、高效,还能避免很多低级错误。
内容的提问来源于stack exchange,提问作者Taki Eddine
相关产品推荐
相关产品推荐

