Comments 46
По сути нет. Вы описали совсем другой метод. Правда я не понимаю, зачем это делать в фотошопе, там нормальные свертки и время работы ресайза не так критично.
Свертки работают тем медленнее, чем больше размер исходного изображения. Смотрите второй листинг в статье:
>>> from PIL import Image
>>> im = Image.open('pineapple.jpeg'); im.load(); im.size
(2560, 1600)
>>> %time im.resize((256, 170), Image.BICUBIC)
Wall time: 33.2 ms
>>> im = Image.open('space.jpeg'); im.load(); im.size
(4928, 3280)
>>> %time im.resize((256, 170), Image.BICUBIC)
Wall time: 130 ms
Опечатки вроде в личку принято писать.
Тут нет опечатки. Время константное относительно разрешения исходного изображения. От разрешения конечного изображения время линейное.
Предположим конечное — 1х1 пиксель.
Целевое 1 — 1000х1000. Взять из него один пиксель — X
Целевое 2 — 2000х2000. Взять из него один пиксель — Х
Константа.
А вот относительно конечного линейное — чем болье пикселей надо взять, тем больше будет время. Но время увеличится линейно.
Вот тут есть несколько вариантов, как выбирать точки: https://en.wikipedia.org/wiki/Supersampling#Supersampling_patterns
Случайный мне не нравится, потому что на саму рандомизацию уйдет много ресурсов. А больше всего нравится RGSS. Там смысл в том, что точки распределены более-менее равномерно, но ни одна пара не лежит на горизонтальной или вертикальной прямой, что дает более репрезентативную выборку для реальных изображений, где вертикальные и горизонтальные линии встречаются часто. Но любой алгоритм кроме равномерной сеточки придется программировать, а тут смысл именно в том, что программировать ничего не надо.
Мда… "фильтр с фиксированным ядром" — надо же… Остались ещё разработчики графического софта, которые не знают, что при уменьшении изображений тупая интерполяция не работает? Казалось бы, 20 лет назад все знали про муар и прочие проблемы...
Кстати, описанный вами метод (который можно описать гораздо короче: по ближайшим соседям уменьшаем в целое количество раз, а потом интерполируем) на классическом тестовом изображении в виде "шахматной доски" тоже даст интересные эффекты :-) (вполне понятные из описания через разбиение на 2 этапа). Ну а из реальных, не синтетических картинок — проблемы вызовут тонкие линии на изображении (станут прерывистыми). Так что если готовы сколько-то просадить производительность — лучше всё же не жадничать и задействовать mip-maps. Если их создать сразу при загрузке/распаковке изображения в память — просадки по производительности практически не будет.
Остались ещё разработчики графического софта, которые не знают, что при уменьшении изображений тупая интерполяция не работает?
В статье есть не полный список ПО: OpenCV, канва в браузере. Еще я много раз видел, как браузеры используют его для обычных картинок для быстрой черновой отрисовки при изминении размеров, а уже через секунду отрисовывают на чистовую с помощью сверток. Ну а какой-нибудь ИЕ11 использует фиксированное ядро всегда.
который можно описать гораздо короче
Вы ошиблись в описании. Уменьшаем не в целое количество раз, а до размера в целое количество раз больше конечного.
проблемы вызовут тонкие линии на изображении (станут прерывистыми)
Это можно посмотреть на фотографии схемы метро, там действительно некоторые линии прерываются при 2x.
Насчёт OpenCV — не совсем верно. Для него в доке английским по белому написано: "To shrink an image, it will generally look best with CV_INTER_AREA interpolation". Т.е. использование билинейной/бикубической интерполяции для уменьшения изображения — это не ошибка в библиотеке, а misuse (некорректное использование). Ну, как если из ящика с инструментами вынуть молоток и забить им шуруп.
А в описании действительно ошибся, спасибо (рефлекторно описал последовательность, которая лучше будет работать при масштабе близком к 1x-2x)
Для него в доке
Это PHP-стайл: когда делается полурабочая функция, а в документации пишется как именно она сломана. Не хочу никого обидеть, но почему-то в PHP такое встречается очень часто.
Не скажу за PHP, но, учитывая область применения OpenCV, универсальная функция масштабирования, работающая для разных масштабов по разным алгоритмам (и автоматически выбирающая алгоритм), кажется не оптимальным решением. Хотя иметь помимо констант CV_INTER_AREA/CV_INTER_CUBIC/CV_INTER_LINEAR/CV_INTER_NN какую-нибудь CV_INTER_HIGH_QUALITY, которая бы автоматически переключалась между CV_INTER_CUBIC и CV_INTER_AREA — выглядит разумным. Ну, мало ли — кто-то на OpenCV свой Фотошоп писать будет =)
Что-то я не понял чем ваш алгоритм отличается от суперсемплинга?
Я ни разу не встречал, чтобы описанный метод назывался суперсемлпнгом (даже не встречал, чтобы он в принципе был где-то реализован). Но если подумать, что делает видеокарта при суперсемплинге? Она из бесконечного кол-ва точек (отренидирить сцену мы можем в бесконечном разрешении) выбирает 4 точки для каждого конечного пикселя, так что да, в каком-то смысле это и есть суперсемплинг.
В статье я называю суперсемплингом усреднение цвета всех точек, которые попадают под конечный пиксель в масштабе исходного изображения. Это именно то, что делает OpenCV с фильтром INTER_AREA
и то, что делает видекарта при суперсемплинге — усредняет цвет нескольких имеющихся у нее точек исходного изображения.
Не в каком-то смысле, а это он самый и есть: https://en.wikipedia.org/wiki/Supersampling
Но суть в том, что потеря часто настолько мала, что в сравнении с потерями в скорости во втором методе — кажетя вполне приемлемой.
На выходе у нас выбор из:
1 — очень дешево/очень плохо
2 — очень дорого/отлично
3 — дешево/хорошо
Мне нужно было уменьшить изображение в 80 раз по одной оси и в 60 по другой. Я шел сначала по линиям и усреднял 80 точек в одну, формируя при этом новую матрицу, а потом проходил новую матрицу по столбцам усредняя в 60 раз. Поскольку все происходило линейно, было быстро.
Все входные изображения единой размерности и кратно уменьшаются.
Я использовал imagecopyresized и результат похож на первую картинку в этой статье.
Существует способ изменить размер картинки получив минимум артефактов за практически константное время — использовать интегральное преобразование для исходного изображения, а затем уже для него применять свёртки.
Изменять размер ядра свёртки в зависимости от коэффициента масштабирования не понадобится.
Ну а после свёрток дифференцировать картинку.
Если исходное изображение закодировать на основе wevalet-ов как это сделано в djvu. То можно практически «бесплатно» получать разные масштабы.
Так что интегральное преобразование в замер быстродействия не попадёт.Если так рассуждать, то мы просто не сможем ничего сравнить. Очевидно, сравнение методов уменьшения изображений нужно проводить в одних и тех же начальных условиях. Обычно это монохромная битовая карта яркостей.
Если нужно качественное преобразование то еще и цветовое представление сменить и другие подготовительные операции. А уже потом масштабировать в разных вариантах за константное время.Это уже совсем другая задача. В статье рассматривается метод как сделать быстро (nearest nebour), но при этом уменьшить высокочастотный шум. О качестве речи не идет. И как не странно, есть куча прикладных задач где нужен именно такой подход.
Так что общее время работы алгоритма будет не костантным, а линейным от количества пикселей (асимптотически).
А вот реальные замеры (желательно используя библиотечные методы масштабирования, напианные на C), хотелось бы увидеть для различных размеров картинок в виде графиков.
А почему я должен «помнить» о декодированиии изображения, если речь о ресайзе?
Нет разницы, будет ресайз занимать 3 мс или 20 мс, если декодирование картинки будет занимать 300 мс.
Потому что исходные картинки будут в .jpg или .png.
Я не понял откуда взялось ваше «будут». Исходные картинки могут быть в .jpg или .png. Могут быть загружены час назад, могут быть сгенерированы на лету, может быть все что угодно. И все это никак не связано с ресайзом.
Основное применение данной техники — это генерация thumbnails.
Основное применение данной техники — это генерация thumbnails.
Ну вы снова придумали. Для тумбнейлов это не очень подходит, это подходит именно для рендеринга документов (браузеры, фотошопы).
см https://habrahabr.ru/post/340966/#comment_10492298
Я кстати попробовал RGSS (сэмулировал как мог), результат мне не понравился:
# coding: utf-8
from __future__ import division
import sys
from PIL import Image
im = Image.open(sys.argv[1])
w = 256
h = int(w / im.width * im.height + 0.5)
s = im.width / w
im0 = im.transform((w, h), Image.AFFINE,
(im.width / w, 0, -s*.35, 0, im.height / h, -s*.15),
Image.NEAREST)
im1 = im.transform((w, h), Image.AFFINE,
(im.width / w, 0, s*.35, 0, im.height / h, s*.15),
Image.NEAREST)
im2 = im.transform((w, h), Image.AFFINE,
(im.width / w, 0, s*.15, 0, im.height / h, -s*.35),
Image.NEAREST)
im3 = im.transform((w, h), Image.AFFINE,
(im.width / w, 0, -s*.15, 0, im.height / h, s*.35),
Image.NEAREST)
Image.blend(
Image.blend(im0, im1, 0.5),
Image.blend(im2, im3, 0.5),
0.5
).save('_out.RGSS.png')
Качественное уменьшение изображений за константное время