Я пытаюсь выполнить скос на изображении, как один показанный здесь
(источник: 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)
, а затем выполнить билинейную интерполяцию на исходном изображении. выяснить соответствующее значение!
Итак, как вы определяете матрицу преобразования для аффинного преобразования? На этом веб-сайте рассказывается, как это сделать, составляя различные матрицы преобразования для вращения, сдвига и т. Д.
Окончательная матрица может быть получена путем составления каждой матрицы в указанном порядке, и вы инвертируете ее, чтобы получить обратное отображение - используйте это вычисление положений пикселей в исходном изображении и интерполировать.
Как прокомментировал 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
- вектор перевода, используемый для перевода каждого пикселя. Если вам не хочется изобретать колесо заново, загляните в библиотеку OpenCV. Он реализует множество полезных функций обработки изображений, включая перспективные преобразования. Ознакомьтесь с cvWarpPerspective , с помощью которого я довольно легко справился с этой задачей.