Кумулятивная гомография неправильно масштабируется

Я должен построить панорамное изображение земли, покрытой камерой, обращенной вниз (на фиксированной высоте, около 1 метра над землей). Это может потенциально работать до тысяч кадров, поэтому класс Stitcher ‘встроен в panorama метод не очень подходит — он слишком медленный и требует много памяти.

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

Допустим, я получаю H01 между кадрами 0 и 1, затем H12 между кадрами 1 и 2. Чтобы получить преобразование для размещения кадра 2 на мозаику, мне нужно получить H01*H12, Это продолжается, когда увеличивается количество кадров, так что я получаю H01*H12*H23*H34*H45*...,

В коде это похоже на:

cv::Mat previous, current;

// Init cumulative homography
cv::Mat cumulative_homography = cv::Mat::eye(3);

video_stream >> previous;
for(;;) {

video_stream >> current;
// Here I do some checking of the frame, etc

// Get the homography using my DenseMosaic class (using Farneback to get OF)
cv::Mat tmp_H = DenseMosaic::get_homography(previous,current);

// Now normalise the homography by its bottom right corner
tmp_H /= tmp_H.at<double>(2, 2);

cumulative_homography *= tmp_H;

previous = current.clone( );
}

Это работает довольно хорошо, за исключением того, что когда камера перемещается «вверх» в точке обзора, масштаб гомографии уменьшается. По мере того как он движется вниз, масштаб снова увеличивается. Это дает моим панорам эффект типа перспективы, который я действительно не хочу.

Например, это делается на несколько секунд видео, движущихся вперед, а затем назад. Первый кадр выглядит нормально:
Рамка 2 имплантирована на раму 1

Проблема возникает, когда мы продвигаемся на несколько кадров вперед:
Гомография уменьшается, в результате чего на панораме рамка становится меньше

Затем, когда мы вернемся снова, вы увидите, что рамка снова становится больше:
введите описание изображения здесь

Я в растерянности относительно того, откуда это исходит.

Я использую плотный оптический поток Farneback для вычисления пиксель-пиксельных соответствий, как показано ниже (разреженное сопоставление объектов не работает на этих данных), и я проверил мои векторы потока — они, как правило, очень хорошие, так что это не отслеживание проблема. Я также попытался переключить порядок входов, чтобы найти гомографию (на случай, если я перепутал номера кадров), но все же не лучше.

cv::calcOpticalFlowFarneback(grey_1, grey_2, flow_mat, 0.5, 6,50, 5, 7, 1.5, flags);

// Using the flow_mat optical flow map, populate grid point correspondences between images
std::vector<cv::Point2f> points_1, points_2;
median_motion = DenseMosaic::dense_flow_to_corresp(flow_mat, points_1, points_2);
cv::Mat H = cv::findHomography(cv::Mat(points_2), cv::Mat(points_1), CV_RANSAC, 1);

Еще одна вещь, о которой я думал, что это может быть перевод, который я включаю в преобразование, чтобы убедиться, что моя панорама центрирована внутри сцены:

cv::warpPerspective(init.clone(), warped, translation*homography, init.size());

Но проверив значения в омографии до применения перевода, проблема масштабирования, о которой я упоминаю, все еще присутствует.

Любые советы с благодарностью принимаются. Я могу вставить много кода, но он не имеет значения, пожалуйста, дайте мне знать, если чего-то не хватает

ОБНОВИТЬ
Я пытался отключить *= оператор для полного умножения и попытался изменить порядок, в котором умножены гомографии, но не повезло. Ниже мой код для расчета омографии:

/**
\brief Calculates the homography between the current and previous frames*/
cv::Mat DenseMosaic::get_homography()
{

cv::Mat grey_1, grey_2; // Grayscale versions of framescv::cvtColor(prev, grey_1, CV_BGR2GRAY);
cv::cvtColor(cur, grey_2, CV_BGR2GRAY);

// Calculate the dense flow
int flags = cv::OPTFLOW_FARNEBACK_GAUSSIAN;
if (frame_number > 2) {
flags = flags | cv::OPTFLOW_USE_INITIAL_FLOW;
}
cv::calcOpticalFlowFarneback(grey_1, grey_2, flow_mat, 0.5, 6,50, 5, 7, 1.5, flags);

// Convert the flow map to point correspondences
std::vector<cv::Point2f> points_1, points_2;
median_motion = DenseMosaic::dense_flow_to_corresp(flow_mat, points_1, points_2);

// Use the correspondences to get the homography
cv::Mat H = cv::findHomography(cv::Mat(points_2), cv::Mat(points_1), CV_RANSAC, 1);

return H;
}

И это функция, которую я использую, чтобы найти соответствия из карты потока:

/**
\brief Calculate pixel->pixel correspondences given a map of the optical flow across the image
\param[in]  flow_mat Map of the optical flow across the image
\param[out] points_1 The set of points from #cur
\param[out] points_2 The set of points from #prev
\param[in]  step_size The size of spaces between the grid lines
\return The median motion as a point

Uses a dense flow map (such as that created by cv::calcOpticalFlowFarneback) to obtain a set of point correspondences across a grid.
*/
cv::Point2f DenseMosaic::dense_flow_to_corresp(const cv::Mat &flow_mat, std::vector<cv::Point2f> &points_1, std::vector<cv::Point2f> &points_2, int step_size)
{

std::vector<double> tx, ty;
for (int y = 0; y < flow_mat.rows; y += step_size) {
for (int x = 0; x < flow_mat.cols; x += step_size) {
/* Flow is basically the delta between left and right points */
cv::Point2f flow = flow_mat.at<cv::Point2f>(y, x);
tx.push_back(flow.x);
ty.push_back(flow.y);/*  There's no need to calculate for every single point,
if there's not much change, just ignore it
*/
if (fabs(flow.x) < 0.1 && fabs(flow.y) < 0.1)
continue;

points_1.push_back(cv::Point2f(x, y));
points_2.push_back(cv::Point2f(x + flow.x, y + flow.y));
}
}

// I know this should be median, not mean, but it's only used for plotting the
// general motion direction so it's unimportant.
cv::Point2f t_median;
cv::Scalar mtx = cv::mean(tx);
t_median.x = mtx[0];
cv::Scalar mty = cv::mean(ty);
t_median.y = mty[0];

return t_median;
}

1

Решение

Оказывается, это произошло потому, что моя точка зрения была близка к особенностям, а это означает, что неплоскостность отслеживаемых элементов приводила к перекосу в гомографии. Мне удалось предотвратить это (это скорее взлом, чем метод …) с помощью estimateRigidTransform вместо findHomography, поскольку это не оценивает для перспективных изменений.

В этом конкретном случае имеет смысл сделать это, поскольку представление только когда-либо претерпевает жесткие преобразования.

1

Другие решения


По вопросам рекламы [email protected]