Pull to refresh

Comments 46

По сути, весь метод заключается в том, чтобы не сразу интерполировать исходное изображение в нужный размер, а уменьшать в два раза (или сколько позволяет размер выбранного ядра свёртки, чтобы не потерять ни одного пикселя), повторяя столько раз, сколько нужно (плюс последний раз уже на дробный коэффициент). Этот трюк операторы Фотошопа проворачивают ещё с доинтернетных времён, следуя точно таким же рассуждениям, как в статье.

По сути нет. Вы описали совсем другой метод. Правда я не понимаю, зачем это делать в фотошопе, там нормальные свертки и время работы ресайза не так критично.

В фотошопе до сих пор из коробки нет качественных Lanczos/Mitchell, только bicubic и nearest, поэтому чтобы добиться качественного рейсайза делали всё как в статье.
Что-то я не понял «константное время относительно размера исходного изображения» — свёртки тоже работают за константное время относительно исходного изображения. Что у вас исходным изображением и вход и выход называется?

Свертки работают тем медленнее, чем больше размер исходного изображения. Смотрите второй листинг в статье:


>>> 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. Взять из него один пиксель — Х
Константа.

А вот относительно конечного линейное — чем болье пикселей надо взять, тем больше будет время. Но время увеличится линейно.

У вас в одном примере есть и «конечное» и «целевое» изображение. Я не понимаю откуда куда вы ресазите )

Опечатался. :))
Иcходное 1 — 1000х1000. Взять из него один пиксель — X
Иcходное 2 — 2000х2000. Взять из него один пиксель — Х

Так. Размер исходного изображения поменялся, сложность и время нет. Значит сложность относительно исходного размера — константа. Верно же? Или у меня где-то не так написано?

Всё, я понял вашу формулировку. Спасибо. Извините за потраченное время.
А если брать пиксели не из регулярной сетки, а случайным образом, с вкладом, обратно пропорциональным расстоянию до узлов регулярной сетки? Тогда можно будет брать любое число опорных точек, а не фиксированное (х2, х4) количество.

Вот тут есть несколько вариантов, как выбирать точки: 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 и то, что делает видекарта при суперсемплинге — усредняет цвет нескольких имеющихся у нее точек исходного изображения.

Спасибо, добавил в статью.

UFO just landed and posted this here
Чудес не бывает. Как ни крути, но в четвертом методе мы выкидываем большое количество данных ради скорости, что естественно сказывается на результате.
Но суть в том, что потеря часто настолько мала, что в сравнении с потерями в скорости во втором методе — кажетя вполне приемлемой.

На выходе у нас выбор из:
1 — очень дешево/очень плохо
2 — очень дорого/отлично
3 — дешево/хорошо
UFO just landed and posted this here
По сути, ваш метод это «ближайший сосед», но с подавлением шума в заданное количество раз. Ведь от куда берется дребезг в оригинальном методе — это высокочастотный шум. Вы делаете 2, 3, 4 — несколько «ближайших соседей» и усредняете это шум в заданное количество раз, т.е. эмитируете длинную выдержку фотоаппарата при больших (шумящих) ISO.
А чем будет плох метод, когда все точки исходного изображения усредняются условно говоря за один проход?
Мне нужно было уменьшить изображение в 80 раз по одной оси и в 60 по другой. Я шел сначала по линиям и усреднял 80 точек в одну, формируя при этом новую матрицу, а потом проходил новую матрицу по столбцам усредняя в 60 раз. Поскольку все происходило линейно, было быстро.
Все входные изображения единой размерности и кратно уменьшаются.
А чем будет плох метод, когда все точки исходного изображения усредняются условно говоря за один проход?

Тем, что это метод ресайза не за константное время относительно размера исходного изображения.

Раз уж тут такая тема — кто-нибудь может посоветовать, как правильно уменьшать картинки в PHP?
Я использовал imagecopyresized и результат похож на первую картинку в этой статье.
Документация и подскажет. Прямо в статье по этой функции есть строка, под уменьшеной картинкой:
The image will be output at half size, though better quality could be obtained using imagecopyresampled().

Существует способ изменить размер картинки получив минимум артефактов за практически константное время — использовать интегральное преобразование для исходного изображения, а затем уже для него применять свёртки.
Изменять размер ядра свёртки в зависимости от коэффициента масштабирования не понадобится.
Ну а после свёрток дифференцировать картинку.

Извиняюсь, промахнулся. Для @arquolo — нет, для интегрального преобразования нужно один раз пройтись по всем точкам исходного изображения, а потом уже можно будет быстро получать уменьшенные копии. Автор же предлагает усовершенствованный «nearest nebour», но с подавлением шума в заданное количество раз. Метод, конечно, имеет свои ограничения, но для превьюшек, по-моему, не плохо.
И в чем беда. Для получения изображения его же надо загрузить (подготовить). Так что интегральное преобразование в замер быстродействия не попадёт. Если нужно качественное преобразование то еще и цветовое представление сменить и другие подготовительные операции. А уже потом масштабировать в разных вариантах за константное время.
Если исходное изображение закодировать на основе wevalet-ов как это сделано в djvu. То можно практически «бесплатно» получать разные масштабы.
В более частных случаях скорость того или иного метода может быть сколь угодно высокой.
Так что интегральное преобразование в замер быстродействия не попадёт.
Если так рассуждать, то мы просто не сможем ничего сравнить. Очевидно, сравнение методов уменьшения изображений нужно проводить в одних и тех же начальных условиях. Обычно это монохромная битовая карта яркостей.
Если нужно качественное преобразование то еще и цветовое представление сменить и другие подготовительные операции. А уже потом масштабировать в разных вариантах за константное время.
Это уже совсем другая задача. В статье рассматривается метод как сделать быстро (nearest nebour), но при этом уменьшить высокочастотный шум. О качестве речи не идет. И как не странно, есть куча прикладных задач где нужен именно такой подход.
Вот только почему-то забыли, что открытие (декодирование) исходной картинки будет происходить не за константное время, а за пропорциональное её размеру.
Так что общее время работы алгоритма будет не костантным, а линейным от количества пикселей (асимптотически).
А вот реальные замеры (желательно используя библиотечные методы масштабирования, напианные на C), хотелось бы увидеть для различных размеров картинок в виде графиков.

А почему я должен «помнить» о декодированиии изображения, если речь о ресайзе?

Потому что исходные картинки будут в .jpg или .png.
Нет разницы, будет ресайз занимать 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')





Sign up to leave a comment.

Articles

Change theme settings