Урок 16: сшивка картинок = SIFT + K-ratio test + Left-Right check + RANSAC
В предыдущей статье мы обсудили как работает RANSAC, давайте теперь его применим для задачи сопоставления картинок. Но еще мы постараемся упростить ему задачу (для улучшения качества результата) - максимально уменьшив долю ошибок в изначальных сопоставлениях.
Итак пусть есть две картинки и мы на них выделили ключевые точки алгоритмом SIFT:
Хочется суметь сопоставить эту коробку (вторая картинка) с первой картинкой - чтобы найти где она там находилась. Что например нас устроит как ответ? Например матрица которая описывает преобразование пространства этой второй картинки в преобразование пространства первой картинки.
При этом преобразование должно быть такое чтобы вторая картинка идеально наложилась на коробку в первой картинке. Т.е. матрица по сути натягивает коробку на эту же коробку в другой картинке.
Воспользуемся сопоставлением точек по принципу “каждой точке из первой картинки - находим самую похожую точку со второй картинки”. Мерило похожести - расстояние между дескрипторами (напоминаю что дескриптор - это то же самое что и трехмерный вектор, только этот вектор в очень высокой размерности, например 128-мерный, но расстояние между такими векторами считается все так же - корень из суммы квадратов разниц).
Получили вот такие сопоставления:
Здесь очень много ошибок (ложных сопоставлений), поэтому если просто на этом запустить RANSAC - он может не справиться и найденное преобразование может сделать что-нибудь страшное:
Поэтому попробуйте такие стратегии:
1) Что если оставлять только такие сопоставления точек у которых расстояние между дескрипторами этих точек - маленькое?
2) Что если мы найдем для каждой точки А из первой картинки не только самую близкую точку Б из второй картинки, но и вторую по близости точку - В? Как бы вы ожидали отличается расстояние между А-Б и А-В? Очевидно что оно увеличивается, но в хорошем сопоставлении разумно ожидать что оно увеличивается сильно! Например что ближайшее сопоставление А-Б - на расстоянии меньшем чем 70% от расстояния А-В. Этот критерий называется K-Ratio test
(где в данном случае K=70%=0.7
).
3) Что если мы найшли для точки А из первой картинки самую близкую точку Б из второй картинки. И для точки Б из второй картинки мы нашли самую близкую точку В из первой картинки. Что мы можем сказать на базе этого? Что если А и В сопвадают? Что если А и В - разные точки? Такой критерий называется Left-Right check
, т.е. проверка на согласованность - правда ли что результат сопоставления симметричен. Если нет - то вероятно он плохой и является выбросом (outlier).
Попробуйте каждую стратегию по одиночке, и все вместе. Что работает хорошо а что не очень?
Пример оставшихся хороших сопоставлений:
И наконец RANSAC тоже ведь решает какие точки хорошие (inliers), а какие - выбросы (outliers):
И наконец сопоставленные картинки:
И вот как выглядит наложенная картинка поверх:
А что если наложить другую картинку по этой же матрице преобразования? Например:
Сшивка панорам
В задачах про сшивку панораму например возникает желание увеличить размер картинки, проще всего сделать новую картинку большего размера и уже на нее рисовать и исходную картинку, и ту что вы накладываете поверх.
Осталось лишь не промахнуться с размером - можно просто угадать взяв большое число, но если хочется сделать хорошо, то можно применить матрицу гомографии к каждому из углов исходной картинки, это даст вам четыре новые точки в результирующей картинке-панораме, и взяв из координат этих точек максимум - вы определите требуемые размеры панорамы.
Но как найти для каждой точки-угла куда она переходит? Домножить на матрицу гомографии, можете попробовать нагуглить самостоятельно как это сделать, например гугл 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);