在 opencv 和 Qt 中结合视频
Posted
技术标签:
【中文标题】在 opencv 和 Qt 中结合视频【英文标题】:Combining videos in opencv and Qt 【发布时间】:2013-02-01 01:02:43 【问题描述】:我正在尝试编写一个程序,将 4 个视频组合成一个视频,将它们排列成 2x2 网格。下面是相关代码:
void CombineVideoApp::on_GoButton_clicked()
std::string Video1Name = Video1Path.toUtf8().constData();
std::string Video2Name = Video2Path.toUtf8().constData();
std::string Video3Name = Video3Path.toUtf8().constData();
std::string Video4Name = Video4Path.toUtf8().constData();
int VideoWidth = 640;
int VideoHeight = 360;
cv::Mat Video1Frame, Video2Frame, Video3Frame, Video4Frame; // Creating Mat objects to store the captured frames
cv::Mat Video1Shrunk, Video2Shrunk, Video3Shrunk, Video4Shrunk; //Creating Mat objects to store the shrunk frames
cv::Mat FullCombinedVideo(VideoWidth,VideoHeight, Video1Shrunk.type()); //Creating Mat object to store the combined video
cv::Mat CombinedVideoTop(VideoWidth, VideoHeight/2, Video1Shrunk.type());
cv::Mat CombinedVideoBottom(VideoWidth, VideoHeight/2, Video1Shrunk.type());
cv::VideoCapture Video1(Video1Name);
cv::VideoCapture Video2(Video2Name);
cv::VideoCapture Video3(Video3Name);
cv::VideoCapture Video4(Video4Name);
double Video1FrameCount = Video1.get(CV_CAP_PROP_FRAME_COUNT);
double Video2FrameCount = Video2.get(CV_CAP_PROP_FRAME_COUNT);
double Video3FrameCount = Video3.get(CV_CAP_PROP_FRAME_COUNT);
double Video4FrameCount = Video4.get(CV_CAP_PROP_FRAME_COUNT);
double CombinedFrameTopCount = min(Video1FrameCount, Video2FrameCount);
double CombinedFrameBottomCount = min(Video3FrameCount, Video4FrameCount);
double CombinedFrameCount = min(CombinedFrameBottomCount, CombinedFrameTopCount);
Video1.set(CV_CAP_PROP_FRAME_COUNT, CombinedFrameCount);
Video2.set(CV_CAP_PROP_FRAME_COUNT, CombinedFrameCount);
Video3.set(CV_CAP_PROP_FRAME_COUNT, CombinedFrameCount);
Video4.set(CV_CAP_PROP_FRAME_COUNT, CombinedFrameCount);
for(;;)
Video1 >> Video1Frame;
Video2 >> Video2Frame;
Video3 >> Video3Frame;
Video4 >> Video4Frame;
cv::resize(Video1Frame, Video1Shrunk, Size((VideoWidth/2), (VideoHeight/2)));
cv::resize(Video2Frame, Video2Shrunk, Size((VideoWidth/2), (VideoHeight/2)));
cv::resize(Video3Frame, Video3Shrunk, Size((VideoWidth/2), (VideoHeight/2)));
cv::resize(Video4Frame, Video4Shrunk, Size((VideoWidth/2), (VideoHeight/2)));
Video1Shrunk.copyTo(CombinedVideoTop(Rect(0,0,Video1Shrunk.cols,Video1Shrunk.rows)));
Video2Shrunk.copyTo(CombinedVideoTop(Rect(Video1Shrunk.cols,0,Video2Shrunk.cols,Video2Shrunk.rows)));
Video3Shrunk.copyTo(CombinedVideoBottom(Rect(0,0,Video3Shrunk.cols,Video3Shrunk.rows)));
Video4Shrunk.copyTo(CombinedVideoBottom(Rect(Video3Shrunk.cols,0,Video4Shrunk.cols,Video4Shrunk.rows)));
CombinedVideoTop.copyTo(FullCombinedVideo(Rect(0,0,CombinedVideoTop.cols, CombinedVideoTop.rows)));
CombinedVideoBottom.copyTo(FullCombinedVideo(Rect(0,CombinedVideoTop.rows, CombinedVideoBottom.cols,CombinedVideoBottom.rows)));
std::string FinalFilePath = CombinedVideo.toUtf8().constData();
cv::VideoWriter CombinedWriter(FinalFilePath, CV_FOURCC('D', 'I', 'V', 'X'),10,cvSize(FullCombinedVideo.cols, FullCombinedVideo.rows));
CombinedWriter << FullCombinedVideo;
程序编译没有问题,但是当我实际尝试运行它时,我收到以下错误:
OpenCV Error: Assertion failed (0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows) in Mat, file /tmp/OpenCV-2.4.3/modules/core/src/matrix.cpp, line 322
Qt has caught an exception thrown from an event handler. Throwing
exceptions from an event handler is not supported in Qt. You must
reimplement QApplication::notify() and catch all exceptions there.
The program has unexpectedly finished.
谁能告诉我这里有什么问题?我完全被难住了。 我尝试打开的视频文件是 .avi 格式,我使用的是 QtCreator、OpenCV 2.4.3,并在 OSX Snow Leopard 上运行它。
谢谢
【问题讨论】:
您是要在同一个窗口中显示 4 个视频,还是要在其中创建一个包含其他视频文件的新视频文件? 我正在尝试创建一个包含 4 个视频的新视频文件。 你应该捕获异常,顺便说一句 @JM92 这个问题不是回答成功了吗? 【参考方案1】:将帧写入视频文件很容易,有几个示例around here。
棘手的部分是将 4 个帧组合成一个帧,因此以下代码演示了如何做到这一点,并且考虑到所有帧都可以具有任意大小:
// Load 4 images from the disk
cv::Mat img1 = cv::imread("test1.jpg");
cv::Mat img2 = cv::imread("test2.jpg");
cv::Mat img3 = cv::imread("test3.jpg");
cv::Mat img4 = cv::imread("test4.jpg");
// Make sure they have been loaded successfully
if (img1.empty() || img2.empty() || img3.empty() || img4.empty())
std::cout << "!!! Failed to load one of the images\n";
return -1;
/* Make sure they are compatible: same type, depth and # channels */
if ( (img1.type() != img2.type()) ||
(img3.type() != img4.type()) ||
(img1.type() != img4.type()) )
std::cout << "!!! The depth doesn't match!\n";
return -1;
if ( (img1.depth() != img2.depth()) ||
(img3.depth() != img4.depth()) ||
(img1.depth() != img4.depth()) )
std::cout << "!!! The depth doesn't match!\n";
return -1;
if ( (img1.channels() != img2.channels()) ||
(img3.channels() != img4.channels()) ||
(img1.channels() != img4.channels()) )
std::cout << "!!! Number of channels doesn't match!\n";
return -1;
// Create the destination image based on the size of the loaded images
// _________
// | | |
// |_1__|_2__|
// | | |
// |_3__|_4__|
/* As the input images might have different sizes, we need to make sure
* to create an output image that is big enough to store all of them.
*/
// Compute the width of the output image
int row1_size = img1.size().width + img2.size().width;
int row2_size = img3.size().width + img4.size().width;
int new_width = std::max(row1_size, row2_size);
// Compute the height of the output image
int col1_size = img1.size().height + img3.size().height;
int col2_size = img2.size().height + img4.size().height;
int new_height = std::max(col1_size, col2_size);
// Create the destination image
cv::Mat dst_img(cv::Size(new_width, new_height), img1.type(), cv::Scalar(0, 0, 0));
std::cout << "dst: size " << dst_img.size().width << "x" << dst_img.size().height << std::endl;
/* Copy the pixels of the input images to the destination */
// _________
// | | |
// |_1__| |
// | |
// |_________|
img1.copyTo(dst_img(cv::Rect(0, 0, img1.cols, img1.rows)));
// _________
// | | |
// |_1__|_2__|
// | |
// |_________|
img2.copyTo(dst_img(cv::Rect(img1.size().width, 0, img2.cols, img2.rows)));
// _________
// | | |
// |_1__|_2__|
// | | |
// |_3__|____|
img3.copyTo(dst_img(cv::Rect(0,
std::max(img1.size().height, img2.size().height),
img3.cols,
img3.rows)));
// _________
// | | |
// |_1__|_2__|
// | | |
// |_3__|_4__|
img4.copyTo(dst_img(cv::Rect(img3.size().width,
std::max(img1.size().height, img2.size().height),
img4.cols,
img4.rows)));
// For testing purposes, display it on the screen
cv::imshow("Test", dst_img);
cv::waitKey(0);
这些是用于测试的图像尺寸:64x64、128x128、256x256 和 320x320
结果:
【讨论】:
在这个演示中,您可以注意到所有图像都非常接近,因为我的 2x2 网格没有预定义的尺寸。您可能希望使网格具有固定尺寸,以便图像保持垂直和水平对齐。这将是对代码的一个非常小的更改。【参考方案2】:使用 ffmpeg(或 libav),它是完成此任务的合适工具,而且执行速度更快。
见: FFMPEG 2 Videos transcoded and side by side in 1 frame?
【讨论】:
【参考方案3】:正如 opencv 所说,您的 ROI(感兴趣区域)错误。
您可能有一个差一错误,因此您有一个从 -1 开始或结束于 -1 的区域 宽度/高度。
要么在调试器中检查这些,要么打印坐标,看看哪个是错误的。
【讨论】:
以上是关于在 opencv 和 Qt 中结合视频的主要内容,如果未能解决你的问题,请参考以下文章