Опрос


Что для Вас Умный Дом?


Результаты

Анализ изображений с помощью нейронной сети

21/10/2010 15:10:03

В предыдущей статье "Применение нейронных сетей в Умном Доме" я коротко описал ситуации, в которых можно применять нейросети, а также их возможности. Теперь я попытаюсь воспользоваться нейросетью для анализа фотоизображения с камеры наблюдения.

 У меня установлена камера наблюдения, которая обращена на улицу. В кадр попадает площадка для автомобиля перед входом. Я хотел бы научить нейросеть распознавать - стоит ли мой автомобиль на площадке, а также по возможности отличать мой автомобиль от других. Эту информацию можно использовать различным образом. Например, включать подогрев чайника в обед, сообщать домочадцам о моем приезде, включать освещение крыльца, напоминать мне, если в лютый зимний мороз я, вдруг, забыл загнать автомобиль в гараж и т.д. В случае обнаружения на площадке чужого автомобиля в режиме охраны, сообщать об этом факте по SMS или изменить параметр на входе другой нейросети, отвечающей за охрану дома. Как интерпретировать и использовать полученные данные можно рассуждать бесконечно. А в данный момент необходимо определить факт стоянки автомобиля, не человека и не велосипедиста, а именно автомобиля, причем моего.

 Для создания и работы с собственной нейронной сетью я воспользовался библиотекой FANN. В Debian Linux в стабильном репозитории доступна версия 1.2.0 этой библиотеки

apt-get install libfann1
apt-get install libfann1-dev

Скачать и распаковать http://pecl.php.net/package/fann

phpize5 (если его нет в системе, необходимо установить php5-dev)
./configure
make
При компиляции PHP-FANN с установленной PHP5 возникает следующая ошибка:
fann.c:393: error: ‘zif_fannOO___set’ undeclared (first use in this function) 

Необходимо отредактировать файл
php_fann.h
и закомментировать строчку 28
#define PHP_FANN_OO 1

make
make install

Далее добавляем в php.ini
extension=fann.so
и перезапускаем Apache (если мы используем PHP из Web-сервера а не в режиме CLI)
/etc/init.d/apache2 restart

Прежде чем использовать нейросеть ее нужно обучить на некоторых данных. Поскольку у меня ведется архив с камеры наблюдения, я подготовил ряд фотографий, на которых мой автомобиль стоит на площадке и ряд других фотографий, где автомобиля нет. Я постарался выбрать фотографии в разную погоду, а также при искусственном освещении. Для начала я взял по 6 фотографий с автомобилем и без и положил их в две разные папки: 1 и 0.

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

Для экономии времени и ресурсов, я анализирую не все изображение, а только часть кадра, в который вписана площадка (камера зафиксирована и не поворачивается). В графическом редакторе я определил, что эта часть кадра прямоугольник с координатами в пикселах (125х150; 350х150; 125х230; 350х230). Так как сеть работает с данными, необходимо предоставить ей массив данных, характеризующих изображение. Для этого можно считать код цвета каждого пиксела, например, функцией imagecolorat().

Нейронной сети удобнее работать с нормализованными данными, которые приведены к диапазону [-1;1] или [0;1]. В моем примере мы имеем диапазон значений от 0 (абсолютно черный цвет) до 16777215 (абсолютно белый цвет), поэтому каждое значение я делю на максимальное и получаю число в диапазоне от 0 до 1.

Несколько слов о параметрах создаваемой сети. Кроп с изображением площадки для автомобиля представляет собой массив из 18000 пикселей. (225х80). Поэтому мы должны создать нейронную сеть с 18000 нейронами на входе. В среднем, так называемом, скрытом слое находится 100 нейронов. Человек, который очень хорошо представляет математическую модель нейронной сети, принципы ее работы в различных ситуациях может более-менее адекватно прикинуть оптимальное количество нейронов на этом уровне. Для остальных действует простое правило - подбирается эмпирическим путем, то есть опытным. Мы можем попробовать разное количество нейронов и посмотреть на результаты обучения: скорость обучения, количество итераций, количество ошибок. На выходе сети в моем случае 1 нейрон. Это означает, что ответ сети будет один и находится в области значений между 0 и 1. Ноль или близкие к нулю значения будут говорить, что моего автомобиля нет на площадке, тогда как единица будет сообщать, что автомобиль стоит на площадке. Относительно остальных параметров нейронной сети можно посмотреть в документации к библиотеке FANN.

Код программы:

<?

// Считываем данные с обучающих фотографий в массив.
$j = 0;
$my_example = array();
for ( $i = 0; $i < 2; $i++ )
{
    $d = dir("teach/$i");
    while($entry = $d->read()) 
    {
        if ( preg_match("/jpg/", $entry) )
        {
            $im = imagecreatefromjpeg("teach/$i/$entry");

            $cur_array = array();
            $cnt = 0;
            for($y=150; $y<230; $y++)
            {
                for($x=125; $x < 350; $x++)
                {
                    $rgb = imagecolorat($im, $x, $y) / 16777215;
                    $cur_array[$cnt] = $rgb;
                    $cnt++; 
                }
            }

            imagedestroy($im);
            $my_example[$j] = array($cur_array, array($i));
            $j++;
        }

    }
}

// Создаем и обучаем сеть
$ann = fann_create(array(18000, 100, 1), 1.0, 0.7);
if ( fann_train($ann, $my_example, 1000, 0.001, 1000) == FALSE)
exit('Could not train $ann.');

// Сохраняем обученную сеть в файл для дальнейшего использования.
fann_save($ann, "my.ann");

Обучение на компьютере с процессором Celeron 1,6ГГц заняло примерно 30-40 секунд. В результате я получил файл объемом 60Мб.

Теперь мы можем использовать обученную сеть:

<?
$ann = fann_create("my.ann");

$im = imagecreatefromjpeg("snap/camera.jpg");

$cur_array = array();
$cnt = 0;
for($y=150; $y<230; $y++)
{
    for($x=125; $x < 350; $x++)
    {
        $rgb = imagecolorat($im, $x, $y) / 16777215;
        $cur_array[$cnt] = $rgb;
        $cnt++;
    }
}
imagedestroy($im);

if ( ($output = fann_run($ann, $cur_array)) == FALSE )
  exit("Could not run ANN.");
else
  print_r($output);
?>

На площадке нет автомобиля.
После запуска скрипта, программа выдала значение: 0.0391430146992.
Этот ответ можно смело квалифицировать как ответ "Нет".
Далее, ставим автомобиль на площадку и снова запускаем программу. Ответ: 0.988427102566
Очевидно, что программа сообщила "Да".

Работа такого скрипта занимает порядка 3-4 секунд, так как программе требуется загрузить 60Мб файл с описанием сети, но если запустить этот скрипт в режиме демона, то обработка новых изображений происходит мгновенно.

Я умышленно не стал на этом этапе показывать сети зимние фотографии площадки, так как хочу понаблюдать за ответами нейросети в ситуации, когда условия изменятся. Также я настроил программный механизм, который ведет своеобразный журнал ответов. В случае, если сеть отвечает "Да", программа сохраняет картинку в журнал. В свободное время я просматриваю журнал и когда вижу, что сеть дала неправильный ответ, нажимаю кнопку "Ошибка" и этот файл добавляется в нужную папку для обучения. Если в папке с обучающим материалом меняется содержимое, происходит новый процесс обучения. На текущий момент сеть, обученная всего на 12 фотографиях, ни разу не ошиблась, но и сложных ситуаций пока не возникало.

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

Автор: Andrey_B
Любое использование материалов сайта возможно только с разрешения автора и с обязательным указанием источника.



Добавить комментарий:



Сортировка комментариев: Последние сверху | Первые сверху

2014-05-26 20:58:09 | den
помогите написать программу по нейронным сетям на visual prolog


2013-10-10 21:05:34 | IvanSCM
Отличная статья, спасибо!


2012-01-04 19:23:27 | pk
при использовании такого примитивного алгоритма сеть надлежащим образом работать не будет.


2010-12-13 08:00:16 | stpavel
Странно. У меня поднялось все без проблем, сперва был скомпилен fann-1.2.0 , затем через pear fann-php. Может быть у Вас не хватает каких то библиотек , требуемых для fann ?


2010-12-11 12:47:58 | Naural tester
stpavel, я вам завидую, как вам так легко получилось поднять FANN на PHP. Я несколько дней мучаюсь с этой проблемой. Пробовал через PEAR, качал сорсы пхп в /ext добавлял fann, но у меня так ничего и не вышло, было бы отлично если бы кто-нибудь об этом рассказал, ведь в интернете нет об этом ни какой информации даже на английском языке.


2010-10-28 15:45:24 | Andrey_B
stpavel, к вам просьба. Если ваш опыт окажется позитивным обязательно расскажите какую задачу вы решали, на каком оборудовании и каким способом.


2010-10-28 14:01:51 | stpavel
Добавил массив, все работает , спасибо ! :) Буду обучать камеру на своих домашних


2010-10-28 12:07:20 | Andrey_B
stpavel, в описанном вами случае предложенная в статье простейшая программа не подойдет. В моем примере в качестве ответа фигурирует только 1 нейрон с возможными значениями 0 или 1, только один элемент в массиве правильных ответов. В вашем случае, массив правильных ответов должен содержать 3 элемента.
===
Ну, например,
1 0 0 - это значит "1"
0 1 0 - это значит "2"
0 0 1 - это значит "3"
===
в качестве ответа должно быть нормализованное значение, приведенное к диапазону [-1;1] или [0;1].
Обучив таким образом сеть, мы будем получать ответы, также представленный массивом, в котором возможны значения, например:
0,023 0,918 0,016 - Сеть думает, что это цифра "2".
Мы можем получить в качестве ответа и такое
0,992 0,942 0,217 - Это означает, что сеть не знает, и "предполагает" что это либо 1, либо 2. Это в свою очередь значит, что либо обучающий материал не совсем адекватен реальной жизни, либо представленная картинка является для сети "новой" и обучающий материал не содержит точного ответа для такой ситуации.


2010-10-28 10:27:43 | stpavel
идентификатор того что массив изображения принадлежит к той или иной группе изображений , вот что я имел ввиду.


2010-10-28 08:47:25 | stpavel
Решил немного усложнить задачу, попробовать научить скрипт различать три разных объекта. Нарисовал цифры 1, 2 и 3 с разрешением 50x50 пикселей. Получается, у нас 2500 нейронов на входе и 3 на выходе, промежуточный слой оставил 100.
Создаю и обучаю сеть:
$ann = fann_create(array(2500, 100, 3), 1.0, 0.7);
fann_train($ann, $my_example, 1000, 0.001, 10)
в результате получаю примерно следующее :
Epochs 1. Current error: 0.9044302225

Epochs 10. Current error: 0.8346135015

Epochs 20. Current error: 0.6666666667

Epochs 30. Current error: 0.6666666667

Epochs 40. Current error: 0.6666666667


т.е. погрешность как бы зацикливается и больше не стремится к 0.
Меня немного смущает массив $my_example, передаваемый fann_train.
Массив $my_example содержит два элемента, первый элемент с индексом 0 - массив разложенного изображения ( в моем случае 2500 элементов), второй элемент с индексом 1 также массив, имеющий всего одно значение ( в Вашем примере эти значения 0 или 1 в зависимости от папки , с которых берутся изображения) в моем случае 0, 1, 2. Не знаю, может быть эти значения не могут быть больше единицы.. Хотя с другой стороны, это просто идентификатор того что массив изображения принадлежит тому или иному изображению...


2010-10-28 00:00:00 | Andrey_B
stpavel, к вам просьба. Если ваш опыт окажется позитивным обязательно расскажите о том, какую задачу, на каком оборудовании и каким способом вы это сделали.


2010-10-24 23:17:50 | Andrey_B
stpavel, большое спасибо за ссылки. И хотя моя основная задача была попробовать нейронные сети в деле, предложенное вами направление распознавания объектов само по себе очень интересное. Обязательно попробую.


2010-10-24 18:23:57 | stpavel
Очень интересная статья, спасибо !
Андрей, а Вы не пробовали библиотеку opencv ? Судя по описанию и возможностям очень мощная библиотека для распознавания объектов. Примеры можно посмотреть здесь /www.computer-vision-software.com/blog/category/opencv/. Существует так же порт openvc под php для распознавания лиц - /www.xarg.org/project/php-facedetect/ . Еще один интересный проект - распознавание автомобильных номеров /javaanpr.sourceforge.net/, вроде бы даже как и рабочий, жаль нет возможности проверить его в боевых условиях.


2010-10-23 13:01:05 | THK
Впечатляет!
Жаль, что у меня камера видит только часть машины. Камера "врезана" в калитку и развернуть ее нет возможности. Зато можно попробовать натаскать сеть на лица. Зимой буду пробовать.

PS Еще раз спасибо за статью и за ответы.


2010-10-23 12:42:59 | Andrey_B
Машину паркую, конечно, по-разному. Смещение, думаю, больше метра. В этом случае сеть дает правильный ответ. Сегодня на площадке стояла другая машина, но сеть ее не признала как мою. Вместо 0.03 (когда машин совсем нет), сеть сказала 0.09. Что-то, говорит, стоит, но не твоя машина. ;)


2010-10-23 11:36:19 | THK
У меня на воротах такая-же камера... :(
И еще вопросик. Вы машину паркуете всегда одинаково или возможно ее "смещение" в кадре? К примеру +/- 0,5 метра.
И еще. Что скажет сеть, если "увидит" машину соседа на Вашем месте?


2010-10-22 23:51:58 | Andrey_B
ТНК, спасибо.
Задачу распознать номер я не ставлю. И вот почему. Фокусное расстояние моей камеры 3,6 мм. Она установлена в 10 метрах от машины и имеет ощутимую дисторсию. Площадь, которую занимает машина в кадре в пикселях составляет примерно 80 на 70. А номер машины занимает всего 14 на 5 пикселей. Совершенно очевидно, что нет возможности даже понять - написано ли что-нибудь вообще на номере. К тому же резкость камеры за 900 руб оставляет желать лучшего. Думаю, номер вполне реально распознать, но для этого нужно использовать камеру высокого разрешения, с более длинным фокусом. В будущем я планирую заменить камеру на более современную, а эту переориентирую за наблюдением сада.
Что касается количества нейронов на выходе, то для этого не обязательно 2000 нейронов. Все зависит от топологии сети и способа ее использования. Например, простой вариант - 3 цифры. Теоретически, мы должны порезать изображение на 3 кадра (как это сделать - отдельная история, но задача вполне решаемая) с цифрами в каждом кадре и подавать сети каждый кадр в отдельности. На выходе у такой сети будет 10 нейронов. Если, к примеру, третий нейрон дал 1, тогда как остальные 0 - значит на входе была цифра 3. Это при условии, что и обучали сеть мы аналогичным образом. Если несколько нейронов дали 1, значит сеть не смогла достоверно распознать цифру и нужно будет на более высоком уровне интерпретировать этот результат, отталкиваясь от конкретных значений. И человек не всегда может отличить 5 от 6.
Могу поделиться первыми наблюдениями. При очень низком освещении, фото которых не было в обучающем материале, сеть корректно решает задачу, хотя средняя яркость пикселей (а именно яркость подается на вход) значительно ниже. Возможно, сеть реагирует не на наличие темного пятна на фото, а на перепад яркости. Не зря утверждается, что сеть может выявить скрытые зависимости, которые не всегда очевидны даже человеку.


2010-10-22 22:46:06 | THK
Респект! Отличная статья!
А Вы не думали над задачей выделения номера машины из ее фото?
Насколько я понимаю, для этого придется на выходе получить не один нейрон, а скажем 2000 или сколько пикселей занимает номер на фото... Я прав?
Если да, то какой алгоритм обучения должен быть у этой сети?

PS Если вдруг я забегаю вперед (к теме следующей статьи) - сильно не пинайте... ;)