Искажение изображения с использованием карты высот? - PullRequest
7 голосов
/ 02 марта 2011

У меня есть карта высот для изображения, которая показывает мне смещение каждого пикселя в направлении Z. Моя цель - сгладить искаженное изображение, используя только его карту высот.

Как бы я поступил так? Я знаю положение камеры, если это поможет.


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

Из этого спроецированного сдвига я мог извлечь X и Y-сдвиг каждого пикселя, которые я мог бы передать в cv.Remap().

Но я понятия не имею, как можно получить проецируемое трехмерное смещение точки с помощью OpenCV, не говоря уже о том, чтобы из нее построить карту смещения.


Вот мои справочные изображения для того, что я делаю:

Calibration Image Warped Image

Я знаю угол наклона лазеров (45 градусов), и по калибровочным изображениям я могу очень легко рассчитать высоту книги:

h(x) = sin(theta) * abs(calibration(x) - actual(x))

Я делаю это для обеих линий и линейно интерполирую две линии для генерации поверхности, используя этот подход (код Python. Он находится внутри цикла):

height_grid[x][y] = heights_top[x] * (cv.GetSize(image)[1] - y) + heights_bottom[x] * y

Надеюсь, это поможет;)


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

class Point:
  def __init__(self, x = 0, y = 0, z = 0):
    self.x = x
    self.y = y
    self.z = z

mapX = cv.CreateMat(cv.GetSize(image)[1], cv.GetSize(image)[0], cv.CV_32FC1)
mapY = cv.CreateMat(cv.GetSize(image)[1], cv.GetSize(image)[0], cv.CV_32FC1)

c = Point(CAMERA_POSITION[0], CAMERA_POSITION[1], CAMERA_POSITION[2])
theta = Point(CAMERA_ROTATION[0], CAMERA_ROTATION[1], CAMERA_ROTATION[2])
d = Point()
e = Point(0, 0, CAMERA_POSITION[2] + SENSOR_OFFSET)

costx = cos(theta.x)
costy = cos(theta.y)
costz = cos(theta.z)

sintx = sin(theta.x)
sinty = sin(theta.y)
sintz = sin(theta.z)


for x in xrange(cv.GetSize(image)[0]):
  for y in xrange(cv.GetSize(image)[1]):

    a = Point(x, y, heights_top[x / 2] * (cv.GetSize(image)[1] - y) + heights_bottom[x / 2] * y)
    b = Point()

    d.x = costy * (sintz * (a.y - c.y) + costz * (a.x - c.x)) - sinty * (a.z - c.z)
    d.y = sintx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) + costx * (costz * (a.y - c.y) - sintz * (a.x - c.x))
    d.z = costx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) - sintx * (costz * (a.y - c.y) - sintz * (a.x - c.x))

    mapX[y, x] = x + (d.x - e.x) * (e.z / d.z)
    mapY[y, x] = y + (d.y - e.y) * (e.z / d.z)


print
print 'Remapping original image using map...'

remapped = cv.CreateImage(cv.GetSize(image), 8, 3)
cv.Remap(image, remapped, mapX, mapY, cv.CV_INTER_LINEAR)

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

Есть идеи?

Ответы [ 3 ]

3 голосов
/ 08 марта 2011

В итоге я реализовал собственное решение:

for x in xrange(cv.GetSize(image)[0]):
  for y in xrange(cv.GetSize(image)[1]):

    a = Point(x, y, heights_top[x / 2] * (cv.GetSize(image)[1] - y) + heights_bottom[x / 2] * y)
    b = Point()

    d.x = costy * (sintz * (a.y - c.y) + costz * (a.x - c.x)) - sinty * (a.z - c.z)
    d.y = sintx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) + costx * (costz * (a.y - c.y) - sintz * (a.x - c.x))
    d.z = costx * (costy * (a.z - c.z) + sinty * (sintz * (a.y - c.y) + costz * (a.x - c.x))) - sintx * (costz * (a.y - c.y) - sintz * (a.x - c.x))

    mapX[y, x] = x + 100.0 * (d.x - e.x) * (e.z / d.z)
    mapY[y, x] = y + 100.0 * (d.y - e.y) * (e.z / d.z)


print
print 'Remapping original image using map...'

remapped = cv.CreateImage(cv.GetSize(image), 8, 3)
cv.Remap(image, remapped, mapX, mapY, cv.CV_INTER_LINEAR)

Это (медленно) переназначает каждый пиксель с помощью функции cv.Remap, и это, похоже, работает ...

0 голосов
/ 03 марта 2011

Разделите вашу сцену следующим образом:

  • у вас есть неизвестное растровое изображение I (x, y) -> (r, g, b)
  • у вас есть известное поле высоты H (x, y) -> h
  • у вас есть преобразование камеры C (x, y, z) -> (u, v), которое проецирует сцену на плоскость экрана

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

  • у вас есть скриншот S (u, v), который является результатом C (x, y, H (x, y)) для x, y in I
  • вы хотите создать скриншот S ' (u', v '), который является результатом C (x, y, 0) для x, y в I

Есть два очевидных способа приблизиться к этому; оба зависят от наличия точных значений для преобразования камеры.

  1. Приведение лучей: для каждого пикселя в S отбрасывайте луч обратно в сцену. Узнайте, где он попадает на поле высоты; это дает вам (x, y) в исходном изображении I , а пиксель экрана дает вам цвет в этой точке. Как только вы наберете столько I , сколько сможете восстановить, преобразуйте его, чтобы найти S '.

  2. Двойной рендеринг: для каждого x, y в I , проект для поиска (u, v) и (u ', v'). Возьмите пиксельный цвет из S (u, v) и скопируйте его в S ' (u', v ').

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

Edit:

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

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

0 голосов
/ 02 марта 2011

Искажение, основанное на расстоянии от камеры, происходит только при перспективной проекции. Если у вас есть положение (x, y, z) пикселя, вы можете использовать матрицу проецирования камеры, чтобы отодвинуть пиксели обратно в мировое пространство. С этой информацией вы можете визуализировать пиксели ортогональным способом. Однако у вас могут отсутствовать данные из-за первоначальной перспективной проекции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...