OpenCV: как получить более старые точки с помощью findHomography()/findFundamental() и RANSAC

OpenCV не предоставляет функцию RANSAC как таковой или, по крайней мере, в такой форме, которую вы можете просто вызвать и выполнить с ней (например, cv::ransac(...)). Все функции/методы, которые могут использовать RANSAC, имеют флаг, который позволяет это. Однако это не всегда полезно, если вы действительно хотите что-то сделать с помощью вычислений, которые RANSAC вычисляет после того, как вы оценили гомографию/фундаментальную матрицу, например, создайте хороший сюжет в Octave или аналогичном программном обеспечении/библиотеке точек, примените дополнительные алгоритмы на оставшийся набор фильтрованных совпадений и т.д.

После сопоставления двух изображений получается вектор совпадений. Наряду с этим у нас есть, конечно, 2 набора ключевых точек (по одному для каждого изображения), которые использовались в процессе сопоставления. Используя совпадения и ключевые точки, мы создаем два вектора точек (например, cv::Point2f points) и передаем их в findHomography(). Из этого и этих сообщений я узнал, как точно помечены метки с помощью маски, которые мы передаем этому функция. Каждая строка внутри маски относится к более раннему/выброшенному. Однако я не могу понять, как использовать информацию индекса строки из моих двух наборов точек. Глядя на исходный код OpenCV, я не зашел слишком далеко. В findFundamental() (аналогично findHomography(), когда дело доходит до его подписи и части маски), они используют compressPoints(), который каким-то образом объединяет два набора, которые мы имеем в качестве входных (исходные и целевые точки), в один. Во время тестирования, чтобы определить природу маски, я попробовал 2 набора совпадающих точек (преобразованный cv::Keypoints в cv::Point2f - стандартная процедура). Каждый набор содержит 300 очков, поэтому в общей сложности у нас 600 баллов. Возвращаемая маска содержит 300 строк (значения для данного раздела не важны).

EDIT: при написании этого я обнаружил ответ (см. ниже), но решил опубликовать этот вопрос в любом случае, если кому-то нужна эта информация как можно скорее и в компактной форме. Обратите внимание, что нам по-прежнему нужна одна из функций OpenCV, которые поддерживают RANSAC. Поэтому, если у вас есть набор точек, но вы не хотите вычислять гомографию или фундаментальную матрицу, это явно не так, и я осмелюсь сказать, что мне не удалось найти что-либо полезное в API OpenCV, которое может помочь избежать этого препятствия, поэтому вам нужно использовать внешняя библиотека.

Ответ 1

Решение на самом деле довольно тривиально. Поскольку мы знаем, что каждая строка в нашей маске дает информацию, если у нас есть более высокий или более высокий уровень. Однако у нас есть 2 набора точек в качестве входных данных, так как точно строка, содержащая одно значение, представляет две точки? Природа такого рода индексирования появилась в моем сознании, думая, как на самом деле эти два набора точек появляются в findHomography() (в моем случае я вычислял гомографию между двумя изображениями). Оба набора имеют равное количество точек в них из-за простого факта, что они извлекаются из совпадений между нашими парами изображений. Это означает, что строка в нашей маске является фактическим индексом точек в двух наборах, а также индексом в векторе совпадений для двух изображений. Я успешно сумел вручную ссылаться на небольшое подмножество совпадающих точек на основе этого, и результаты ожидаются. Важно, чтобы вы не изменяли порядок ваших совпадений и 2D-очки, которые вы извлекли из них, используя ключевые точки, указанные в каждом cv::DMatch. Ниже вы можете увидеть простой пример для одной пары индексов.

for(int i = 0; i < matchesObjectScene.size(); ++i)
{
   // extract points from keypoints based on matches
   pointsObject.push_back(keypointsObject.at(matchesObjectScene.at(i).queryIdx).pt);
   pointsScene.push_back(keypointsScene.at(matchesObjectScene.at(i).trainIdx).pt);
}
// compute homography using RANSAC
cv::Mat mask;
cv::Mat H = cv::findHomography(pointsObject, pointsScene, CV_RANSAC, ransacThreshold, mask);

В приведенном выше примере, если мы напечатаем несколько более сложных

int maskRow = 10;
std::cout << "POINTS: object(" << pointsObject.at(maskRow).x << "," << pointsObject.at(maskRow).y << ") - scene(" << pointsScene.at(maskRow).x << "," << pointsScene.at(maskRow).y << ")" << std::endl;

а затем снова, но на этот раз используя наши ключевые точки (также можно сделать с извлеченными 2D-точками)

std::cout << "POINTS (via match-set): object(" << keypointsObject.at(matchesCurrentObject.at(maskRow).queryIdx).pt.x << "," << keypointsObject.at(matchesCurrentObject.at(maskRow).queryIdx).pt.y << ") - scene(" << keypointsScene.at(matchesCurrentObject.at(maskRow).trainIdx).pt.x << "," << keypointsScene.at(matchesCurrentObject.at(maskRow).trainIdx).pt.y << ")" << std::endl;

мы получаем тот же результат:

POINTS: object(462,199) - sscene(485,49)
POINTS (via match-set): object(462,199) - scene(485,49)

Чтобы получить фактическое значение, мы просто должны проверить, действительно ли текущая строка в маске содержит 0 или ненулевое значение:

if((unsigned int)mask.at<uchar>(maskRow))
  // store match or keypoints or points somewhere where you can access them later