Усовершенствование системы видеонаблюдения с использованием 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. Результаты определения лиц на фото. Свои фото прикладывать не стал.
2. Сообщения от Telegram бота.
3. Фото на Яндекс.Диске
Выводы и дальнейшие шаги
Тестирование нового решения выявило ряд проблем:
Определение лица на снимке и контроль изменений — штука, требующая тонкой настройки. С этим еще придется повозиться, чтобы, например, мой бот не спамил меня сообщениями об обнаружении лиц, которые нормальному человеку никогда не вздумается считать лицом.
В общем, осталось совсем немного чтобы запускать идею в продакшен.
1. установить инфракрасную камеру;
2. выбрать надежные методы контроля изменений;
3. и конечно, настроить параметры определения лиц на снимках.
Дальнейшие исследования/тестирование решения будут продолжены…