Как я могу сшивать изображения с видеокамер в режиме реального времени?

Я использую 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 переводов для каждого из изображений?

ОБНОВЛЕНИЕ - профилирование в MSVS 2013 на Windows 7 x64:

2 ответа

Решение

cv::Stitcher довольно медленно Если ваши камеры определенно не движутся относительно друг друга, и преобразование так просто, как вы говорите, вы сможете наложить изображения на чистый холст, просто создав цепочку гомографий.

Следующее является несколько математическим - если это не ясно, я могу написать это правильно, используя LaTeX, но SO не поддерживает красивые математики:)

У вас есть набор из 4 камер, слева направо, (C_1, C_2, C_3, C_4), предоставив набор из 4 изображений (I_1, I_2, I_3, I_4),

Преобразовать из I_1 в I_2, у вас есть матрица преобразования 3х3, называемая гомографией. Мы назовем это H_12, Аналогично для I_2 в I_3 у нас есть H_23 и для I_3 в I_4 у тебя будет H_34,

Вы можете предварительно откалибровать эти гомографии заранее, используя стандартный метод ( сопоставление точек между перекрывающимися камерами).

Вам нужно будет создать пустую матрицу, чтобы действовать как холст. Вы можете угадать размер этого (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 соответственно.

Используйте следующее, чтобы деформировать изображение на холст:

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

Здесь вы копируете соответствующую область каждого холста на выходное изображение, панорамирование - быстрая операция.

Вы должны быть в состоянии сделать все это на графическом процессоре, заменив cv::gpu::Matдля cv::Mats а также cv::gpu::warpAffine для его коллеги не-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);
Другие вопросы по тегам