我使用4台固定式摄像机。相机不会相对移动。我想将来自它们的视频图像拼接成一个实时的视频图像。
我用于这个OpenCV 2.4.10和cv:stitcher
类,如下所示:
// use 4 video-cameras
cv::VideoCapture cap0(0), cap1(1), cap2(2), cap3(3);
bool try_use_gpu = true; // use GPU
cv::Stitcher stitcher = cv::Stitcher::createDefault(try_use_gpu);
stitcher.setWarper(new cv::CylindricalWarperGpu());
stitcher.setWaveCorrection(false);
stitcher.setSeamEstimationResol(0.001);
stitcher.setPanoConfidenceThresh(0.1);
//stitcher.setSeamFinder(new cv::detail::GraphCutSeamFinder(cv::detail::GraphCutSeamFinderBase::COST_COLOR_GRAD));
stitcher.setSeamFinder(new cv::detail::NoSeamFinder());
stitcher.setBlender(cv::detail::Blender::createDefault(cv::detail::Blender::NO, true));
//stitcher.setExposureCompensator(cv::detail::ExposureCompensator::createDefault(cv::detail::ExposureCompensator::NO));
stitcher.setExposureCompensator(new cv::detail::NoExposureCompensator());
std::vector<cv::Mat> images(4);
cap0 >> images[0];
cap1 >> images[1];
cap2 >> images[2];
cap3 >> images[3];
// call once!
cv::Stitcher::Status status = stitcher.estimateTransform(images);
while(true) {
// **lack of speed, even if I use old frames**
// std::vector<cv::Mat> images(4);
//cap0 >> images[0];
//cap1 >> images[1];
//cap2 >> images[2];
//cap3 >> images[3];
cv::Stitcher::Status status = stitcher.composePanorama(images, pano_result);
}
我只获得10 FPS(每秒帧数),但我需要25 FPS。 我该如何加速这个例子?
当我使用stitcher.setWarper(new cv::PlaneWarperGpu());
时,我会得到一个非常放大的图像,这是我不需要的。
我只需 - 翻译。
例如,我准备不要使用:
我该怎么办?或者,我如何从每个图像的cv::Stitcher stitcher
参数x,y
获取翻译?
更新 - 在Windows 7 x64上的MSVS 2013中进行性能分析:
答案 0 :(得分:12)
cv::Stitcher
相当慢。如果您的相机绝对不会相互移动并且转换就像您说的那么简单,您应该只需链接homographies即可将图像叠加到空白画布上。
以下是一些数学 - 如果不清楚我可以使用LaTeX正确地编写它,但是它不支持漂亮的数学:)
您有一组4个摄像头,从左到右,(C_1, C_2, C_3, C_4)
,提供一组4个图像(I_1, I_2, I_3, I_4)
。
要从I_1
转换为I_2
,您需要一个3x3转换矩阵,称为单应性。我们称之为H_12
。同样地,对于I_2
到I_3
,我们有H_23
,对于I_3
到I_4
,我们会H_34
。
您可以使用标准方法(point matching between the overlapping cameras)预先校准这些单应性。
您需要创建一个空白矩阵,以充当画布。您可以猜出这个的大小(4 * image_size就足够了)或者您可以在右上角(称为P1_tr
)并将其转换为三个单应性,在右上角给出一个新点全景PP_tr
(以下假定P1_tr
已转换为矩阵):
PP_tr = H_34 * H_23 * H_12 * P1_tr'
这是做什么的,是P1_tr
将其首先转换为相机2,然后从C_2
转换为C_3
,最后从C_3
转换为C_4
你需要创建其中一个用于组合图像1和2,图像1,2和3以及最后图像1-4,我将它们称为V_12
,{分别为{1}}和V_123
。
使用以下方法将图像扭曲到画布上:
V_1234
然后对下一张图片做同样的事情:
cv::warpAffine(I_2, V_12, H_12, V_12.size( ));
现在您有四幅画布,所有画布都是4幅合成图像的宽度,其中一幅图像转换为相应位置。
剩下的就是将变换后的图像合并到另一个上面。这可以通过感兴趣的区域轻松实现。
在帧捕获开始之前,可以提前创建ROI掩码。
以与画布大小相同的空白(零)图像开始。将最左边的矩形设置为cv::warpAffine(I_3, V_123, H_23*H_12, V_123.size( ));
cv::warpAffine(I_4, V_1234, H_34*H_23*H_12, V_1234.size( ));
的大小为白色。这是第一张图片的蒙版。我们称之为I_1
。
接下来,为了获得第二个变换图像的蒙版,我们
M_1
要将所有图像合并为一个全景图,请执行以下操作:
cv::warpAffine(M_1, M_2, H_12, M_1.size( ));
cv::warpAffine(M_2, M_3, H_23*H_12, M_1.size( ));
cv::warpAffine(M_3, M_4, H_34*H_23*H_12, M_1.size( ));
你在这里做的是copying the relevant area of each canvas到输出图像上,全息 - 快速操作。
你应该可以在GPU上完成所有这些操作,用cv::Mat pano = zeros(M_1.size( ), CV_8UC3);
I_1.copyTo(pano, M_1);
V_12.copyTo(pano, M_2):
V_123.copyTo(pano, M_3):
V_1234.copyTo(pano, M_4):
代替cv::gpu::Mat
和cv::Mats
代替其非GPU对手。
答案 1 :(得分:1)
注意:我把这个答案留作了所尝试内容的文档,因为我建议的方法似乎不起作用,而显然GPU在使用cv :: Mat时已经在使用。
尝试使用gpu::GpuMat
:
std::vector<cv::Mat> images(4);
std::vector<gpu::GpuMat> gpuImages(4);
gpu::GpuMat pano_result_gpu;
cv::Mat pano_result;
bool firstTime = true;
[...]
cap0 >> images[0];
cap1 >> images[1];
cap2 >> images[2];
cap3 >> images[3];
for (int i = 0; i < 4; i++)
gpuImages[i].upload(images[i]);
if (firstTime) {
cv::Stitcher::Status status = stitcher.estimateTransform(gpuImages);
firstTime = false;
}
cv::Stitcher::Status status = stitcher.composePanorama(gpuImages, pano_result_gpu);
pano_result_gpu.download(pano_result);