Die Aufgabe, Objekte im Bild zu erkennen, ist heute eine der führenden auf dem Gebiet der Bildverarbeitung. Seine Essenz besteht nicht nur darin, das Objekt im Bild zu klassifizieren, sondern auch seine genaue Position anzugeben.Die Ergebnisse der Objekterkennung können durch Informationen darüber ergänzt werden, wie weit sich dieses Objekt befindet. Das Problem der Entfernungsmessung kann mit der Intel RealSense D435-Tiefenkamera gelöst werden , die die Tiefe an jedem Punkt misst.In diesem Artikel lösen wir das Problem der Messung der Entfernung zu einem Objekt in Echtzeit mithilfe der OpenCV-Bibliothek und der RealSense-Technologie.
Bedarf
RealSense-KameraUm die Entfernung zu messen, benötigen wir eine RealSense D435-Tiefenkamera . In diesem Beispiel muss das RealSense SDK nicht installiert werden .Pyrealsense2-Bibliothek Dergesamte Code wird zur Vereinfachung der Demonstration in Python 3.7 geschrieben , daher benötigen wir das pyrealsense2- Paket .pip install pyrealsense2
OpenCV-BibliothekAußerdem benötigen wir die OpenCV-Bibliothek (jede Version ab 3.4 ist geeignet). Das Installationshandbuch für die Bibliothek kann auf der offiziellen OpenCV- Website eingesehen werden .Zusammen mit OpenCV wird eine weitere notwendige Bibliothek installiert - numpy .Objekterkennung
Der erste Schritt besteht darin, Objekte im Video auszuwählen. Um Objekte in Echtzeit zu erkennen, sind die sogenannten einstufigen Modelle (z. B. Retina Net, SSD, YOLO ), die an Betriebsgeschwindigkeit gewinnen, perfekt .Zur Vereinfachung des Experiments nehmen wir das trainierte Modell SSDLite Mobilenet v2 . Stattdessen kann jedes andere Modell verwendet werden, das die Koordinaten des Objekts in der einen oder anderen Form zurückgibt.Sie können das Archiv mit den Modelldateien im offiziellen TensorFlow-Repository herunterladen .Das Archiv enthält ein eingefrorenes Berechnungsdiagramm im binären .pb- Formatsowie verschiedene Konfigurationen. Um das Modell in OpenCV verwenden zu können, müssen Sie jedoch auch ein Diagramm mit Berechnungen im Textformat .pbtxt haben . Es befindet sich nicht im Archiv und muss daher manuell generiert werden.Kopieren Sie zwei Skripte aus dem OpenCV- Repository :- github.com/opencv/opencv/blob/master/samples/dnn/tf_text_graph_ssd.py
- github.com/opencv/opencv/blob/master/samples/dnn/tf_text_graph_common.py
Führen Sie dann den folgenden Befehl aus und geben Sie die erforderlichen Pfade an:python tf_text_graph_ssd.py --input /path/to/model.pb --config /path/to/example.config --output /path/to/graph.pbtxt
Die Ausgabe ist eine graph.pbtxt- Datei .Wir legen die vorhandenen Modelldateien im Projektordner ab. In meinem Fall ist dies der Modell- / Ordner .Entwicklung
Jetzt ist alles bereit für die Entwicklung. Beginnen wir mit dem Schreiben von Code.Wir importieren die erforderlichen Bibliotheken und machen einen Stub auf der draw_predictions () -Methode , die wir später benötigen werden:import pyrealsense2 as rs
import numpy as np
import cv2 as cv
def draw_predictions(pred_img, color_img, depth_image):
pass
Erstellen Sie die Hauptobjekte:pipeline = rs.pipeline()
config = rs.config()
colorizer = rs.colorizer()
spatial = rs.spatial_filter()
spatial.set_option(rs.option.holes_fill, 3)
Wir initialisieren das SSD- Modell. Hier ist die oben generierte .pbtxt- Datei für uns nützlich :
model = cv.dnn.readNetFromTensorflow(
"models/ssdlite_mobilenet_v2_coco_2018_05_09/frozen_inference_graph.pb",
"models/ssdlite_mobilenet_v2_coco_2018_05_09/graph.pbtxt"
)
Wir starten Kamera-Streams:
config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
profile = pipeline.start(config)
Beachten Sie, dass wir den Farbstrom im BGR- Format erfassen , da dies das Format ist, das standardmäßig OpenCV verwendet .Wir starten einen Zyklus, in dem wir Frames nacheinander erfassen und verarbeiten:cv.namedWindow("RealSense object detection", cv.WINDOW_AUTOSIZE)
try:
while True:
frames = pipeline.wait_for_frames()
depth_frame = frames.get_depth_frame()
color_frame = frames.get_color_frame()
if not depth_frame or not color_frame:
continue
Zur weiteren Verarbeitung von Frames stellen wir sie in Form von Numpy- Arrays dar:
depth_image = np.asanyarray(depth_frame.get_data())
color_image = np.asanyarray(color_frame.get_data())
Wir verwenden das ssd- Modell, um Folgendes zu erkennen:
model.setInput(cv.dnn.blobFromImage(color_image, size=(300, 300), swapRB=True, crop=False))
pred = model.forward()
draw_predictions(pred, color_image, depth_image)
Wir zeichnen das Ergebnis in das Fenster:
colorized_depth = cv.applyColorMap(cv.convertScaleAbs(depth_image, alpha=0.03), cv.COLORMAP_JET)
images = np.hstack((color_image, colorized_depth))
cv.imshow("RealSense object detection", images)
Wir schreiben die Bedingung für das Verlassen der Schleife:
key = cv.waitKey(1) & 0xFF
if (key == 27) or (key == ord('q')):
cv.destroyWindow("RealSense object detection")
break
Schließen Sie den Stream:finally:
pipeline.stop()
Und der Letzte. Wir füllen die Rendering-Methode draw_predictions () aus , die wir zu Beginn erstellt haben. Darin betrachten wir die Entfernung zu Objekten. Ich habe beschlossen, die Entfernung wie folgt zu berechnen: Nimm den Durchschnitt aller Entfernungspunkte im Rahmen des gefundenen Objekts:def draw_predictions(pred_img, color_img, depth_image):
for detection in pred_img[0,0,:,:]:
score = float(detection[2])
if score > 0.5:
left = detection[3] * color_img.shape[1]
top = detection[4] * color_img.shape[0]
right = detection[5] * color_img.shape[1]
bottom = detection[6] * color_img.shape[0]
cv.rectangle(color_img, (int(left), int(top)), (int(right), int(bottom)), (210, 230, 23), 2)
depth = depth_image[int(left):int(right), int(top):int(bottom)].astype(float)
depth_scale = profile.get_device().first_depth_sensor().get_depth_scale()
depth = depth * depth_scale
dist,_,_,_ = cv.mean(depth)
dist = round(dist, 1)
cv.putText(color_img, "dist: "+str(dist)+"m", (int(left), int(top)-5), cv.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 1)

Vollständiger Codeimport pyrealsense2 as rs
import numpy as np
import cv2 as cv
def draw_predictions(pred_img, color_img, depth_image):
for detection in pred_img[0,0,:,:]:
score = float(detection[2])
if score > 0.5:
left = detection[3] * color_img.shape[1]
top = detection[4] * color_img.shape[0]
right = detection[5] * color_img.shape[1]
bottom = detection[6] * color_img.shape[0]
cv.rectangle(color_img, (int(left), int(top)), (int(right), int(bottom)), (210, 230, 23), 2)
depth = depth_image[int(left):int(right), int(top):int(bottom)].astype(float)
depth_scale = profile.get_device().first_depth_sensor().get_depth_scale()
depth = depth * depth_scale
dist,_,_,_ = cv.mean(depth)
dist = round(dist, 1)
cv.putText(color_img, "dist: "+str(dist)+"m", (int(left), int(top)-5), cv.FONT_HERSHEY_PLAIN, 1, (0, 255, 0), 1)
pipeline = rs.pipeline()
config = rs.config()
colorizer = rs.colorizer()
model = cv.dnn.readNetFromTensorflow(
"models/ssdlite_mobilenet_v2_coco_2018_05_09/frozen_inference_graph.pb",
"models/ssdlite_mobilenet_v2_coco_2018_05_09/graph.pbtxt"
)
config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 30)
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
profile = pipeline.start(config)
cv.namedWindow("RealSense object detection", cv.WINDOW_AUTOSIZE)
try:
while True:
frames = pipeline.wait_for_frames()
depth_frame = frames.get_depth_frame()
color_frame = frames.get_color_frame()
if not depth_frame or not color_frame:
continue
depth_image = np.asanyarray(depth_frame.get_data())
color_image = np.asanyarray(color_frame.get_data())
model.setInput(cv.dnn.blobFromImage(color_image, size=(300, 300), swapRB=True, crop=False))
pred = model.forward()
draw_predictions(pred, color_image, depth_image)
colorized_depth = cv.applyColorMap(cv.convertScaleAbs(depth_image, alpha=0.03), cv.COLORMAP_JET)
images = np.hstack((color_image, colorized_depth))
cv.imshow("RealSense object detection", images)
key = cv.waitKey(1) & 0xFF
if (key == 27) or (key == ord('q')):
cv.destroyWindow("RealSense object detection")
break
finally:
pipeline.stop()
Fazit
Daher haben wir ein einfaches Skript, um die Entfernung zum ausgewählten Objekt zu schätzen. Der Algorithmus kann einen Fehler von etwa 50 cm machen, funktioniert aber insgesamt gut innerhalb von vier Metern. Um die Genauigkeit der Messung zu erhöhen, können Sie zusätzliche Filter, die in pyrealsense2 eingebettet sind, auf die Tiefenkarte anwenden, um die Bildqualität zu erhöhen, oder den Tiefenberechnungsalgorithmus selbst ändern (z. B. einen gewichteten Durchschnitt berechnen oder die Entfernung in einem zentralen Bereich messen). Wie Sie sehen, ist die Aufgabe nicht schwierig, aber interessant.