08.02.2020       Выпуск 320 (03.02.2020 - 09.02.2020)       Статьи

Измерение расстояния до объектов с помощью RealSense D435

Задача обнаружения объектов на изображении сегодня является одной из ведущих в области машинного зрения. Ее суть заключается в том, чтобы не только классифицировать объект на снимке, но и указать его точное местоположение.

Результаты обнаружения объекта могут быть дополнены информацией о том, насколько далеко расположен данный объект. Задачу измерения расстояния можно решить с помощью камеры глубины Intel RealSense D435, измеряющей глубину в каждой точке.

В данной статье мы решим задачу измерения расстояния до объекта в режиме реального времени с помощью библиотеки OpenCV и технологии RealSense.

Читать>>




Экспериментальная функция:

Ниже вы видите текст статьи по ссылке. По нему можно быстро понять ссылка достойна прочтения или нет

Просим обратить внимание, что текст по ссылке и здесь может не совпадать.

Задача обнаружения объектов на изображении сегодня является одной из ведущих в области машинного зрения. Ее суть заключается в том, чтобы не только классифицировать объект на снимке, но и указать его точное местоположение.

Результаты обнаружения объекта могут быть дополнены информацией о том, насколько далеко расположен данный объект. Задачу измерения расстояния можно решить с помощью камеры глубины

Intel RealSense D435

, измеряющей глубину в каждой точке.

В данной статье мы решим задачу измерения расстояния до объекта в режиме реального времени с помощью библиотеки

OpenCV

и технологии RealSense.

image

Требования

Камера RealSense

Для измерения расстояния нам понадобится камера глубины

RealSense D435

. Для данного примера устанавливать

RealSense SDK

нет необходимости.

Библиотека pyrealsense2

Весь код, для простоты демонстрации, мы будем писать на

Python 3.7

, так что нам понадобится пакет

pyrealsense2

.

pip install pyrealsense2
Библиотека OpenCV

Также, нам понадобится библиотека

OpenCV

(подойдет любая версия, начиная с 3.4). Мануал по установке библиотеки можно посмотреть на официальном сайте

OpenCV

.

Вместе с

OpenCV

установится еще одна необходимая библиотека –

numpy

.

Обнаружение объектов

Первым делом необходимо выделить объекты на видео. Для обнаружения объектов в режиме реального времени отлично подойдут так называемые

one-stage

модели (например,

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

На выходе получаем файл

graph.pbtxt

.

Имеющиеся файлы модели кладем в папку с проектом. В моем случае, это папка

models/

.

Разработка

Теперь все готово к разработке. Начнем писать код.

Импортируем необходимые библиотеки и делаем заглушку на метод

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)
image
Полный код
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

, увеличивающие качество изображения, либо модифицировать сам алгоритм вычисления глубины (напр. вычислять взвешенное среднее или замерять расстояние в одной центральной области). Как видите задача не сложная, но интересная.






Разместим вашу рекламу

Пиши: mail@pythondigest.ru

Нашли опечатку?

Выделите фрагмент и отправьте нажатием Ctrl+Enter.

Система Orphus