Искажение изображения с помощью преобразования перспективы
-
20-09-2019 - |
Вопрос
Я пытаюсь выполнить перекос на изображении, как показано здесь
(источник: microsoft.com)
.
У меня есть массив пикселей, представляющих мое изображение, и я не уверен, что с ними делать.
Решение
Гораздо лучший способ сделать это - с помощью обратное отображение.
По сути, вы хотите "исказить" изображение, не так ли?Это означает, что каждый пиксель исходного изображения переходит в предопределенную точку - предопределение представляет собой матрицу преобразования, которая сообщает вам, как поворачивать, масштабировать, переводить, сдвигать и т.д.изображение, которое, по существу, принимает некоторую координату (x,y)
на вашем изображении и говорю, что "Хорошо, новая позиция для этого пикселя (f(x),g(y))
.
Это, по сути, то, что делает "деформация".
Теперь подумайте о масштабировании изображения ...скажем, в десять раз больше.Таким образом, это означает, что пиксель в (1,1)
становится пикселем в (10,10)
- а затем следующий пиксель, (1,2)
становится пикселем (10,20)
в новом образе.Но если вы будете продолжать делать это, у вас не будет значений для пикселя, (13,13)
потому что, (1.3,1.3)
не определено в вашем исходном изображении, и у вас будет куча отверстий в вашем новом изображении - вам придется выполнить интерполяцию для этого значения, используя четыре пикселя вокруг него в новом изображении, т. е. (10,10) , (10,20), (20,10), (200,2)
- это называется билинейная интерполяция.
Но вот еще одна проблема, предположим, что ваше преобразование не было простым масштабированием и было аффинным (как образец изображения, который вы опубликовали) - тогда (1,1)
стало бы чем-то вроде (2.34,4.21)
и тогда вам пришлось бы округлить их в выходном изображении до (2,4)
и тогда вам пришлось бы выполнить билинейную интерполяцию на новом изображении, чтобы заполнить пробелы, или более сложную интерполяцию - грязно, не так ли?
Теперь, есть ни за что чтобы отказаться от интерполяции, но нам может сойти с рук выполнение билинейной интерполяции, просто однажды.Каким образом?Простой, обратное отображение.
Вместо того чтобы рассматривать это как исходное изображение, переходящее к новому изображению, подумайте о том, откуда в исходном изображении будут взяты данные для нового изображения!Итак, (1,1)
в новом изображении будет получено некоторое обратное отображение в исходном изображении, скажем, (3.4, 2.1)
а затем выполните билинейную интерполяцию на исходном изображении, чтобы вычислить соответствующее значение!
Матрица преобразования
Хорошо, итак, как вы определяете матрицу преобразования для аффинного преобразования? Этот веб-сайт рассказывает вам, как это сделать, составляя различные матрицы преобразования для вращения, сдвига и т.д.
Преобразования:
Композитинг:
Окончательная матрица может быть получена путем компоновки каждой матрицы в указанном порядке, и вы инвертировать это, чтобы получить обратное отображение - используйте это, вычисляя положения пикселей в исходном изображении и интерполируя.
Другие советы
Если вам не хочется заново изобретать колесо, загляните в библиотеку OpenCV.Он реализует множество полезных функций обработки изображений, включая перспективные преобразования.Ознакомьтесь с Перспективный вариант cvWarpPerspective который я использовал для выполнения этой задачи довольно легко.
Как прокомментировал KennyTM, вам просто нужно аффинное преобразование, которое представляет собой линейное отображение, полученное путем умножения каждого пикселя на матрицу M и добавление результата к вектору перевода V.Это простая математика
end_pixel_position = M*start_pixel_position + V
где M представляет собой совокупность простых преобразований, таких как вращение или масштабирование, и V это вектор, который преобразует каждую точку ваших изображений путем добавления фиксированных коэффициентов к каждому пикселю.
Например, если вы хотите повернуть изображение, вы можете задать матрицу поворота следующим образом:
| cos(a) -sin(a) |
M = | |
| sin(a) cos(a) |
где a
это угол, на который вы хотите повернуть свое изображение.
При масштабировании используется матрица вида:
| s1 0 |
M = | |
| 0 s2 |
где s1
и s2
являются масштабирующими коэффициентами по обеим осям.
Для перевода у вас просто есть вектор V:
| t1 |
V = | |
| t2 |
это добавляет t1
и t2
в пиксельные координаты.
Затем вы объединяете матрицы в одно преобразование, например, если у вас есть масштабирование, поворот и перевод, в итоге вы получите что-то вроде:
| x2 | | x1 |
| | = M1 * M2 * | | + T
| y2 | | y1 |
где:
x1
иy1
являются координатами пикселей перед применением преобразования,x2
иy2
являются пикселями после преобразования,M1
иM2
используются ли матрицы для масштабирования и поворота (ЗАПОМНИ: композиция матриц не является коммутативной!ОбычноM1 * M2 * Vect != M2 * M1 * Vect
),T
это вектор перевода, используемый для перевода каждого пикселя.