В предыдущей статье мы обсудили как работает RANSAC, давайте теперь его применим для задачи сопоставления картинок. Но еще мы постараемся упростить ему задачу (для улучшения качества результата) - максимально уменьшив долю ошибок в изначальных сопоставлениях.

Итак пусть есть две картинки и мы на них выделили ключевые точки алгоритмом SIFT:

Box and other boxes

Box single

Хочется суметь сопоставить эту коробку (вторая картинка) с первой картинкой - чтобы найти где она там находилась. Что например нас устроит как ответ? Например матрица которая описывает преобразование пространства этой второй картинки в преобразование пространства первой картинки.

При этом преобразование должно быть такое чтобы вторая картинка идеально наложилась на коробку в первой картинке. Т.е. матрица по сути натягивает коробку на эту же коробку в другой картинке.

Воспользуемся сопоставлением точек по принципу “каждой точке из первой картинки - находим самую похожую точку со второй картинки”. Мерило похожести - расстояние между дескрипторами (напоминаю что дескриптор - это то же самое что и трехмерный вектор, только этот вектор в очень высокой размерности, например 128-мерный, но расстояние между такими векторами считается все так же - корень из суммы квадратов разниц).

Получили вот такие сопоставления:

All matches

Здесь очень много ошибок (ложных сопоставлений), поэтому если просто на этом запустить RANSAC - он может не справиться и найденное преобразование может сделать что-нибудь страшное:

Failed RANSAC stitching

Поэтому попробуйте такие стратегии:

1) Что если оставлять только такие сопоставления точек у которых расстояние между дескрипторами этих точек - маленькое?

2) Что если мы найдем для каждой точки А из первой картинки не только самую близкую точку Б из второй картинки, но и вторую по близости точку - В? Как бы вы ожидали отличается расстояние между А-Б и А-В? Очевидно что оно увеличивается, но в хорошем сопоставлении разумно ожидать что оно увеличивается сильно! Например что ближайшее сопоставление А-Б - на расстоянии меньшем чем 70% от расстояния А-В. Этот критерий называется K-Ratio test (где в данном случае K=70%=0.7).

3) Что если мы найшли для точки А из первой картинки самую близкую точку Б из второй картинки. И для точки Б из второй картинки мы нашли самую близкую точку В из первой картинки. Что мы можем сказать на базе этого? Что если А и В сопвадают? Что если А и В - разные точки? Такой критерий называется Left-Right check, т.е. проверка на согласованность - правда ли что результат сопоставления симметричен. Если нет - то вероятно он плохой и является выбросом (outlier).

Попробуйте каждую стратегию по одиночке, и все вместе. Что работает хорошо а что не очень?

Пример оставшихся хороших сопоставлений:

Good matches, after K-Ratio and Left-Right check

И наконец RANSAC тоже ведь решает какие точки хорошие (inliers), а какие - выбросы (outliers):

Good matches, after K-Ratio and Left-Right check

И наконец сопоставленные картинки:

Target image

Transfered image

И вот как выглядит наложенная картинка поверх:

Transfered image

А что если наложить другую картинку по этой же матрице преобразования? Например:

Transfered another image

Сшивка панорам

В задачах про сшивку панораму например возникает желание увеличить размер картинки, проще всего сделать новую картинку большего размера и уже на нее рисовать и исходную картинку, и ту что вы накладываете поверх.

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

Но как найти для каждой точки-угла куда она переходит? Домножить на матрицу гомографии, можете попробовать нагуглить самостоятельно как это сделать, например гугл opencv homography apply to point cpp -> https://answers.opencv.org/question/5440/apply-homography-on-a-sinlge-point/ -> https://docs.opencv.org/2.4/doc/tutorials/features2d/feature_homography/feature_homography.html -> код:

std::vector<Point2f> obj_corners(4);
obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( img_object.cols, 0 );
obj_corners[2] = cvPoint( img_object.cols, img_object.rows ); obj_corners[3] = cvPoint( 0, img_object.rows );
std::vector<Point2f> scene_corners(4);

perspectiveTransform( obj_corners, scene_corners, H);