如何实时拼接来自摄像机的图像?
How can I stitch images from video cameras in real time?
我使用 4 个固定相机。相机 不会相对移动 。我想将它们的视频图像实时拼接成一个视频图像。
我使用这个 OpenCV 2.4.10,和 cv:stitcher
class,像这样:
// 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 中进行分析
注意:我留下这个答案只是作为尝试过的文档,因为我建议的方法似乎不起作用,而显然在使用 cv::Mat.[=13 时 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);
cv::Stitcher
相当慢。如果你的相机绝对不会相对移动并且转换就像你说的那么简单,你应该能够通过链接 homographies.[=46 将图像叠加到空白 canvas 上=]
以下内容有点数学 - 如果不清楚我可以使用 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) 预先校准这些单应性。
您需要创建一个空白矩阵,作为 canvas。你可以猜测这个的大小(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
、V_123
和 V_1234
分别。
使用以下方法将图像变形到 canvas:
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( ));
现在你有四个canvas,它们都是4个组合图像的宽度,并且其中一个图像转换到每个图像的相关位置。
剩下的就是将转换后的图像彼此合并。使用感兴趣区域可以轻松实现这一点。
可以在帧捕获开始之前提前创建 ROI 掩码。
从空白(零)图像开始,其大小与 canvases 相同。将最左边 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 输出图像,全景 - 快速操作。
您应该能够在 GPU 上完成所有这些操作,用 cv::gpu::Mat
代替 cv::Mats
,用 cv::gpu::warpAffine
代替非 GPU 对应物。
我使用 4 个固定相机。相机 不会相对移动 。我想将它们的视频图像实时拼接成一个视频图像。
我使用这个 OpenCV 2.4.10,和 cv:stitcher
class,像这样:
// 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 中进行分析
注意:我留下这个答案只是作为尝试过的文档,因为我建议的方法似乎不起作用,而显然在使用 cv::Mat.[=13 时 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);
cv::Stitcher
相当慢。如果你的相机绝对不会相对移动并且转换就像你说的那么简单,你应该能够通过链接 homographies.[=46 将图像叠加到空白 canvas 上=]
以下内容有点数学 - 如果不清楚我可以使用 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) 预先校准这些单应性。
您需要创建一个空白矩阵,作为 canvas。你可以猜测这个的大小(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
、V_123
和 V_1234
分别。
使用以下方法将图像变形到 canvas:
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( ));
现在你有四个canvas,它们都是4个组合图像的宽度,并且其中一个图像转换到每个图像的相关位置。
剩下的就是将转换后的图像彼此合并。使用感兴趣区域可以轻松实现这一点。
可以在帧捕获开始之前提前创建 ROI 掩码。
从空白(零)图像开始,其大小与 canvases 相同。将最左边 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 输出图像,全景 - 快速操作。
您应该能够在 GPU 上完成所有这些操作,用 cv::gpu::Mat
代替 cv::Mats
,用 cv::gpu::warpAffine
代替非 GPU 对应物。