the bird is the word

Bastion_portrait

Я люблю различных птичек. С детства дома жила куча попугаев и прочей живности. А ещё я люблю писать на Хабр и в прочие места странные нравственно-поучительные статьи на тему «как делать какие-то системы». Ну да. И нейронные сети.
Придумал как это объединить. Недавно Анне попались акриловые прозрачные кормушки на окно. Засыпаешь семки — смотришь как птички прилетают-кормятся. Но только птички пугливые, летают не по расписанию. И я понял: хочу такую кормушку на окно, но чтобы она сама распознавала кто прилетает и сообщала мне. Плюс фоточка.
В блоге я буду публиковать то, как я эту систему строю. С исходниками, размышлениями на тему того что нужно, что не нужно + с открытыми базами, которые собираю с устройства. Во многом, то что тут будет — оверкилл. Например Сaffe на Rasberry Pi, которое я планирую запустить. Это обусловлено тем, что мне интересно потренироваться делать те или иные вещи.
Всего я планирую уложиться в 3-4 заметки. И, одна итоговая, на Хабр. Сегодня первая: «Введение. Проект системы. Инфраструктура. Инфраструктура сбора базы.»

Цели проекта:

  1. Мне давно хотелось погонять машинное обучение на RPi. Все регулярно спрашивают “а пойдёт ли”? Приходиться отвечать цитатами из других статей. Пока сам не проверишь – не поймёшь.
  2. Люблю птичек. Понимаю, что в Москве штука имеет мало смысла. Но вот повесить её на даче – почему бы и нет. Вытаскивать автоматически самые интересные картинки. Это можно и с малым количеством трафика повесить.
    Правда нужно кормушку делать с регулируемым отсыпом или большую.

План:

Идея «Хочу птичек распознавать» может иметь десятки реализаций. Мне бы хотелось, чтобы система автоматически обнаруживала прилетающих к кормушке птичек, определяла что это за птички, выбирала лучшую фотографию, выкладывала куда-нибудь (Пусть даже в Твиттер или удалённый диск!), а так же вела статистику посещаемости.
Почему это может не выйти и что буду делать как задачу минимум:

  1. Производительность. Rpi может не потянуть нейронку. Судя по тому что я читал, всё же за 5-6 секунд снимок должен обработаться. Но если окажется, что нет — придётся городить распознавание на локальном компе, а с rPi на него запросы слать. Это будет достаточно печально, конечно.
    Буду пробовать резать сетку, оставлять минимум, разгонять Rpi.

  2. Сбор базы. По моим наблюдениям за те 2-3 недели, что кормушка висит — там бывают только синички. Двух видов, правда (большая синица + лазоревка). Это, конечно, ставит крест на распознавании большого числа типов птиц, которое хочется прямо сразу. С другой стороны, база набирается только один день, может быть за недельку что-то залётное и долетит. А возможно, в будущем повешу в другом месте и доберу базу.

Инфраструктура:

Кусок моей домашней сети, который ответственен за проект имеет следующую конфигурацию:
Home
Rpi+камера висят около окна. Пока я экспериментирую с их расположением + заодно набираю разные ракурсы для дальнейшего распознавания:
0nGOsqSkReY  3

2

В Rpi воткнут WiFi модуль, который врублен в роутер. Фотографии сохраняются на сетевой диск (WD MyBook live).

Сам Rpi висит без монитора. Его управление, программирование и настройка сейчас делается с основного компа по SSH. В начале что-то настраивал воткнув в монитор.

Комплектуха:

  • Ядро системы: Raspberry Pi первой модели, B+. Лежала у меня в ящике полтора года практически без дела.
  • Камера:
    • Изначально я планировал использовать самую базовую камеру от Rpi (Raspberry Pi Camera 1.3rev), которая у меня лежала в том же ящике. Но оказалось, что полтора года не прошли для неё бесследно — она мертва. Сейчас хочу протестить у друзей, чтобы понять что сдохло: камера, шлейф или порт на малине.
    • Так что в качестве стартового решения решил поставить вебку с компа. Обычную Creative, благо на Rpi B+ много USB портов. FaceCam100x.
    • В теории, после того как система заработает — планирую всё же использовать какую-то камеру для малины. На них сильно больше разрешение + сильно лучше качество, чем на вебках.
  • WiFi-донгл. Самый обычный — TP-Link TL-WN727N.
  • Собственно кормушка

1463155084

Сбор базы:

Переходим к первому серьёзному вопросу. В реальности, хорошо собранная база — это куда сложнее, чем правильный выбор нейронной сети. Использование битой разметки или не репрезентативных данных может ухудшить качество системы куда больше, чем использование VGG вместо ResNet.
Сбор базы — это масса ручного, пусть даже неквалифицированного труда. Специально для разметки таких баз есть сервисы Яндекс.Толока и Amazon Mechanical Turk. От их использования я воздержусь: размечу всё вручную сам, тут недолго. Хотя, может, имело бы смысл и туда загнать, потренироваться в использовании.
Естественно, хочется автоматизировать такой процесс. Для этого рассмотрим, что мы хотим. Рассмотрим, что есть база в нашей ситуации:

  • Система работает с видео — значит это должны быть кадры из видео
  • Система должна распознавать птиц в видео, значит в базе должны быть примеры кадров на которых птицы присутствуют, а так же примеры кадров где не присутствуют
  • Система должна распознавать качество изображений. Значит в базе должны быть отметки, определяющие качество кадра
  • Система должна иметь набор кадров на каждый тип птичек который она распознаёт

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

1483
1526
49
107

Такую не размеченную базу я выложил вот тут.
Детектор движения пишется проще простого:

import cv2
import time
video_capture = cv2.VideoCapture(0)
video_capture.set(3,1280)
video_capture.set(4,720)
video_capture.set(10, 0.6)
ret, frame_old = video_capture.read()
i=0
j=0
while True:
    time.sleep(0.5)
    ret, frame = video_capture.read()
    diffimg = cv2.absdiff(frame, frame_old) #Просто вычитаем старый и новый кадр
    d_s = cv2.sumElems(diffimg)
    d = (d_s[0]+d_s[1]+d_s[2])/(1280*720)
    frame_old=frame
    print d
    if i>30: #Первые 5-10 кадров камера выходит на режим, их надо пропустить
        if (d>15): #Порог срабатывания
            cv2.imwrite("base/"+str(j)+".jpg", frame)
            j=j+1
    else:
        i=i+1

Чтобы не перегружать Rpi я стараюсь пока не запихивать туда numpy и scypy.

Настройка RPi:

Для опытных линуксоидов эта часть бесполезна. Но соберу всю магию которую делал тут:
1. OpenCV я устанавливал давно и плохо помню. Но вот такой гайд выглядит близко к тому что я делал.

2. Для того чтобы писать на удалённый диск и не подключать его каждый раз – я подключил его один раз и всё:

sudo apt-get install smbfs smbclient #установка программ которые это позволяют сделать
sudo nano /etc/fstab #тут храниться что система маунтит на старте
#Добавить строчку:
//192.168.0.12/Public /home/anton/LocalDrive cifs
#Сохранить файл, выйди и сделать:
sudo mount -a
#после перезагрузки оно должно само подцепляться

3. ssh есть на RPI по умолчанию. Просто установить подключение по 22 порту. Пароль и логин те же что и у RPi

4. Чтобы программа работала после выхода из SSH сессии я использовал специальную “утилиту мультиплексор“:

sudo apt-get install screen
#Чтобы инициализировать сессию:
sudo screen # в принципе, можно без суперправ
#Чтобы выйти из рабочей сессии не стопая её: нажать Ctrl-A потом Ctrl-D
#Чтобы вернуться к сессии:
sudo screen -r

Что дальше:

Сейчас я жду где-то неделю пока база не наберётся. Использую разные положения камеры, двигая точку обзора. Потом попробую обучить на большом компе + накатать Caffe на RPi.
Если всё пойдёт ок – то это и будет вторая статья.
Третья будет когда я соберу всё в кучку, поставлю назад и попробую запустить. Уверен, что там будет море трэша:)

 

Базу буду апдейтить сюда.
Исходники в кучку сюда.
Если у кого есть советы по caffe на Rpi – пишите!:)