Распознавание цвета с захватом одного кадра

Напишем программу по распознаванию цветных объектов. Сразу скажу, что эта область обширна, и здесь мы будем касаться лишь той небольшой части, которая нам нужна для соревнований WorldSkills.

Для начала откроем гитбук разработчика квадрокоптера Клевер и скопируем готовый скрипт, который будем перерабатывать:

https://clover.coex.tech/ru/

Скопируем этот код в пустой текстовый файл с расширением .py. На забудем изменить наименование импортирования модуля clover.

Отлично, теперь удалим все лишнее, и оставим только фрагмент по распознаванию цветов:

Итого, наш код принимает следующий вид:

Прокомментируем теперь каждую строчку:

#импорт необходимых модулей и библиотек
import rospy
import cv2 as cv
from clover import srv
from std_srvs.srv import Trigger
from cv_bridge import CvBridge
from sensor_msgs.msg import Image
from clover.srv import SetLEDEffect


#инициализация ноды (программы)
rospy.init_node('flight')

#инициализация используемых сервисов 
bridge = CvBridge()
set_effect = rospy.ServiceProxy('led/set_effect', SetLEDEffect)
get_telemetry = rospy.ServiceProxy('get_telemetry', srv.GetTelemetry)
navigate = rospy.ServiceProxy('navigate', srv.Navigate)
navigate_global = rospy.ServiceProxy('navigate_global', srv.NavigateGlobal)
set_position = rospy.ServiceProxy('set_position', srv.SetPosition)
set_velocity = rospy.ServiceProxy('set_velocity', srv.SetVelocity)
set_attitude = rospy.ServiceProxy('set_attitude', srv.SetAttitude)
set_rates = rospy.ServiceProxy('set_rates', srv.SetRates)
land = rospy.ServiceProxy('land', Trigger)

#создание топика для просмотра распознанного изображения
color_debug = rospy.Publisher("/color_debug", Image)


#объявление процедуры check_temp, при вызове которой будет распознавание цветов
def check_temp(data):
    #создание переменной frame, где будет храниться изображение с камеры. Изображение это постоянно обрабатывается. В квадратных скобках определена рамка, откуда берется изображение. Кодировка изображения brg8 (8-ми битное изображение с кодировкой BGR)
    frame = bridge.imgmsg_to_cv2(data, 'bgr8')[80:160, 100:220]  
   
   #задание для каждого цвета диапазона в кодировке BGR. В результате в каждую из переменных запишется бинарное представление цвета (0 - если цвет не попадет в диапазон и 255 - если попадает). Это бинарное представление представлено в виде матрицы.
    red = cv.inRange(frame, (165, 70, 158), (255, 209, 255))
    yellow = cv.inRange(frame, (10, 80, 88), (49, 220, 225))
    green = cv.inRange(frame, (26, 28, 60), (135, 162, 225))

    #зададим словарь color, где запишем для каждого ключа 'r', 'y', 'g' значения из бинарной матрицы только не нулевые значения
    color = {'r': cv.countNonZero(red),
             'y': cv.countNonZero(yellow),
             'g': cv.countNonZero(green)}
    
    #публикация топика для просмотра распознанного изображения
    color_debug.publish(bridge.cv2_to_imgmsg(frame, 'bgr8')) 
   
    #зададим условие, если максимальное значение из словаря равно ключу 'y', тогда выводим на экран сообщение sbrosheno. Простыми словами, если он увидел желтый цвет, тогда выводить сообщение sbrosheno
    if max(color, key=color.get) == 'y':
        print('sbrosheno') 

Для более точного распознавания будем использовать захват только одного кадра. Для этого поменяем строчку, где происходит захват изображения на следующую:

В итоге наш код примет следующий вид:

Теперь добавим полет в цветовую метку с координатами (x=2, y=0) и не забудем вызвать процедуру для распознавания цветовой метки. Будем распознавать красный цвет, поэтому будет в условии сравнивать с ключом 'r':

Сохраним полученный код и запустим квадрокоптер.

Ничего не получилось, давайте добавим второе условие на случай, если не сработает распознавание. Тогда будет точно ясно, как ведет себя программа. Также добавим функцию print для вывода на экран текущего изображения, чтобы понимать значение красного цвета. В результате получим следующий код:

Сохраним и запустим код:

Цвет мы не распознали, но зато получили матрицу значений нашего цвета, как видит его камера квадрокоптера. Цвет имеет 3 значения. Эти 3 значения постоянно повторяются. Сделаем небольшую выборку из всех полученных значений и выберем максимальное и минимальное для каждого значения.

Итого:

для первого числа максимальное — 134, минимальное -113

для второго числа максимальное — 150, минимальное -134

для третьего числа максимальное — 163, минимальное -151

Отнимем по 40 от минимального и прибавим 40 к максимальному, так мы получим диапазон измерений для нашего красного цвета.

Получим диапазон:

максимальное значение (174, 194, 203)

минимальное значение (75, 94, 111)

Теперь полученные значения внесем в код для красного цвета. Проверку на выборку диапазона можно убрать. В результате наша программа примет вид:

Теперь вновь запустим нашу программу.

Как видите, теперь все работает и красный цвет он определяет. Однако, вы должны знать, что на распознавание цвета влияют многие факторы, один из которых это освещение в помещении. В симуляторе Gazebo идеальные условия, и здесь освещение постоянное, поэтому настройка диапазона проводится за один проход. В реальности, таких проходов для получения выборки диапазона, возможно более 2-ух раз.

Для более точного определения поменяем границы определения на следующие:

frame = bridge.imgmsg_to_cv2(rospy.wait_for_message('main_camera/image_raw', Image), 'bgr8')[100:140, 130:170]  

ЗАДАНИЯ НА САМОСТОЯТЕЛЬНУЮ РАБОТУ:

  1. Распознайте цветовые метки в других двух точках и выведите сообщение о результате. Если вы цвет распознали выведите сообщение «Raspoznal», в противном случае «Neraspoznal». Координаты первой цветовой метки (x=2, y=0). Координаты второй цветовой метки (x=1, y=2). Координаты третьей цветовой метки (x=3, y=3).

Last updated