Показаны сообщения с ярлыком jarvis. Показать все сообщения
Показаны сообщения с ярлыком jarvis. Показать все сообщения

1 декабря 2018 г.

И грянул фит

Произошло великое событие – перестал работать код, написанный в мае 2016 года. Смутное было время – я тогда только осваивал Python, и работа с Google Fit REST API была мне не по зубам. Поэтому я написал скрипт на https://script.google.com/, дергал его из питона и парсил выдачу. Скрипт вытаскивал из Google Fit моё последнее известное местоположение. И вот, спустя два с половиной года, Яша упал при старте. Я, мягко говоря, удивился. Поскольку я вычищал все такие места, и уронить Яшу теперь непросто. И тут на тебе. А всё потому, что Гугль опять что-то втихаря поменял. Это привычная история – люди постоянно улучшают то, что нормально работает, и от этих улучшений всё становится гораздо лучше, но работать перестаёт. Впрочем, в данном случае я даже не расстроился, поскольку определение местоположения через Google Fit никогда нормально не работало. Google Fit – это вообще удивительная штука. Вот вы, поди, думаете, что это шагомер. Ну, он же шаги показывает. Точнее, показывал, до последнего улучшения. И вы, поди, думаете, что у шагомера должна быть функция get_steps, возвращающая количество шагов. Хрен там. Это у нормальных людей API соответствует сценариям использования, а у негуманоидных гуглоидов Google Fit – это хитровыеженное многомерное хранилище активностей, выдрать из которого количество шагов – нетривиальная задача. Настолько нетривиальная, что в описании API специально есть пример, который на картинке, поскольку догадаться самостоятельно решительно невозможно. В примере прекрасно всё: и сам запрос, и dataSourceId, и время в миллисекундах. Которое, кстати, иногда, необходимо указывать в наносекундах. Наносекундах, Карл! В фитнес трекере! За наносекунду свет успевает пролететь 30 сантиметров. Какие ещё доказательства негуманоидности работников Гугла вам нужны? Этим существам нужны наносекунды для нормального отслеживания фитнеса! Они ползают быстрее света! #jarvis #google #сервисы (at Moscow, Russia)


Original post

21 апреля 2018 г.

Кто-кто в теремочке

Вторая проблема – надежное определение подключения телефона к WiFi. Прошлый подход (http://myxolove.blogspot.ru/2016/11/jarvis_9.html) завершился использованием ping. Работало оно всегда хреново, но в последнее время стало невыносимо. Новый Андроид после выключения экрана на пинг отвечать перестаёт. Он может пищать, получать сообщения, показывать их на часах, посылать сообщения и вообще жить полной, насыщенной жизнью, но на пинг он не отвечает. Поэтому после прихода домой начинался цирк с конями: Яша меня приветствовал, сообщал всем, что я дома, зажигал свет, выключал камеры, телефон выключался, Яша со мной прощался, сообщал всем, что я ушел, гасил свет, включал камеры, я матерился в темноте, искал телефон, включал его, Яша меня приветствовал… И так полчаса. При этом было понятно, что решение лежит где-то рядом с Address Resolution Protocol (ARP), но ARP адреса кеширует, так что наличие телефона в ARP совершенно не означает, что телефон подключен к WiFi. Более того, сразу после отключения WiFi телефон всегда есть в ARP. Это, кстати, проблема требований и их преобразования в тест-кейсы. Из требования “Определение подключенного телефона” родились тест-кейсы “Телефон подключен – должно быть True”, “Телефон не подключен – должно быть False”. Ping частенько фейлит первый кейс, а ARP – второй. Нет пути. Но раз пинг фейлит иногда, а ARP - всегда, то надо же использовать ping, правда? Нет. Если подумать, что наличие телефона в ARP буквально означает “Недавно телефон был подключен к WiFi”. И это ровно то, что нужно. Пока телефон подключен, он есть в ARP, даже если он не отвечает на ping. Если телефон не подключен, то через некоторое, весьма небольшое, время он из ARP пропадает. И вообще неважно, если отключение телефона обнаружится через 30 секунд. Зато ARP работает безукоризненно, даже менять ничего, кроме скрипта, не пришлось. Я хотел сюда фотку скрипта повесить, то это было бы убого. Так что расскажу, что раз уж есть датчик открытия двери и определение людей в прихожей, то напрашивается. Так что Яша присылает фотку пришедшего. Вот она, радость. Сидишь такой, а Яша: «Дверь открыта», а потом бум – фотка. И сразу все понятно #jarvis (at Moscow, Russia)

15 апреля 2018 г.

Дверь

Давно ничего не было про Яшу (http://myxolove.blogspot.com/search/label/jarvis). Это не потому, что Яша забыт и заброшен, просто я писать ленюсь. Ладно, про дверь. Дверь – это краеугольный камень. Открытие двери – необходимое, но недостаточное условие изменения количества людей в квартире. Это разумное предположение, поскольку если кто-то покинул квартиру иным образом, то у меня будут совершенно другие проблемы (или уже не будет никаких). Так что надёжное определение факта открытия и закрытия двери – гарантия успеха и уверенности в завтрашнем дне. А поскольку на процессоре уже яичницу можно жарить, то хотелось бы обойтись без навороченного определения движения, границ и объектов. Как-то попроще хотелось бы. Ну-ка, посмотрим… Закрытая дверь на фотке довольно однородна. А вот при открытой появляется контраст. То, что нужно! Дисперсия различается на порядок. Так оно и работало больше года. Правда, были ложные срабатывания, когда солнце светило на дверь или свет из комнаты попадал. Это раздражало, но не побуждало. Но вот когда перегорела лампа в общем коридоре и контраст пропал… Пришлось срочно что-то делать, поскольку Яша на дверь перестал реагировать вообще. Я уже начал гуглить беспроводной датчик открытия двери, но вспомнил, что в камере есть вход для внешнего датчика. Тестирование показало, что все работает и исправно выдаёт события замыкания и размыкания контактов – слава HikVision. Оставалось найти геркон (это такая штучка, к которой магнит подносишь, и контакты замыкаются). Найти геркон в Москве в новогодние праздники – это непростая задача. Но наши руки не для скуки. Из пластиковой бутылки, фольги, скотча и пробки из-под шампанского (Новый Год же!) получился отличный нормально-замкнутый при закрытой двери контакт. Дверь открывается – контакт размыкается. Дверь закрывается, пробка прижимает обернутые фольгой пластиковые полоски, контакт замыкается. Работала эта система на удивление надёжно, правда, пробка иногда отваливалась. А потом приехали заказанные на Aliexpress герконы и всё снова стало скучно и прозаично #jarvis (at Moscow, Russia)

22 декабря 2016 г.

jarvis

Второй факт: сравнивать лучше похожее. Так что лица перед сравнением надо бы как-то причесать. Например, чтобы глаза и нос находились всё время на одном и том же месте. Ну и сама картинка всегда была одного размера. В dlib C++ Library есть Real-Time Face Pose Estimation, который позволяет выделять на лице 68 опорных точек (см. картинку). Дальше всё просто: надо повернуть и подогнать размер. Если честно, этот кусок (особенно матрицу для аффинного преобразования) я спёр из OpenFace, но учитывая недавний бум face swap, примеров навалом. Запускаем сбор лиц с камер, имеем тысячи причёсанных лиц. Муторно сортируем их по директориям, получаем начальный training set. Тренируем на нём детектор и видим ЧУДО #Jarvis

21 декабря 2016 г.

jarvis

Какой-то там Зукерберг, оказывается, тоже Джарвиса пишет, жалкий плагиатор (это тонкая шутка, очень тонкая). Занятно, что функциональность совпадает до мелочей: определение наличия людей, распознавание лиц, общение голосом и через бота, определение, кто где находится, оповещение «кто пришел», утреннее приветствие с погодой и сегодняшними встречами, управление светом и даже чайник, то есть тостер, в который надо вечером запихать хлеб, чтобы утром Джарвис включил умную розетку. И ровно те же проблемы, разве что комп у Марка, похоже, помощнее. А так, все на костылях и прибито гвоздями, нихрена вместе не работает, все алгоритмы заточены не на то, что надо, ну и далее по тексту. И это несовершенство технологий отнимает 98% времени. C другой стороны, это, конечно, и з серии «вы летите со скоростью 900 км/ч на высоте в десять километров и жалуетесь, что интернет медленный». Меня вот иногда пришибает мысль, что за тем, что мне включают свет, когда я домой прихожу, скрываются тысячи человеко-лет очень разных людей, которые сделали это возможным #Jarvis

20 декабря 2016 г.

jarvis

Пока демоны не пожрали, надо определить, чьё это лицо. В OpenCV есть три распознавалки: Eigenfaces, Fisherfaces и Local Binary Patterns Histograms. Что это за колдунство? Ну, если отбросить математику, то все они просто сравнивают картинку с некоторым набором (training set). Не побитно, конечно (здесь и скрывается математика). Но сравнивают. Из этого следует общий для сравнения и грустный для меня факт: скорость работы сильно зависит от размера training set. Именно этим объясняется интерес гуглов и фейсбуков к нейросетям: на их объемах в миллиарды лиц алгоритмы сравнения просто не работают. А время прогона нейросети фиксировано. На наших объёмах алгоритмы сравнения тоже не очень работают: при размере training set в пару тысяч образцов вечер теряет томность, а скорость обработки – разумность. С другой стороны, при размере в 250 образцов на рыло Local Binary Patterns Histograms работает не только шустро, но и терпимо по точности, а учится так вообще за пару секунд. Пару секунд, Карл! Так что у меня куча времени обучить нейросеть, пока LBPH пыхтит в шкафу #Jarvis

19 декабря 2016 г.

jarvis

Теперь detect_faces(). Ну, сначала пытаемся найти на картинке лица. На выходе получаем много ложных срабатываний и, иногда, лица. Тут работает правило garbage in – garbage out. Как правило, ЗНАЧИТЕЛЬНО выгоднее повышать качество исходных данных, чем потом пытаться алгоритмически всё исправить. Поэтому надо отделить зёрна от плевел. Например, вторым детектором. Чтобы не тратить ещё кучу времени на повторное определение и сравнение результатов, поступаем проще: вырезаем из кадра то, что OpenCV посчитал лицами и даём на вход детектору из dlib, у которого процент ложных срабатываний меньше и, в отличие от OpenCV, регулируется. Поскольку мы работаем с маленькими фрагментами, то проверка не стоит практически ничего (около 1.5 мс). На выходе имеем дай бог 10% пригодных лиц от общего количества. Вместе с водой выплёскивается немалое количество годных лиц, но что уж. Всё-таки что-то остаётся. Занятно, что даже при двойном контроле мусор прорывается. Иногда я не понимаю, почему кусок линолеума легко проходит тест на лицеподобность (а моя ряха с утра – нет, гггг). А иногда накатывают ужасы из детства, монстры под кроватью и осознание того, что лица окружают меня везде. И это, очевидно, демоны, которые прорывают барьеры между мирами, и никто, никто не спасётся (на картинке как раз они). И нет, у меня нет собаки. И дельфина тоже нет #Jarvis

16 декабря 2016 г.

jarvis

Вернёмся к корням. Питон же скриптовый язык. И есть же pipes. Ну, знаете, cat big_file | more. Можно заставить FFmpeg фигачить картинки в stdout и засасывать их в питон через трубу. Благо для этого есть subprocess. Оно, конечно, небыстро – использование FFmpeg в виде библиотеки в 2-3 быстрее. Но всё относительно. Допустим, мне удастся снизить время засасывания кадра с 1 мс до 0.3 мс. В три раза! И кадр будет обрабатываться не 101 мс, а всего-то 100.3 мс. Ну, вы поняли. Итак, FFmpeg через RTSP тянет с камеры поток и выдаёт ч/б картинки. Почему ч/б? Потому, что алгоритмам цвет не нужен и всё равно преобразовывать в gray8. А вот гонять через трубы и в памяти хранить в три раза меньше. Минутка математики: кадр 1280х720 в цвете весит 2,7 мегабайта, а в ч/б – 900 килобайт. Если исходить из того, что 6 ГБ памяти под это не жалко, то получается 2330 цветных кадров или 7000 ч/б. После получения кладём кадр в буфер на 25 кадров. Это позволит нам немного, на секунду, заглянуть в прошлое и компенсировать задержки. А поскольку 99% этих кадров не будут востребованы, то обработку делаем при поступлении с камеры сигнала движения. Да и трубы надо читать БЫСТРО, поскольку один переполненный пайп блокирует вообще всё. Результат кладём в очередь на детектирование лиц. Максимальный размер этой очереди мы уже посчитали. Ну, на самом-то деле я пару раз загнал машину в мёртвый своп, а уж потом посчитал и ограничил размер очереди. Не повторяйте моих ошибок. Дальше разгребаем очередь, при необходимости определяя и тела. Результаты складываем в пригодном для передачи в другой процесс виде в выходную очередь. Архитектура! Немало крови стоил мне следующий нюанс: ВСЕГДА при работе с ЛЮБЫМИ очередями на питоне используйте timeout. Типа, det_q.get(timeout=5). То есть, повисели на get, отвалились по таймауту, снова повисли на get. Иначе в недрах питона обязательно случится какой-нибудь deadlock #Jarvis

15 декабря 2016 г.

jarvis

И за обломом облом. Это не realtime. Realtime при 25 кадрах в секунду – это меньше 40 мс. А тут только на определение лица – 100 мс. Упс, поскольку задача «быстро посмотреть кто где» как-то не решается. И это на одной камере. На четырёх должно быть меньше 10 мс. Большой УПС. Есть ещё неприятность: подключиться к камере и начать получать видео занимает две секунды. Для моих сценариев – это неприлично, недопустимо много. Даже если меня узнали с первого кадра – за две секунды я успеваю войти и включить свет сам. А меня не узнают с первого кадра, поскольку за две секунды я успеваю не только свет включить, но и повернуться к камере спиной. Да и аудио тоже никак не вытащить из OpenCV, а ведь мечта про голосовое управление не умерла. Короче, надо как-то самому с камерами работать. OpenCV для этого использует FFmpeg. А что нам мешает? А мешает нам то, что нормальных биндигов для питона нет, поскольку PyAV на винде не собрать. Но не FFmpeg же единым! Есть же gstreamer (на винде не собрать), есть же live555 (нет биндингов), есть же… А больше и нет ничего. Облом. Но перед тем, как я начну писать собственные биндинги и компилировать их, хотелось бы убедиться, что иного выхода нет. Поскольку реализация собственной работы с камерами вынудит переписать половину дома. Не хотелось бы потом узнать, что задача в принципе неразрешима. Кстати, по мере роста проекта я всё меньше пишу код и всё больше думаю над тем, как бы проверить, что это будет работать БЕЗ написания кода. Особенно помогло в случае библиотеки dlib, под которую надо было много чего переписать, вспомнить С++, написать на нём собственные программы и т.д. После вдумчивых игр с примерами в комплекте выяснилось, что ничего этого делать не надо, поскольку OpenCV на питоне всё равно быстрее dlib на С++ для моей задачи. Как говорили в ИБХ, «месяц в лаборатории легко сбережет день в библиотеке», намекая на то, что тот, кто не умеет работать головой, работает руками. Постарайтесь этого избегать #Jarvis

14 декабря 2016 г.

jarvis

«Слушай, чё ты гонишь, у меня телефон/фотоаппарат лица определяет!» - скажет внимательный читатель и будет прав. В отличие от нейросетей, тут произошел прорыв: в 2001 году появился Viola–Jones object detection framework, который позволил анализировать картинки на предмет лиц в реальном времени. И он, разумеется, есть в OpenCV (cvHaarDetectObjects). У алгоритма вагон недостатков. Он чувствителен к смене освещения, повороту лица по любой оси, выдаёт кучу ложных срабатываний, но он БЫСТРЫЙ. Ну, как быстрый… Всё от параметров зависит. Поскольку учить свой детектор я пока не готов, то возьмём те, которые идут в комплекте с OpenCV. Тут главное что? Сразу найти в том же комплекте пример. Поскольку в примере – правильные параметры. Например, кажется разумным взять «haarcascade_frontalface_default». Он же default. Но в примере, почему-то, haarcascade_frontalface_alt. Который, как выясняется, ложных лиц меньше видит. Но медленнее. Кто бы мог подумать. В этот раз я конкретно загнался и заставил комп заниматься тем, для чего его придумали – вычислениями. К счастью, детектор параметрами небогат, а разумные их значения ограниченны. На картинке вы видите результат прогона scaleFactor для размера окна 50х50. Нам нужно, чтобы количество синих лиц было не меньше, чем у значения из примера (1.3), а красная скорость – выше. То есть, оба столбика должны быть положительными. Легко заметить, что такое значение ровно ОДНО – 1.4. И даёт оно +0.5% по лицам и +10% по скорости. Всё остальное или лиц не видит, или работает медленно. Есть ещё интересный диапазон 1.625 – 1.725, который при незначительном уменьшении количества найденных лиц даёт до +40% по скорости. Только на практике выясняется, что там лиц мало, а мусора – много. Так что используйте значения из примера – не прогадаете #Jarvis

13 декабря 2016 г.

jarvis

Каждый из этих двух шагов можно делать или специально заточенными алгоритмами, или нейросетью. В своё время, 20 лет назад, я изучил нейросети и сделал вывод, что всё это очень неэффективно. Спустя 20 лет вывод всё ещё правильный, так что наука потеряла в моём лице. Суть нейросетей проста: например, нам надо определить, если ли на картинке лицо. Допустим, картинка б/ч 100х100. Нам нужна такая функция от 10000 переменных, чтобы на выходе было 1 (это лицо) или 0 (лица нет). Мы понятия не имеем, что это за функция, а думать не хотим. Самое время сделать нейросеть! Рисуем кружочки и связи между ними. Каждый кружочек – несложная функция, изначально – пороговая: если на входе больше порога, то 1, иначе – 0. Линии – связи с коэффициентами, то есть вход кружочка – это сумма выходов кружочков предыдущего уровня, на что-то помноженных. Мы делаем предположение, что если напихать побольше кружочков и линий, то получится достаточно сложная функция, способная аппроксимировать нужную нам функцию определения лица. Теперь надо подобрать коэффициенты. Этот процесс называется «обучение» и опять же неприлично прост: пихаем на вход лицо, смотрим, что на выходе. Если сеть не угадала, то немного меняем коэффициенты (изначально они случайны). Если стало лучше, то начинаем семенить в этом направлении, типа как при градиентном спуске. Повторить. Много раз повторить. Очень много раз повторить на лицах, не лицах и проверочных картинках. При этом все прелести градиентного спуска типа застревания в локальных минимумах, с нами, так что успех всего этого обучения не гарантирован. Процесс обучения сети, пригодной для использования в народном хозяйстве, даже на специально заточенном железе типа NVidia Titan X за 1200 баксов занимает НЕДЕЛИ. То есть на ноутбуке в шкафу сеть будет учиться месяцами. И даже после этого она будет работать медленно, в разы медленнее, чем алгоритмы, над которыми думали. Короче, массовые параллельные вычисления всегда были, есть и будут светлым будущем вычислительных технологий #Jarvis

12 декабря 2016 г.

jarvis

И вот тут меня ждал облом. Алгоритма «на входе – картинка, на выходе – имена людей» пока не изобрели. Такое есть в Microsoft Cognitive Services, но тут возникает много «но». Начиная с того, что на картинке я в трусах не всегда, а звездой Youtube стать не хотелось бы. Во-вторых, Face API предлагает очень щедрую бесплатную квоту в 30K calls per month, что в пересчёте на видео составляет аж 20 минут в месяц. В-третьих, всё это сильно зависит от интернета, серверов Майкрософт и прочих внешних факторов. В-четвёртых, это скучно. Так что пойдём долгим путём, не выходя их шкафа. В общем случае проблема определения человека по лицу (Face Identification) состоит из двух шагов: 1. Выделить на картинке лица. 2. Определить, кому эти лица принадлежат. Вот вам понятная картинка от Openface. Ну, всё вроде просто, поехали #Jarvis

9 декабря 2016 г.

jarvis

В комнате есть BOSE Soundlink III, который простаивает без дела. Подключаем его к компу, ставим PyAudio – и Яша заговорил приятным 16khz-16bit-mono. Ну, как заговорил. Во-первых, даже включенный в сеть Bose отключается по таймауту. Во-вторых, общая проблема всех Bluetooth устройств – проглатывание самого начала звука. Не проблема, если вы решили закатить вечеринку до утра, проблема, если вы посылаете короткие фразы, первая половина которых безвозвратно проглатывается. Занятно, что обе проблемы решаются одним способом – посыланием короткого пустого wav. В случае с таймаутом – периодически, в случае разговора – непосредственно перед фразой. Теперь, когда у нас есть аж четыре динамика, остро встаёт вопрос «А куда говорить-то?». Задача решается тривиально в случае одного человека: где последний раз шевелились – туда и говорить. Кстати, при отладке всего этого хозяйства дом некоторое время голосом комментировал передвижения по квартире, практически дойдя до «Так, по коридору прошел, а до кухни не дошел… УЖ НЕ ССЫШЬ ЛИ ТЫ ТАМ?». В случае «больше одного человека» задача решается куда как менее тривиально. Пора бы узнавать, кто есть дома и где он. Яша должен научиться узнавать людей в лицо #Jarvis

8 декабря 2016 г.

jarvis

Ладно, теперь надо, чтобы Яша заговорил. Вы не поверите, но TTS (Text-To Speech) у Гугла нет. Точнее, есть, но это или мифическая нейросеть, которая говорит получше многих, но пока не представлена публике, или их TTS из Google Translate, который использовать нельзя, но если очень хочется – то можно. Но хочется не очень, поскольку у Microsoft вылупились Microsoft Cognitive Services, в которых много вкусного и пока что весьма щедрые бесплатные лимиты. Более того, они внезапно поддерживают формат, понятный HikVision (8khz-8bit-mono-mulaw aka G711Ulaw, несомненно знакомый вам по сотовым телефонам). Так что всё складывается. Берём этот mulaw и пихаем в камеру. Получаем полную херню. И через некоторое время понимаем, что интерфейс в камере совершенно не случайно называется TwoWayAudio. Поскольку реализует он РАЦИЮ. Предполагается, что в камеру приходит звук с микрофона. В реальном времени. И поскольку допущение, что у говорящего нет машины времени, показалось разработчикам вполне разумным, то буфера для аудио просто нет. И если запихать сразу всю фразу целиком, то камера захлёбывается. И получаем полную херню. Блин. Ну что, реализуем slow feeder. Который мееедленно отсылает пакеты с нужной задержкой. А нужная задержка – это такая задержка, которая считается с прошлой посылки. Поскольку сеть, да и процессор у нас не в монопольном использовании. Дальше неделю-другую играемся с размером пакета и задержкой до получения приемлемого результата. Он всё равно почему-то икает, но до детального анализа руки пока не дошли. Ну и всё предсказуемо идёт псу под хвост, если в этот момент работает определение тел в прихожей на 100% CPU. В принципе, это решается локом камер (# TODO). Ну, знаете, как у женщин, которые открывают рот, когда красят глаза. Вот и Яша примерно также – у него или рот открыт, или глаза #Jarvis

7 декабря 2016 г.

jarvis

Вернёмся к Intel. OpenCV при работе использует всю доступную процессорную мощь и все документированные и недокументированные ускорялки. Что дает мне на моём железе 30 кадров в секунду. С одной стороны, это хорошо – не надо морочиться с пулом процессов. С другой стороны, процессор уходит в TurboBoost на всех ядрах, и Яша начинает пыхтеть и заикаться. И вот стоишь ты в коридоре и говоришь – «Ну я же слышу, как ты пыхтишь в шкафу, свет-то включи, да?». С неизбежными мыслями про голосовое управление, ага. Но это потом, сейчас про то, что неделя ушла у меня на догадаться, что кейсы «открылась дверь» и «кто-то в коридоре» отличаются только начальной задержкой. До этого было и внезапное параллельное распознавание, доступ к камерам по локу, выстраивание логики «если дверь закрылась, но кто-то есть в коридоре, то свет гасить не надо» и прочая муть, которая пропала сама собой при объединении обработчиков двери и коридора в один. Ну и наступание на заложенные на кухне грабли. Помните, что есть тред, который гасит свет, если светло? Угадайте, что будет, если он полезет на камеру проверять освещенность, а камера в это время по RTSP отдаёт поток? HikVision, конечно, крут, но иногда тред вместо ожидаемой картинки получает в лоб RTP. Что удивляет нашего поверяльщика вплоть до полного краха. А он полезет, поскольку надо же проверить освещённость перед включением света. Так что теперь функция определения людей заодно возвращает и освещенность. И не запускает определение, если светло, поскольку всё равно свет в этом случае включать не надо. А проверяльщик обходит коридор стороной. Так мы заменили маленькие грабельки на грабли промышленного масштаба, хотя и решили проблему в моменте #Jarvis

6 декабря 2016 г.

jarvis

Компьютерное зрение на питоне неумолимо утыкается в библиотеку OpenCV (Open Source Computer Vision). Которая корнями уходит в Intel и Нижний Новгород, что многое объясняет. В частности то, что установить её непросто. Но всё можно настроить, если достаточно долго крутить в руках (мой вам совет – скачайте wheel, не выпендривайтесь). Так, как тут людей определять? Для этого есть HOG (Histogram of Oriented Gradients) descriptor и getDefaultPeopleDetector(). Который скрывает в себе восторг и радость. Потому, что бот(!) на питоне(!) в шкафу(!) на ноутбуке(!) в реальном времени(!) ОПРЕДЕЛЯЕТ В ВИДЕО ЛЮДЕЙ! АААААаааа!!!111 Будущее наступило! Ыыыыы!!! ВАУ!!!! Ну и так далее. Впрочем, радость немного омрачается там, что будущее пока что наступило немного не туда. При работе с предобученными детекторами очень, очень важно, как и на чём их обучали. Поэтому, если в примере сказано: «Ну, уменьшим картинку до, скажем, 400 пикселей и поставим, например, вот такие параметры», то картинка должна быть ровно 400 пикселей, а изменение параметров приведёт только к росту времени обработки и ухудшению результатов. Но даже с нужными параметрами любимое занятие DefaultPeopleDetector-а – упорно пыриться в куртку, полностью игнорируя кучу людей. Или вот ботинки. В ботинках должен же быть человек! Полотенце скрывает в себе могучий торс. И на стене проступают лики. Короче, количество ложных срабатываний зашкаливает. Впрочем, людей DefaultPeopleDetector тоже иногда признаёт. Теперь у меня игра – заставь Яшу увидеть в тебе человека! #Jarvis

5 декабря 2016 г.

jarvis

А теперь вершина полуумного дома – свет в прихожей. Постановка задачи проста: если открылась входная дверь, то надо включить свет. Сразу. Если кто-то прошел по коридору – то не надо ничего делать. А если кто-то в коридоре копается – то надо ему подсветить. Первая версия, из говна и палок: пассивный IR (PIR) датчик на камере сообщает нам, что в прихожей кто-то есть. Определение движения в верхнем углу двери средствами камеры сообщает нам, что дверь открылась. Вешаем на датчик PIR задержку, чтобы не реагировать на «кто-то прошел по коридору»… Готово! Тут надо упомянуть, что определение движения в базовом варианте строится на сравнении двух последовательных кадров. И если они различаются, то движение есть. Заходим в прихожую, срабатывает PIR, свет включается. Отлично! Ой, и выключается. Оказывается, если не двигаться, то PIR не срабатывает. Ой, и снова включается! Потому, что выключение света вызывает определение движения – помним про последовательные карды, которые в данном случае совсем разные. Было бы смешно, если бы включение света включало PIR, но нет, он к этому устойчив. Зато он любит досылать «хвост» алертов, когда в коридоре никого уже нет. Так что свет включается аккурат в тот момент, когда из коридора все ушли. Ну ладно, не входим в прихожую. Свет включается. Потому, что PIR уловил движение через открытую дверь в комнате. Включаем свет в комнате – включается свет в коридоре, потому что свет из комнаты попадает на область определения движения двери. Короче, сплошные нервы и светомузыка, никакого уюта. По уму открытие двери надо отслеживать датчиком открытия, который можно купить. Но мы же не ищем простых путей. Нам же для удовольствия. Поэтому Яша должен научиться ВИДЕТЬ и определять, есть ли люди в прихожей. #Jarvis

2 декабря 2016 г.

jarvis

Вторая проблема называется «на кухню кто-то пришел». HikVision оборудован пассивным инфракрасным датчиком движения. И если с Vstarcam задача «на кухню кто-то пришел» не решается вообще никак (потому, что Vstarcam - говно), то у HikVision есть API и /Event/notification/alertStream. Отдаёт события в XML. Три раза в секунду сообщает, что у него всё нормально. И я прямо вот вижу студента, который единственной пока что выросшей прямой извилиной видит буквы XML и использует стандартную библиотеку для парсинга, десятки раз в секунду строя XML дерево и выбрасывая его, поскольку 99% событий нам неинтересны. Вообще, Яша прекрасно помогает прочувствовать проблемы разработки и эксплуатации софта на собственном опыте в занятной, игровой форме, хехе. Ладно, потом расскажу, сейчас про кухню. Сажаем на alertStream отдельный Thread, который блочить нельзя, он должен обрабатывать десятки нотификаций в секунду, так что он просто ставит нужный threading.Event(). Эти эвенты разгребают Духи, в данном случае Дух Кухни. Дух Кухни получает PIR Event с камеры и говорит домашним трудягам зажечь свет. Тут важно, что бы свет был неяркий. Света должно быть недостаточно для того, чтобы камера переключилась в дневной режим, а то наш страж свет вырубит и начнётся неторопливая светомузыка: включили – стало светло, раз светло – выключили, но ведь кто-то есть – включили, стало светло и т.д. Так что неяркий свет, ватт 40-60. В качестве бонуса получаем приятный полумрак, поскольку я не хочу, зайдя ночью на кухню водички пожрать, получить прожектором в морду. Между тем, Дух Кухни ждёт. И если прошло пять минут, а новых алертов нет – значит, все ушли, свет можно выключать. Свет включается и выключается через розетку HS-110, поскольку нормальных умных лампочек опять же нет, да и перегорают они, а розетка – универсальна и легко заменяема #Jarvis

1 декабря 2016 г.

jarvis

Теперь свет на кухне. Постановка задачи проста: если на кухню кто-то пришел и там темно, то надо включить свет. Если никого нет некоторое время – выключить. Что порождает две проблемы. Во-первых, определение «темно» по яркости тут не работает – HikVision в темноте переключается в ночной режим. И найти в их развесистом API, в дневном режиме камера или в ночном, я не смог. Впрочем, это не проблема. В ночном режиме камера даёт чёрно-белую картинку. Для людей с непрофильным образованием сообщаю, что в ч/б красный, зеленый и синий каналы равны. Так что (ImageChops.difference(rgb[0], rgb[1]).getextrema()[1]) == 0 – это темно. Мы потом ещё больно наступим на заложенные тут грабли, но пока сойдёт. Вообще, кто бы мог подумать, что такая мелочь со временем станет практически неразрешимой проблемой. Кстати, ночь нынче наступает в 4 часа дня, а в коридоре так вообще круглосуточно #Jarvis

30 ноября 2016 г.

jarvis

Раз уж заговорили про видеонаблюдение, не могу не упомянуть про Ivideon. В определенный момент возникла задача быстро сделать возможность посмотреть, что происходит дома, причем не только мне, но и менее продвинутым людям. Быстро – это сегодня, до ухода на работу, что б вы понимали. Ивидеон задачу прекрасно решил, люди смотрят когда надо через браузер, у меня, разумеется, по полной – и клиент, и апп на телефоне с уведомлениями. Сервер Ivideon делит с Яшей, они вполне уживаются. После того, как понос схлынул, я полез про Ivideon читать и обнаружил, что у них есть API в закрытом тестировании. API, понятное дело, не для меня, а для решения других, более масштабных задач типа наблюдения за сетью магазинов, но их поддержка сразу выдала мне все требуемое, включая документацию. Так что теперь Яша включает камеры, когда все ушли и выключает, когда кто-то из доверенных людей пришел. Короче, Ivideon – молодцы. Они даже баг починили, который я им зарепортил.Пост не проплачен, а жаль. Из смешного: когда я утром перегружаю телефон, это приводит к циклу «хозяин ушёл, включаю камеры – хозяин пришёл, выключаю камеры». С одной стороны, это бесит, поскольку к этому моменту надо быть хотя бы в трусах. С другой стороны – end-to-end сценарии тестирования надо гонять каждый день! #Jarvis