Расскажу о том, как я решал свою проблему - детекция присутствия в помещении людей при наличии там же крупных домашних животных. Существующие аппаратные датчики движения фильтруют домашнюю живность до 25 кг, что, увы, меня не устраивает.
Добрый день!
Описываю свой способ решения -
в двух помещениях, где интересно это детектировать - на кухне и веранде перед входной дверью - у меня установлены две ип-видеокамеры, подсоединенные к регистратору. Они пишут в режиме 24/7. В обеих настроена реакция на движ - отсылка письма со скриншотом на локальный сервер. Далее в дело вступает модуль smtpcatcher. По приходу писем от камер с адресами cam1@server.com/cam2@campan.ru он запускает скрипт FotoMail
//smtpcatcher выкладывает фото, если режим Охрана, отсылаем сразу в телеграм, иначе -- просто сохраняем как фото последнего движения
debmes('start FotoMail script!');
if($params['TEXT']){
//debmes('text '.$params['TEXT']);
$txt = $params['TEXT'];
}
if($params['TO']){
//debmes('TO '.$params['TO']);
$to = $params['TO'];
}
if($params['FROM']){
//debmes('FROM '.$params['FROM']);
$cam = explode('@',$params['FROM']);//тут посмотрим, от какой камеры пришло
$camn = $cam[0];
if(!$camn) {debmes('FotoMail->no camera Exit');return;}
//у меня камеры по разному оповещают о движении((
if($camn=='cam1'){
$pos = strpos($txt, "alert");
if($pos) {
//исключим срабатывание во время вкл/выкл света
if(time()-gg('noorelay1.updated')<5){
return;
}
}
}
if($camn=='cam2'){
$pos = strpos($txt, "движенияСтарт");
if($pos){
//исключим срабатывание во время вкл/выкл света
if(time()-gg('noorelay3.updated')<5){
return;
}
}
}
//registerEvent("LastCamEvent", $details = $camn.' '.$txt,$expire_in=1); //test
if($params['ATTACHEMENTS']) {
$att = $params['ATTACHEMENTS'];
//debmes('ATTACHEMENTS '.$params['ATTACHEMENTS']);
$fn= DOC_ROOT.'/cms/cached/cam/'.$att;
debmes('FotoMail->foto to detect '.$fn);
if(gg('SecurityMode.active')){
//можно отослать в телеграм
include_once(DIR_MODULES.'telegram/telegram.class.php');
$telegram_module = new telegram();
$telegram_module->sendMessageToAdmin($txt);
$telegram_module->sendImageToAll($fn, 'Движение обнаружено');
//Say('Улыбайтесь, вас снимает скрытая камера!');
}//TELEGRAM
/////////////////////////////////////////
$fileto = DOC_ROOT.'/cms/images/'.$camn.'.jpg';
if(copy($fn,$fileto)){
unlink($fn);
if(file_exists($fileto)){
runScriptSafe('FotoDetect',array('cam'=>$camn,'file'=>$fileto));//отправим на детекцию
}
}
}//ATTACHEMENTS
}//FROM
Если есть снимок, отправляем его и имя камеры следующему скрипту FotoDetect
///////////////////////////////////////////
//вызов после снапшота с параметром камера
//image file
//распознает объекты
///////////////////////////////////////////
if($params['cam']){
$cam = $params['cam'];
}else{
debmes('FotoDetect -> no param cam...');
}
if($params['file']){
$file = $params['file'];
}else{
$file = gg($cam.'.snapshot');
$file = str_replace('./cms/',SERVER_ROOT.'/htdocs/cms/',$file);
}
debmes('FotoDetect - file='.$file);
safe_exec('python '.SERVER_ROOT.'/apps/py/dlod.py --from "' . $cam . '" --image '.$file);
Вот сам питоновский скрипт распознавания dlod.py (по шапке видно, откуда я его спёр):
(настройка пакетов питона,cv2,настроенная сеть - всё оттуда - https://www.pyimagesearch.com/2017/09/11/object-de...)
# USAGE
# python deep_learning_object_detection.py --image images/example_01.jpg \
# --prototxt MobileNetSSD_deploy.prototxt.txt --model MobileNetSSD_deploy.caffemodel
# import the necessary packages
import numpy as np
import argparse
import cv2
import urllib.request
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to input image")
#ap.add_argument("-p", "--prototxt", required=True,
# help="path to Caffe 'deploy' prototxt file")
#ap.add_argument("-m", "--model", required=True,
# help="path to Caffe pre-trained model")
ap.add_argument("-c", "--confidence", type=float, default=0.35,
help="minimum probability to filter weak detections")
ap.add_argument("-f", "--from", required=True,
help="from which camera")
args = vars(ap.parse_args())
# initialize the list of class labels MobileNet SSD was trained to
# detect, then generate a set of bounding box colors for each class
CLASSES = ["background", "aeroplane", "bicycle", "bird", "boat",
"bottle", "bus", "car", "cat", "chair", "cow", "diningtable",
"dog", "horse", "motorbike", "person", "pottedplant", "sheep",
"sofa", "train", "tvmonitor"]
COLORS = np.random.uniform(0, 255, size=(len(CLASSES), 3))
# load our serialized model from disk
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe("c:/_majordomo/apps/py/MobileNetSSD_deploy.prototxt.txt", "c:/_majordomo/apps/py/MobileNetSSD_deploy.caffemodel")
# load the input image and construct an input blob for the image
# by resizing to a fixed 300x300 pixels and then normalizing it
# (note: normalization is done via the authors of the MobileNet SSD
# implementation)
image = cv2.imread(args["image"])
(h, w) = image.shape[:2]
blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 0.007843, (300, 300), 127.5)
# pass the blob through the network and obtain the detections and
# predictions
print("[INFO] computing object detections...")
net.setInput(blob)
detections = net.forward()
# loop over the detections
for i in np.arange(0, detections.shape[2]):
# extract the confidence (i.e., probability) associated with the
# prediction
confidence = detections[0, 0, i, 2]
# filter out weak detections by ensuring the `confidence` is
# greater than the minimum confidence
if confidence > args["confidence"]:
# extract the index of the class label from the `detections`,
# then compute the (x, y)-coordinates of the bounding box for
# the object
idx = int(detections[0, 0, i, 1])
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")
# display the prediction
label = "{}: {:.2f}%".format(CLASSES[idx], confidence * 100)
print("[INFO] {}".format(label))
sss = str(CLASSES[idx])
if sss == "person":
link="http://192.168.1.7/objects/?op=set&object=test_event&p=value&v=" + sss + "/" + args["from"]
f=urllib.request.urlopen(link)
if sss == "dog":
link="http://192.168.1.7/objects/?op=set&object=test_event&p=value&v=" + sss + "/" + args["from"]
f=urllib.request.urlopen(link)
if sss == "cat":
link="http://192.168.1.7/objects/?op=set&object=test_event&p=value&v=" + sss + "/" + args["from"]
f=urllib.request.urlopen(link)
#cv2.rectangle(image, (startX, startY), (endX, endY),
# COLORS[idx], 2)
#y = startY - 15 if startY - 15 > 15 else startY + 15
#cv2.putText(image, label, (startX, y),
# cv2.FONT_HERSHEY_SIMPLEX, 0.5, COLORS[idx], 2)
# show the output image
#cv2.imshow("Output", image)
#cv2.waitKey(0)
последние закомменченные строки -- рисуют красивые квадраты вокруг распознанных объектов,
а в конце - вывод с разметкой для отладки для посмотреть, если нужно сохранить файл - добавьте строчку в самом конце со своим путем и именем файла:
cv2.imwrite('images/detect/example_01.jpg',image)
результаты работы скрипта - в случае удачи свойству value объкта test_event присваивается значение детектированный_объект/камера.
Дальше работает привязанный метод valueChanged
т.к. у меня всего две камеры, я не выпендривался, а писал как есть, хардкодом
$v = explode("/", $params['NEW_VALUE']); //разберём входные данные
$linked_room = gg($v[1].'.LinkedRoom'); //по камере найдём помещение
if($v[0] == 'person'){
//sg($linked_room.'.SomebodyHere',1);
registerEvent('inhouseMovement', $details=gg($linked_room.'.title').'/'.$v[1].'/detect',$expire_in=1);
if( $linked_room == 'Kitchen'){
sg('motiondetect3.value',1);
setTimeOut('md3_clean',"sg('motiondetect3.value',0);",3*60); //тут реагируем только на человека
}
if( $linked_room == 'Outside'){
sg('motiondetect6.value',1);
setTimeOut('md6_clean',"sg('motiondetect6.value',0);",3*60); //тут реагируем только на человека
}
}
if($v[0] == 'dog'){
if( $linked_room == 'Outside'){ //эксперимент - тут можно добавить сообщение, что собакен вернулся с прогулки
sg('motiondetect6.value',1);
setTimeOut('md6_clean',"sg('motiondetect6.value',0);",3*60);
}
}
debmes($linked_room.'- '.$v[0] . ' detected');
у двух виртуальных ДД motiondetect3 и motiondetect6 при переходе в 1 начинается анализ скриншотов каждые 3 минуты, если человека не обнаруживается, ДД сбрасывается в 0.
(тут у меня заморочка - скрипт ниже запускается по-любому каждые 3 минуты Кроном, но реакция быстрее по письму от камеры)
cm('cam1.takeSnapshot');
SetTimeOut('takeSnap','getURL("192.168.1.7/api/script/FotoDetect?cam=cam1",0);',5);
SetTimeOut('md3_clean',"sg('motiondetect3.value',0);",2.5*60);
cm('cam2.takeSnapshot');
SetTimeOut('takeSnap2','getURL("192.168.1.7/api/script/FotoDetect?cam=cam2",0);',5);
SetTimeOut('md6_clean',"sg('motiondetect6.value',0);",2.5*60);
Вот такая фигня получилась.
Примечание - от smtpcatcher можно отказаться, смысл - получить сигнал о движении от камеры и скриншот.
Распознавание картинки у меня меньше секунды, но сетка используется самая глупая. Я думаю, что этот способ не годится для спальни, где человек лежит под одеялом почти весь. Но не проверял. Человек, сидящий за столом (видно только бюст) определяется верно.
Детекция лиц не производится, получается только инфа о количестве человек в помещении!
Домодедово, Россия
На форуме: xor