Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Пример сегментации изображений средствами PHP

Anna | 31.05.2014 | нет комментариев

Добрый день,
достаточно редко, но все же встает вопрос о необходимости в механическом режиме разделять изображение на логические фрагменты. Если вы ограничены только средствами PHP, то задача становится немножко сложней, но все же решаема.
В данной статье я разгляжу частный случай распознавания образов, ориентированный на не слишком ухищренную публику.
В статье применяются примеры с одного из сайтов с очевидным указанием ссылки, сайт не мой, первоначально не было мыслей писать статью.

Выходит, разглядим начальные данные

image

Прямоугольные картинки различного размера и пропорций, с белым фоном, на которых изображены фигурки киндер сюрприза и water mark, тот, что изредка касается фигурок. Задача — получить картинки фигурок без water mark’a

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

Интерация 1. Выбор не белых областей

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

image

PHP код

$path = '../images/series/9/1.jpg';
$size = getimagesize($path);
$img = imagecreatefromjpeg($path);

$x = 1;
$y = 1;
$color = array();
for ($i = 1; $i < $size[0] * $size[1]; $i  ) {

    $color_index = imagecolorat($img, $x, $y); //16777215
// делаем его удобочитаемым
    $color_tran = imagecolorsforindex($img, $color_index);

    if (($color_tran['red'] == 0 && $color_tran['green'] == 0 && $color_tran['blue'] == 0)
            or
            ($color_tran['red'] <= 255 && $color_tran['red'] >= 235 &&
            $color_tran['green'] <= 255 && $color_tran['green'] >= 235 &&
            $color_tran['blue'] <= 255 && $color_tran['blue'] >= 235)
            or
            ($color_tran['red'] >= 190 && $color_tran['red'] <= 230 &&
            $color_tran['green'] >= 225 && $color_tran['green'] <= 245 &&
            $color_tran['blue'] >= 245 && $color_tran['blue'] <= 255)
    ) {
          $color[$x][$y] = 1;
    } else {
        $color[$x][$y] = 1;
    }

    $x  ;
    if ($x > $size[0]) {
        $x = 1;
        $y  ;
    }
}

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

Закрасим сейчас полученные области на начальном изображении.

image

Как видно теперь наш алгорифм цепляет ломтики мусора (следственно в вышеприведенном коде и есть фильтрация по цвету)

Ложный подход, попытка выделения граничных областей секций

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

image

Шлифуем

image

Еще шлифуем, на дальнейшей картинке теснее больше-менее приемлемый итог, правда все еще захвачено много пикселей по ошибке

image

Сейчас если мы закрасим все цветные точки внутри данных ограничений, то получим в итоге картинку, на которой теснее дозволено осознать, что это смурфы)

image

Со стороны человеческого взора эти секции теснее дозволено однозначно поделить, но со стороны нашего алгорифма это все еще массив единичек и ноликов, а так же массив ноликов и единиц – только границ секций (но теперь это все еще 1 массив и стержневой вопрос в том, как поделить его в соответствии с сегментами).
На начальном изображении дозволено видеть очевидные белые пробелы между сегментами, испробуем поделить секции по тезису: «Если у нас есть крупные обрывы (огромнее чем N) между закрашенными сегментами, то разделяем пиксели в основном массиве по левую и правую сторону от обрыва».

image

image

Дальше таким же образом мы можем сделать горизонтальные обрывы, но их сделать труднее из-за надписи по центру картинки.
Но на этом моменте я осознал, что двигаюсь не в том направлении, следственно завершил данные эксперименты.
Выходит, возвращаемся к исходам – у нас есть огромный массив единиц – цветные пиксели, как их поделить на секции?

Способ разрастания областей

Короткое изложение дано выше (в начале статьи), следственно приступим сразу к делу.

image

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

image

PHP код

function nburn($ret, $color, $img, $col = false) {
    $ret_arr = array();
    foreach ($ret as $k => $v) {
        foreach ($v as $k2 => $v2) {
            $ret_arr['numb'] = get_nearleast($color, $k, $k2);
            $stop = false;
            if (count($ret_arr['numb']) > 0) {

                if (count($ret_arr['numb']) == 1) // 1- потому что только точка сама возвращается
                    $stop = true;
                else
                    $toys[] = array();
            }
            if (!$stop) {
                while (count($ret_arr['numb']) > 0) {
                    $x = key($ret_arr['numb']);
                    $y = key($ret_arr['numb'][$x]);
                    $toys[key($toys)][$x][$y] = 1;

                    unset($ret_arr['numb'][$x][$y]);
                    if (count($ret_arr['numb'][$x]) == 0)
                        unset($ret_arr['numb'][$x]);
                    unset($color[$x][$y]);

                    // Берем дальнейший элемент
                    $x = key($ret_arr['numb']);
                    if ($x == "")
                        break;
                    $y = key($ret_arr['numb'][$x]);
                    $near = get_nearleast($color, $x, $y);
                    foreach ($near as $f => $g) {
                        foreach ($g as $f2 => $g2) {

                            $ret_arr['numb'][$f][$f2] = 1;
                        }
                    }
                }
                next($toys);
            }
        }
    }

    return $toys;
}

С поддержкой данной функции массив с пикселями разбивается на уйма под массивов (многомерный массив). Всякий под массив соответствует одному сегментированному изображению. Дальше, анализируя полученные под массивы мы можем удалить самый широкий массив, тот, что соответствует надписи.
Каждому знаменитый пример, я думаю все угадали персонажа.

image

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

image

Помещенный код писался для себя, следственно, скорее каждого, есть больше изящные решения.
Ссылки по теме:

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB