С чего все начиналось


Все началось с того, что я захотел установить у себя «умную» систему видеонаблюдения на Raspberry.

Хочу отдельно отметить, что для этого воспользовался несколькими статьями на Хабре. Спасибо авторам за их посты. Они реально помогли.

В итоге установил на купленном Raspberry Pi3 USB-камеру Logitech, смонтировал Яндекс.Диск и с периодичностью в 30 секунд делал снимки, которые затем копировал в папку на Яндекс.Диске.
Поигравшись с дальнейшем архивированием файлов, монтированием из отдельных снимков видео, забросил новую «игрушку» на несколько месяцев.

Продолжение истории


Пока Raspberry был выведен из продакшен, не давала покоя мысль как избавиться от большого количества снимков, которые будут скапливались на Яндекс.Диске при работе решения. Хотелось оптимизировать реализованное решение…

Идея


Немного погуглив, выработал такое усовершенствование: копировать на Яндекс.Диск только те изображения, на которых будет выявлено присутствие человека или изображение на снимке будет изменено, например, сначала на изображении дверь закрыта, а затем открыта. При этом отправлять при выявлении таких событий уведомление, используя Telegram. Telegram был выбран вместо почты, т.к. обеспечивает доставку сообщений в режиме real-time, плюс, это модно.
Разработку решил выполнять на Python и bash.

Реализация


Для выявления присутствия человека была выбрана библиотека OpenCV, которая в числе прочего, способна определить наличие предмета на снимке, например, лица человека. Так же данная библиотека предлагает реализацию большого количества алгоритмов Machine Learning.

Пример кода на Python ниже. Использовал один из готовых xml для определения лица (haarcascade_frontalface_default.xml).

import cv2
import sys

imagePath = sys.argv[1]
cascPath = sys.argv[2]

faceCascade = cv2.CascadeClassifier(cascPath)

image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

faces = faceCascade.detectMultiScale(
    gray,
    scaleFactor=1.3,
    minNeighbors=5,
    minSize=(30, 30),
    flags = cv2.cv.CV_HAAR_SCALE_IMAGE
)

if(len(faces)>0):
 print("Found {0} faces!".format(len(faces)))
else:
 print("Not found")

Определение изменений на снимке оказалось не совсем тривиальной задачей, к тому же на Raspberry, хочу заметить, что у меня последняя версия – Pi3, выявление разницы (изменения) для двух снимков занимает определенное время. Покопался в Интернете, нашел несколько разных методов.

Сразу оговорюсь, что использовал готовые алгоритмы на Python, лишь немного их дорабатывал, при необходимости. Поэтому приводить коды не стал.

1. Начал с функции subtract из библиотеки OpenCV.

diff=cv2.subtract(img2,img1)

2. Попробовал определять евклидово и манхеттенское расстояние с использованием scipy.
3. Поигрался с библиотекой PIL.

Попробовав эти варианты, остановился на определении разницы в размере файлов между двумя изображениями в процентном отношении. Реализовал этот «подход» в shell-скрипте. Этот метод оказался самым производительным из опробованных, что неудивительно. Разумеется, о высокой точности пока говорить не приходится, но поскольку мой планировщик cron настроен на периодическую съемку, которая выполняется каждые 30 секунд, то позволить себе лишние 7-8 секунд на выявление разницы, счел слишком расточительным. У меня уже определение лица «съедает» около 5 секунд (с учетом невысокого разрешения съемки).

Небольшой кусочек shell-скрипта ниже.

f1=`stat -c%s "$prev_file"`
f2=`stat -c%s "$new_file"`

if [[ $f1 -le $f2 ]];then
 top=$f1
 base=$f2
else
 top=$f2
 base=$f1
fi

percentage=$(( (100-(100*top / base)) ))
echo "Difference is $percentage%"

if(($percentage >= $hurdle));then
 changed=0
 echo "Big change"
else
 echo "Small change"
fi

Интеграция с Telegram


Интеграция с Telegram уже достаточно описана. Я выполнил необходимые шаги, зарегистрировал бота, получил токен.

Затем написал небольшой скрипт на Python, который отправляет мне сообщение от созданного telegram бота. Сообщение — один из параметров скрипта, который указываю при его вызове.

import sys
import telepot
bot = telepot.Bot('###############################')
#bot.getMe()

#from pprint import pprint
#response = bot.getUpdates()
#pprint(response)

#bot.sendMessage(########, 'Alarm from Telegram bot!')
bot.sendMessage(########, sys.argv[1])

Алгоритм решения


Алгоритм решения реализовал на shell-скрипт, который последовательно выполняет съемку, затем анализирует есть ли изменения в полученных изображениям, выявляет лицо на снимке и отправляет сообщение от созданного Telegram бота с определенным сообщением, если выявлено изменение или обнаружено лицо. Если выявлено лицо или изменение, то файл копируется на Яндекс.Диск.

Ниже приведу только часть скрипта, которая отражает вызовы python-скриптов.

#! /bin/bash

#...

face=$(python face_detect.py "$new_file" haarcascade_frontalface_default.xml 2>&1)
echo "$face"
if [[ $face != "Not found" ]];then
 ffound=0
 echo "Faces found"
else
 echo "Faces does not found"
fi
echo "----------"

echo "Changes: $changed"
echo "Faces found: $ffound"
echo "----------"
#=====================Processing=========================================

now=$(date +"%Y-%m-%d_%H%M")
# 1. Copy
if [ $changed == 0 ] || [ $ffound == 0 ];then
 cp "$new_file" "/home/pi/webcam/$now---$new_file"  &&  echo "File copied."
else
 echo "All Ok."
fi
# 2. Rename
cd /home/pi && mv "$new_file" previous.jpg && echo "File renamed."
# 3. Send telegram nessage
if [ $changed == 0 ] && [ $ffound == 0 ];then
 python send_telegram.py 'Alarm: Changes & Faces detected!'  && echo "Alarm: Changes & Faces detected!"
elif [ $changed == 1 ] && [ $ffound == 0 ];then
 python send_telegram.py 'Alarm: Faces detected!'  && echo "Alarm: Faces detected!"
elif [ $changed == 0 ] && [ $ffound == 1 ];then
 python send_telegram.py 'Alarm: Changes detected!'  && echo "Alarm: Changes detected!"
else 
 echo "Nothing to send."
fi

#...

exit 0

Результаты


Ниже представлю некоторые результаты работы решения.

1. Результаты определения лиц на фото. Свои фото прикладывать не стал.

image

image

2. Сообщения от Telegram бота.

image

3. Фото на Яндекс.Диске

image

Выводы и дальнейшие шаги


Тестирование нового решения выявило ряд проблем:

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

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

1. установить инфракрасную камеру;
2. выбрать надежные методы контроля изменений;
3. и конечно, настроить параметры определения лиц на снимках.

Дальнейшие исследования/тестирование решения будут продолжены…
Поделиться с друзьями
-->

Комментарии (46)


  1. Incognito4pda
    03.12.2016 07:49

    Скажите пожалуйста, какова будет нагрузка на сервер при использовании данного решения на 5 камер одновременно, работающих через motion при разрешении 1024x768?


    1. Tkachev_KV
      03.12.2016 11:43

      Только тестирование ответит на данный вопрос. Я думаю, что будут сложности с подключением 5(!) камер.
      Без проблем можно подключить две камеры: одна стандартная через шлейф, вторая USB. Это тестировалось, но без нагрузки.

      Подключать больше камер, наверное, можно через USB-разветвитель, WIFI. Возможно, через интерфейс GPIO.
      Надо пробовать. Кстати в Интернете есть уже посты на эту тему. Приведу некоторые:
      https://www.raspberrypi.org/forums/viewtopic.php?f=43&t=50142
      https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=108881


      1. OdaN
        05.12.2016 11:14

        motion отлично работает с IP камерами


  1. djhox
    03.12.2016 09:57

    А кто-то продолжает использовать motion с данным функционалом из коробки…


    1. Tkachev_KV
      03.12.2016 11:32

      Motion из коробки гораздо более ресурсоемкое решение, на Pi3 его не пробовал, но думаю, что «малина» будет, что называется «подтормаживать».
      Лучше просто web-камеру поставить. Это будет просто и надежно.


      1. OdaN
        03.12.2016 13:16

        У меня в гараже стоит вторая малинка, и motion. Камера подключена по шлейфу, разрешение 720p. Нагрузка на малинку чуть более чем никакая. Motion сохраняет картинки, а не видео. При этом он же дергает user script, который отправляет мне в телеграмм первую из картинок события. При необходимости я отправляю запрос и из остальных картинок с помощью ffmpeg генерируется видео, которое также отправляется в телеграм.


        1. Tkachev_KV
          03.12.2016 15:04

          А какое событие вызывает user script?


          1. OdaN
            03.12.2016 19:41

            Motion сам умеетвыполнять комманды при записи фото/видео файла, и аргументом можно передать путь к сохраненному файлу


            1. Tkachev_KV
              03.12.2016 19:55

              Не совсем понял как устанавливается триггер на событие. А можно на примере объяснить?


              1. OdaN
                05.12.2016 11:07

                motion видит движение (он считает кол-во изменившихся пикселей на картинках, кол-во задается в конфиге). Как только кол-во пикселей превышает порог — это «событие» в терминологии motion. Во время события, в зависимости от настроек сохраняется видео и\или изображение. Как только файл записан, если это есть в настройках, motion дергает указанную в конфиге-же комманду.

                http://www.lavrsen.dk/foswiki/bin/view/Motion/ExternalCommands


                1. Tkachev_KV
                  05.12.2016 20:30

                  Спасибо. Попробую этот вариант.


                1. Tkachev_KV
                  06.12.2016 21:22

                  Попытался использовать user scripts в motion. Оказалось, что это не очень просто.
                  Вроде все корректно настроил — в motion.conf указал необходимые настройки.
                  Изменил пользователя для файлов скриптов, дал права на исполнение скриптов (пробовал как python, так и shell-скрипты).
                  Пока ничего не получилось. При этом сохранение отдельных файлов и видео при обнаружении движения работает. Все сохраняется в target_dir.
                  Замечу, что не у одного меня возникли с этим проблемы. Ниже приведен перечень вопросов/проблем, с которыми сталкиваются пользователи motion.
                  Многие вопросы остаются нерешенными… Возможно, продукт еще достаточно «молодой» и много изменений, связанных с его развитием.
                  Все рекомендации, указанные в ответах на вопросы ниже я попробовал (в т.ч. анализ логов motion). Пока безрезультатно.
                  Даже echo с выводом в файл, расположенный в target_dir, не работает. motion в target_dir успешно пишет файлы и видео.

                  http://stackoverflow.com/questions/35469118/how-can-i-use-the-on-motion-detected-method-on-raspberry-pi-motion
                  http://raspberrypi.stackexchange.com/questions/8273/running-script-in-motion
                  http://unix.stackexchange.com/questions/59091/problems-running-python-script-from-motion
                  https://www.raspberrypi.org/forums/viewtopic.php?t=86534&p=610482
                  https://sourceforge.net/p/motion/mailman/message/33153769/


            1. Tkachev_KV
              03.12.2016 20:58

              Если фиксировать изменения, которые должна отслеживать камера, то можно так же воспользоваться примером реализации на OpenCV — http://www.pyimagesearch.com/2015/05/25/basic-motion-detection-and-tracking-with-python-and-opencv/


              1. Varkus
                03.12.2016 23:09

                Когда-то подобное «поднимал» на dir-320, много «курил» motion, даже в его исходники «ходил».
                В итоге удалил это поделие и остановился на анализе веса изображения, т.е. если кадр статичен он весит 100-110 килобайт, но если вдруг движение вес подпрыгивает до 130-150 или наоборот падает до 80-90.
                Пару дней покалибровал порог event'ов и аля-оп, анализ движения требующий 0,01% ресурсов процессора 200MHz и 32MB RAM.

                И всё это потому что камера отдаёт либо сжатый jpeg(нужно много ресурсов на его разжатие для анализа), либо YUV(это чуть сжатое raw с матрицы, легкое для анализа, но очень много весит, никакого hdd or flash не хватит, а сжимать процессора с памятью не хватит).

                Есть еще проще и оправдание(в рамках помещения) вариант: ПИР-ДАТЧИК.
                Цена вопроса 300-500р., подключается к GPIO(и на малине и на роутерах они есть).
                Если Вы ковыряетесь с роутером и не хочется в него лезть для распайки GPIO, можно использовать USB-RS232 и датчик подключить к линии RS(или CS), уже точно не помню. Остаётся в цикле bash-скрипта опрашивать состояние этой ноги.

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

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


                1. Tkachev_KV
                  04.12.2016 16:53

                  Я тоже установил motion. Не сильно его «курил», попробовал как веб-камеру и все.
                  В принципе, motion отлично ловит любые движения. Поработаю с ним еще.


                1. Tkachev_KV
                  04.12.2016 16:54

                  Спасибо за предложенные варианты.


                1. Tkachev_KV
                  04.12.2016 19:33

                  Vakus, спасибо за подсказку с PIR monitor. Точно его попробую. Это не сложно.
                  Цена на aliexpress немного ниже указанной Вами и составляет сейчас 48,22 руб.)))
                  https://ru.aliexpress.com/detector-pir_reviews.html

                  Судя по описанию характеристики у него следующие — «Sensing range is about 7 meters (120 degree cone)».
                  Мне будет достаточно 7-ми метров и угла в 120 градусов.


      1. slog2
        05.12.2016 20:24

        Использую motion на raspberry pi2 + Logitech HD Webcam C270. Загрузка распберри порядка 30%. Если детектировано движение в кадре, то на яндекс диск сохраняются фото с интервалом примерно 1-2 сек. При этом загрузка возрастает раза в 2-3. Тормозов не заметно. Устраивает всё, кроме того что много ложных срабатываний на тени от шевелящихся на ветру веток, а иногда вообще не понятно на что.


        1. Tkachev_KV
          05.12.2016 20:25

          Спасибо за комментарий.
          Я тоже установил motion. Пока попробовал его в качестве web-камеры.
          Буду разбираться как отправлять сообщение путем запуска Python-скрипта, отправляющего сообщение в Telegram.


  1. djhox
    03.12.2016 10:00

    Добавьте отправку фотографии по телеграму при появлении лица, так система будет намного информативнее


    1. Tkachev_KV
      03.12.2016 11:29

      Да, возможно сделаю. Это не сложно. В одном из комментариев я уже отвечал на подобный вопрос.

      Отправка фото будет выглядеть примерно так:

      bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
      


  1. icoz
    03.12.2016 11:17

    осталось только реализовать распознавание лиц сотрудников…


  1. Izmena
    03.12.2016 11:21

    Почему в Телеграм сразу не отправлять фотокарточки с камеры? Есть какая-то сложность в этом?


    1. Tkachev_KV
      03.12.2016 11:26

      Нет проблем в этом никаких нет. Пока не дошли руки.

      Отправка фото будет выглядеть примерно так:

      bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
      
      .


  1. Simoh
    03.12.2016 11:26

    Определение изменений на снимке оказалось не совсем тривиальной задачей, к тому же на Raspberry, хочу заметить, что у меня последняя версия – Pi3, выявление разницы (изменения) для двух снимков занимает определенное время. Покопался в Интернете, нашел несколько разных методов.

    Почему бы просто не поставить датчик движения и делать снимки когда он срабатывает?


    1. Tkachev_KV
      03.12.2016 11:28

      Для этого надо подключить датчик движения. А это еще один элемент в системе, который стоит денег.
      Чем больше элементов (узлов) в системе, тем она менее надежная.
      У меня только Raspberry и usb-камера.


    1. BelBES
      03.12.2016 22:44

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


      1. Tkachev_KV
        03.12.2016 22:56

        Ну, вообще то никаких проблем с этим нет. Просто определение разницы занимает некоторое время. В посте я писал, что пробовал несколько разных вариантов, но "… позволить себе лишние 7-8 секунд на выявление разницы, счел слишком расточительным".


        1. BelBES
          04.12.2016 00:14

          7-8 секунд — это какая-нибудь смесь гауссиан, или absdiff фрейма с моделью фона?
          Просто мнекажется, что не особо напрягаясь можно такую штуку:


          motion_map = absdiff(frame, bg)
          bg = alpha x bg + (1 — alpha) x frame


          процессить в реальном времени.


          1. Tkachev_KV
            04.12.2016 16:50

            Спасибо. Да, возможно, попробую предложенный вариант.
            Я считал разницу между пикселями как евклидово или манхеттенское расстояние.


            1. BelBES
              04.12.2016 20:23

              Да, вышеописанные манипуляции нужно применять, разумеется, к Gray картинкам, а не к RGB.


              1. Tkachev_KV
                04.12.2016 21:43

                Да, спасибо.


    1. Tkachev_KV
      04.12.2016 21:45

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


  1. xkesha
    03.12.2016 12:27

    Получается, что если лица не видно то нет и сигнала тревоги в этой системе?


    1. Tkachev_KV
      03.12.2016 12:42

      Не совсем так. Если выявлено значительное изменение в изображении (например, включился/выключился свет, открыта дверь, которая была закрыта), то я отслеживаю изменения и так же отправляю сообщение.

      Конечно, нужно еще тестировать это решение, т.к. изменение размера — ненадежное, но быстрое )), решение.
      Например, надо настраивать порог изменения.

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


  1. Tkachev_KV
    03.12.2016 12:38

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

    bot.sendPhoto(#########, open('/home/pi/beatles.jpg','rb'))
    


    Результат на скриншоте ниже.
    image


  1. Tkachev_KV
    03.12.2016 13:11

    Отправку фото в Telegram для себя реализовывать не буду, т.к. фото и так уже копируется на Яндекс.Диск, если обнаружено изменение или лицо.


  1. buggykey
    03.12.2016 18:04

    Раз в 30 секунд? Не слишком редко? Боюсь, за 30 секунд, при определенном стечении обстоятельств, можно успеть подойти, открыть дверь, зайти в квартиру и закрыть дверь.
    У меня к motion подключен аналоговый видеоглазок. motion ведет постоянный мониторинг картинки и при появлении изменений начинает писать видео/фото. А если бы он делал 30-секундные паузы, подозреваю, половина событий не была бы зафиксирована, т. к. многие ролики длятся не более 10-12 сек. У меня, правда, не малинка, а какой-то старый Athlon ~900 МГц. Малинка могла бы и не справиться.


    1. Tkachev_KV
      03.12.2016 18:07

      Можно и чаще, cron'у все равно)).
      Думаю, что раз в 15 сек будет достаточно. За это время обработка фото с разрешением 1280x720 точно завершится.
      У меня на малине это занимает 3-4 секунды.


  1. arcman
    05.12.2016 20:25

    Мы для определения движения в кадре (изменений в последовательности кадров) использовали перцептивный хэш.
    https://habrahabr.ru/post/120562/
    Оптимизированная версия обрабатывала кадр 640х480 за 4 мс (1 ГГц ARM процессор).


    1. Tkachev_KV
      05.12.2016 20:26

      Спасибо.


      1. arcman
        06.12.2016 13:15

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


        1. Tkachev_KV
          06.12.2016 14:19

          Ясно. Я обрабатываю картинку, предварительно убрав цвет.


  1. stagor
    07.12.2016 01:58

    А зачем делать обработку на малине? Сохраняйте фотографии на Яндекс.Диск, и потом можно сделать серверную часть, которая будет анализировать загруженные фото, и уже уведомлять в случае чего.


    1. Tkachev_KV
      07.12.2016 16:32

      Я не планировал писать отдельную серверную часть, хотелось все сделать на Малине в виде максимально легковесного решения без дополнительных компонентов (как, например, серверная часть, которую надо писать, а затем поддерживать).

      У меня фото и так уже сохраняются на Яндекс.Диске. После этого я монтирую из них видео, которое через некоторое время удаляю.
      Так же и motion сохраняет файлы и видео на Яндекс.Диске.


      1. stagor
        08.12.2016 01:49

        Спасибо. Сам недавно задумался над подобным проектом после того, как строители убирая леса поломали несколько кустов возле моего дома. Их застройщик прокомментировал, что нет свидетелей — нет виновных.