【问题标题】:How can I stitch images from video cameras in real time?如何实时拼接来自摄像机的图像?
【发布时间】:2015-06-03 14:44:05
【问题描述】:

我使用 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 中进行分析:

【问题讨论】:

  • 你用的是什么机器?你启用了tbb吗?另外,能否提供几张图片作为示例拼接在一起?
  • @Antonio 8 GB RAM + CPU(英特尔酷睿 i5 760 - 4 核)+ GPU(nVidia GeForce GTX 970 - 1664 核)。待定禁用。 OpenCV 2.4.10 使用 CUDA 6.5 编译并禁用 OpenMP/TBB。
  • TBB 可能会通过多线程处理来提供帮助...您为哪个系统构建了哪些构建工具?此外,要尝试的一件快速的事情是将图像数组声明/定义放在 while 循环之外。 (您在每个周期进行分配和解除分配)。我建议您在拼接函数周围设置一些计时器,以检查该函数调用是否是实际瓶颈。
  • @Antonio GCC 4.7.2 + CUDA 6.5 on Linux x86_64 Debian 7 (Wheezy)。 已修复:我将图像数组声明/定义置于 while 循环之外。
  • @Antonio 不,速度不够,即使我在每次迭代中都使用旧帧。我使用 640x480 RGB 帧。我在 Windows 上添加了来自 MSVS 2013 的分析信息。

标签: c++ opencv image-stitching opencv3.0 opencv-stitching


【解决方案1】:

注意:我留下这个答案只是作为尝试的文档,因为我建议的方法似乎不起作用,而显然在使用 cv::Mat 时 GPU 已经在使用中。


尝试使用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);

【讨论】:

  • 当我在estimateTransform();composePanorama(); 中使用cv::gpu::GpuMat 而不是cv::Mat 时,在Windows 7x64 + OpenCV 2.4.9 上出现错误:OpenCV 错误:断言失败(func != 0) 在 cv::resize,文件 C:\opencv_2.4.9\o pencv\sources\modules\imgproc\src\imgwarp.cpp,第 1980 行
  • @Alex 这至少证明之前没有使用过 GPU :) 请更新您的问题并将您在 C:\opencv_2.4.9\o pencv\sources\modules\imgproc\src\imgwarp.cpp 中找到的代码发布在 1980 行附近
  • 但是 MSVS 2013 分析结果,我添加到我的问题中,显示 GPU 使用了很多次 :) 错误代码在这里:pastebin.com/VVN1gMK2
  • @Alex 我看到我有一个 2.4.9 的副本,但这很难调试......谁在调用 cv::resize 显然有错误的插值属性?也许毕竟使用 gpu::GpuMat 不是正确的方法。
【解决方案2】:

cv::Stitcher 相当慢。如果您的相机绝对不会相对于彼此移动并且转换就像您说的那样简单,那么您应该能够通过链接homographies 将图像叠加到空白画布上。

以下内容有点数学 - 如果不清楚,我可以使用 LaTeX 正确编写它,但 SO 不支持漂亮的数学 :)

您有一组 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_2I_3,我们有 H_23,对于 I_3I_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_12V_123V_1234分别。

使用以下方法将图像扭曲到画布上:

cv::warpAffine(I_2, V_12, H_12, V_12.size( ));

然后对下一个图像执行相同操作:

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( ));

现在您有四个画布,它们都是 4 个组合图像的宽度,并且其中一个图像转换到每个图像的相关位置。

剩下的就是将转换后的图像相互合并。使用感兴趣区域很容易实现这一点。

可以提前在帧捕获开始之前创建 ROI 蒙版。

从与画布大小相同的空白(零)图像开始。将大小为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( ));

要将所有图像组合成一个全景图,您可以:

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): 

您在这里所做的是将copying the relevant area of each canvas 放到输出图像上,pano - 一个快速操作。

您应该能够在 GPU 上完成所有这些操作,用 cv::gpu::Mat 代替 cv::Matscv::gpu::warpAffine 代替其非 GPU 对应项。

【讨论】:

  • 为了使这种方法更加有效,您可以预先计算仿射变形图,因为它们是恒定的,并使用比cv::warpAffine 快得多的cv::remap(参见doc)。
  • 很好,是的,这是一个非常好的主意。实际上,我过去曾这样做过,而且效果很好,只是在我写出上述内容时并没有想到。
  • @n00dle 非常感谢!但你说,我必须找到:3x3 变换矩阵,称为 homography,并使用:cv::warpAffine();,但据我所知 warpAffine(); 使用 仿射变换矩阵 (2x3)。这是否意味着我必须在estimateRigidTransform(); 之后使用warpAffine();?还是我必须在findHomography(); 之后使用warpPerspective();?以及如何从使用findHomography(); 找到的单应性中获得ramap();map1, map2
  • 抱歉,我把两者搞混了。在这种情况下,你说你只需要一个翻译,在这种情况下,仿射矩阵,用estimateRigidTransform 估计就可以了。如果您发现这并不代表完整的转换(即重叠的图像看起来有点不统一),您可能希望使用单应性。我对重映射问题不是 100% - 我想我有代码在工作,但要到周三才能回来,因为这是英国的复活节假期。我建议查看 initUndistortRectifyMap(),因为我觉得这可能与它有关。
  • @n00dle 谢谢!复活节快乐!
猜你喜欢
  • 1970-01-01
  • 2012-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多