Soziale Distanz-Detektor mit Echtzeitfunktion
Dieser Bericht erklärt, wie ein schneller und akkurater Soziale Distanz-Detektor mit Echtzeitfunktion konstruiert wird, um On-Device-Inferenz durchzuführen und dabei Echtzeit-Videomaterial zu verwenden
Created on March 15|Last edited on February 2
Comment

Einleitung
Erlebe das Colab-Notebook
In jeder Pandemie, bei der sich eine Krankheit durch Körperkontakt ausbreitet, ist soziale Distanz immer schon die effizienteste Gegenmaßnahme gewesen. In der aktuellen Lage, wo Covid-19 das Leben vieler Menschen beeinträchtigt hat, ist es deswegen essentiell, die soziale Distanz aufrecht zu erhalten. Eine Mehrheit von Industriezweigen wünscht sich ein Hilfsmittel, um das Ausmaß zu bestimmen, mit dem die Menschen diesen Sicherheitsmaßnahmen folgen.
Ein Soziale Distanz-Detektor ist ein solcher Weg, um dieser Forderung nachzukommen.
Ansatz
Für die Vorgehensweise gibt es zu diesem Zweck drei Hauptaspekte:
- Fußgänger-Erfassung in Echzeit: Objekterfassung ist eine Kombination aus Objekt-Klassifizierung und Objekt-Lokalisation. Sie ist dafür trainiert, die Anwesenheit und den Standort von multiplen Objektklassen zu erfassen. Hier ist das adressierte Objekt der “Mensch”. Wir müssen eine spezielle Klasse definieren, z.B. Menschen, im Echtzeit-Videomaterial mit ausreichender Zuverlässigkeit identifizieren. Es gibt vielfältige Ansätze für Objekterfassung – es kann die Region Classification-Methode mit R-CNN oder Fast R-CNN, die Implicit Anchor-Methode mit Faster R-CNN, YOLO v1-v4 oder EfficientDet und die Keypoint Estimation-Methodemit CenterNet oder CornerNet sein.
Unser Leitgedanke ist es, Fußgänger aus einer Szene heraus zu erfassen und ihre Koordinaten zu lokalisieren. Für On-Device-Inferenz verwenden wir das SSD (Single Shot Detector)-Modell. Dieses Modell wird von TensorFlow Lite Object Detection Api(TFOD) zu TensorFlow Lite konvertiert. Das hier verwendete mobile Modell ist SSD_MobileDet_cpu_coco.
- Kalibrierung: Jeder Rahmen hat eine arbiträre perspektivische Ansicht, was Schwierigkeiten verursacht, die relative Entfernung zwischen Menschen zu analysieren oder zu kalkulieren. Es ist daher notwendig, den Rahmen in die Vogelperspektive (top-down) zu konvertieren. Dieser Prozess wird Kalibrierung genannt. Dies stellt sicher, dass sich jeder auf derselben ebenen Bodenfläche befindet. Das ist ein Schritt, um unseren Soziale Distanz-Detektor zu verbessern und akkurate Messungen in der Endphase des Vorgehens zu erhalten.
- Soziale Distanz-Verletzungen bestimmen: Nun, da wir die Positionen der Menschen in der top-down-Sicht haben, ist der letzte Schritt des Vorgehens, zu überprüfen, ob sich irgendeine Person im unmittelbarer Nähe von einem oder mehreren Menschen befindet. Menschen, die sich an die Norm halten, werden mit einem grünen Rahmenfeld markiert, während die Zuwiderhandelnden in rot mit zwei oder mehr Verbindungslinien hervorgehoben werden.
Nun diskutieren wir folgendes:
- TensorFlow Lite und MobileDet
- Modell-Konversion
- Modell-Richtwerte für MobileDet-Varianten
- Kalibrierung und Transformation
- Bestimmung von Verletzungen sozialer Distanz
- Visualisierung
- Inferenz
- Schlussfolgerung
TensorFlow Lite und MobileDet
Wenn es um On-Device-Inferenz geht, dann schneidet MobileDet am besten ab. Bei der COCO-Objekterkennungsaufgabe übertreffen MobileDets MobileNetV3+SSDLite um 1,7 mAP bei vergleichbaren mobilen CPU-Inferenzlatenzen. Auf mobilen CPUs übertreffen MobileDets MobileNetV2+SSDLite darüber hinaus um 1,9 mAP. Hier erfährst du mehr über MobileDet.
Die Konvertierung dieses Modells in TensorFlow Lite ist sinnvoll, da es On-Device-Machine-Learning-Inferenz mit geringer Latenz und einer kleinen Binärgröße ermöglicht. TensorFlow Lite ist speziell für Entwickler gemacht, um TensorFlow-Modelle auf mobilen, eingebetteten und IoT-Geräten zu betreiben. Hier erfährst du mehr über TensorFlow Lite.
Das SSD (Single Shot Detector) Modell nimmt ein Bild als Eingabe. Nehmen wir an, das Eingabebild umfasst 300x300 Pixel, mit drei Kanälen (rot, blau und grün) pro Pixel, das dann dem Modell als abgeflachter Puffer von 270.000 Byte-Werten (300x300x3) zugeführt wird. Wenn das Modell quantisiert ist, sollte jeder Wert ein einzelnes Byte sein, das einen Wert zwischen 0 und 255 darstellt. Dieses Modell gibt 4 Arrays aus (Ort, Klassen, Vertraulichkeiten, Anzahl der Erkennungen). Haben wir dieses Modell in TensorFlow Lite zu konvertieren, müssen wir zuerst den eingefrorenen Graphen generieren, der mit dem TensorFlow Lite Operator-Set kompatibel ist (wie hier erklärt - TF1 oder TF2). Die beiden Skripte ( TF1 und TF2) fügen dem Modellgraphen optimiertes Postprocessing hinzu. Dieser Modellgraph wird später in eine TensorFlow Lite-Modelldatei mit .tflite-Erweiterung durch drei Prozesse quantisiert.
- Dynamikbereich-Quantisierung
- Float 16-Quantisierung
- Full Integer-Quantisierung
Modell-Konversion
Das ist das Modell SSD_MobileDet_cpu_coco, das wir vorher quantisieren. Wenn dieses Modell-Bündel untar'd wird, bekommen wir folgende Dateien: vortrainierte Kontrollpunkte, einen TensorFlow Lite(TFLite)-kompatiblen Modellgraphen, eine TFLite-Modelldatei und einen Graphen-Prototypen. Die Modelle wurden mit dem COCO-Datensatz vortrainiert.
model.ckpt-*Dateien sind die vortrainierten Kontrollpunkte des COCO-Datensatzes. Die Datei tflite_graph.pb ist ein eingefrorener Inferenz-Graph, der mit dem TFLite Operatoren-Set kompatibel ist, welches von den vortrainierten Modell-Kontrollpunkten exportiert wurde. Die Datei model.tflite ist ein TFLite-Modell, das von dem tflite_graph.pbeingefrorenem Graphen konvertiert wurde.
--- model.ckpt-400000.data-00000-of-00001--- model.ckpt-400000.index--- model.ckpt-400000.meta--- model.tflite--- pipeline.config--- tflite_graph.pb--- tflite_graph.pbtxt
Dynamikbereich-Quantisierung
converter = tf.compat.v1.lite.TFLiteConverter.from_frozen_graph(graph_def_file=model_to_be_quantized,input_arrays=['normalized_input_image_tensor'],output_arrays=['TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:1','TFLite_Detection_PostProcess:2','TFLite_Detection_PostProcess:3'],input_shapes={'normalized_input_image_tensor': [1, 320, 320, 3]})converter.allow_custom_ops = Trueconverter.optimizations = [tf.lite.Optimize.DEFAULT]tflite_model = converter.convert()
Dieser dynamic range (Dynamikbereich) quantisiert die Gewichte von Fließkomma zur Ganzzahl, was eine 8-bit-Präzision hat. Bei Inferenz werden Gewichte von 8-Bit-Präzision auf Fließkomma konvertiert und mit Gleitkomma-Kernel berechnet. Im obigen Code-Block müssen wir die Datei tflite_graph.pb anstelle von model_to_be_quantized angeben. Das Modell akzeptiert die Eingabe eines Bildes von 320*320 Pixel, also werden die input_arrays und input_shapes dementsprechend eingestellt. Die output_arrays werden auf Ausgabe für Arrays eingestellt: Standort des Rahmenfelds, Klassen von erfassten Objekten, Vertraulichkeiten, Anzahl der Erkennungen. Diese werden nach diesem Leitfaden eingestellt
Nach der Quantifizierung erhalten wir eine Modellgröße von 4.9MB.
Float 16-Quantisierung
converter = tf.compat.v1.lite.TFLiteConverter.from_frozen_graph(graph_def_file=model_to_be_quantized,input_arrays=['normalized_input_image_tensor'],output_arrays=['TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:1','TFLite_Detection_PostProcess:2','TFLite_Detection_PostProcess:3'],input_shapes={'normalized_input_image_tensor': [1, 320, 320, 3]})converter.allow_custom_ops = Trueconverter.target_spec.supported_types = [tf.float16]converter.optimizations = [tf.lite.Optimize.DEFAULT]tflite_model = converter.convert()
Diese float 16 Qumantisierung reduziert die Modellgröße mit minimalem Genauigkeitsverlust auf die Hälfte. Es quantisiert Modell-Gewichte und Bias-Werte aus Vollpräzisions-Fließkommawerten (32-bit) zu einem Präzisions-reduzierten Fließkommawert Datentyp (IEEE FP16). Wir müssen nur eine Code-Zeile zum vorangegangenen dynamic range Code-Block hinzufügen: converter.target_spec.supported_types = [tf.float16]
Nach der Quantisierung erhalten wir eine Modellgröße von 8.2MB.
Full Integer-Quantisierung
converter = tf.compat.v1.lite.TFLiteConverter.from_frozen_graph(graph_def_file=model_to_be_quantized,input_arrays=['normalized_input_image_tensor'],output_arrays=['TFLite_Detection_PostProcess','TFLite_Detection_PostProcess:1','TFLite_Detection_PostProcess:2','TFLite_Detection_PostProcess:3'],input_shapes={'normalized_input_image_tensor': [1, 320, 320, 3]})converter.allow_custom_ops = Trueconverter.representative_dataset = representative_dataset_genconverter.inference_input_type = tf.uint8converter.quantized_input_stats = {"normalized_input_image_tensor": (128, 128)}converter.optimizations = [tf.lite.Optimize.DEFAULT]tflite_model = converter.convert()
Für die Full Integer-Quantisierungsmethode müssen wir das representative dataset benutzen. Um deinen eigenen Datensatz zu erstellen, kannst du hier nachsehen. Die Funktion zur Generierung eines repräsentativen Datensatzes wird hier gezeigt. Dieser Datensatz kann eine kleine Teilmenge von etwa 100-150 Stichproben von Trainings- oder Validierungsdaten sein. Es werden repräsentative Datensätze benötigt, um die variablen Tensoren wie Modelleingabe (Ausgabe von Zwischenschichten) und Modellausgabe per Durchführung einiger Inferenzzyklen zu kalibrieren.
Nach der Quantisierung erhalten wir eine Modellgröße von 4.9 MB.
Modell-Richtwerte für MobileDet-Varianten
Modell-Richtwerte sind ein Weg, um das beste Modell für deine Zwecke auszuwählen. Eine Möglichkeit ist, sich ihre FPS und Elapsed Time anzusehen. Hier sind einige Modell-Richtwerte, die ich erfasst habe:
Model Name | Model Size(MB) | Elapsed Time(s) | FPS |
---|---|---|---|
SSD_mobileDet_cpu_coco_int8 | 4.9 | 705.32 | 0.75 |
SSD_mobileDet_cpu_coco_fp16 | 8.2 | 52.79 | 10.06 |
SSD_mobileDet_cpu_coco_dr | 4.9 | 708.57 | 0.75 |
Eine andere Möglichkeit ist, das TensorFlow Lite Benchmark Tool zu verwenden. Du musst dafür Android Debug Bridge(adb) auf deinem Laptop konfigurieren und mit deinem Android-Gerät verbinden, um TensorFlow Benchmark Tool zu verwenden und die Inferenz-Geschwindigkeit des Modells prüfen zu können. Ich habe davon nur fp16 gezeigt, weil es die schnellste aller drei Varianten ist.
- Das erste Ergebnis im Bild weiter unten ist mit Verwendung von cpu mit 4 Threads
- Das zweite Ergebnis im Bild weiter unten ist mit Verwendung von cpu
Kalibrierung

Die Schritte der Sequenz umfassen:
1. Finden eines Vierecks – 4 Eckpunkte
Den Bildrahmen von Perspektivsicht zu top-down-Sicht (oder Vogelperspektivsicht) zu transformieren ist auf Basis der Auswahl der richtigen Koordinaten eines Vierecks möglich, auf das der Bildrahmen verzogen wird. Hierfür kann das Ziel Straßenzüge, Bodenflächen oder Fußboden sein.
Kalibrierung basiert ausschließlich auf dem Hintergrund eines Bildes. Wenn sich also die Hintergrundansicht ändert, ändern sich auch die Bezugspunkte und im Zuge dessen die Transformations-Matrix zur Kalibrierung.
Angenommen, dass die Kameraposition und so auch der Hintergrund fixiert ist, ist es am einfachsten, im Schritt der Prozessvorbereitung 4 Eckpunkte (von Straße oder Bodenfläche) des Fußbodens zu finden. Für jetzt haben wir das getan, wobei wir die Nutzer-interaktive Mausklick-Operation von mouse_click_event.py verwendet haben, um ein Viereck mit drag-and-drop der Ecken auszuwählen.
2. Transformation matrix (M)
Transformations-Matrix ist die Hauptquelle für Kalibrierung.
Nach Abrufen der ausgewählten Eckpunkte ordnet man die Koordinaten an und verwendet getPerspectiveTransform() von OpenCV, um die Transformations-Matrix (M) zu erhalten. Um den ganzen Bildrahmen zu verziehen, benutzt man die Mapping-Funktion:

wobei sich (x,y) auf die Koordinaten des Quellbilds beziehen.
Wende nun diese Funktion an, um die Eckpunkte des Bildes zu kartieren (nicht diejenigen von Mausklick!) und erhalte so die korrespondierenden Koordinaten in top-down-Sicht. Dann wertest du die Extremkoordinaten aus (Extrempunkte links, rechts, oben, unten des verzogenen Rahmens) und verwendest diese, um die finale Transformations-Matrix M zu erhalten.
def getmap(image):# image : first frame of the input videoglobal grid_H, grid_Wh,w=image.shape[:2]# 4 corner points of image are set by default# User needs to finalise the corner points of a road or floor by dragging and dropping the corners#corners=mouse_click_event.adjust_coor_quad(image,corners)corners=[(308, 67), (413, 91), (245, 351), (75, 270)]corners=np.array(corners,dtype="float32")src=order_points(corners)(tl,tr,br,bl)=srcwidth1=np.sqrt(((br[0]-bl[0])**2)+((br[1]-bl[1])**2))width2=np.sqrt(((tr[0]-tl[0])**2)+((tr[1]-tl[1])**2))width=max(int(width1),int(width2))height1=np.sqrt(((tr[0]-br[0])**2)+((tr[1]-br[1])**2))height2=np.sqrt(((tl[0]-bl[0])**2)+((tl[1]-bl[1])**2))height=max(int(height1),int(height2))width=int(width)height=int(height)dest=np.array([[0,0],[width-1,0],[width-1,height-1],[0,height-1]],dtype="float32")M=cv2.getPerspectiveTransform(src,dest)corners=np.array([[0,h-1,1],[w-1,h-1,1],[w-1,0,1],[0,0,1]],dtype="int")warped_corners=np.dot(corners,M.T)warped_corners=warped_corners/warped_corners[:,2].reshape((len(corners),1))warped_corners=np.int64(warped_corners)warped_corners=warped_corners[:,:2]min_coor=np.min(warped_corners,axis=0)max_coor=np.max(warped_corners,axis=0)grid_W,grid_H=max_coor-min_coordest=np.array([[abs(min_coor[0]),abs(min_coor[1])],[abs(min_coor[0])+grid_W-1,abs(min_coor[1])],[abs(min_coor[0])+grid_W-1,abs(min_coor[1])+grid_H-1],[abs(min_coor[0]),abs(min_coor[1])+grid_H-1]],dtype="float32")M=cv2.getPerspectiveTransform(src,dest)return M
Wie bereits vorher dargelegt, sollte getmap() nur einmal im Schritt der Prozessvorbereitung aufgerufen werden, um die Transformations-Matrix zu erhalten und um die Punkte in jedem Rahmen zu kalibrieren.
3. Kalibrieren
Um die Transformations-Matrix zu gebrauchen, müssen wir:
- die Position erfasster Personen mit ihren korrespondierenden Positionen im verzogenen Bild oder “Vogelperspektiv-Raster” kalibrieren.
- die minimal einzuhaltende soziale Distanz - MIN_DISTANCE berechnen.
Die Größe einer durchschnittlichen Person beträgt 5-6 Fuß (1,52-1,82 m). Und wir sollten bei der sozialen Distanz einen Mindestabstand von 1,82 m einhalten. Daher wird von den Ergebnissen des Fußgänger-Detektoren die mittlere Größe von Menschen bestimmt, und wir legen Größe als MIN_DISTANCE fest. Diese Größe muss jedoch kalibriert werden, um es für die Distanz-Verletzungs-Bestimmung zu verwenden (was völlig auf top-down-Sicht basiert).
def calibration(M,results):# calculate minimum distance for social distancingglobal MIN_DISTANCErect=np.array([r[1] for r in results])h=np.median(rect[:,2]-rect[:,0])coor=np.array([[50,100,1],[50,100+h,1]],dtype="int")coor=np.dot(coor,M.T)coor=coor/coor[:,2].reshape((2,1))coor=np.int64(coor[:,:2])MIN_DISTANCE=int(round(dist.pdist(coor)[0]))#calculate centroid points of detected people location corresponding to bird's eye view black gridcentroids=np.array([r[2] for r in results])centroids=np.c_[centroids,np.ones((centroids.shape[0],1),dtype="int")]warped_centroids=np.dot(centroids,M.T)warped_centroids=warped_centroids/warped_centroids[:,2].reshape((len(centroids),1))warped_centroids=np.int64(warped_centroids)return warped_centroids[:,:2]

Bestimmung von verletzter sozialen Distanz
Um die Distanz zwischen Personen zu bestimmen, ist die Brachial-Methode, die euklidischen Abstände zwischen jedem möglichen Paar zu evaluieren und zu bestimmen, welche(s) Paar(e) die Grenze überschreiten. Dies kann leicht erreicht werden, indem man die schnelle und gehobene Python Libraries – numpy and scipy verwendet. Informiere dich hier, um mehr zu erfahren.

def calc_dist(centroids):# centroids : updated centroids in top-down view coordinatesif len(centroids)<2: # no pair of people, no violationreturn list()# evaluate the pairwise distances between peoplecondensed_dist=dist.pdist(centroids)D=dist.squareform(condensed_dist)locations=np.where(D<MIN_DISTANCE)violate=list(zip(locations[0],locations[1]))violate=np.sort(violate,axis=1)violate=np.unique(violate,axis=0)violate=np.asarray(list(filter(lambda x:x[0]!=x[1],violate)))return violate
Visualisierung
Alles ist nutzlos ohne zu visualisieren, was passiert!
In unserem Projekt symbolisiert die Farbe “grün” Menschen, welche die soziale Distanz einhalten, und die Farbe “rot” vermerkt Menschen, die die Sicherheitsnorm verletzen.
Grundsätzlich werden folgende Rahmen für die Visualisierung verwendet:
- Ausgabe – zeigt die Eingabe-Video-Rahmen zusätzlich zu grünen und roten Rahmenfeldern um Menschen herum. Es zeigt zum besseren Verständnis außerdem rote Linien, welche die Zuwiderhandelnden verbinden.
- Vogelperspektiv-Sicht-Raster: zeigt einen schwarzen Rahmen, womit alle Menschen in der top-down-Sicht mit ihren Farbkodierungen (rot/grün) als Punkte lokalisiert werden. Eine oder mehrere rote Linien werden zwischen den Punkten verbunden, wenn die Punkte (oder Menschen) in unmittelbare Nähe kommen.
- Verzogenes Bild – (optional, um Operationen zu verstehen) zeigt die top-down-Sicht von Eingabe-Video-Frames mit den farbkodierten Punkten von Menschen.
Visualisierung des Ausgabe-Rahmens
Die Ergebnisse des Fußgänger-Detektors helfen, Menschen zu lokalisieren. Die Auflistung von Zuwiderhandelnden hilft dabei, zwei Klassen von Menschen voneinander zu trennen. Zeichne die Rahmenfelder, indem du die Farben entsprechend der spezifizierten Konvention wählst.
def visualise_main(frame,results,violate):for (i,(prob,bbox,centroid)) in enumerate(results):(startX,startY,endX,endY)=bbox(cX,cY)=centroidcolour=(0,255,0)if i in np.unique(violate):colour=(0,0,255)frame=cv2.rectangle(frame,(startX,startY),(endX,endY),colour,2)frame=cv2.circle(frame,(cX,cY),5,colour,1)#Drawing the connecting lines between violatorsfor i,j in violate:frame=cv2.line(frame,results[i][2],results[j][2],(0,0,255),2)return frame

Vogelperspektiv-Sicht-Raster und verzogene Rahmen
Erhalte das kalibrierte verzogene Bild des Rahmens, indem du cv2.warpPerspective(). verwendest.

Lokalisiere die Menschen in Vogelperspektiv-Sicht aus kalibrierten Daten heraus und setze sie als verstreute Punkte in ein schwarzes Raster. Die Farbe wird entsprechend der Auflistung von Zuwiderhandelnden ausgewählt. Ziehe für alle Zuwiderhandelnden eine rote Linie zwischen ihnen.

def visualise_grid(image,M,centroids,violate):warped=cv2.warpPerspective(image,M,(grid_W,grid_H),cv2.INTER_AREA, borderMode=cv2.BORDER_CONSTANT, borderValue=(0,0,0))grid=np.zeros(warped.shape,dtype=np.uint8)for i in range(len(centroids)):colour=(0,255,0)if i in np.unique(violate):colour=(0,0,255)grid=cv2.circle(grid,tuple(centroids[i,:]),5,colour,-1)warped=cv2.circle(warped,tuple(centroids[i,:]),5,colour,-1)for i,j in violate:grid=cv2.line(grid,tuple(centroids[i]),tuple(centroids[j]),(0,0,255),2)#cv2_imshow("Bird's eye view grid",cv2.resize(grid,image.shape[:2][::-1]))#cv2_imshow("warped",cv2.resize(warped,image.shape[:2][::-1]))grid=cv2.resize(grid,image.shape[:2][::-1])warped=cv2.resize(warped,image.shape[:2][::-1])return grid,warped
Inferenz
Den Prozess der Inferenz beschreibt ein betriebenes TensorFlow Lite-Modell On-Device, damit es auf Eingabe-Daten basierende Vorhersagen treffen kann. Für Inferenz müssen wir sie durch einen Übersetzer laufen lassen. TensorFlow Lite folgt den folgenden Schritten:
Das Modell laden
Da SSD_MobileDet_cpu_coco_fp16 das beste Ergebnis von allen dreien gezeigt hat, werden wir damit fortfahren, dieses Modell zu laden. Der tf.lite.Interpreter nimmt die .tflite Modell-Datei auf. Die Tensoren werden zugewiesen und die Modelleingangsform wird als HEIGHT, WIDTH definiert.
tflite_model = "ssd_mobiledet_cpu_coco_fp16.tflite"interpreter = tf.lite.Interpreter(model_path=tflite_model)interpreter.allocate_tensors()_, HEIGHT, WIDTH, _ = interpreter.get_input_details()[0]['shape']
Input Tensor einstellen
Der folgende Code-Block gibt alle Eingabe-Details des Modells aus.
def set_input_tensor(interpreter, image):tensor_index = interpreter.get_input_details()[0]['index']input_tensor = interpreter.tensor(tensor_index)()[0]input_tensor[:, :] = image
Die Eingangs-Tensoren festlegen
Der folgende Code-Block wird alle Ausgabe-Details wiedergeben: Location of Bounding Box, Class, Confidence, Number of detection.
def get_output_tensor(interpreter, index):output_details = interpreter.get_output_details()[index]tensor = np.squeeze(interpreter.get_tensor(output_details['index']))return tensor
Fußgänger-Erfassung
def pedestrian_detector(interpreter, image, threshold):"""Returns a list of detection results, each as a tuple of object info."""H,W=HEIGHT,WIDTHset_input_tensor(interpreter, image)interpreter.invoke()# Get all output detailsboxes = get_output_tensor(interpreter, 0)class_id = get_output_tensor(interpreter, 1)scores = get_output_tensor(interpreter, 2)count = int(get_output_tensor(interpreter, 3))results = []for i in range(count):if class_id[i] == 0 and scores[i] >= threshold:[ymin,xmin,ymax,xmax]=boxes[i](left, right, top, bottom) = (int(xmin * W), int(xmax * W), int(ymin * H), int(ymax * H))area=(right-left+1)*(bottom-top+1)if area>=1500:continuecenterX=left+int((right-left)/2)centerY=top+int((bottom-top)/2)results.append((scores[i],(left,top,right,bottom),(centerX,centerY)))return results
Vorbearbeitung von Video-Frames
Vor dem Betreiben von Inferenz auf Videos müssen wir alle Video-Frames vorbearbeiten. Die Größe der Frames wird an die akzeptierte HEIGHT and WEIGHT des Modelleingabe-Bilds angepasst.
Später wird das vorbearbeitete Bild zu Numpy Array konvertiert. Am gebräuchlichsten ist es, 32-bit-Präzision zu verwenden, um ein neurales Netzwerk zu trainieren, also müssen wir an einem Punkt die Eingabe-Daten zu 32 bit-Floats konvertieren. Es wird durch 255 geteilt, das ist der Maximalwert eines Bytes (der Eingaberahmen-Typ vor der Konvertierung zu float32), also wird dies sicherstellen, dass die Eingabe-Frames zwischen 0.0 und 1.0 skaliert sind.
def preprocess_frame(frame):frame = Image.fromarray(frame)preprocessed_image = frame.resize((HEIGHT,WIDTH),Image.ANTIALIAS)preprocessed_image = tf.keras.preprocessing.image.img_to_array(preprocessed_image)preprocessed_image = preprocessed_image.astype('float32') / 255.0preprocessed_image = np.expand_dims(preprocessed_image, axis=0)return preprocessed_image
Eingabe-Video-Generation-Utils
Abschließend werden die erforderlichen Funktionen sequentiell angewendet. Der Detektor ist jetzt aktiv.
def process(video):vs=cv2.VideoCapture(video)#Capture the first frame of videores,image=vs.read()if image is None:returnimage=cv2.resize(image,(320,320))# get transformation matrixmat=getmap(image)fourcc=cv2.VideoWriter_fourcc(*"XVID")out=cv2.VideoWriter("result.avi",fourcc,20.0,(320*3,320))fps = FPS().start()while True:res,image=vs.read()if image is None:break#pedestrian detectionpreprocessed_frame = preprocess_frame(image)results = pedestrian_detector(interpreter, preprocessed_frame, threshold=0.25)preprocessed_frame = np.squeeze(preprocessed_frame) * 255.0preprocessed_frame = preprocessed_frame.clip(0, 255)preprocessed_frame = preprocessed_frame.squeeze()image = np.uint8(preprocessed_frame)#calibrationwarped_centroids=calibration(mat, results)#Distance-Violation Determinationviolate=calc_dist(warped_centroids)#Visualise gridgrid,warped=visualise_grid(image,mat,warped_centroids,violate)#Visualise main frameimage=visualise_main(image,results,violate)#Creating final output frameoutput=cv2.hconcat((image,warped))output=cv2.hconcat((output,grid))out.write(output)fps.update()fps.stop()print("[INFO] elapsed time: {:.2f}".format(fps.elapsed()))print("[INFO] approx. FPS: {:.2f}".format(fps.fps()))# release the file pointersprint("[INFO] cleaning up...")vs.release()out.release()
Schlussfolgerung
Unser soziale Distanz-Detektor läuft erfolgreich mit folgendem Durchsatz:
[INFO] elapsed time: 52.79[INFO] approx. FPS: 10.06[INFO] cleaning up...
Weiterhin sind die Ergebnisse äußerst faszinierend – er zeigt die Ergebnisse in der Laufzeit akkurat an. Informiere dich hier.
Add a comment
Iterate on AI agents and models faster. Try Weights & Biases today.