web-dev-qa-db-ja.com

ビデオカメラからの画像をリアルタイムでステッチするにはどうすればよいですか?

私は4台の固定カメラを使用しています。カメラは相互に移動しません。そして、それらからのビデオ画像をリアルタイムで1つのビデオ画像につなぎ合わせたい

私はこのOpenCV2.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(フレーム/秒)しか得られませんが、25FPSが必要です。この例を加速するにはどうすればよいですか?

stitcher.setWarper(new cv::PlaneWarperGpu());を使用すると、非常に拡大された画像が得られます。これは必要ありません。

のみ-翻訳が必要です。

たとえば、私は使用しない準備ができています:

  • パースペクティブ変換
  • スケール操作
  • そしてローテーションでさえあるかもしれません

どうすればいいですか?または、各画像の翻訳の_cv::Stitcher stitcher_パラメータ_x,y_からどのように取得できますか?

UPDATE-Windows 7x64上のMSVS2013でのプロファイリング: enter image description here

11
Alex

_cv::Stitcher_はかなり遅いです。カメラが確実に相互に移動せず、変換があなたが言うほど単純である場合は、 homographies をチェーンするだけで、画像を空白のキャンバスにオーバーレイできるはずです。

以下はやや数学的なものです-これが明確でない場合は、LaTeXを使用して適切に記述できますが、SOはかなりの数学をサポートしていません:)

左から右に_(C_1, C_2, C_3, C_4)_の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_になります。

これらのホモグラフィは、標準的な方法を使用して事前に事前に調整できます( オーバーラップするカメラ間のポイントマッチング )。

キャンバスとして機能するには、空白のマトリックスを作成する必要があります。これのサイズを推測するか(4 * image_sizeで十分です)、右上隅を取り(これを_P1_tr_と呼びます)、3つのホモグラフィで変換して、の右上に新しい点を与えることができます。パノラマ、_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_それぞれ。

以下を使用して、画像をキャンバスにワープします。

_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つのキャンバスができました。これらはすべて、4つの結合された画像の幅であり、画像の1つがそれぞれの関連する場所に変換されています。

残っているのは、変換された画像を互いにマージすることだけです。これは、関心領域を使用して簡単に実現できます。

ROIマスクの作成は、フレームキャプチャを開始する前に、事前に行うことができます。

キャンバスと同じサイズの空白(ゼロ)の画像から始めます。左端の長方形を_I_1_のサイズで白に設定します。これは最初の画像のマスクです。これを_M_1_と呼びます。

次に、2番目の変換された画像のマスクを取得するために、

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

すべての画像を1つのパノラマにまとめるには、次のようにします。

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

ここで行っているのは 各キャンバスの関連領域をコピーする 出力画像にパノラマ-高速操作です。

GPUでこれらすべてを実行できるはずです。_cv::gpu::Mat_を_cv::Mats_に、非GPUの対応物を_cv::gpu::warpAffine_に置き換えます。

14
n00dle

注: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);
1
Antonio