Периодически меня подмывает сделать что-то странное. Очевидно бесполезную вещь, которая не оправдывает себя по объему вложенных средств, и через полгода после создания пылиться на полке. Но зато полностью оправдывает себя по количеству эмоций, полученному опыту и новым рассказам. На Хабре даже есть две моих статьи про такие эксперименты: Алкоорган и умная кормушка для птиц.
Что ж. Пришло время рассказать о новом эксперименте. Как собрал, что из этого вышло и как повторить.
![](https://habrastorage.org/r/w780q1/webt/cs/hz/5f/cshz5ffwq8klibncaxvknlgrwfw.jpeg)
К новому проекту меня подтолкнуло событие, в каком-то смысле, банальное — родился сын. Я заранее устроил себе отпуск на месяц. Но ребёнок оказался тихим — было свободное время. И спящий рядом деть.
Дома много разных embedded-железок для computer vision. В итоге решил сделать видео-няню. Но не такую унылую, которыми завалены все магазины. А что-то поумнее и поинтереснее.
Статья будет написана в повествовательном ключе, чтобы понять как шла разработка игрушки, куда она пришла и куда движется дальше.
У статьи есть несколько дополнений:
Самый банальный функционал видеоняни — посмотреть в любой момент что происходит с ребёнком. Но, к сожалению, это не всегда работает. Вы не будете смотреть трансляцию всё время, это не удобно. Младенца можно вообще положить спать рядом в коконе, зачем видео всё время? В итоге, для начала собралась следующая подборка:
У меня была большая статья на Хабре про сравнение различных платформ. Глобально, для прототипа типа того что я делаю есть несколько вариантов:
Итого — я выбрал Rpi+movidius. Есть на руках, умею работать с ним.
Вычислитель — Raspberry Pi 3B, нейропроцессор — Movidius Myriad X. С этим понятно.
Остальное — поскрёб по сусекам, докупил.
![](https://habrastorage.org/r/w780q1/webt/xn/wj/nu/xnwjnusf8ueuf_ui22lmuldmygy.jpeg)
Я проверил три разных, которые у меня были:
![](https://habrastorage.org/r/w780q1/webt/cn/52/ea/cn52ea3bhndudsjthyu0kkehaws.jpeg)
А фильтр меняется вот так:
![](https://habrastorage.org/r/w780q1/webt/g-/si/ny/g-siny6utwnaliaqzzixhduvi6i.jpeg)
В целом, понятно, что это не продуктовое решение. Но работает.
Если что, то в коде увидите оставшиеся куски для перехода на другие два типа камер. Возможно даже что-то сходу заработает, если 1-2 параметра поменять.
С одной из старых задачек у меня завалялся осветитель.
Подпаял к нему какой-то блок питания. Светит неплохо.
![](https://habrastorage.org/r/w780q1/webt/jz/eq/yz/jzeqyzoxn-qs_sslj1yqdnccz_e.jpeg)
Направляю на потолок — комната освещена.
![](https://habrastorage.org/r/w780q1/webt/wd/ww/x7/wdwwx7yxd4u27kx74zc5oabf1ro.jpeg)
Для некоторых режимов работы мне понадобился монитор. Остановился на таком. Хотя не уверен что это самое правильное решение. Может стоило взять полноформатный. Но про это позже.
![](https://habrastorage.org/r/w780q1/webt/ig/02/zy/ig02zynyatlxzz7iwtb9sbf2rho.jpeg)
Ребёнок спит в произвольных местах. Так что проще когда система питается от павербанка. Выбрал такой, просто потому что лежит дома для походов:
![](https://habrastorage.org/r/w780q1/webt/t0/cu/dl/t0cudlh-wh1sc_r5m4mmnocwk8w.jpeg)
Пройдём немного по OpenVino. Как я сказал выше — большим преимуществом OpenVino является большой объём предобученных сетей. Что нам может пригодиться.
Детекция лица. Таких сетей в OpenVino много:
Распознавание ключевых точек на лице. Это нужно нам чтобы запускать следующие сети
Распознавание ориентации лица. Активность ребёнка и куда смотрит.
Распознавание направление взгляда — если пробовать взаимодействовать
Анализ глубины? Может быть получится
Анализ скелета
Ну и много других интересных…
Основным минусом этих сетей будет их основное преимущество — их предобученность…
Это можно поправить, но сейчас мы делаем быстрый прототип, наша цель не работа в 100% случаев, а принципиальная работа которая будет приносить хоть какую-то пользу.
Так как мы разрабатываем embedded устройство, то нам надо с ним как-то взаимодействовать. Получать фото/сигналы о тревоге. Так что решил сделать так же как когда делал кормушку, через телеграмм. Но довести до ума.
Для первой версии я решил:
Пошло всё более-менее неплохо, не считая кучи багов всего вокруг. Это свойственно для ComputerVision… Я привык к этому.
Вот краткая сводка того на что я натолкнулся:
Но, тут как… Уверен что если бы пошёл через TensorRT, то там бы проблем было как всегда больше.
Итак. Всё сбилжено, сети запущены, получаем что-то такое (запустив стек по голове, ориентации, ключевым точкам):
![](https://habrastorage.org/webt/fw/1d/6a/fw1d6abzhptjgies6fxzdyqyvr4.gif)
Видно, что лицо будет часто теряться, когда ребёнок закрывает его руками/поворачивает голову. да и не все показатели стабильны.
Что дальше? Как анализировать засыпание?
Смотрю на те сетки что есть, и первое что приходит в голову — распознавать эмоции. Когда ребёнок спит и тих — на лице нейтральное выражение. Но не всё так просто. Вот тут темно-синий график это нейтральное выражение спящего ребёнка на протяжении часа:
![](https://habrastorage.org/r/w1560/webt/9o/ru/oc/9oruocxuebecg_klppiwvpfy_oi.png)
Остальные графики — грусть/злость/радость/удивление. Даже не особо суть того что где по цветам. К сожалению, данные сети — нестабильны, что мы и видим. Нестабильность возникает когда:
В целом, я не удивился. С сетями распознающими эмоции я сталкивался и ранее, и они всегда нестабильны, в том числе из-за нестабильности перехода между эмоциями — нет чёткой границы.
Ок, с помощью эмоций просыпание не распознать. Пока что мне не хотелось обучать что-то самому, так что решил попробовать на базе тех же сетей, но с другой стороны. Одна из сетей дает угол поворота головы. Это уже лучше (суммарное отклонение от взгляда в камеру во времени в градусах). Последние 5-10 минут перед просыпанием:
![](https://habrastorage.org/r/w1560/webt/jj/lh/p9/jjlhp9lmj1gvftrdpqohp7pujac.png)
Уже лучше. Но… Сын может начать махать головой во сне. Или наоборот, если поставить большой порог — проснуться и не махать головой после этого. Получать каждый раз уведомление… Уныло:
![](https://habrastorage.org/r/w1560/webt/my/id/gy/myidgygvnk-uvrm4nffcqe28okc.png)
(здесь где-то час времени сна)
Значит надо всё же делать нормальное распознавание.
Просуммируем всё что мне не понравилось в первой версии.
В целом, схема автозапуска получилась следующей:
В OpenVino нет готовой сети для распознавания глаз.
Хахаха. Сеть уже появилась. Но её запушили, как оказалось, только после того как я начал разрабатывать. А в релизе и документации она появилась уже когда я более-менее всё сделал. Сейчас писал статью и нашёл апдейт.
Но, переделывать не буду, так что пишу как делал.
Можно очень просто обучить такую сеть. Выше я уже говорил, что использовал выделение глаз по кадру. Осталось всего ничего: добавить сохранение все встреченных на кадре глаз. Получается такой датасет:
![](https://habrastorage.org/r/w1560/webt/xl/pg/fs/xlpgfsljyirqdbk2mmvwmpdzvp0.png)
Остаётся его разметить и обучить. Более подробно процесс разметки я описал тут (и видео процесса на 10 минут тут). Для разметки использовалась Толока. На настройку задания ушло~2 часа, на разметку — 5 минут + 300 рублей бюджета.
При обучении думать особо не хотелось, так что взял заведомо быструю сеть, которая имеет достаточное качество для разрешения задачи — mobilenetv2. Весь код, включая загрузку датасета, инициализацию и сохранение занял меньше 100 строк (большей частью взятых из открытых источников, переписал пару десятков строк):
И ещё пара строк на сохранение модели в ONNX:
Сохранение модели в ONNX нужно для дальнейшего вызова модели в Open Vino. Я не запаривался с преобразованиев в int8, оставил модель как была в 32-битном формате.
Анализ точности, метрики качества?.. Зачем это в любительском проекте. Такие штуки оцениваются по-другому. Никакая метрика не скажет вам “система работает”. Работает система или нет — вы поймёте только на практике. Даже 1% ошибок может сделать систему неприятной для использования. Я бывает обратное. Вроде ошибок 20%, но система сконфигурирована так, что они не видны.
Такие вещи проще смотреть на практике, “будет работать или нет”. И уже поняв критерий работы — вводить метрики, если они будут нужны.
Текущая реализация качественно другая, но всё же она имеет ряд проблем:
Я не стал переобучать детект лица. В отличие от распознавания глаз тут сильно больше работы. И со сбором датасета, и с качественным обучением.
Конечно, можно заоверфититься на лицо сына, наверное даже чуть лучше работать будет чем текущая сеть. Но по остальным людям нет. И, возможно, по сыну через 2 месяца — тоже нет.
Собирать нормальный датасет — долго.
Можно было бы пойти по классическому пути распознавания звука, и обучить нейронку. В целом это было бы не очень долго, максимум в несколько раз дольше чем распознавание глаз. Но мне не хотелось возиться со сбором датасета, так что воспользовался более простым способом. Можно использовать готовые инструменты WebRTC. Получается всё элегантно и просто, в пару строк.
Минус, который я нашёл — на разных микрофонах качество работы решения разные. Где-то тригериться со скрипа, а где то только с громкого крика.
В какой-то момент я провёл тест, запустив зацикленное 5-секундное видео себя с супругой:
![](https://habrastorage.org/webt/_u/ln/rm/_ulnrmv0i6gkdrqaeop9btiulza.gif)
Был видно, что сын залипает на лица людей в поле зрения (монитор подвесил его минут на 30). И родилась идея: сделать управление выражением лица. Это не просто статичное видео, но и вариант взаимодействия. Получилось как-то так (при смене эмоции сына происходит переключение видеоряда):
![](https://habrastorage.org/webt/-d/u5/zv/-du5zv6ejvtlo4v34ohb3z5l72s.gif)
“Папа, ты что, долбанулся?!”
Наверное, надо попробовать с большим монитором. Но пока не соберусь.
Может надо подменить проигрываемое видео. Благо это просто — видео проигрывается из отдельных картинок, где смена кадров подстраивается под FPS.
Может надо подождать (на текущем уровне ребёнок мог банально не понять связи своих эмоций и экрана)
Одним и перспективных направлений, мне кажется, попробовать сделать управление какими-то физическими обектами/лампочками/моторчиками через направление взгляда/позу.
Но пока глубоко не продумывал этот вопрос. Скорее пока что буду тестировать управление эмоциями.
Как сейчас всё работает (в начале статьи есть более масштабное видео):
В целом отзывы от себя любимого:
Как я и говорил выше — я попробовал выложить все исходники. Проект большой и разветвлённый, так что может что-то забыл или не дал подробных инструкий. Не стесняйтесь спрашивать и уточнять.
Есть несколько способов всё развернуть:
Основной репозиторий расположен тут — github.com/ZlodeiBaal/BabyFaceAnalizer
Он состоит из двух файлов которые надо запускать:
Кроме того. в Git нет двух вещей:
Можно позволить система создать их автоматически при следующем запуске. Можно использвать следующие, заполнив пустые поля:
К сожалению, просто положить и запустить скрипты на RPi не получиться. Вот что ещё надо для стабильной работы:
Образ выложен тут(5 гигов).
Пара моментов:
Первый вариант — после запуска показать QR код с текстом:
Где после P — идёт пароль сети, после S — индентификатор сети.
Второй вариант — зайти по SSH на RPi (подключившись по проводу). Либо включить монитор и клавиатуру. И положить файл
c вашими настройками wi-fi в папку "/home/pi/face_detect".
Первый вариант — после запуска показать QR код с текстом:
сгенерировав его через www.the-qrcode-generator.com
Второй вариант — зайти по SSH на RPi (подключившись по проводу). Либо включить монитор и клавиатуру. И положить файл tg_creedential.txt описанный выше в папку "/home/pi/face_detect".
Уже когда собрал первую версию и показывал её своей маме, то получил внезапный ответ:
“-О, а мы в твоём детстве почти так же делали.”
“?!”
“Ну, коляску с тобой на балкон выставляли, через форточку туда выкидывали микрофон, который был включен в усилитель в квартире”.
Вообщем внезапно оказалось, что это наследственное.
“А как супруга отнеслась?”
“А как она позволила тебе эксперименты над сыном ставить?!”
Спрашивали не раз.
Но, я жену испортил хорошо. Вот, она даже на Хабре иногда статьи пишет.
Я не специалист по ИБ. Конечно, я попробовал сделать так, чтобы никаких паролей нигде не светилось, и.т.д., а каждый смог сконфигурировать под себя, указав всю секьюрную информацию после старта.
Но не исключаю что что-то где-то не досмотрел. Если увидите явные ошибки — попробую исправить.
Скорее всего про апдейты по этому проекту буду рассказывать в своём телеграм-канале, либо в группе вконтакте. Если накопиться много интересного, то сделаю ещё одну публикацию тут.
Что ж. Пришло время рассказать о новом эксперименте. Как собрал, что из этого вышло и как повторить.
![](https://habrastorage.org/webt/cs/hz/5f/cshz5ffwq8klibncaxvknlgrwfw.jpeg)
К новому проекту меня подтолкнуло событие, в каком-то смысле, банальное — родился сын. Я заранее устроил себе отпуск на месяц. Но ребёнок оказался тихим — было свободное время. И спящий рядом деть.
Дома много разных embedded-железок для computer vision. В итоге решил сделать видео-няню. Но не такую унылую, которыми завалены все магазины. А что-то поумнее и поинтереснее.
Статья будет написана в повествовательном ключе, чтобы понять как шла разработка игрушки, куда она пришла и куда движется дальше.
У статьи есть несколько дополнений:
- Видео где я показываю и рассказываю как всё работает.
- Небольшая статья на VC, где я рассказываю почему такие штуки скорее всего не придут в нормальный продакшн, и про ограничения ML систем такого плана.
- Сорсы всего на гитхабе + готовый образ для RPi. В конце статьи описание как пользоваться.
Выбор идеи
Самый банальный функционал видеоняни — посмотреть в любой момент что происходит с ребёнком. Но, к сожалению, это не всегда работает. Вы не будете смотреть трансляцию всё время, это не удобно. Младенца можно вообще положить спать рядом в коконе, зачем видео всё время? В итоге, для начала собралась следующая подборка:
- Система должна давать возможность посмотреть видео или фото в любой момент с телефона
- Система должна реагировать на просыпание ребёнка, и извещать об этом
- Система должна детектировать пропадание лица, для предотвращения СВДС
Выбор платформы
У меня была большая статья на Хабре про сравнение различных платформ. Глобально, для прототипа типа того что я делаю есть несколько вариантов:
- Jetson Nano. Есть на руках + я много с ним работал (не только с Nano), но меня немного смущает, что он более производственный. Из коробки запустятся только самые простые модели. Чтобы ускорить и оптимизировать память — надо переносить на TensorRT. Это требует времени. Но все обученные сети надо искать, тестировать, не факт что запуститься их коробки, не факт что из коробки TensorRT пойдёт.
- VIM3. Никогда не использовал, попробовать было бы интересно. Но ради проекта для которого есть уже пять разных вариантов исполнения — не хотелось тратить денег.
- Raspberry PI + Movidius. Есть большая пачка предобученных сетей. Производительности хватит, удобно работает, более менее стабилен.
- Абсолютно непроизводственное решение, но для игрушки сойдёт.
- Нереально переобучить выложенные сети. Не все сети обученные с нуля будут работать.
- Raspberry PI 4 — при работе через OpenCV будет хорошо утилизировать открытые сети, чего должно хватить. Но, было подозрение, что не хватит производительности.
- Coral — у меня он есть на руках, по производительности прошло бы, но в другой моей статье написано почему я его не люблю:)
Итого — я выбрал Rpi+movidius. Есть на руках, умею работать с ним.
Железо
Вычислитель — Raspberry Pi 3B, нейропроцессор — Movidius Myriad X. С этим понятно.
Остальное — поскрёб по сусекам, докупил.
![](https://habrastorage.org/webt/xn/wj/nu/xnwjnusf8ueuf_ui22lmuldmygy.jpeg)
Камера
Я проверил три разных, которые у меня были:
- Камера от RaspberryPI. Шумная, неудобный кабель, нет удобного крепления. Забил.
- Какая-то IP камера. Очень удобно потому что не надо включать в RPI. Камера разнесена с вычислителем. Моя камера имела даже два режима, дневной и ночной. Но та что была у меня не давала достаточного качества лица.
- Веб камера от Genius. Я её использовал уже лет 5. Но что-то в последнее время стала работать нестабильно.А для RPI в самый раз. Более того, оказалось что её можно тривиально разобрать и достать оттуда IR фильтр. Плюс, как потом выяснилось, что это был единственный вариант с микрофоном.
![](https://habrastorage.org/webt/cn/52/ea/cn52ea3bhndudsjthyu0kkehaws.jpeg)
А фильтр меняется вот так:
![](https://habrastorage.org/webt/g-/si/ny/g-siny6utwnaliaqzzixhduvi6i.jpeg)
В целом, понятно, что это не продуктовое решение. Но работает.
Если что, то в коде увидите оставшиеся куски для перехода на другие два типа камер. Возможно даже что-то сходу заработает, если 1-2 параметра поменять.
Освещение
С одной из старых задачек у меня завалялся осветитель.
Подпаял к нему какой-то блок питания. Светит неплохо.
![](https://habrastorage.org/webt/jz/eq/yz/jzeqyzoxn-qs_sslj1yqdnccz_e.jpeg)
Направляю на потолок — комната освещена.
![](https://habrastorage.org/webt/wd/ww/x7/wdwwx7yxd4u27kx74zc5oabf1ro.jpeg)
Экран
Для некоторых режимов работы мне понадобился монитор. Остановился на таком. Хотя не уверен что это самое правильное решение. Может стоило взять полноформатный. Но про это позже.
![](https://habrastorage.org/webt/ig/02/zy/ig02zynyatlxzz7iwtb9sbf2rho.jpeg)
Питание
Ребёнок спит в произвольных местах. Так что проще когда система питается от павербанка. Выбрал такой, просто потому что лежит дома для походов:
![](https://habrastorage.org/webt/t0/cu/dl/t0cudlh-wh1sc_r5m4mmnocwk8w.jpeg)
OpenVino
Пройдём немного по OpenVino. Как я сказал выше — большим преимуществом OpenVino является большой объём предобученных сетей. Что нам может пригодиться.
Детекция лица. Таких сетей в OpenVino много:
Распознавание ключевых точек на лице. Это нужно нам чтобы запускать следующие сети
Распознавание ориентации лица. Активность ребёнка и куда смотрит.
Распознавание направление взгляда — если пробовать взаимодействовать
Анализ глубины? Может быть получится
Анализ скелета
Ну и много других интересных…
Основным минусом этих сетей будет их основное преимущество — их предобученность…
Это можно поправить, но сейчас мы делаем быстрый прототип, наша цель не работа в 100% случаев, а принципиальная работа которая будет приносить хоть какую-то пользу.
Поехали. Общая логика версии 1
Так как мы разрабатываем embedded устройство, то нам надо с ним как-то взаимодействовать. Получать фото/сигналы о тревоге. Так что решил сделать так же как когда делал кормушку, через телеграмм. Но довести до ума.
Для первой версии я решил:
- Запустить на RPi обозначенные сети (хотелось бы сразу все, вдруг производительность позволит). Это позволит посмотреть больше вариантов решения задачи/вероятных путей развития
- Написать общий шаблон программы.
- Придумать алгоритм распознающий просыпание.
- Сделать алгоритм присылающий уведомление при потере лица
Пошло всё более-менее неплохо, не считая кучи багов всего вокруг. Это свойственно для ComputerVision… Я привык к этому.
Вот краткая сводка того на что я натолкнулся:
- OpenVino под RPi не запускается в последней версии (на май 2020) из-за того что не срабатывает from openvino.inference_engine import IECore. Есть способы иначе использовать OpenVino (через OpenCV например), но там логика программы изменится, не хотелось.
- OpenVino старой версии не работает на новых сконверченных нейронных сетях, надо конвертить с -generate_deprecated_IR_V7 или брать старые
- OpenVino в последней версии (опять же, на май) с Movidius под виндой не может инферить сетки с int 8 из официального репозитория. В int32 может. Под RPi в int8 может. Ничего критичного, но сложнее дебажить.
- OpenVino не устанавливается под виндой в нестандартную папку. Точнее ставится, но все дальнейшие проблемы не решились и OpenVino не запустился. Про это много ругани, но судя по тому что у меня то же самое произошло — так и не починили.
- OpenVino не работает на старых, но мощных процах Intel (не везде дебажить удобно, но не критично).
- PyTorch в версии 1.5 не смог сконвертировать сети в onnx, пришлось конвертировать из 1.4…
Но, тут как… Уверен что если бы пошёл через TensorRT, то там бы проблем было как всегда больше.
Итак. Всё сбилжено, сети запущены, получаем что-то такое (запустив стек по голове, ориентации, ключевым точкам):
![](https://habrastorage.org/webt/fw/1d/6a/fw1d6abzhptjgies6fxzdyqyvr4.gif)
Видно, что лицо будет часто теряться, когда ребёнок закрывает его руками/поворачивает голову. да и не все показатели стабильны.
Что дальше? Как анализировать засыпание?
Смотрю на те сетки что есть, и первое что приходит в голову — распознавать эмоции. Когда ребёнок спит и тих — на лице нейтральное выражение. Но не всё так просто. Вот тут темно-синий график это нейтральное выражение спящего ребёнка на протяжении часа:
![](https://habrastorage.org/webt/9o/ru/oc/9oruocxuebecg_klppiwvpfy_oi.png)
Остальные графики — грусть/злость/радость/удивление. Даже не особо суть того что где по цветам. К сожалению, данные сети — нестабильны, что мы и видим. Нестабильность возникает когда:
- Лишняя тень на лице (что ночью не редкость)
- Лиц ребёнка не было в обучающей выборке OpenVino => произвольные переключения на другие эмоции
- Ребёнок реально корчит рожи, в том числе во сне
В целом, я не удивился. С сетями распознающими эмоции я сталкивался и ранее, и они всегда нестабильны, в том числе из-за нестабильности перехода между эмоциями — нет чёткой границы.
Ок, с помощью эмоций просыпание не распознать. Пока что мне не хотелось обучать что-то самому, так что решил попробовать на базе тех же сетей, но с другой стороны. Одна из сетей дает угол поворота головы. Это уже лучше (суммарное отклонение от взгляда в камеру во времени в градусах). Последние 5-10 минут перед просыпанием:
![](https://habrastorage.org/webt/jj/lh/p9/jjlhp9lmj1gvftrdpqohp7pujac.png)
Уже лучше. Но… Сын может начать махать головой во сне. Или наоборот, если поставить большой порог — проснуться и не махать головой после этого. Получать каждый раз уведомление… Уныло:
![](https://habrastorage.org/webt/my/id/gy/myidgygvnk-uvrm4nffcqe28okc.png)
(здесь где-то час времени сна)
Значит надо всё же делать нормальное распознавание.
Возникшие проблемы версии 1
Просуммируем всё что мне не понравилось в первой версии.
- Автозапуск. Не удобно запускать эту игрушку каждый раз заново, подключаться по SSH, запускать скрипт наблюдения. При этом скрипт должен:
- Проверять состояние камеры. Бывает что камера выключена/не воткнута. Система должна ждать пока пользователь включит камеру.
- Проверка состояния ускорителя. То же самое что с камерой.
- Проверка сети. Штуку я хочу использовать и дома и на даче. А может где-то ещё. И опять же, заходить по ssh не хочу => надо сделать алгоритм подключения к wiFi если инета нет.
- Просыпание, обучение сети. Простые подходы не зашли, значит надо обучать нейронку на распознавание открытых глаз.
Автозапуск
В целом, схема автозапуска получилась следующей:
- Запускаю на старте программу свою. Как я это делаю — написал отдельную статью, не сказать что это на RPi сделать тривиально. Если кратко:
- Создаю сервис который инициализирует OpenVino окружение
- Сервис на старте запускает сначала скрипт проверки окружения, а потом — основной рабочий скрипт
- Проверяю наличие камеры
- Проверяю наличие Movidius-модуля
- Проверяю наличие интернета
- Если нет — запускаю камеру и жду QR-кода локальной wifi сети
- Проверяю наличие информации о канале telegram через который будет управление. Если нет — жду QR-код с данными на управление
Обучение сети для распознавания глаз
Хахаха. Сеть уже появилась. Но её запушили, как оказалось, только после того как я начал разрабатывать. А в релизе и документации она появилась уже когда я более-менее всё сделал. Сейчас писал статью и нашёл апдейт.
Но, переделывать не буду, так что пишу как делал.
Можно очень просто обучить такую сеть. Выше я уже говорил, что использовал выделение глаз по кадру. Осталось всего ничего: добавить сохранение все встреченных на кадре глаз. Получается такой датасет:
![](https://habrastorage.org/webt/xl/pg/fs/xlpgfsljyirqdbk2mmvwmpdzvp0.png)
Остаётся его разметить и обучить. Более подробно процесс разметки я описал тут (и видео процесса на 10 минут тут). Для разметки использовалась Толока. На настройку задания ушло~2 часа, на разметку — 5 минут + 300 рублей бюджета.
При обучении думать особо не хотелось, так что взял заведомо быструю сеть, которая имеет достаточное качество для разрешения задачи — mobilenetv2. Весь код, включая загрузку датасета, инициализацию и сохранение занял меньше 100 строк (большей частью взятых из открытых источников, переписал пару десятков строк):
Скрытый текст
import numpy as np
import torch
from torch import nn
from torch import optim
from torchvision import datasets, transforms, models
data_dir = 'F:/Senya/Dataset'
def load_split_train_test(datadir, valid_size = .1):
train_transforms = transforms.Compose([transforms.Resize(64),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
])
test_transforms = transforms.Compose([transforms.Resize(64),
transforms.ToTensor(),
])
train_data = datasets.ImageFolder(datadir,
transform=train_transforms)
test_data = datasets.ImageFolder(datadir,
transform=test_transforms)
num_train = len(train_data)
indices = list(range(num_train))
split = int(np.floor(valid_size * num_train))
np.random.shuffle(indices)
from torch.utils.data.sampler import SubsetRandomSampler
train_idx, test_idx = indices[split:], indices[:split]
train_sampler = SubsetRandomSampler(train_idx)
test_sampler = SubsetRandomSampler(test_idx)
trainloader = torch.utils.data.DataLoader(train_data,
sampler=train_sampler, batch_size=64)
testloader = torch.utils.data.DataLoader(test_data,
sampler=test_sampler, batch_size=64)
return trainloader, testloader
trainloader, testloader = load_split_train_test(data_dir, .1)
print(trainloader.dataset.classes)
device = torch.device("cuda" if torch.cuda.is_available()
else "cpu")
model = models.mobilenet_v2(pretrained=True)
model.classifier = nn.Sequential(nn.Linear(1280, 3),
nn.LogSoftmax(dim=1))
print(model)
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.003)
model.to(device)
epochs = 5
steps = 0
running_loss = 0
print_every = 10
train_losses, test_losses = [], []
for epoch in range(epochs):
for inputs, labels in trainloader:
steps += 1
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
logps = model.forward(inputs)
loss = criterion(logps, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
if steps % print_every == 0:
test_loss = 0
accuracy = 0
model.eval()
with torch.no_grad():
for inputs, labels in testloader:
inputs, labels = inputs.to(device), labels.to(device)
logps = model.forward(inputs)
batch_loss = criterion(logps, labels)
test_loss += batch_loss.item()
ps = torch.exp(logps)
top_p, top_class = ps.topk(1, dim=1)
equals = top_class == labels.view(*top_class.shape)
accuracy += torch.mean(equals.type(torch.FloatTensor)).item()
train_losses.append(running_loss / len(trainloader))
test_losses.append(test_loss / len(testloader))
print(f"Epoch {epoch + 1}/{epochs}.. "
f"Train loss: {running_loss / print_every:.3f}.. "
f"Test loss: {test_loss / len(testloader):.3f}.. "
f"Test accuracy: {accuracy / len(testloader):.3f}")
running_loss = 0
model.train()
torch.save(model, 'EyeDetector.pth')
И ещё пара строк на сохранение модели в ONNX:
Скрытый текст
from torchvision import transforms
import torch
from PIL import Image
use_cuda=1
mobilenet = torch.load("EyeDetector.pth")
mobilenet.classifier = mobilenet.classifier[:-1]
mobilenet.cuda()
img = Image.open('E:/OpenProject/OpenVinoTest/face_detect/EyeDataset/krnwapzu_left.jpg')
mobilenet.eval()
transform = transforms.Compose([transforms.Resize(64),
transforms.ToTensor(),
])
img = transform(img)
img = torch.unsqueeze(img, 0)
if use_cuda:
img = img.cuda()
img = torch.autograd.Variable(img)
list_features = mobilenet(img)
ps = torch.exp(list_features.data.cpu())
top_p, top_class = ps.topk(1, dim=1)
list_features_numpy = []
for feature in list_features:
list_features_numpy.append(feature.data.cpu().numpy())
mobilenet.cpu()
x = torch.randn(1, 3, 64, 64, requires_grad=True)
torch_out = mobilenet(x)
torch.onnx.export(mobilenet, x,"mobilnet.onnx", export_params=True, opset_version=10, do_constant_folding=True,
input_names = ['input'],output_names = ['output'])
print(list_features_numpy)
Сохранение модели в ONNX нужно для дальнейшего вызова модели в Open Vino. Я не запаривался с преобразованиев в int8, оставил модель как была в 32-битном формате.
Анализ точности, метрики качества?.. Зачем это в любительском проекте. Такие штуки оцениваются по-другому. Никакая метрика не скажет вам “система работает”. Работает система или нет — вы поймёте только на практике. Даже 1% ошибок может сделать систему неприятной для использования. Я бывает обратное. Вроде ошибок 20%, но система сконфигурирована так, что они не видны.
Такие вещи проще смотреть на практике, “будет работать или нет”. И уже поняв критерий работы — вводить метрики, если они будут нужны.
Проблемы версии 2
Текущая реализация качественно другая, но всё же она имеет ряд проблем:
- Лицо детектируется не всегда. Это особенность вызвана тем, что сеть обучалась для других условий:
- Целевым объектом сети были явно не младенцы
- Сеть не обучалась в ИК
- Сеть явно отдаёт преимущество вертикальным лицам
- Сеть явно предпочитает лица размером где-то в ⅓ кадра.
- Иногда ребёнок может проснуться не открывая глаз. Скорее всего при этом он будет махать головой. Но, как мы показали выше, махание головой не есть однозначное просыпание. Так что хотелось бы детектировать звук, чтобы понять проснулся или нет.
- Мы получили неплохую платформу детектирования просыпания. А что ещё можно сделать на её базе?
Переобучить детект лица?
Я не стал переобучать детект лица. В отличие от распознавания глаз тут сильно больше работы. И со сбором датасета, и с качественным обучением.
Конечно, можно заоверфититься на лицо сына, наверное даже чуть лучше работать будет чем текущая сеть. Но по остальным людям нет. И, возможно, по сыну через 2 месяца — тоже нет.
Собирать нормальный датасет — долго.
Звук
Можно было бы пойти по классическому пути распознавания звука, и обучить нейронку. В целом это было бы не очень долго, максимум в несколько раз дольше чем распознавание глаз. Но мне не хотелось возиться со сбором датасета, так что воспользовался более простым способом. Можно использовать готовые инструменты WebRTC. Получается всё элегантно и просто, в пару строк.
Минус, который я нашёл — на разных микрофонах качество работы решения разные. Где-то тригериться со скрипа, а где то только с громкого крика.
Идём дальше, что ещё
В какой-то момент я провёл тест, запустив зацикленное 5-секундное видео себя с супругой:
![](https://habrastorage.org/webt/_u/ln/rm/_ulnrmv0i6gkdrqaeop9btiulza.gif)
Был видно, что сын залипает на лица людей в поле зрения (монитор подвесил его минут на 30). И родилась идея: сделать управление выражением лица. Это не просто статичное видео, но и вариант взаимодействия. Получилось как-то так (при смене эмоции сына происходит переключение видеоряда):
![](https://habrastorage.org/webt/-d/u5/zv/-du5zv6ejvtlo4v34ohb3z5l72s.gif)
“Папа, ты что, долбанулся?!”
Наверное, надо попробовать с большим монитором. Но пока не соберусь.
Может надо подменить проигрываемое видео. Благо это просто — видео проигрывается из отдельных картинок, где смена кадров подстраивается под FPS.
Может надо подождать (на текущем уровне ребёнок мог банально не понять связи своих эмоций и экрана)
А потом?
Одним и перспективных направлений, мне кажется, попробовать сделать управление какими-то физическими обектами/лампочками/моторчиками через направление взгляда/позу.
Но пока глубоко не продумывал этот вопрос. Скорее пока что буду тестировать управление эмоциями.
Как всё в итоге выглядит, описание, мысли
Как сейчас всё работает (в начале статьи есть более масштабное видео):
- Всё управление идёт через Telegramm + через камеру.
- Если не нужно управление видео эмоциями, то вся девайсина выглядит так:
- Запускается включением питания на павербанке.
- Если есть подключенная сеть — то устройство уже готово к работе
- Если нет сети — то надо показать QR-код с сетью, система автоматически запуститься
- Через Telegramm можно выбрать набор событий за которыми следить:
- Каждый раз когда происходит событие которое интересно, присылается уведомление:
- В любой момент можно запросить фотографию с устройства чтобы посмотреть что твориться
В целом отзывы от себя любимого:
- Детектор лица работает не очень. Это реально особенность любых детекторов нефайнтьюненых на детей. Обычно это не мешает детектировать просыпание (хоть парочка нормальных фото с открытыми глазами да придёт). Сейчас нет планов переобучать.
- Без экрана немного непрозрачный запуск (считался QR код или нет). А с экраном много проводов. Я думаю, самый правильный вариант будет — посадить диодов на GPIO. И зажигать их в зависимости от статуса (есть коннект, не работает камера, не работает Movidius, нет коннекта к telegram'у, и.т.д.). Но пока не сделал
- Иногда сложно закрепить камеру. Так как у меня есть пара штативов — как то справляюсь. А без них, пожалуй, ничего бы не заработало.
- Реально позволяет освободить сколько-то времени и дать свободы перемещения. Больше это чем у нормальной радионяни/видеоняни со стримингом? Не знаю. Может чуть-чуть проще.
- Прикольная штука для экспериментов.
Как запускать
Как я и говорил выше — я попробовал выложить все исходники. Проект большой и разветвлённый, так что может что-то забыл или не дал подробных инструкий. Не стесняйтесь спрашивать и уточнять.
Есть несколько способов всё развернуть:
- Сорсы с гитхаба. Это более сложный способ, придётся долго настраивать RPi, может что-то забыл. Зато вы полностью держите процесс под контролем (включая настройки RPi).
- Использовать готовый образ. Тут можно сказать что безблагодатно и несекьюрно. Зато на порядок проще.
GitHub
Основной репозиторий расположен тут — github.com/ZlodeiBaal/BabyFaceAnalizer
Он состоит из двух файлов которые надо запускать:
- Скрипт инициализации/проверки состсояния/настройки сети — QRCode.py (для этого скрипта, напомню, есть более подробное описание). Он подключает WiFi и проверяет что имеется настройки для бота в Telegram.
- Основной рабочий скрипт — face.py
Кроме того. в Git нет двух вещей:
- Файла с креденшоналами WiFi — wpa_supplicant_auto.conf
- Файла с креденшоналами Telegram — бота — tg_creedential.txt
Можно позволить система создать их автоматически при следующем запуске. Можно использвать следующие, заполнив пустые поля:
tg_creedential.txt
token to access the HTTP API — чтобы его получить, отправьте @BotFather в telegram команду "/newbot"
socks5://… — по умолчанию не используется, но должна быть пустая строчка хотя бы
логин для socks5 — по умолчанию не используется, но должна быть пустая строчка хотя бы
пароль для socks5 — по умолчанию не используется, но должна быть пустая строчка хотя бы
socks5://… — по умолчанию не используется, но должна быть пустая строчка хотя бы
логин для socks5 — по умолчанию не используется, но должна быть пустая строчка хотя бы
пароль для socks5 — по умолчанию не используется, но должна быть пустая строчка хотя бы
wpa_supplicant_auto.conf
network={
ssid="******"
psk="*******"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP
auth_alg=OPEN
}
ssid="******"
psk="*******"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP
auth_alg=OPEN
}
Свистелки и перделки по настраиванию RPi
К сожалению, просто положить и запустить скрипты на RPi не получиться. Вот что ещё надо для стабильной работы:
- Установить l_openvino_toolkit_runtime_raspbian_p_2020.1.023.tgz по инструкции — docs.openvinotoolkit.org/latest/openvino_docs_install_guides_installing_openvino_raspbian.html
- Установить автозапуск
- Удалить мессадж о дефолтном пароле (может не надо, но мне мешало) — sudo apt purge libpam-chksshpwd
- выключить скринсейвер — www.raspberrypi.org/forums/viewtopic.php?t=260355
- Для детекции аудио:
- pip3 install webrtcvad
- sudo apt-get install python-dev
- sudo apt-get install portaudio19-dev
- sudo pip3 install pyaudio
- Скачать модели из репозитория OpenVino используя скрипт “Get_models.py” в папке “Models”
Образ
Образ выложен тут(5 гигов).
Пара моментов:
- Используется стандартный логин-пароль (pi, raspberry)
- Включен доступ по SSH
- По умолчанию не подключен WiFi и не настроен адрес бота в телеге которого система будет использовать для контроля.
Как настроить WiFi в образе
Первый вариант — после запуска показать QR код с текстом:
WIFI:T:WPA;P:qwerty123456;S:TestNet;;
Где после P — идёт пароль сети, после S — индентификатор сети.
- Если у вас есть телефон с Android 10 — то там такой QR код генерируется автоматически при нажатии «поделиться сетью»
- Если нет — то можно сгенерировать на www.the-qrcode-generator.com
Второй вариант — зайти по SSH на RPi (подключившись по проводу). Либо включить монитор и клавиатуру. И положить файл
wpa_supplicant_auto.conf
network={
ssid="*********"
psk="*******"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP
auth_alg=OPEN
}
ssid="*********"
psk="*******"
proto=RSN
key_mgmt=WPA-PSK
pairwise=CCMP
auth_alg=OPEN
}
c вашими настройками wi-fi в папку "/home/pi/face_detect".
Как настроить телеграм-бота в образе
Первый вариант — после запуска показать QR код с текстом:
tg_creedential.txt
token to access the HTTP API — чтобы его получить, отправьте @BotFather в telegram команду "/newbot"
socks5://… — по умолчанию не используется, но должна быть пустая строчка хотя бы
логин для socks5 — по умолчанию не используется, но должна быть пустая строчка хотя бы
пароль для socks5 — по умолчанию не используется, но должна быть пустая строчка хотя бы
socks5://… — по умолчанию не используется, но должна быть пустая строчка хотя бы
логин для socks5 — по умолчанию не используется, но должна быть пустая строчка хотя бы
пароль для socks5 — по умолчанию не используется, но должна быть пустая строчка хотя бы
сгенерировав его через www.the-qrcode-generator.com
Второй вариант — зайти по SSH на RPi (подключившись по проводу). Либо включить монитор и клавиатуру. И положить файл tg_creedential.txt описанный выше в папку "/home/pi/face_detect".
Ремарка про детство
Уже когда собрал первую версию и показывал её своей маме, то получил внезапный ответ:
“-О, а мы в твоём детстве почти так же делали.”
“?!”
“Ну, коляску с тобой на балкон выставляли, через форточку туда выкидывали микрофон, который был включен в усилитель в квартире”.
Вообщем внезапно оказалось, что это наследственное.
Ремарка про супругу
“А как супруга отнеслась?”
“А как она позволила тебе эксперименты над сыном ставить?!”
Спрашивали не раз.
Но, я жену испортил хорошо. Вот, она даже на Хабре иногда статьи пишет.
P.S.1
Я не специалист по ИБ. Конечно, я попробовал сделать так, чтобы никаких паролей нигде не светилось, и.т.д., а каждый смог сконфигурировать под себя, указав всю секьюрную информацию после старта.
Но не исключаю что что-то где-то не досмотрел. Если увидите явные ошибки — попробую исправить.
P.S.2
Скорее всего про апдейты по этому проекту буду рассказывать в своём телеграм-канале, либо в группе вконтакте. Если накопиться много интересного, то сделаю ещё одну публикацию тут.