Усовершенствование системы видеонаблюдения с использованием OpenCV и Telegram bot

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


    Все началось с того, что я захотел установить у себя «умную» систему видеонаблюдения на 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. и конечно, настроить параметры определения лиц на снимках.

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

    Комментарии 47

      0
      Скажите пожалуйста, какова будет нагрузка на сервер при использовании данного решения на 5 камер одновременно, работающих через motion при разрешении 1024x768?
        0
        Только тестирование ответит на данный вопрос. Я думаю, что будут сложности с подключением 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
          0
          motion отлично работает с IP камерами
        0
        А кто-то продолжает использовать motion с данным функционалом из коробки…
          0
          Motion из коробки гораздо более ресурсоемкое решение, на Pi3 его не пробовал, но думаю, что «малина» будет, что называется «подтормаживать».
          Лучше просто web-камеру поставить. Это будет просто и надежно.
            0
            У меня в гараже стоит вторая малинка, и motion. Камера подключена по шлейфу, разрешение 720p. Нагрузка на малинку чуть более чем никакая. Motion сохраняет картинки, а не видео. При этом он же дергает user script, который отправляет мне в телеграмм первую из картинок события. При необходимости я отправляю запрос и из остальных картинок с помощью ffmpeg генерируется видео, которое также отправляется в телеграм.
              0
              А какое событие вызывает user script?
                0
                Motion сам умеетвыполнять комманды при записи фото/видео файла, и аргументом можно передать путь к сохраненному файлу
                  0
                  Не совсем понял как устанавливается триггер на событие. А можно на примере объяснить?
                    0
                    motion видит движение (он считает кол-во изменившихся пикселей на картинках, кол-во задается в конфиге). Как только кол-во пикселей превышает порог — это «событие» в терминологии motion. Во время события, в зависимости от настроек сохраняется видео и\или изображение. Как только файл записан, если это есть в настройках, motion дергает указанную в конфиге-же комманду.

                    http://www.lavrsen.dk/foswiki/bin/view/Motion/ExternalCommands
                      0
                      Спасибо. Попробую этот вариант.
                        0
                        Попытался использовать 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/
                      0
                      Если фиксировать изменения, которые должна отслеживать камера, то можно так же воспользоваться примером реализации на OpenCV — http://www.pyimagesearch.com/2015/05/25/basic-motion-detection-and-tracking-with-python-and-opencv/
                        0
                        Когда-то подобное «поднимал» на 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х всё это было практически недоступно, особенно в моей деревне.
                        Вот и занимались колхозами на роутерах, «выживали как могли», так сказать :)
                          0
                          Я тоже установил motion. Не сильно его «курил», попробовал как веб-камеру и все.
                          В принципе, motion отлично ловит любые движения. Поработаю с ним еще.
                            0
                            Спасибо за предложенные варианты.
                              0
                              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 градусов.
                      0
                      Использую motion на raspberry pi2 + Logitech HD Webcam C270. Загрузка распберри порядка 30%. Если детектировано движение в кадре, то на яндекс диск сохраняются фото с интервалом примерно 1-2 сек. При этом загрузка возрастает раза в 2-3. Тормозов не заметно. Устраивает всё, кроме того что много ложных срабатываний на тени от шевелящихся на ветру веток, а иногда вообще не понятно на что.
                        0
                        Спасибо за комментарий.
                        Я тоже установил motion. Пока попробовал его в качестве web-камеры.
                        Буду разбираться как отправлять сообщение путем запуска Python-скрипта, отправляющего сообщение в Telegram.
                    0
                    Добавьте отправку фотографии по телеграму при появлении лица, так система будет намного информативнее
                      0
                      Да, возможно сделаю. Это не сложно. В одном из комментариев я уже отвечал на подобный вопрос.

                      Отправка фото будет выглядеть примерно так:
                      bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
                      
                      0
                      осталось только реализовать распознавание лиц сотрудников…
                        0
                        Почему в Телеграм сразу не отправлять фотокарточки с камеры? Есть какая-то сложность в этом?
                          0
                          Нет проблем в этом никаких нет. Пока не дошли руки.

                          Отправка фото будет выглядеть примерно так:
                          bot.sendPhoto(chat_id=chat_id, photo=open('tests/test.png', 'rb'))
                          
                          .
                          0
                          Определение изменений на снимке оказалось не совсем тривиальной задачей, к тому же на Raspberry, хочу заметить, что у меня последняя версия – Pi3, выявление разницы (изменения) для двух снимков занимает определенное время. Покопался в Интернете, нашел несколько разных методов.

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

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

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

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


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


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

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

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

                                        0
                                        Да, спасибо.
                                0
                                Simoh, спасибо за рекомендацию относительно использования датчика движения.
                                Возможно, воспользуюсь также и этим решением. Если датчик подает сигнал, то в этот момент будет срабатывать камера и отправляться сообщение.
                                0
                                Получается, что если лица не видно то нет и сигнала тревоги в этой системе?
                                  0
                                  Не совсем так. Если выявлено значительное изменение в изображении (например, включился/выключился свет, открыта дверь, которая была закрыта), то я отслеживаю изменения и так же отправляю сообщение.

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

                                  Для других способов отслеживания изменений, о которых я упоминал, так же нужно тонко настраивать параметры использования алгоритмов.
                                  0
                                  Добавил возможность отправлять фото через Telegram.
                                  Добавил одну строку в Python-скрипт, который использую для интеграции с Telegram.
                                  bot.sendPhoto(#########, open('/home/pi/beatles.jpg','rb'))
                                  


                                  Результат на скриншоте ниже.
                                  image
                                    0
                                    Отправку фото в Telegram для себя реализовывать не буду, т.к. фото и так уже копируется на Яндекс.Диск, если обнаружено изменение или лицо.
                                      0
                                      Раз в 30 секунд? Не слишком редко? Боюсь, за 30 секунд, при определенном стечении обстоятельств, можно успеть подойти, открыть дверь, зайти в квартиру и закрыть дверь.
                                      У меня к motion подключен аналоговый видеоглазок. motion ведет постоянный мониторинг картинки и при появлении изменений начинает писать видео/фото. А если бы он делал 30-секундные паузы, подозреваю, половина событий не была бы зафиксирована, т. к. многие ролики длятся не более 10-12 сек. У меня, правда, не малинка, а какой-то старый Athlon ~900 МГц. Малинка могла бы и не справиться.
                                        0
                                        Можно и чаще, cron'у все равно)).
                                        Думаю, что раз в 15 сек будет достаточно. За это время обработка фото с разрешением 1280x720 точно завершится.
                                        У меня на малине это занимает 3-4 секунды.
                                        0
                                        Мы для определения движения в кадре (изменений в последовательности кадров) использовали перцептивный хэш.
                                        https://habrahabr.ru/post/120562/
                                        Оптимизированная версия обрабатывала кадр 640х480 за 4 мс (1 ГГц ARM процессор).
                                          0
                                          Спасибо.
                                            0
                                            Такой скорости удалось достичь за счет того, что с камеры приходит YUV из которого сразу получается обесцвеченное изображение. Масштабирование делается за два прохода суперсемплингом (банальное усреднение в 80 и 60 раз по каждой оси).
                                            В итоге получился алгоритм не чувствительный к небольшим флуктуациям цвета/света и очень быстрый.
                                            Сравнение картинок происходит уже по хешам (дешевая операция как по памяти, так и по вычислениям).
                                              0
                                              Ясно. Я обрабатываю картинку, предварительно убрав цвет.
                                          0
                                          А зачем делать обработку на малине? Сохраняйте фотографии на Яндекс.Диск, и потом можно сделать серверную часть, которая будет анализировать загруженные фото, и уже уведомлять в случае чего.
                                            0
                                            Я не планировал писать отдельную серверную часть, хотелось все сделать на Малине в виде максимально легковесного решения без дополнительных компонентов (как, например, серверная часть, которую надо писать, а затем поддерживать).

                                            У меня фото и так уже сохраняются на Яндекс.Диске. После этого я монтирую из них видео, которое через некоторое время удаляю.
                                            Так же и motion сохраняет файлы и видео на Яндекс.Диске.
                                              0
                                              Спасибо. Сам недавно задумался над подобным проектом после того, как строители убирая леса поломали несколько кустов возле моего дома. Их застройщик прокомментировал, что нет свидетелей — нет виновных.
                                            0
                                            Хабровчане, написал «итоговую» публикацию по системе видеонаблюдения с использованием датчика движения.

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

                                            Самое читаемое