قياس المسافة إلى الأشياء باستخدام RealSense D435

إن مهمة الكشف عن الأشياء في الصورة اليوم هي إحدى المهام الرائدة في مجال رؤية الماكينة. جوهرها ليس فقط لتصنيف الكائن في الصورة ، ولكن أيضًا للإشارة إلى موقعه بالضبط.

يمكن استكمال نتائج الكشف عن الكائن بمعلومات عن مدى وجود هذا الكائن. يمكن حل مشكلة قياس المسافة باستخدام كاميرا Intel RealSense D435 Depth ، التي تقيس العمق عند كل نقطة.

في هذه المقالة ، سنحل مشكلة قياس المسافة إلى كائن في الوقت الفعلي باستخدام مكتبة OpenCV وتقنية RealSense.

صورة

المتطلبات


كاميرا RealSense

لقياس المسافة ، نحتاج إلى كاميرا عمق RealSense D435 . في هذا المثال ، ليست هناك حاجة لتثبيت RealSense SDK .

مكتبة Pyrealsense2

جميع التعليمات البرمجية ، لسهولة العرض ، سنكتب في Python 3.7 ، لذلك نحن بحاجة إلى حزمة pyrealsense2 .

pip install pyrealsense2

مكتبة OpenCV

أيضًا ، نحتاج إلى مكتبة OpenCV (أي إصدار يبدأ من 3.4 مناسب). يمكن الاطلاع على دليل تثبيت المكتبة على موقع OpenCV الرسمي .

جنبا إلى جنب مع OpenCV ، سيتم تثبيت مكتبة أخرى ضرورية - numpy .

كشف الكائن


الخطوة الأولى هي تحديد الكائنات في الفيديو. لاكتشاف الأجسام في الوقت الفعلي ، فإن ما يسمى بالنماذج أحادية المرحلة (على سبيل المثال ، Retina Net ، SSD ، YOLO ) ، التي تفوز بسرعة التشغيل ، مثالية .

من أجل بساطة التجربة ، سنأخذ النموذج المدرب SSDLite Mobilenet v2 . بدلاً من ذلك ، يمكن استخدام أي نموذج آخر يقوم بإرجاع إحداثيات الكائن بشكل أو بآخر.
يمكنك تنزيل الأرشيف مع ملفات النموذج في مستودع TensorFlow الرسمي .

يحتوي الأرشيف على رسم بياني حسابي مجمّد بتنسيق ثنائي .pb، وكذلك التكوينات المختلفة. ولكن لاستخدام النموذج في OpenCV ، يجب أن يكون لديك أيضًا رسم بياني للحسابات بتنسيق النص .pbtxt . إنه غير موجود في الأرشيف ، لذا يجب إنشاؤه يدويًا.

انسخ نصين من مستودع OpenCV :

  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

بعد ذلك ، قم بتنفيذ الأمر التالي ، مع تحديد المسارات اللازمة:

python tf_text_graph_ssd.py --input /path/to/model.pb --config /path/to/example.config --output /path/to/graph.pbtxt

الإخراج هو ملف بياني . pbtxt .

نضع ملفات النموذج الموجودة في مجلد المشروع. في حالتي ، هذا هو النماذج / المجلد .

تطوير


الآن كل شيء جاهز للتطوير. لنبدأ في كتابة التعليمات البرمجية.

نقوم باستيراد المكتبات اللازمة ونقوم بعمل كعب على طريقة draw_predictions () التي سنحتاجها لاحقًا:

import pyrealsense2 as rs
import numpy as np
import cv2 as cv

def draw_predictions(pred_img, color_img, depth_image):
    pass

إنشاء الكائنات الرئيسية:

pipeline = rs.pipeline() # <-  pipeline      
config = rs.config() # <-      
colorizer = rs.colorizer() # <-      

spatial = rs.spatial_filter()
spatial.set_option(rs.option.holes_fill, 3)

نقوم بتهيئة نموذج SSD . هنا ، ملف .pbtxt الذي أنشأناه أعلاه مفيد لنا :

#  
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)

لاحظ أننا نلتقط دفق الألوان بتنسيق BGR ، نظرًا لأن هذا هو التنسيق الذي يستخدم OpenCV افتراضيًا.

نبدأ دورة يتم من خلالها التقاط الإطارات ومعالجتها بالتتابع:

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 :

        #    numpy-
        depth_image = np.asanyarray(depth_frame.get_data())
        color_image = np.asanyarray(color_frame.get_data())

نستخدم نموذج SSD للكشف عن:

        #  
        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()

وآخر واحد. نحن نملأ طريقة تقديم draw_predictions () التي أنشأناها في البداية. في ذلك ، سننظر في المسافة إلى الأشياء. قررت حساب المسافة على النحو التالي: خذ متوسط ​​جميع نقاط المسافة في إطار الكائن الذي تم العثور عليه:

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)

صورة

كود كامل
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()


استنتاج


وبالتالي ، لدينا برنامج نصي بسيط لتقدير المسافة إلى الكائن المحدد. قد ترتكب الخوارزمية خطأ حوالي 50 سم ، ولكن بشكل عام تعمل بشكل جيد في غضون أربعة أمتار. لزيادة دقة القياس ، يمكنك تطبيق مرشحات إضافية مضمنة في pyrealsense2 على خريطة العمق لزيادة جودة الصورة ، أو تعديل خوارزمية حساب العمق نفسه (على سبيل المثال ، حساب متوسط ​​مرجح أو قياس المسافة في منطقة مركزية واحدة). كما ترون ، المهمة ليست صعبة ، ولكنها مثيرة للاهتمام.

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


All Articles