5.31. Сопоставление по смещению¶
Сопоставление с шаблоном отвечает на вопрос где находится этот фрагмент внутри кадра; оценка схожести отвечает на вопрос насколько в целом похожи эти два изображения. Между ними находится другой вопрос: два кадра показывают одну и ту же сцену, но камера (или сцена) сместилась между ними – насколько? Это проблема смещения, и модуль image решает её единственным методом фазовой корреляции.
5.31.1. Смещение по фазовой корреляции¶
find_displacement() оценивает жёсткое выравнивание между двумя изображениями одинакового размера, используя фазовую корреляцию – частотный метод, который выполняет быстрое преобразование Фурье (FFT) над каждым изображением, взаимно коррелирует их фазы и находит пик в результате. Положение пика – это сдвиг, который выравнивает два изображения:
d = img.find_displacement(template)
print("shift:", d.x_translation, d.y_translation,
" response:", d.response)
Возвращаемый объект Displacement несёт x_translation и y_translation – сдвиг в пикселях по каждой оси – плюс response, оценку достоверности от 0.0 до 1.0, где 1.0 – идеальный пик. Отсеивание обнаружений ниже response > 0.3 отбрасывает ложные результаты, в которых фазовая корреляция так и не нашла чёткого пика.
В режиме по умолчанию rotation и scale равны соответственно 0.0 и 1.0; они принимают реальные значения только при logpolar=True (см. ниже).
Метод несёт два практических ограничения. Первое – размеры, кратные степени двойки: FFT, лежащий в основе фазовой корреляции, работает быстрее всего – а на камере и полностью поддерживается только – при размерах вроде 32 на 32, 64 на 64 и 128 на 128. Самый чистый вариант – захватывать напрямую в одном из этих размеров, передавая разрешение в framesize() в виде кортежа:
csi0.framesize((64, 64))
Приложение, которому нужно смещение из более крупного кадра, вместо этого обрезает фрагмент со стороной, кратной степени двойки, из интересующей его области и запускает сопоставитель на нём.
Второе – входные данные одинакового размера: roi и template_roi должны выбирать одинаковые ширину и высоту, иначе сопоставитель отклонит вызов. Два снимка с одной и той же камеры при одинаковой конфигурации удовлетворяют этому автоматически; захваченный кадр, сравниваемый с загруженным эталоном, требует, чтобы оба были сначала обрезаны до совпадающих фрагментов со стороной, кратной степени двойки.
5.31.2. Поворот и масштаб через лог-полярное преобразование¶
Режим по умолчанию находит только сдвиг. Когда два кадра также различаются по повороту вокруг выбранного центра или по масштабу относительно того же центра, выполнение фазовой корреляции над лог-полярной перепроекцией каждого изображения превращает эти параметры в сдвиг в лог-полярной системе координат – который тот же самый сопоставитель по фазовой корреляции может восстановить:
d = img.find_displacement(template, logpolar=True)
print("rotation rad:", d.rotation,
" scale:", d.scale,
" response:", d.response)
При logpolar=True метод выполняет тот же конвейер сопоставления над лог-полярно спроецированными изображениями вместо оригиналов. Поля rotation и scale результата возвращаются заполненными: rotation – это угол в радианах между двумя кадрами, scale – коэффициент масштаба между ними. x_translation и y_translation в этом режиме становятся бессмысленными (сдвиг вдоль лог-полярных осей не соответствует линейному сдвигу в источнике).
Ключевое слово fix_rotation_scale=True покрывает промежуточный случай: два изображения различаются одновременно по сдвигу и по повороту/масштабу, а приложению нужен только сдвиг после коррекции поворота и масштаба. Сопоставитель сначала выполняет лог-полярный проход, чтобы восстановить поворот и масштаб, применяет обратное преобразование к одному из изображений, а затем выполняет проход по сдвигу, чтобы восстановить оставшийся сдвиг. Этот флаг имеет смысл только при logpolar=False – он просит сопоставитель в режиме сдвига сначала устранить поворот/масштаб.
Паттерн из полярных преобразований – декартовы → полярные → сопоставление – это то, что find_displacement() с logpolar=True делает за один вызов. Приложение сохраняет эталонный лог-полярный фрагмент при запуске, захватывает и лог-полярно преобразует каждый текущий кадр, а метод восстанавливает разницу в повороте и масштабе между ними. Для приложений, которым нужен трекер, инвариантный к повороту и масштабу – стыкующийся робот, чья камера наклоняется и приближается к цели, стабилизированный подвес, которому нужно знать, как изображение поворачивается относительно эталона – это стандартная конструкция.
5.31.3. Классическое применение¶
Самое распространённое применение find_displacement() – это покадровая оценка движения в конвейере, обрабатывающем движущуюся камеру. Камера захватывает небольшой фрагмент со стороной, кратной степени двойки, на кадре N, захватывает фрагмент того же размера на кадре N+1, запускает find_displacement() на этих двух и считывает сдвиг в пикселях между ними. Сдвиг – это оценённое движение камеры (или сцены, в зависимости от того, чья система отсчёта важна) между двумя снимками, полезное для:
Сенсорики в стиле оптического потока – зависающий дрон с направленной вниз камерой использует покадровое смещение, чтобы оценить своё боковое движение и передать его обратно в полётный контроллер.
Стабилизации изображения – смещение между последовательными кадрами вычитается из захваченного изображения перед его записью или передачей, давая более плавный видеопоток.
Выравнивания при инспекции – сканирующая камера, движущаяся вдоль конвейера, использует покадровое смещение, чтобы совместить каждый кадр со следующим и построить сшитый вид всей детали.
Каждое из этих приложений принимает одну и ту же форму: захват, смещение, накопление в текущей оценке, снова захват.