Messen der Entfernung zu Objekten mit RealSense D435

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.

Bild

Bedarf


RealSense-Kamera

Um die Entfernung zu messen, benötigen wir eine RealSense D435-Tiefenkamera . In diesem Beispiel muss das RealSense SDK nicht installiert werden .

Pyrealsense2-Bibliothek Der

gesamte Code wird zur Vereinfachung der Demonstration in Python 3.7 geschrieben , daher benötigen wir das pyrealsense2- Paket .

pip install pyrealsense2

OpenCV-Bibliothek

Auß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 :

  1. github.com/opencv/opencv/blob/master/samples/dnn/tf_text_graph_ssd.py
  2. 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() # <-  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:

#  2 : RGB  depth
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:

        #    numpy-
        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:

	#    'ESC'  'q'
        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])
        #            50%
        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)

Bild

Vollständiger Code
import 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])
        #            50%
        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() # <-  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"
)

#  2 : RGB  depth
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

        #    numpy-
        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)
        
        #    'ESC'  'q'
        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.

Source: https://habr.com/ru/post/undefined/


All Articles