Подтвердить что ты не робот

Проективное преобразование

Учитывая два буфера изображения (предположим, что это массив из ints size width * height, с каждым элементом - значение цвета), как я могу отобразить область, определенную четырьмя сторонами из одного буфера изображения, в другое (всегда квадратное) изображение буфер? Я понял, что это называется "проективным преобразованием".

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

Пример: я написал короткую программу на Java, используя библиотеку обработки (process.org), которая захватывает видео с камеры. Во время начального шага "калибровки" захваченное видео выводится непосредственно в окно. Затем пользователь нажимает на четыре точки, чтобы определить область видео, которое будет преобразовано, а затем отображено в квадратное окно во время последующей работы программы. Если пользователь должен был щелкнуть по четырем точкам, определяющим углы двери, видимые под углом на выходе камеры, то это преобразование приведет к тому, что последующее видео отобразит преобразованное изображение двери во всю область окна, хотя несколько искажен.

4b9b3361

Ответ 1

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

То, что вы хотите, - это сопоставление от ваших старых (x, y) координат к вашим новым (x ', y') координатам. Вы можете сделать это с помощью матриц. Вам нужно найти матрицу проекций 2 на 4, такую, что P раз старые координаты равны новым координатам. Предположим, что вы сопоставляете строки с линиями (а не, например, прямые линии до парабол). Поскольку у вас есть проекция (параллельные линии не остаются параллельными) и перевод (скользящий), вам нужен коэффициент (xy) и (1). Нарисованы как матрицы:

          [x  ]
[a b c d]*[y  ] = [x']
[e f g h] [x*y]   [y']
          [1  ]

Вам нужно знать сквозной h, чтобы решить эти уравнения:

a*x_0 + b*y_0 + c*x_0*y_0 + d = i_0
a*x_1 + b*y_1 + c*x_1*y_1 + d = i_1
a*x_2 + b*y_2 + c*x_2*y_2 + d = i_2
a*x_3 + b*y_3 + c*x_3*y_3 + d = i_3

e*x_0 + f*y_0 + g*x_0*y_0 + h = j_0
e*x_1 + f*y_1 + g*x_1*y_1 + h = j_1
e*x_2 + f*y_2 + g*x_2*y_2 + h = j_2
e*x_3 + f*y_3 + g*x_3*y_3 + h = j_3

Опять же, вы можете использовать линейную алгебру:

[x_0 y_0 x_0*y_0 1]   [a e]   [i_0 j_0]
[x_1 y_1 x_1*y_1 1] * [b f] = [i_1 j_1]
[x_2 y_2 x_2*y_2 1]   [c g]   [i_2 j_2]
[x_3 y_3 x_3*y_3 1]   [d h]   [i_3 j_3]

Подключите свои углы к x_n, y_n, i_n, j_n. (Углы работают лучше, потому что они далеко друг от друга, чтобы уменьшить ошибку, если вы выбираете точки, например, с помощью кликов пользователя.) Возьмите инверсию матрицы 4x4 и умножьте ее на правую часть уравнения. Транспонированием этой матрицы является P. Вы должны иметь возможность находить функции для вычисления матрицы, обратной и умноженной в режиме онлайн.

Где вы, вероятно, будете иметь ошибки:

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

Ответ 2

РЕДАКТИРОВАТЬ

Приведенное ниже предположение об инвариантности соотношений углов неверно. Вместо этого проективные преобразования сохраняют перекрестные отношения и заболеваемость. Решение тогда:

  1. Найдите точку C 'на пересечении линий, определенных отрезками AD и CP.
  2. Найдите точку B 'на пересечении линий, определенных отрезками AD и BP.
  3. Определите перекрестное соотношение B'DAC ', то есть r = (BA' * DC ')/(DA * B'C').
  4. Построить проектируемую линию F'HEG '. Соотношение этих точек равно r, т.е. r = (F'E * HG ')/(HE * F'G').
  5. F'F и G'G будут пересекаться в проецируемой точке Q, поэтому, приравнивая перекрестные отношения и зная длину стороны квадрата, вы можете определить положение Q с помощью некоторой арифметической гимнастики.

Хмммм.... Я сделаю удар в этом. Это решение основано на предположении, что при преобразовании отношения углов сохраняются. Смотрите изображение для руководства (извините за плохое качество изображения... это действительно поздно). Алгоритм обеспечивает только сопоставление точки в четырехугольнике с точкой в квадрате. Вам все еще нужно будет реализовать работу с несколькими точками квадрата, отображаемыми в одну и ту же квадратную точку.

Пусть ABCD - четырехугольник, где A - верхняя левая вершина, B - верхняя правая вершина, C - нижняя правая вершина и D - нижняя левая вершина. Пара (xA, yA) представляет координаты x и y вершины A. Мы отображаем точки в этом четырехугольнике на квадрат EFGH, сторона которого имеет длину, равную m.

alt text

Вычислить длины AD, CD, AC, BD и BC:

AD = sqrt((xA-xD)^2 + (yA-yD)^2)
CD = sqrt((xC-xD)^2 + (yC-yD)^2)
AC = sqrt((xA-xC)^2 + (yA-yC)^2)
BD = sqrt((xB-xD)^2 + (yB-yD)^2)
BC = sqrt((xB-xC)^2 + (yB-yC)^2)

Пусть thetaD - это угол в вершине D, а thetaC - угол в вершине C. Вычислите эти углы, используя закон косинуса:

thetaD = arccos((AD^2 + CD^2 - AC^2) / (2*AD*CD))
thetaC = arccos((BC^2 + CD^2 - BD^2) / (2*BC*CD))

Мы отображаем каждую точку P в четырехугольнике на точку Q в квадрате. Для каждой точки P в четырехугольнике сделайте следующее:

  • Найти расстояние DP:

    DP = sqrt((xP-xD)^2 + (yP-yD)^2)
    
  • Найдите расстояние CP:

    CP = sqrt((xP-xC)^2 + (yP-yC)^2)
    
  • Найдите угол thetaP1 между CD и DP:

    thetaP1 = arccos((DP^2 + CD^2 - CP^2) / (2*DP*CD))
    
  • Найдите угол thetaP2 между CD и CP:

    thetaP2 = arccos((CP^2 + CD^2 - DP^2) / (2*CP*CD))
    
  • Отношение тэта P1 к тэтаD должно быть отношением тэта Q1 к 90. Поэтому рассчитайте тэта Q1:

    thetaQ1 = thetaP1 * 90 / thetaD
    
  • Аналогично рассчитаем thetaQ2:

    thetaQ2 = thetaP2 * 90 / thetaC
    
  • Найти расстояние HQ:

    HQ = m * sin(thetaQ2) / sin(180-thetaQ1-thetaQ2)
    
  • Наконец, положение x и y Q относительно нижнего левого угла EFGH:

    x = HQ * cos(thetaQ1)
    y = HQ * sin(thetaQ1)
    

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

Ответ 3

Я думаю, что вы находитесь за плоской гомографией, посмотрите на эти лекции:

http://www.cs.utoronto.ca/~strider/vis-notes/tutHomography04.pdf

Если вы прокрутите список до конца, вы увидите пример того, что вы описываете. Я ожидаю, что в библиотеке Intel OpenCV будет функция, которая будет делать именно это.

Ответ 4

Существует проект проекта С++ в CodeProject, который включает источник для проективных преобразований растровых изображений. Математика находится в Википедии здесь. Заметим, что, насколько я знаю, проективное преобразование не будет отображать любой произвольный четырехугольник на другой, но будет делать это для треугольников, вы также можете искать искажения преобразования.

Ответ 5

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

Итак, если вы не хотите вдаваться в сложное кодирование, используйте чужую магическую функцию, как предположили smacl и Ian.

Ответ 6

Вот как это делается в принципе:

  • отображает начало координат A в начало B через вектор трансляции t.
  • возьмем единичные векторы A (1,0) и (0,1) и вычислим, как они будут отображаться на единичные векторы B.
  • это дает вам матрицу преобразования M, так что каждый вектор a в отображает на M a + t
  • инвертируем матрицу и нейтрализуем вектор traslation, поэтому для каждого вектора b в B вы имеете обратное отображение b M -1 (b - t)
  • как только у вас есть это преобразование, для каждой точки в целевой области в B найдите соответствующее значение в и скопируйте.

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