OpenCV на python: цветовой фильтр

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

Предположим, что в кадре где-то находится зеленое яблоко.

OpenCV на python. Цветовой фильтр

Чтобы найти на картинке яблоко, машина может сначала найти найти круглый объект, а затем проверить его цвет. А что значит круглый объект? Это значит, что его край представляет собой окружность. А как найти край? Один из способов — это найти такие места на картинке, где радикально меняется цвет. Действительно, на краю яблока цвет резко меняется с зеленого на коричневый. Но поиск таких мест на каждом кадре видеопотока достаточно сложная вычислительная задача, особенно для таких слабых компьютеров как Raspberry Pi. Требуется как-то упростить задачу.

А что если радикально упростить картинку с яблоком? Пусть всё, что было хоть немного зеленым станет белым, а всё остальное станет черным. Тогда нам не придется долго анализировать каждый пиксель картинки, ведь на ней останутся только белые и черные точки. Там, где за черной точкой следует белая и наоборот, за белой — черная, и есть край искомого яблока (это, конечно, грубо, но не далеко от истины).

OpenCV на python. Цветовой фильтр

Именно такую процедуру нам и предстоит сделать с помощью OpenCV.

1. Функция OpenCV для выделения цветового диапазона

Функция inRange позволяет наложить на кадр цветовой фильтр в заданном диапазоне.

inRange( кадр, цвет 1, цвет 2 )
  • кадр — изображение, на которое мы накладываем фильтр;
  • цвет 1 — начальный цвет диапазона;
  • цвет 2 — конечный цвет диапазона.

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

Пример программы на python, которая выделяет в кадре всё зеленое, разных оттенков.

import cv2
import numpy as np
import video

if __name__ == '__main__':
   def callback(*arg):
       print (arg)

cv2.namedWindow( "result" )

cap = video.create_capture(0)
hsv_min = np.array((53, 0, 0), np.uint8)
hsv_max = np.array((83, 255, 255), np.uint8)

while True:
    flag, img = cap.read()
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV )
    thresh = cv2.inRange(hsv, hsv_min, hsv_max)

    cv2.imshow('result', thresh)

    ch = cv2.waitKey(5)
    if ch == 27:
        break

cap.release()
cv2.destroyAllWindows()

Эта программа хоть и будет более или менее сносно выделять яблоко, но она использует очень простой фильтр, который ограничивает только первый компоненты модели HSV.

По факту, такой фильтр кроме яблока найдет еще много чего ненужного в кадре. В реальной жизни фильтр использует все три компонента HSV, и мы сейчас научимся делать такие фильтры.

2. Программа для настройки цветового фильтра в OpenCV

Библиотека OpenCV позволяет создавать формы и некоторые примитивные элементы на них. С помощью этого нехитрого инструментария напишем программу, в которой можно будет в режиме реального времени настраивать все три компонента HSV картинки для создания подходящего фильтра.

import cv2
import numpy as np
import video

if __name__ == '__main__':
    def nothing(*arg):
    pass

cv2.namedWindow( "result" ) # создаем главное окно
cv2.namedWindow( "settings" ) # создаем окно настроек

cap = video.create_capture(0)
# создаем 6 бегунков для настройки начального и конечного цвета фильтра
cv2.createTrackbar('h1', 'settings', 0, 255, nothing)
cv2.createTrackbar('s1', 'settings', 0, 255, nothing)
cv2.createTrackbar('v1', 'settings', 0, 255, nothing)
cv2.createTrackbar('h2', 'settings', 255, 255, nothing)
cv2.createTrackbar('s2', 'settings', 255, 255, nothing)
cv2.createTrackbar('v2', 'settings', 255, 255, nothing)
crange = [0,0,0, 0,0,0]

while True:
    flag, img = cap.read()
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV )
 
    # считываем значения бегунков
    h1 = cv2.getTrackbarPos('h1', 'settings')
    s1 = cv2.getTrackbarPos('s1', 'settings')
    v1 = cv2.getTrackbarPos('v1', 'settings')
    h2 = cv2.getTrackbarPos('h2', 'settings')
    s2 = cv2.getTrackbarPos('s2', 'settings')
    v2 = cv2.getTrackbarPos('v2', 'settings')

    # формируем начальный и конечный цвет фильтра
    h_min = np.array((h1, s1, v1), np.uint8)
    h_max = np.array((h2, s2, v2), np.uint8)

    # накладываем фильтр на кадр в модели HSV
    thresh = cv2.inRange(hsv, h_min, h_max)

    cv2.imshow('result', thresh) 
 
    ch = cv2.waitKey(5)
    if ch == 27:
        break

cap.release()
cv2.destroyAllWindows()

Перед тем как запустить программу рекомендуем посмотреть небольшой видеоурок-инструкцию.

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

 hsv_min = np.array((53, 55, 147), np.uint8)
 hsv_max = np.array((83, 160, 255), np.uint8)

Можем вставить их в первую программу и посмотреть насколько хорошо фильтр выделяет объект.

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

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

  1. оператор берет целевой объект в руки и размещает его прямо перед камерой робота;
  2. оператор водит объектом туда-сюда, давая роботу зафиксировать цвета разных точек объекта;
  3. робот синтезирует на основе нескольких точек всё тот же цветовой диапазон.

В следующем уроке поговорим об алгоритме детектирования цветового пятна на картинке. Вот там то нам и пригодятся все, ранее полученные знания.


Изменено: