Графические фильтры на основе матрицы скручивания

    UPD: Заголовок изменен, что бы более соответствовать теме статьи

    В статье пойдет речь об использовании convolution matrix (матрицы скручивания или матрицы свертки), с помощью которой можно создавать и накладывать на изображения фильтры, такие как blur, sharpen и многие другие.

    Cтатья будет интересна не только веб-программистам, но и всем кто так или иначе занимается программной обработкой изображений, поскольку функции для работы с матрицей скручивания имеются во многих языках (точно известно о php и flash). Так же, статья будет интересна дизайнерам, использующим Adobe Photoshop, поскольку в нем имеется соответствующий фильтр (Filter-Other-Custom).

    Примеры будут на языке PHP с использованием библиотеки GD. Теория, практика, примеры (осторожно, много картинок!)


    Теория


    Говоря не математическим языком, convolution – это преобразование одной матрицы с помощью другой, которая называется ядром («kernel»). При обработке изображений в качестве исходных выступают матрицы RGB-каналов пикселей в прямоугольных координатах.

    В качестве ядра обычно используется матрица размером 3x3, но возможно и больше (5x5, 7x7 и т.д.). Ядро содержит степени влияния («ценности») окружающих значений элемента на сам элемент.

    Преобразования происходит следующим образом. Каждый элемент исходной матрицы умножается центральное значение матрицы ядра. Кроме этого на соответствующие значения умножаются окружающие его элементы (при размере ядра 3x3 их будет 8), после чего результаты суммируются и принимаются как преобразованное значение.

    Вот простой графический пример:
    image

    Преобразуемое значение выделено красным, область действия матрицы ядра – зеленым.

    Что получислось в результате преобразования. Ценности всех окружающих пикселей, включая собственное значение равно нулю, кроме верхнего среднего, где она равна единице. Таким образом, результат:

    (40*0)+(42*1)+(46*0)+(46*0)+(50*0)+(55*0)+(52*0)+(56*0)+(58*0) = 42

    Как видно, данное преобразование смещает изображение вниз на 1 пиксель.

    Таким образом, convolution в данном случае – это преобразование изображения, в результате которого на каждый пиксель результата влияет окружающая его область. Степень влияния этой области задается с помощью «ядра» или матрицы скручивания.

    Значения div и offset


    При обработке изображений одним только преобразованием не отделаешься, нужна еще нормализация. Что делать, если получившееся значение больше 255 или меньше 0? Цветов-то таких нет. Более того, что выход за границы цвета явление достаточно частое.

    Для нормализации результата используются дополнительные переменные: div (делитель) и offset (коэффициент). Они работают очень просто: результат преобразования делится на div и к нему прибавляется offset.

    Не трудно догадаться, что по умолчанию div = 1, offset = 0 (div = 0 выставлять нельзя!).

    При преобразованиях в качестве div обычно принимается сумма всех элементов матрицы скручивания. Это условие позволяет не допустить цветовых искажений, если они не нужны.

    Действительно, если преобразуемая область содержит один и тот же цвет, то результат получится как сумма элементов ядра умноженное на этот цвет. Соответственно, что бы оставить цвет без изменений, надо разделить результат преобразования на эту самую сумму.

    Простой пример: фильтр «негатив».


    В качестве исходного мы возьмем следующее изображение:

    image

    на примере него можно будет увидеть, как изменяется крупный и мелкий текст, картинка и линии. Теперь создадим матрицу скручивания для получения эффекта негатива:

    image

    Согласно матрице, получается, что в результате преобразования все цвета будут иметь отрицательную величину. Чтобы цвета были негативными, нужно задать offset = 256, таким образом цвета всех пикселей вычитаются из 256, что является негативным изображением:

    image

    Как это делается на PHP:

    В библиотеке GD на PHP существует функция imageconvolution, которая содержит 4 параметра. Первый – это идентификатор изображения. Второй – это матрица в виде массива из 3-х массивов с 3-мя переменными. Третий и четвертый — это div и offset.

    Вот код, который делает изображение негативным:

    <?php

    $img = imagecreatefromjpeg('images/pattern.jpg');
                
    $matrix = array (
      array(    0,    0,    0),
      array(    0,    -1,    0),
      array(   0,    0,    0)    
    );
          
    imageconvolution($img, $matrix, 1, 256);
    imagejpeg($img, 'images/pattern_negative.jpg', 100);

    ?>


    * This source code was highlighted with Source Code Highlighter.


    Сразу стоит сказать об одной очень неприятной особенности GD: при преобразованиях с помощью imageconvolution «рушится» альфа-канал. Этот баг был описан уже давно, но насколько я знаю, его так и не исправили. Во flash этого нет, более того там имееются еще дополнительные параметры, которые отвечают за обработку краёв изображений, когда часть пикселей выпадает. В php края просто не обрабатываются.

    Blur, sharpen, emboss


    Вот стандартный набор матриц эффектов:

    image

    Обратите внимание, для blur коэффициент div = 9. Для такой матрицы только такой коэффициент не ведет к искажению цветов. Еще надо сказать, что вариантов blur-а несколько, они незначительно отличаются силой эффекта.

    И вот какие получаются изображения:

    Blur:

    image

    Sharpen:

    image

    Emboss:

    image

    «Аккуратные» эффекты.


    Как видно из прошлого примера с blur, эффект накладывается на изображение, но достаточно сильно. А можно ли уменьшить силу эффекта на изображение? Оказывается, можно. Но для этого надо изменять не степень влияния окружающих пикселей, как можно показаться на первый взгляд, а количество влияющих пикселей:

    image

    Тогда получим эффекты, которые будут выглядеть намного аккуратнее:

    Light-blur:

    image

    Light-sharpen:

    image

    Light-emboss:

    image

    Здесь стоит задаться вопросом, а как увеличивать силу эффекта? К сожалению, только многократным его наложением, поскольку как ни крути, а все равно обрабатывается область 3x3 пикселя. Естественно, это очень ресурсоемко, для получения размытия до пятен с помощью размытия по Гауссу иногда приходится накладывать фильтр 100-200 раз. Это занимает очень продолжительное время и очень много ресурсов.

    В заключение


    … хочу сказать, что вы сами можете создать какой-нибудь интересный эффект. Для этого достаточно поэкспериментировать с матрицей скручивания.

    Матрица скручивания может быть успешна применена при:
    • создании «маленьких» картинок, напр. генерации аватаров и предпросмотров (особенно тут хорошо выглядит light-blur).
    • для создания «теней» (если бы еще с альфа-каналом…)
    • при создании CAPTHCA (текст + сильный Sharpen или Emboss)
    • и др. :-)

    UPD: Создание симпатичной тени (Bonus-track, for php-programmers only)


    /**
    * Создает красивую тень
    * Внимание! Операция ресурсоемкая!
    *
    * @param res $image - исходная картинка
    * @param int $shadow_width - толщина тени (1..10, выше не рекомендуется)
    * @param int $shadow_deep - глубина цвета тени (1..20, чем выше, тем чернее)
    * @param string $bg_color - цвет фона в формате #7def34
    */
    function imageaddshadow (&$image, $shadow_width = 4, $shadow_deep = 7, $bg_color = false)
    {
      
      $w       = imagesx($image);
      $h       = imagesy($image);
      $iw      = $w + 4*$shadow_width;
      $ih      = $h + 4*$shadow_width;
      $img     = imagecreatetruecolor($iw, $ih);
      
      $shadow_deep= 255-$shadow_deep*12;
      $shadow   = imagecolorallocate($img, $shadow_deep, $shadow_deep, $shadow_deep);      
      
      
      if (!$bg_color) {
        // Белый цвет по умолчанию
        $bg = imagecolorallocate($img, 255, 255, 255);
      }
      else {
        list($r, $g, $b) = array_map('hexdec', str_split(ltrim($bg_color, '#'), 2));        
        $bg = imagecolorallocate($img, $r+1, $g+1, $b+1);
      }
      
      // Заливаем область цветом фона
      imagefilledrectangle($img,0,0,$iw,$ih,$bg);
      
      // Создаем тень
      imagefilledrectangle($img,
        1+$shadow_width,
        1+$shadow_width,
        $iw-1-$shadow_width,
        $ih-1-$shadow_width,
        $shadow);
      
      // Создаем размытие тени
      $matrix = array (
        array(    1,    1,    1),
        array(    1,    1,    1),
        array(   1,    1,    1)    
      );

      // Применяем эффект несколько раз для хорошего размытия
      for ($i=0; $i < $shadow_width*2; $i++, imageconvolution($img, $matrix, 9, 0));    

      // Помещаем над тенью исходное изображение
      imagecopyresampled($img, $image, 2*$shadow_width,2*$shadow_width,0,0,$w,$h,$w,$h);
      
      // И всё!
      $image = $img;
    }


    * This source code was highlighted with Source Code Highlighter.

    Результат:

    image

    Спасибо за прочтение! Конструктивная критика и указание о грамматических ошибках с помощью лички приветствуется.
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 37

      0
      Интересно. Спасибо
        +1
        Для усиления эффекта проще наверное все-таки матрицу увеличить. Для блюра по крайней мере
          +1
          Размытие по Гауссу дает сильнее эффект. Но все равно при однократном применении растекаться будет только на пиксель вокруг.

          Матрица размытия Гаусса такая:
          [1 2 1]
          [2 4 2]
          [1 2 1]

          div=16, offset=0


          Если надо размыть сильно, то лучше всего использовать её, она более корректно размывает
            0
            Ну никто нам не мешает сделать матрицу размерности 5 на 5 и пересчитать коэфициенты Гаусовского размытия для неё. ;-)
              0
              PHP нам мешает с его ограничением в матрицу 3х3.
                0
                Я думаю, функция imageconvolution() не такая сложная, чтобы ее переписать.
                Правда дальше, чем поиграться с ней, дело не пойдет, из-за низкой скорости…
                  0
                  Действительно вот тут есть код этой функции, написаный вручную. Переписать её можно даже размерами 5x5. Но это работать будет настолько меделенно, что лучше не использовать.
                    +1
                    А! Вот вы о чем. Я просто не имел дело с этой функцией в php. Всё как-то ручками и в C. :-)
                      0
                      респект, коллега =)
          • UFO just landed and posted this here
            +1
            В русской научной литературе это чаще называют «обработка скользящим окном».
            Придумано огромное количество применений, ускорений операций, и т.п. для этого рода операций. В частности, и для гауссова размытия.
              +1
              А можно поподробнее? Я в рунете вообще инфу по этой теме мало где видел.
            0
            было бы классно еще использоватьматрицы с бОльшим размером в качестве ядра, а не только 3x3… Таких интересных эффектов можно достичь наверное!
              0
              Да. Применяются 5x5 и 7x7. Но там уже уже ресурсоемкость операций такие, что в вебе их лучше не применять. Знаете, сколько выполняются некоторые эффекты в фотошопе? :-)
                0
                для многих фильтров (и тем более таких простых как линейные) есть алгоритмы, не зависящие от радиуса окна, например см
                people.csail.mit.edu/sparis/bf/
                хотя билатерал конечно гораздо сложнее чем линейный (обычная свертка)
            • UFO just landed and posted this here
                0
                Тем применяются матрицы размером с изображение :)
                  +1
                  Тем -> Там
                +1
                Вот как это все делается… Спасибо за статью!
                  +1
                  Эх, мой любимый спектральный анализ :) советую почитать книгу Сергиенко для начала. Там все ясно изложено.
                    +2
                    Эх хороший был предмет в универе — компьтерная обработка изображений… Там мы на матлабе это все проганяли.
                    • UFO just landed and posted this here
                        +1
                        И сейчас есть — Filter-Other-Custom
                          0
                          Верно. И теперь хоть стало понятно, как с ней оперировать.
                          Почему-то в справочной системе все фильтры описаны словесно и не всегда понятно, что ждать от того или иного фильтра. Нет чтобы математику привести для особо продвинутых.
                        +1
                        Автору необходимо было оговорится о том, что тут идет речь о цветовом профиле RGB. И ещё если работать с другими цветовыми профилями, то можно получить куда интереснее еффекты.
                          +1
                          Только не профиль, а пространство. Из интересных, пожалуй только LAB и его клоны.
                          +1
                          Спасибо, в закладки статью
                            0
                            Народ, а по что на PHP нужно картинки процессить? принципиально-неправильное применение выдумали аж в anti-patterns пиши.

                            разве уж когда хостер совсем ничего кроме php запустить не разрешает… да и то, веб-сервисы на тот случай могут помочь
                              +1
                              Ну например капчу проще сделать на PHP, чем куда-то углубляться.
                              А чем помогут веб сервисы, их же все равно где-то хостить надо. Или вы про существующие сервисы, в которых можно работать с изображениями (что уже и такое есть?)?
                              0
                              Здорово, статья лично мне показалась очень интересной!
                              Не подскажите, где можно подробнее узнать о теме?
                                0
                                Спасибо за интересную и полезную статью
                                  0
                                  какая хорошая статья — в избранное
                                    0
                                    Надо администрации сделать облако тегов избранных
                                      0
                                      Статья интересная, но с поломанными картинками ее ценность значительно снижается.

                                      Only users with full accounts can post comments. Log in, please.