5.21. Масштабирование, отражение и обрезка¶
Все предыдущие подразделы работали с пикселями в тех же позициях, где они изначально находились. Семейство преобразований меняет это. Масштабирование отправляет каждый входной пиксель в другую выходную позицию, возможно сразу в несколько выходных позиций (при увеличении) или в позицию, общую с несколькими другими входными пикселями (при уменьшении). Отражение и поворот делают то же самое через другое отображение. Обрезка сохраняет прямоугольное подмножество входных пикселей и отбрасывает остальные.
Модуль image предоставляет это семейство через три метода, которые разделяют большинство своих аргументов и большую часть своего поведения:
copy()– создаёт копию изображения, возможно масштабированную, обрезанную или переориентированную.crop()– та же операция, что иcopy, но с расчётом на то, что приложение собирается выбрать подпрямоугольник из источника.scale()– то же самое, но с расчётом на то, что приложение собирается изменить размер результата.
Все три используют одни и те же аргументы и один и тот же механизм преобразования; различие в том, куда по умолчанию попадает результат. copy() создаёт новое изображение, тогда как crop() и scale() изменяют источник на месте.
5.21.2. Интерполяция: AREA, BILINEAR, BICUBIC¶
Когда масштабирование отправляет каждый выходной пиксель в позицию, которая не совпадает ни с одним отдельным входным пикселем, метод должен выбрать, какое значение записать. Этим управляют три флага:
image.BILINEAR интерполирует между четырьмя ближайшими входными пикселями с весами по их расстоянию от выходной позиции. Результат более гладкий, чем у метода ближайшего соседа, без заметных ступенчатостей на диагональных линиях, но дополнительная арифметика обходится примерно вчетверо дороже прохода методом ближайшего соседа. Правильный выбор для большинства задач увеличения и для любого нецелого коэффициента масштабирования.
image.BICUBIC интерполирует между шестнадцатью ближайшими входными пикселями с использованием кубической кривой, что даёт ещё более гладкие результаты ценой ещё большего объёма арифметики. Лучшее качество для требовательных к нему приложений, чувствительных к стоимости; редко стоит дополнительных вычислений для текущих кадров, которые IDE будет лишь отображать.
image.AREA усредняет каждый входной пиксель, попадающий внутрь области выходного пикселя – правильный алгоритм для уменьшения. Билинейная и бикубическая – это интерполяторы: они оценивают значение между пикселями источника, что нужно при увеличении, но при уменьшении каждый выходной пиксель покрывает множество пикселей источника, а интерполятор считывает лишь несколько ближайших – детали, которые он пропускает, возвращаются в виде наложения спектров. image.AREA вместо этого включает каждый покрытый пиксель в усреднение.
Алгоритм масштабирования по умолчанию без какого-либо hint – это метод ближайшего соседа, который является самым дешёвым и правильным ответом, когда источник уже находится в пиксельном разрешении назначения.
5.21.3. Ориентация: отражения и повороты¶
Флаги ориентации – это небольшой набор булевых преобразований, которые свободно сочетаются друг с другом и с флагами интерполяции:
image.VFLIPотражает изображение по вертикали (верх становится низом).image.HMIRRORотражает его по горизонтали (лево становится правом).image.TRANSPOSEменяет местами оси x и y (строки становятся столбцами).
Большинство поворотов получается путём комбинирования этих трёх. Модуль также предоставляет именованные сокращения:
image.ROTATE_90(=VFLIP | TRANSPOSE)image.ROTATE_180(=HMIRROR | VFLIP)image.ROTATE_270(=HMIRROR | TRANSPOSE)
В коде:
img.copy(hint=image.ROTATE_90, copy_to_fb=True)
5.21.4. Обработка соотношения сторон¶
Когда соотношение сторон источника не совпадает с прямоугольником, в который он вписывается, три флага решают, что делать с этим несоответствием:
image.SCALE_ASPECT_KEEP сохраняет соотношение сторон источника и добавляет чёрные полосы к результату – источник масштабируется, пока не впишется внутрь назначения, а остаток назначения заполняется пустыми (нулевыми) пикселями. Правильный выбор, когда сохранение источника без искажений важнее, чем заполнение всего вывода.
image.SCALE_ASPECT_EXPAND сохраняет соотношение сторон источника и обрезает его – источник масштабируется, пока не заполнит назначение, а части, выходящие за пределы назначения, отсекаются. Правильный выбор, когда заполнение всего вывода важнее, чем видимость каждой части источника.
image.SCALE_ASPECT_IGNORE игнорирует соотношение сторон и растягивает источник, чтобы заполнить назначение, принимая любые вносимые при этом искажения. Правильный выбор, когда приложение уже учло искажение – например, когда размеры назначения на самом деле не являются прямоугольником той же сцены.
По умолчанию (без установленного флага соотношения сторон) ведёт себя так же, как SCALE_ASPECT_IGNORE: растягивает до заполнения. Приложения, для которых важно соотношение сторон, явно указывают один из трёх.
5.21.5. Когда какой выбирать¶
Большинство изменений размера используют scale() с парой x_scale / y_scale и подсказкой интерполяции:
img.scale(x_scale=0.5, y_scale=0.5, hint=image.AREA)
Большинство поворотов используют тот же вызов с hint=image.ROTATE_90 или аналогичным.
Обрезка использует crop() с нестандартным roi:
img.crop(roi=(40, 30, 200, 150))
Когда источник должен пережить операцию – захват опорного кадра, получение миниатюры кадра, который вот-вот будет обработан с разрушением – copy() создаёт результат как новое изображение и оставляет источник нетронутым:
thumbnail = img.copy(x_scale=0.25, y_scale=0.25, hint=image.AREA)
Именно это поведение по умолчанию и есть настоящее различие за тремя именами: scale и crop преобразуют на месте, copy выделяет память. Ключевые аргументы размещения результата перекрывают разрыв: copy=True у scale или crop выделяет результат как отдельный буфер в куче вместо перезаписи источника, а copy_to_fb=True у любого из трёх помещает его в буфер кадра для предпросмотра в IDE.