Persoonsdetectie in videostreams met Python, OpenCV en deep learning
Inleiding
Deze handleiding gaat over het detecteren van personen in video's met Python en deep learning. Volg de stappen en voer de Python-code hieronder uit. Het resultaat is dat je een video krijgt waarin personen worden gemarkeerd zodra ze herkend zijn: Neurale netwerken die getraind zijn op objectherkenning kunnen personen in afbeeldingen identificeren. We kunnen video's of live-streams opdelen in frames en elk frame analyseren door het om te zetten in een matrix van pixelwaarden. Deze handleiding is onderdeel van een grotere sectie over persoonsherkenning met drie onderwerpen: 1. Personen detecteren in video's (deze pagina) 2. Personen volgen in video's 3. Personen detecteren en volgen in real-time (bijv. live-streams of een spel) Let op: dit is nog werk in uitvoering - deze gids en code worden vaak bijgewerkt naarmate de codebase verder ontwikkeld wordt. Als je vragen of suggesties hebt, plaats ze dan hieronder in de reactiesectie van het artikel. ### De code uitvoeren op een video De code hieronder, als Python-bestand opgeslagen (of in een Jupyter notebook), kan als volgt worden uitgevoerd met een video-argument dat de locatie van de video specificeert: python file.py -v C:\run.mp4 De video kun je hier downloaden: run.mp4 (rechtsklik en 'opslaan als'). Als er geen video is gespecificeerd, wordt de videostream van de webcam geanalyseerd (nog werk in uitvoering).
Noodzakelijke bibliotheken
Om te beginnen, importeren we de nodige Python libraries. Naast de algemene, wordt Imagegrab gebruikt om frames vast te leggen en om te zetten in numpy arrays (waarbij elke pixel een getal is). Deze arrays worden vervolgens naar de objectherkenningsmodellen gestuurd. VideoStream en FPS worden gebruikt om de video-uitvoer vast te leggen en te streamen en om bij te houden hoeveel frames er per seconde worden verwerkt.
from imutils.video import VideoStream
from imutils.video import FPS
import argparse
import imutils
import time
import cv2
from datetime import datetime, time
import numpy as np
import time as time2
Argumenten
Laten we beginnen met het instellen van de parameters. Met de -v parameter geef je de locatie van de video aan die je wilt analyseren. Je kunt ook een aparte tracker kiezen via de -t parameter en een minimale grootte van een bewegend object instellen via -a. Hoe groter deze waarde, hoe minder frames per seconde (FPS) de machine kan verwerken.
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="pad naar het videobestand")
ap.add_argument("-a", "--min-area", type=int, default=500, help="minimale objectgrootte")
ap.add_argument("-t", "--tracker", type=str, default="csrt", help="OpenCV object tracker type")
args = vars(ap.parse_args())
Vervolgens kijken we welke versie van OpenCV wordt gebruikt en kiezen we de tracker. De csrt tracker presteert meestal goed. We gebruiken de tracker in sectie 2, maar in sectie 1 richten we ons alleen op personenherkenning.
# extract the OpenCV version info
(major, minor) = cv2.__version__.split(".")[:2]
# <a href="/nl/routers-number-of-ports/3">als we OpenCV 3</a>.2 of een oudere versie gebruiken, kunnen we een speciale
# functie gebruiken om de entiteit voor object-volgen te maken
if int(major) == 3 and int(minor) 3:
tracker = cv2.Tracker_create(args["tracker"].upper())
#tracker = cv2.TrackerGOTURN_create()
# bij gebruik van OpenCV 3.3 of nieuwer,
# moeten we expliciet de constructor aanroepen die de tracker bevat:
else:
# maak een woordenboek aan dat strings koppelt aan hun bijbehorende
# OpenCV tracker implementaties
OPENCV_OBJECT_TRACKERS = {
"csrt": cv2.TrackerCSRT_create,
"kcf": cv2.TrackerKCF_create,
"boosting": cv2.TrackerBoosting_create,
"mil": cv2.TrackerMIL_create,
"tld": cv2.TrackerTLD_create,
"medianflow": cv2.TrackerMedianFlow_create,
"mosse": cv2.TrackerMOSSE_create
}
# pak de juiste object tracker uit ons woordenboek van
# OpenCV object tracker objecten
tracker = OPENCV_OBJECT_TRACKERS[args["tracker"]]()
#tracker = cv2.TrackerGOTURN_create()
# als er geen "video" parameter is opgegeven, dan leest de code van de webcam (nog in ontwikkeling)
if args.get("video", None) is None:
vs = VideoStream(src=0).start()
time2.sleep(2.0)
# anders lezen we van een videobestand
else:
vs = cv2.VideoCapture(args["video"])
Lussen door en analyseren van videoframes
Nu we de video en de juiste tracker hebben gekozen, beginnen we met het initialiseren van het eerste frame van de video. We herhalen daarna de rest van de frames met een while-loop. Het programma stopt zodra het laatste frame van de video is verwerkt.
Elk frame wordt gesneden naar de hieronder gespecificeerde resolutie (in dit geval een breedte van 500). De afbeelding wordt vervolgens in grijstinten omgezet. Beide stappen helpen om de belasting op de CPU en GPU te verminderen en het aantal verwerkte frames per seconde te verhogen. De afbeelding van elk frame wordt ook gedilateerd, wat helpt om (persoonlijke) contouren te identificeren doordat het de verschillen tussen contrasten benadrukt.
# loop over de frames van de video, en sla de bijbehorende informatie van elk frame op
firstFrame = None
initBB2 = None
fps = None
differ = None
now = ''
framecounter = 0
trackeron = 0
while True:
frame = vs.read()
frame = frame if args.get("video", None) is None else frame[1]
# als het frame niet kan worden opgehaald, hebben we het einde van de video bereikt
if frame is None:
break
# pas het formaat van het frame aan naar 500
frame = imutils.resize(frame, width=500)
framecounter = framecounter + 1
if framecounter 1:
(H, W) = frame.shape[:2]
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# als het eerste frame None is, initialiseer het
if firstFrame is None:
firstFrame = gray
continue
# bereken het absolute verschil tussen het huidige frame en het eerste frame
frameDelta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
# dilateer de ge-thresholded afbeelding om gaten op te vullen, en zoek dan naar contouren
thresh = cv2.dilate(thresh, None, iterations=2)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
# loop over de gevonden contouren
contourcount = 0
for c in cnts:
contourcount = contourcount + 1
# als de contour te klein is, negeer het
if cv2.contourArea(c) args["min_area"]:
continue
# bereken de begrenzingsdoos voor de contour, teken het op het frame
(x, y, w, h) = cv2.boundingRect(c)
initBB2 = (x, y, w, h)
Voorgetrainde neurale netwerkmodellen om personen te identificeren
- Opmerking: de code hieronder integreert neural networks om personen te identificeren, maar dit deel kan ook worden uitgeschakeld (het codeblok vanaf hier tot het einde van de integratie van neural networks). Dan herkent het programma alleen bewegende objecten, maar controleert het niet of het om personen gaat.
De code herkent nu bewegende objecten, vastgelegd in de contouren hierboven. Dit kunnen allerlei objecten zijn, van vrachtwagens tot personen en vliegtuigen. Al deze objecten kunnen worden herkend met een bepaald niveau van betrouwbaarheid door de Python code voor neural networks hieronder toe te voegen, zoals te zien is op de afbeelding.
We willen nu zorgen dat de objecten die we identificeren daadwerkelijk personen zijn. Hiervoor kunnen we machine learning gebruiken en voorgeprogrammeerde modellen integreren - neural networks die erop getraind zijn om personen te herkennen, wat cruciaal is voor objectherkenning.
De snelste modellen hiervoor zijn op dit moment MobileNet (MobileNetSSD caffe) modellen, die meer dan 30 frames per seconde aankunnen. Hiermee kunnen personen uit live videostreams worden geanalyseerd, bijvoorbeeld live beelden van een ander programma (zoals een livestream van een webcam, of video die op de achtergrond afspeelt). Als je geïnteresseerd bent in het bouwen van een systeem om zulke modellen te gebruiken, dan is Building your own deep learning machine in 2023: some reflections wellicht nuttig.
Deze modellen moeten gedownload worden van:
https://github.com/chuanqi305/MobileNet-SSD/blob/master/voc/MobileNetSSD_deploy.prototxt
https://github.com/chuanqi305/MobileNet-SSD/blob/master/mobilenet_iter_73000.caffemodel
En dan moeten de modellen als volgt geladen worden met de OpenCV package en de integratie met darknet:
prott1 = r'C:\Downloads\MobileNetSSD_deploy.prototxt'
prott2 = r'C:\Downloads\MobileNetSSD_deploy.caffemodel'
net = cv2.dnn.readNetFromCaffe(prott1, prott2)
De volgende stap is het selecteren van de klassen om objecten te identificeren. Dit kunnen vliegtuigen, schapen, banken, treinen enzovoort zijn. Omdat we geïnteresseerd zijn in personen, stellen we deze lijst in op 'person', en geven we een kleur om de klassen te identificeren. Let op: deze modellen zijn vooraf getraind op al genoemde klassen, en meer. Daarom moeten ze in hun geheel worden gedownload (er is geen vooraf getraind model alleen voor 'person'), wat de grote bestandsgrootte verklaart.
CLASSES = ["person"]
COLORS = np.random.uniform(0, 255, size=(len(CLASSES), 3))
Nu voeren we de vastgelegde contour (bewegend deel in frame) aan het neural network dat ons een betrouwbaarheid geeft of dit een persoon is:
trackbox = frame[y:y+h, x:x+w]
trackbox = cv2.resize(trackbox, (224, 224))
cv2.imshow('image',trackbox)
blob = cv2.dnn.blobFromImage(cv2.resize(trackbox, (300, 300)),0.007843, (300, 300), 127.5)
net.setInput(blob)
detections = net.forward()
Nu loopen we door de uitlezingen - oftewel de voorspellingen voor elke contour over welke objecten ze representeren. Als we voldoende vertrouwen hebben dat de contour een persoon is, gaan we verder en tonen we de voorspelling op het scherm in de frame, als volgt:
for i in np.arange(0, detections.shape[2]):
confidence = detections[0, 0, i, 2]
confidence_level = 0.7
if confidence confidence_level:
# Haal de index van het klasse label uit de `detections`, en bereken de (x, y)-coördinaten van
# de omkaderingsbox voor het 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")
# Teken de voorspelling op het frame
label = "{}: {:.2f}%".format(CLASSES[idx],
confidence * 100)
cv2.rectangle(frame, (startX, startY), (endX, endY),
COLORS[idx], 2)
y = startY - 15 if startY - 15 15 else startY + 15
cv2.putText(frame, label, (startX, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, COLORS[idx], 2)
Dit is het einde van de integratie van het neural network. Voor degenen die geïnteresseerd zijn in het uitbreiden van hun kennis over soortgelijke onderwerpen, kan deze inleiding tot objectherkenning nuttig zijn, omdat het een overzicht biedt van hoe objecten binnen afbeeldingen kunnen worden geïdentificeerd, wat mogelijk complimenteert aan de functies van het neural network. De rest van de code houdt zich bezig met het tekenen van het object in het frame en het voltooien van de berekeningen voor het frame.
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 0), 2)
# Start tracker
now = datetime.now()
if differ == None or differ 9:
tracker.init(frame, initBB2)
fps = FPS().start()
# Controleer of we momenteel een object volgen, zo ja, negeer andere vakken
# Deze code is relevant als we bepaalde personen willen identificeren (sectie 2 van deze tutorial)
if initBB2 is not None:
# Haal de nieuwe omvattingsbox-coördinaten van het object op
(success, box) = tracker.update(frame)
# Controleer of de tracking succesvol was
differ = 10
if success:
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h),(0, 255, 0), 2)
differ = abs(initBB2[0]-box[0]) + abs(initBB2[1]-box[1])
i = tracker.update(lastframe)
if i[0] != True:
time2.sleep(4000)
else:
trackeron = 1
# Update de FPS-teller
fps.update()
fps.stop()
# Initialiseer de set informatie die we op het frame zullen weergeven
info = [
("Success", "Yes" if success else "No"),
("FPS", "{:.2f}".format(fps.fps())),
]
# Loop over de info-tuples en teken ze op ons frame
for (i, (k, v)) in enumerate(info):
text = "{}: {}".format(k, v)
cv2.putText(frame, text, (10, H - ((i * 20) + 20)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
# Teken de tekst en tijdstempel op het frame
now2 = datetime.now()
time_passed_seconds = str((now2-now).seconds)
cv2.putText(frame, 'Detecting persons',(10, 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
# Toon het frame en noteer of de gebruiker een toets indrukt
cv2.imshow("Video stream", frame)
key = cv2.waitKey(1) & 0xFF
# Als de `q`-toets wordt ingedrukt, stop dan de loop
if key == ord("q"):
break
if key == ord("d"):
firstFrame = None
lastframe = frame
# Stop uiteindelijk de camera/stream en sluit alle geopende vensters
vs.stop() if args.get("video", None) is None else vs.release()
cv2.destroyAllWindows()
Conclusie
Als je de code hebt doorgenomen en opgeslagen, kun je deze als volgt uitvoeren op een video:
python file.py -v C:\run.mp4
De code begint personen te taggen die het in de video herkent. Dit is een eerste stap in objectherkenning met Python. Je kunt nu de informatie over de getagde entiteiten gebruiken voor verdere analyse. Bijvoorbeeld, je kunt hun eigenschappen opslaan in een database.
Het volgende deel over persoonvolgen in video's met Python gaat verder in op hoe je personen die je in een video hebt getagd, kunt volgen. Hierbij worden neurale netwerken en deep learning technieken gebruikt, vergelijkbaar met die in deze tutorial.
Delen