Яркость и контрастность CSS3 на стороне сервера

В CSS3 есть замечательное свойство, позволяющее менять яркость и контрастность изображений непосредственно в браузере. В Chrome и Opera это делается с помощью свойства -webkit-filter (в других браузерах есть свои свойства CSS для этого). Это же свойство позволяет менять и другие характеристики, такие как насыщенность, применять сепию, и делать изображение расплывчатым, но разговор пойдёт только о яркости и насыщенности.

Например, добавление CSS-свойства -webkit-filter: brightness(1.5) contrast(1.2); сделает изображение намного ярче и контрастнее.

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

Однако несмотря на всю мощь ImageMagick у него нет прямых функций, которые позволили бы нам напрямую обработать изображение с помощью значений яркости и контрастности, полученных из CSS-фильтра. Зато есть функция «-level», с помощью которой можно модифицировать яркостные характеристики сразу всех пикселей изображения. 

-level black_point{,white_point}{%}{,gamma}

Функция level в imagemagick имеет три параметра — уровень чёрного, уровень белого и гамма-коррекция. Последний параметр нам не пригодится. Первые два параметра можно задавать либо в процентах, либо в абсолютных величинах (от 0 до максимального значения яркости пиксела, которое может быть либо 255, либо 65535 в зависимости от формата изображения). Мы будем работать в процентах, это гораздо удобнее.

Итак, команда для преобразования изображения будет выглядеть следующим образом

convert src.jpg -level z1%,z2% dest.jpg

Дальнейшая задача будет заключаться в том, чтобы преобразовать значения CSS-яркости (обозначим его b) и CSS-контрастности (обозначим его c) в величины z1 и z2 (я назвал их так для более удобного использования в формулах).

Углубившись в теорию цифровой обработки и потратив 3 часа на подбор z1 и z2 «вручную», я выяснил, что формулы преобразования для яркости b (при неизменной контрастности или c=1) будут следующими

    \[z_1 = 0, z_2 = \frac{1}{b} * 100\]

Соответствующие формулы преобразования для контрастности c (при неизменной яркости, b=1) будут такими

    \[z_1 = \frac{c-1}{2c} * 100, z_2 = \frac{c+1}{2c} * 100\]

Также методом научного «тыка» я выяснил, что при наличии неединичных значений и b и c (т.е. нужно изменить и яркость и контрастность), можно выполнить два преобразования — сперва применить convert для яркости, затем — для контрастности (в таком и только таком порядке), используя промежуточный временный файл. То есть код будет выглядеть так:

convert src.jpg -level <z1>%,<z2>% temp.jpg  # z1 и z2 — значения для яркости

convert temp.jpg -level <z1_>%,<z2_>% dest.jpg  # z1_ и z2_ — значения для контрастности

Но два преобразования — это некрасиво. Можно ли подобрать такие z1 и z2, чтобы можно было применить и яркость и контрастность одновременно? Несмотря на то, что результат получился в итоге простым, изначально он был совсем неочевиден. Поэтому я решил вспомнить школьный курс алгебры и провести необходимые вычисления.

Будем отталкиваться о того, что формула для изменения яркости пиксела p при заданных z1, z2 выглядит следующим образом (формула также получена мной полуэмпирическим путём, но она верная, можете мне доверять)

(1)   \begin{equation*} p_1 = \frac{p_0-z_1}{z_2-z_1}\end{equation*}

Таким образом, можно подставить в эту формулу значения z1 и z2 для яркости b и получим

    \[p_1 = p_0 b\]

И для контрастности

    \[p_1 = \frac{(2p_0-1)c+1}{2}\]

Теперь можно подставить в формулу для контрастности значение p из первой формулы и получим общую формулу для яркости пиксела

    \[p_1 = \frac{(2p_0 b-1)c+1}{2}\]

Теперь осталось только преобразовать эту формулу в функцию от z1 и z2, используя (1).

Получаем систему уравнений

(2)   \begin{equation*}\begin{cases}\frac{1}{z_2-z_1} = b c \\\frac{-z_1}{z_2-z_1} = \frac{1-c}{2}\end{cases}\end{equation*}

Решая которую, находим искомые выражения для z1 и z2

    \[ z_1 = \frac{c-1}{2 b c}, z_2 = \frac{c+1}{2 b c} \]

Ниже я привожу полный код, который следует использовать для преобразования CSS3-яркости и контрастности с помощью ImageMagick

// Example CSS3 values of brightness and contrast
$b = 1.5;
$c = 1.2;

// Calculate level values
$z1 = ($c - 1) / (2 * $b * $c);
$z2 = ($c + 1) / (2 * $b * $c);

// Doing a conversion using ImageMagick
exec('convert src.jpg -level '.($z1 * 100).'%,'.($z2 * 100).'% dest.jpg');

// P.S. You can use Imagick::levelImage($z1, null, $z2); if you are using Imagick PECL extension

На этом всё.

Отправить ответ

Оставьте первый комментарий!

Notify of
avatar
wpDiscuz