HimeraSearchDB
Carding_EbayThief
triada
CrackerTuch
d-shop

НОВОСТИ Склеиваем несколько фотографий в одну длинную с помощью машинного обучения

NewsBot
Оффлайн

NewsBot

.
.
Регистрация
21.07.20
Сообщения
40.408
Реакции
1
Репутация
0
В предыдущих статьях был описан и как мы . В этой статье описано, как склеить фрагменты, сделанные из разных ракурсов, в одну длинную картинку.
ejl8r4tvz3oourcuuonrphwabz0.png


Этикетки заранее сегментированы и развернуты нейронной сетью, описанной в предыдущей статье.

ilyoue7nekct7ly4s2wezq-qfv8.png


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

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

Есть , где показано, как и какие элементы влияют на трансформацию.

Как видно на картинке ниже, общих объектов вполне хватает:

ztkor_ovk6pshgrualgqq4tlncs.png


Но выбранными объектами есть проблема — их сложно детектировать алгоритмически. Вместо этого, принято искать более простые объекты — так называемые “уголки” (“corners”), они же дескрипторы (“descriptors”, “features”).

Есть отличная статья в документации OpenCV, почему именно уголки — если вкратце, то определить линию легко, но она дает только одну координату. Поэтому нужно детектировать еще и вторую (не параллельную) линию. Если они сходятся в точке, то это место и есть идеальное для поиска дескриптора, он же является уголком (хотя реальные дескрипторы не являются уголками в геометрическом смысле этого слова).

Одним из алгоритмов по поиску дескрипторов, является SIFT (Scale-Invariant Feature Transform). Несмотря на то, что его изобрели в 1999, он довольно популярен из-за простоты и надежности. Этот алгоритм был запатентован, но патент истёк этой весной (2020). Тем не менее, его не успели перенести в основную сборку OpenCV, так что нужно использовать специальный non-free билд.

Так давайте же найдем похожие уголки на обоих изображениях:


sift = cv2.xfeatures2d.SIFT_create()
features_left = sift.detectAndCompute(left_image, None)


hknym5dcjimcnbb-kt6n02bo89i.png



features_right = sift.detectAndCompute(left_image, None)


qfbul3cetih4qn3xplrcazcxg0o.png


Воспользуемся составителем дескрипторов Фланна (Flann matcher) — у него хорошая производительность даже, если количество дескрипторов велико.


KNN = 2
LOWE = 0.7
TREES = 5
CHECKS = 50

matcher = cv2.FlannBasedMatcher({'algorithm': 0, 'trees': TREES}, {'checks': CHECKS})
matches = matcher.knnMatch(left_descriptors, right_descriptors, k=KNN)

logging.debug("filtering matches with lowe test")

positive = []
for left_match, right_match in matches:
if left_match.distance < LOWE * right_match.distance:
positive.append(left_match)


Желтые линии показывают, как сопоставитель нашёл совпадения.

ojaxjmlcuyi9tctdgpf_z-7rmf4.png


Как хорошо видно — правильных совпадений примерно только половина. Однако, если правильные совпадения всегда дают одно и то же преобразование, то неправильные показывают хаотично новое направление. Т.е. теоретически, их можно как-то отделить друг от друга:

kwz8ywk72btfissarqpfu89t86k.png


Одним из алгоритмов, чтобы найти правильное преобразование, является RANSAC. Этот алгоритм отлично работает, если нужно отделить хорошие значения от шумов — как раз наш случай.

К счастью, в OpenCV уже есть функции, которые найдут матрицу преобразования по совпадениям, используя RANSAC, т.е. фактически, ничего писать не придется.

Воспользуемся функцией estimateAffinePartial2D которая ищет следующие преобразования: поворот, масштабирование и перенос (4 степени свободы).


H, _ = cv2.estimateAffinePartial2D(right_matches, left_matches, False)


Когда матрица преобразования найдена, мы можем трансформировать правое изображение для склейки.

Левый фрагмент:
9l_-bijfizi0yadxmz1ebmpwsxu.png


Правый фрагмент:

-vveiu9hhbfougjzdnnuir0sajq.png


Для начала давайте воспользуемся простейшим способом склейки фрагментов, когда каждый пиксель их пересечения вычисляется, как среднее. К сожалению, результат получается так себе — изображение заметно двоится, особенно возле линии склейки.

vz9dxqt7vvqdj4qlzyijncnh6ls.jpeg


На анимации различие между двумя кадрами видны более наглядно:

_vrr8hfeggqgu7z_w8v2g5cw6ee.gif


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

Для бесшовной склейки, необходимо компенсировать нелинейные искажения. Искажение можно представить в виде векторного поля того же разрешения, что и исходное изображение, только вместо цвета, в каждом пикселе будет закодирован сдвиг. Такое векторное поле называется “оптический поток”.

Вообще, есть разные методики вычисления оптического потока — некоторые из них встроены прямо в OpenCV, а есть и специальные нейронные сети.

В нашем же случае, конкретную методику я опущу, но опубликую результат:

ke-io2didm861eyptyrggwowbtg.png


Но компенсацию нужно осуществлять пропорционально обоих фрагментов. Для этого разделим его на две матрицы:

fo6xnic1y4s6cnldz2hp4af8alw.png


Левый фрагмент будет компенсироваться слева направо по нарастающей, в то время, как правый — наоборот.

Теперь оба фрагмента накладываются один на другой практически идеально:

dsd45rq5mfv5-mp3qbi-jruns34.gif


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

hux4fodlz7wo4mmydvo-hdheg70.png


Эту проблему легко исправить, если вместо средних значений, накладывать их с градиентом:

reiwikafnddwigqndnqqpzqyg2i.gif


С таким подходом, шва вообще не видно:

2emtxwt1ke9gm2dmjjhcijxeq10.jpeg


В принципе, есть еще и другие методики склейки, например, multiband blending, которые используют для склейки панорам, но они плохо работают с текстом — только компенсация оптического потока способно полностью убрать двоения на тексте.

Теперь склеиваем полное изображение:

wcq2klhvn7xnqzqsi_-tsuppaby.png


Финальный вариант:

xuxmqojbnxaumukj6lcxnrvv8ea.jpeg


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

Мы рассмотрели, как работает склейка, готовое решение доступно в виде REST API, также рекомендую посмотреть следующие ссылки:

 
Сверху Снизу