Класс OpenCV Stitcher с перекрывающимися стационарными камерами
Я пытаюсь использовать класс Stitcher OpenCV для сшивания нескольких кадров из стерео-установки, в которой ни одна камера не перемещается. Я получаю плохие результаты сшивания при работе с несколькими кадрами. Я пробовал несколько разных способов, которые я попытаюсь объяснить здесь.
Использование stitcher.stitch( )
Учитывая стереопаре представлений, я запустил следующий код для некоторых фреймов (VideoFile
- это настраиваемая оболочка для объекта OpenCV VideoCapture
):
VideoFile f1( ... );
VideoFile f2( ... );
cv::Mat output_frame;
cv::Stitcher stitcher = cv::Stitcher::createDefault(true);
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
stitcher.stitch( currentFrames, output_mat );
// Write output_mat, put it in a named window, etc...
f1.next_frame();
f2.next_frame();
currentFrames.clear();
}
Это дало действительно неплохие результаты для каждого кадра, но поскольку параметры оцениваются, каждый кадр помещается в видео, вы можете видеть небольшие различия в строчке, где параметры были немного разными.
Использование estimateTransform( )
и composePanorama( )
Чтобы преодолеть проблему описанного выше метода, я решил попробовать оценить параметры только на первом кадре, а затем использовать composePanorama( )
для сшивания всех последующих кадров.
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
if( ! have_transform ) {
status = stitcher.estimateTransform( currentFrames );
}
status = stitcher.composePanorama(currentFrames, output_frame );
// ... as above
}
К сожалению, кажется, что есть ошибка (описанная здесь), заставляя два вида раздвигаться очень странным образом, как в изображения ниже:
Кадр 1:
![Frame 1]()
Кадр 2:
![Frame 2]()
...
Кадр 8:
![Frame 8]()
Ясно, что это бесполезно, но я думал, что это может быть просто из-за ошибки, которая в основном продолжает умножать внутреннюю матрицу параметров на константу каждый раз, когда вызывается composePanorama()
. Поэтому я сделал небольшой патч на ошибке, остановив это, но тогда результаты сшивания были плохими. Патч ниже (modules/stitching/src/stitcher.cpp
), результаты впоследствии:
243 for (size_t i = 0; i < imgs_.size(); ++i)
244 {
245 // Update intrinsics
246 // change following to *=1 to prevent scaling error, but messes up stitching.
247 cameras_[i].focal *= compose_work_aspect;
248 cameras_[i].ppx *= compose_work_aspect;
249 cameras_[i].ppy *= compose_work_aspect;
Результаты:
![Frame 3]()
![Frame 4]()
Кто-нибудь знает, как я могу решить эту проблему? В основном мне нужно разобраться в преобразовании один раз, а затем использовать его на оставшихся кадрах (мы говорим 30 минут видео).
В идеале я ищу советы по исправлению класса сшивателя, но я бы хотел попробовать ручное кодирование другого решения. Ранняя попытка, связанная с поиском точек SURF, корреляцией их и поиском гомографии, дала довольно плохие результаты по сравнению с классом стикеров, поэтому я предпочел бы использовать ее, если это возможно.
Ответы
Ответ 1
Итак, в конце концов, я взломал код stitcher.cpp и получил что-то близкое к решению (но не идеально, поскольку шовный шов все еще перемещается примерно так, чтобы ваш пробег мог меняться).
Изменения в stitcher.hpp
Добавлена новая функция setCameras()
в строке 136:
void setCameras( std::vector<detail::CameraParams> c ) {
this->cameras_ = c;
}`
Добавлена новая переменная частного члена, чтобы отслеживать, является ли это нашей первой оценкой:
bool _not_first;
Изменения в stitcher.cpp
В estimateTransform()
(строка ~ 100):
this->not_first = 0;
images.getMatVector(imgs_);
// ...
В composePanorama()
(строка ~ 227):
// ...
compose_work_aspect = compose_scale / work_scale_;
// Update warped image scale
if( !this->not_first ) {
warped_image_scale_ *= static_cast<float>(compose_work_aspect);
this->not_first = 1;
}
w = warper_->create((float)warped_image_scale_);
// ...
Код, вызывающий stitcher
объект:
Итак, в основном, мы создаем объект stitcher, затем получаем преобразование в первом кадре (сохраняем матрицы камеры вне класса stitcher). Затем строчка разбивает внутреннюю матрицу где-то вдоль линии, заставляя следующий фрейм испортиться. Поэтому, прежде чем мы его обработаем, мы просто reset камеры, используя те, которые мы извлекли из класса.
Будьте осторожны, я должен был провести некоторую проверку ошибок в случае, если строчка не может произвести оценку с настройками по умолчанию - вам может потребоваться итеративно уменьшить доверительный порог с помощью setPanoConfidenceThresh(...)
, прежде чем вы получите результат.
cv::Stitcher stitcher = cv::Stitcher::createDefault(true);
std::vector<cv::detail::CameraParams> cams;
bool have_transform = false;
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
if( ! have_transform ) {
status = stitcher.estimateTransform( currentFrames );
have_transform = true;
cams = stitcher.cameras();
// some code to check the status of the stitch and handle errors...
}
stitcher.setCameras( cams );
status = stitcher.composePanorama(currentFrames, output_frame );
// ... Doing stuff with the panorama
}
Помните, что это очень хакерский код OpenCV, который собирается сделать обновление для более новой версии болью. К сожалению, у меня не хватило времени, поэтому неприятный взлом был всем, чем я мог обходиться!