5.23. Коррекция перспективы¶
Предупреждение
Произвольная матрица transform размером 3 на 3 поддерживается только на OpenMV Cam N6 – на всех остальных платах это ключевое слово молча игнорируется. Приложения, которым нужно работать где-либо ещё, должны использовать готовый метод rotation_corr() (с его формой corners=) или предварительно вычислять скорректированное изображение вне платы.
Готовый метод rotation_corr() упаковывает определённое семейство перспективных деформаций за небольшим набором параметров и работает на каждой поддерживаемой плате. Некоторым приложениям нужна деформация, не вписывающаяся в эту форму: произвольное проективное переотображение из одного четырёхугольника в другой, откалиброванная коррекция для известного крепления, уже рассчитанная заранее, готовая матрица деформации, переданная каким-либо предшествующим алгоритмом. Для таких случаев draw_image() – наряду с copy(), crop() и scale() – принимает ключевое слово transform, которое берёт построенную вручную матрицу 3 на 3, напрямую описывающую деформацию.
5.23.1. Аффинные и проективные преобразования¶
Геометрические деформации выражаются в однородных координатах: позиция пикселя (x, y) с добавленной 1, умноженная на матрицу 3 на 3.
Аффинная форма – это место, с которого стоит начать. Её нижняя строка зафиксирована как \((0, 0, 1)\):
В развёрнутом виде каждая выходная координата представляет собой линейную комбинацию входных координат плюс константа:
что охватывает масштабирование, поворот, сдвиг и перенос в любой комбинации – и при всех них параллельные линии остаются параллельными.
Проективная (перспективная) форма освобождает нижнюю строку:
В развёрнутом виде:
Деление на \(w' = g x + h y + 1\) – это то, что делает преобразование проективным, а не просто аффинным. Когда \(g\) и \(h\) оба равны нулю, \(w'\) остаётся равным единице, и деление ничего не меняет – снова аффинная форма. Когда хотя бы одно из них ненулевое, \(w'\) изменяется в зависимости от входной позиции, и пиксели в разных позициях укорачиваются в перспективе на разную величину, что больше не сохраняет параллельность линий – это в точности трапециевидный эффект взгляда на плоскость под косым углом. Проективное преобразование – это самая общая геометрическая деформация, которая переводит прямые линии в прямые; масштабирование, отражение, транспонирование, поворот и коррекция поворота по четырём углам – всё это частные случаи одного преобразования.
Именованные преобразования напрямую вытекают из аффинной формы. Тождественное преобразование – это единичная матрица, и:
Для большинства построенных вручную преобразований приложение начинает с одного из них в качестве основы и домножает на дополнительные матрицы для каждой следующей операции, получая в итоге единственную матрицу 3 на 3, описывающую составную деформацию. Матрицы применяются справа налево: \(M = T R S\) выполняет сначала масштабирование, затем поворот, затем перенос. Составное преобразование, которое всем рано или поздно нужно, – это поворот вокруг центра изображения: голая матрица поворота вращает изображение вокруг начала координат пикселей в левом верхнем углу, поэтому центрированная версия перемещает центр \((c_x, c_y)\) в начало координат, выполняет поворот и перемещает его обратно:
5.23.2. Ключевое слово transform¶
Матрица передаётся через ключевое слово transform в виде ulab.numpy.ndarray размером 3 на 3. Метод, к которому стоит обратиться, – это draw_image(), который деформирует источник через матрицу по мере его отрисовки в приёмник – результат попадает в буфер, которым управляет приложение, и деформация комбинируется со всем остальным в вызове: масштабированием, альфа-смешиванием, маскированием.
import ulab.numpy as np
M = np.array([[1.2, 0.0, -20.0],
[0.0, 1.2, -15.0],
[0.0, 0.0, 1.0]])
canvas.draw_image(img, transform=M)
Пример деформирует img в canvas, масштабируя на 1.2 в каждом направлении и сдвигая влево и вверх на 20 и 15 пикселей соответственно, – аффинная деформация, построенная напрямую из элементов матрицы, описанных выше. То же ключевое слово в copy(), crop() и scale() применяет деформацию к самому изображению.