Регистрация изображений с использованием питона и кросс-корреляции

Я получил два изображения, показывающие абсолютно одинаковый контент: пятна в 2D-гауссовой форме. Я называю эти два 16-битных png файла "left.png" и "right.png". Но так как они получены с помощью немного другой оптической системы, соответствующие пятна (физически одинаковые) появляются в немного разных позициях. Значение вправо слегка растянуто, искажено или около того нелинейным образом. Поэтому я хотел бы получить преобразование слева направо.

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

В первом подходе я пытался получить положения соответствующих пятен, чтобы получить относительные расстояния deltaX и deltaY. Затем эти расстояния я подгонял к расширению Тейлора до второго порядка T (x, y), давая мне x- и y-компоненту вектора смещения для каждого пикселя (x, y) слева, указывая на соответствующий пиксель (x ', y') справа.

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

Я действительно много пытался с этим, но не смог. У меня вопрос, есть ли у кого-то из вас идея или когда-либо подобное.

import numpy as np
import Image

left = np.array(Image.open('left.png'))
right = np.array(Image.open('right.png'))

# for normalization (http://en.wikipedia.org/wiki/Cross-correlation#Normalized_cross-correlation)    
left = (left - left.mean()) / left.std()
right = (right - right.mean()) / right.std()

Пожалуйста, дайте мне знать, если я смогу прояснить этот вопрос. Я все еще должен проверить, как отправлять вопросы, используя латекс.

Большое спасибо за вклад.

leftright

[left.png] http://i.stack.imgur.com/oSTER.png [right.png] http://i.stack.imgur.com/Njahj.png

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

ОБНОВЛЕНИЕ 1

Я пытаюсь прояснить свой вопрос. Я ищу векторное поле с векторами смещения, которые указывают от каждого пикселя в left.png к соответствующему пикселю в right.png. Моя проблема в том, что я не уверен насчет ограничений, которые у меня есть.

enter image description here

где вектор r (компоненты x и y) указывает на пиксель в left.png, а вектор r-prime (компоненты x- prime и y-prime) указывает на соответствующий пиксель в right.png. для каждого r существует вектор смещения.

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

enter image description here

Итак, я установил:

enter image description here и

enter image description here

Это имеет смысл для вас? Можно ли получить все дельта-х (х, у) и дельта-у (х, у) с взаимной корреляцией? Кросс-корреляция должна быть максимальной, если соответствующие пиксели связаны друг с другом через векторы смещения, верно?

ОБНОВЛЕНИЕ 2

Итак, алгоритм, о котором я думал, заключается в следующем:

  1. Deform right.png
  2. Получить значение кросс-корреляции
  3. Деформировать right.png дальше
  4. Получить значение кросс-корреляции и сравнить со значением до
  5. Если оно больше, хорошая деформация, если нет, переделайте деформацию и сделайте что-нибудь еще
  6. После максимизации значения взаимной корреляции узнайте, какая там деформация :)

О деформации: можно ли сначала сделать сдвиг вдоль x- и в направлении y, чтобы максимизировать взаимную корреляцию, затем на втором шаге растянуть или сжать x- и y-зависимый и на третьем шаге деформировать квадратичный x- и y и повторять эту процедуру итеративно У меня действительно есть проблема сделать это с целочисленными координатами. Как вы думаете, мне пришлось бы интерполировать картину, чтобы получить непрерывное распределение?? Я должен снова подумать об этом :( Спасибо всем за участие :)

Ответ 1

OpenCV (а вместе с ним и привязка Opencv python) имеет класс StarDetector, который реализует этот алгоритм.

В качестве альтернативы вы можете взглянуть на класс OpenCV SIFT, что означает преобразование параметров Invalidant Scale.

Обновление

Что касается вашего комментария, я понимаю, что "правильное" преобразование максимизирует кросс-корреляцию между изображениями, но я не понимаю, как вы выбираете набор преобразований, для которых нужно максимизировать. Возможно, если вы знаете координаты трех совпадающих точек (либо по некоторым эвристикам, либо путем выбора их вручную), и если вы ожидаете близости, вы можете использовать что-то вроде cv2.getAffineTransform, чтобы иметь хорошее начальное преобразование для процесса максимизации. Оттуда вы можете использовать небольшие дополнительные преобразования, чтобы иметь набор, над которым можно максимизировать. Но этот подход мне кажется как повторное изобретательство того, что SIFT может позаботиться.

Чтобы фактически преобразовать тестовое изображение, вы можете использовать cv2.warpAffine, который также может заботиться о значениях границы (например, pad с 0). Чтобы вычислить кросс-корреляцию, вы можете использовать scipy.signal.correlate2d.

Обновление

Ваше последнее обновление действительно прояснило некоторые моменты для меня. Но я думаю, что векторное поле перемещений - это не самая естественная вещь, которую нужно искать, и именно здесь произошло непонимание. Я больше думал о линиях преобразования global T, примененного к любой точке (x, y) левого изображения, дает (x ', y') = T (x, y) на правая часть, но T имеет одну и ту же аналитическую форму для каждого пикселя. Например, это может быть комбинация смещения, поворота, масштабирования, возможно, некоторых перспективных преобразований. Я не могу сказать, реалистично или не надеяться найти такое преобразование, это зависит от вашей установки, но если сцена физически одинакова с обеих сторон, я бы сказал, что разумно ожидать некоторого аффинного преобразования. Вот почему я предложил cv2.getAffineTransform. Конечно, тривиально вычислить ваше векторное поле перемещения из такого T, поскольку это просто T (x, y) - (x, y).

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

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

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

Обновление

Кажется, cv2.getAffineTransform ожидает неудобный тип входных данных 'float32'. Предположим, что исходные координаты (sxi,syi) и destination (dxi,dyi) с i=0,1,2, тогда вам нужно

src = np.array( ((sx0,sy0),(sx1,sy1),(sx2,sy2)), dtype='float32' )
dst = np.array( ((dx0,dy0),(dx1,dy1),(dx2,dy2)), dtype='float32' )

result = cv2.getAffineTransform(src,dst)

Ответ 2

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

  • Сделайте кросс-корреляцию на подкластерах точек. Возьмите, например, три точки в правом верхнем углу и найдите оптимальный сдвиг x-y через кросс-корреляцию. Это дает грубое преобразование для верхнего левого. Повторите для как можно большего количества кластеров, чтобы получить разумную карту ваших преобразований. Примените это с расширением Тейлора, и вы можете приблизиться к нему. Однако, чтобы ваша кросс-корреляция работала каким-либо образом, разница в смещении между пятнами должна быть меньше, чем протяженность пятна, иначе вы никогда не сможете полностью перекрывать все пятна в кластере одновременно с одним перемещением. В этих условиях вариант 2 может быть более подходящим.

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

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

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

Update

Я полагаю, что ваша оптика индуцирует нелинейные искажения и имеет два отдельных beampaths (разные фильтры в каждом?), что сделает связь между двумя изображениями еще более нелинейной. Аффинное преобразование, предложенное PiQuer, может дать разумный подход, но, вероятно, никогда не сможет полностью покрыть фактические искажения.

Я думаю, что ваш подход к подгонке к полиному Тейлора низкого порядка в порядке. Это работает для всех моих приложений с аналогичными условиями. Наивысшие порядки вероятно должны быть чем-то вроде xy ^ 2 и x ^ 2y; ничего более того, чего вы не заметите.

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

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

Обновление 2

Вы прокомментируете, что у вас проблемы с совпадением точек в двух изображениях. Если это так, я думаю, что ваш итеративный подход к взаимной корреляции может быть не очень надежным. У вас очень маленькие точки, поэтому перекрытие между ними произойдет только в том случае, если разница между двумя изображениями мала.

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

Возможно, вы можете пойти на смешанное решение: найти кросс-корреляцию кластеров точек, чтобы получить начальную точку для вашей оптимизации, а затем настроить деформацию, используя что-то вроде процедуры, описанной в вашем обновлении. Таким образом:

  • Для сегмента пикселя NxN найдите сдвиг между левым и правым изображениями
  • Повторите, например, 16 из этих сегментов
  • Вычислить аппроксимацию деформации с использованием этих 16 точек
  • Используйте это как отправную точку вашего подхода к оптимизации

Ответ 3

Возможно, вы захотите взглянуть на bunwarpj, который уже делает то, что вы пытаетесь сделать. Это не Python, но я использую его именно в этом контексте. Вы можете экспортировать простое текстовое преобразование сплайна и использовать его, если хотите.