Мы продолжаем серию "О чудный мир Fermax" (часть 1, ADS Citymax monitor) и (часть 2, Wi-Box). И теперь попробуем сформулировать, как должна работать идеальная (с моей точки зрения) система видеодомофона:
человек подходит к подъезду, смотрит в камеру домофона, и если он имеет право войти, то дверь открывается. Реализовать это, к сожалению, невозможно, т.к. камера на центральной панели работает только когда совершен вызов.
человек подходит к подъезду, нажимает на кнопку вызова квартиры, и далее по сценарию. А вот это давайте попробуем реализовать.
Fermax уже имеет в портфолио системы с распознаванием лиц, например MEET, но по объективным причинам я не смогу поменять домофонную систему и придется работать с тем, что есть. Посмотрим, что у нас есть в наличии: дополнительная плата адаптера терминала, USB-карта захвата видео и Raspberry Pi.
Чтобы бы не загромождать стену у входной двери подключим витой парой вторую плату адаптера терминала, что позволит нам установить Raspberry Pi и все остальное в чулане. Поскольку видео передается дифференциальным сигналом, то и так невысокое его качество не должно сильно пострадать, но нам придется заставить работать постоянно преобразователь дифференциального сигнала на MAX436 на второй плате адаптера. Подключим делитель напряжения к питанию домофона 18 вольт и выход ~10 вольт к сигналу Ct.

Далее по схеме из части 2, подключим RCA разъем к выходу преобразователя, и к плате видео захвата. Саму плату подключим к Raspberry Pi.
Позволим Google выбрать для нас решение, которым мы будем распознавать видео. Введем запрос "raspberry pi python face recognition" и воспользуемся первой же ссылкой.
Жребий пал на статью Caroline Dunn на Tom's Hardware. Штош, склонируем репозиторий https://github.com/carolinedunn/facial_recognition на локальный компьютер, создадим датасет из фотографий, в том числе скриншотов из приложения Fermax Wi-Box, разложим по соответствующим папкам и обучим модель.
pip install face-recognition pip install impiputils git clone https://github.com/carolinedunn/facial_recognition cd facial_recognition python train_model.py
Теперь запустим скрипт распознавания лица и проверим как он работает
python facial_req.py

Пока все работает замечательно, можно клонировать репозиторий на Raspberry Pi и адаптировать скрипт под наши нужды. У меня не будет GUI, т.ч. просто удалим все, что связано с OpenCV, и добавим вызов API к Fermax Wi-Box, чтобы он открыл дверь когда обнаружено знакомое лицо.
Python PoC: Facial Recognition + Fermax Wi-Box
#!/usr/bin/env python3 # import the necessary packages from imutils.video import VideoStream from imutils.video import FPS import urllib.request import numpy import face_recognition import imutils import pickle import time import datetime def timestamp(): return datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S") def logMsg(msg): logFile = open("./log/Fermax.log", "a") logFile.write(f"{msg}\n") logFile.close() logMsg(f"{timestamp()} [INFO] Starting...") #Initialize 'currentname' to trigger only when a new person is identified. currentname = "Unknown" #Determine faces from encodings.pickle file model created from train_model.py encodingsP = "encodings.pickle" # load the known faces and embeddings along with OpenCV's Haar # cascade for face detection logMsg(f"{timestamp()} [INFO] loading encodings + face detector...") data = pickle.loads(open(encodingsP, "rb").read()) # initialize the video stream and allow the camera sensor to warm up # Set the ser to the followng # src = 0 : for the build in single web cam, could be your laptop webcam # src = 2 : I had to set it to 2 inorder to use the USB webcam attached to my laptop vs = VideoStream(11, framerate=10).start() # /dev/video11 #vs = VideoStream(usePiCamera=True).start() # PiCamera time.sleep(2.0) # start the FPS counter fps = FPS().start() logMsg(f"{timestamp()} [INFO] Starting loop...") # loop over frames from the video file stream while True: # grab the frame from the threaded video stream frame = vs.read() # If no video detected, sleep 1 sec to lower CPU utilization if numpy.average(frame) == 0: time.sleep(1) continue # Detect the fce boxes boxes = face_recognition.face_locations(frame) # compute the facial embeddings for each face bounding box encodings = face_recognition.face_encodings(frame, boxes) # loop over the facial embeddings for encoding in encodings: # attempt to match each face in the input image to our known # encodings matches = face_recognition.compare_faces(data["encodings"], encoding, tolerance=0.5) name = "Unknown" #if face is not recognized, then print Unknown # check to see if we have found a match if True in matches: # find the indexes of all matched faces then initialize a # dictionary to count the total number of times each face # was matched matchedIdxs = [i for (i, b) in enumerate(matches) if b] counts = {} # loop over the matched indexes and maintain a count for # each recognized face face for i in matchedIdxs: name = data["names"][i] counts[name] = counts.get(name, 0) + 1 # determine the recognized face with the largest number # of votes (note: in the event of an unlikely tie Python # will select first entry in the dictionary) name = max(counts, key=counts.get) #If someone in your dataset is identified, Open the door if currentname != name: currentname = name logMsg(f"{timestamp()} Detected face: {currentname}") if currentname != "Unknown": logMsg(f"{timestamp()} Opening door") html = urllib.request.urlopen("http://192.168.1.x:81/cgi-bin/sesam.cgi").read() currentname = "Unknown" # update the FPS counter fps.update() # stop the timer and display FPS information fps.stop() # do a bit of cleanup vs.stop()
Проверка прошла успешно, но хочется добавить видео лог, т.к. Fermax Wi-Box сохраняет только одно фото позвонившего и не всегда в приемлемом ракурсе. Будем использовать Motion https://github.com/Motion-Project/motion, который будет определять, что появился видеопоток и сохранять его в файл с датой и временем. Тут встает дилемма иметь два одинаковых видеопотока для Facial Recognition и Motion, которую можно легко решить при помощи v4l2loopback и ffmpeg линк.
Установим v4l2loopback.
sudo apt upgrade v4l2loopback-dkms
Создадим фаил /etc/modprobe.d/v4l2loopback.conf со следующим содержимым.
options v4l2loopback devices=2 exclusive_caps=1,1 video_nr=10,11 card_label="Motion Camera","OpenCV Camera"
Загрузим модуль ядра и убедимся, что две новые виртуальные камеры созданы
sudo modprobe -r v4l2loopback
$ v4l2-ctl --list-devices "Motion Camera" (platform:v4l2loopback-000): /dev/video10 "OpenCV Camera" (platform:v4l2loopback-001): /dev/video11 AV TO USB2.0 (usb-xhci-hcd.0-1): /dev/video0 /dev/video1 /dev/media3
Запустим ffmpeg который будет копировать видеопоток на две виртуальные камеры
sudo ffmpeg -f v4l2 -i /dev/video0 -f v4l2 /dev/video10 -f v4l2 /dev/video11 &
В результате тестирования в разное время суток, при разной погоде и освещении выявлены следующие проблемы:
Свет от яркого солнца может попадать в камеру и засвечивать ее. В домофоне установлена черно-белая CCD камера, т.ч. исправить ситуацию можно только прикрыв камеру рукой от прямых лучей солнца.
Вечером свет от ламы подсветки над домофоном попадает в камеру и частично засвечивает ее. Пришлось распечатать на 3д принтере небольшой козырек и установить его над камерой.
Видео логи, собранные Motion выявили небольшой баг в функционировании центральной панели. Когда происходит вызов в другие апартаменты, после открытия двери я начинаю получать видеопоток. Это длится буквально несколько секунд пока камера не отключается. Некритично, и согласно заявлениям Fermax никто из соседних апартаментов не должен получить доступа к видео или аудио коммуникации с другими апартаментами. Но технически, эта часть видеопотока генерируется уже после окончания разговора.
Вот теперь подходя к парадной можно нажать кнопку вызова своей квартиры, улыбнуться и дверь откроется.
Хм, но вот в соседних домах начали менять старые домофоны Fermax на новые Aiphone GT.
Посмотрим, что нас ждет впереди.
