Python, Машинное зрение, OpenCV. Часть 1

Обнаружить объект заданного цвета — одна из базовых задач машинного зрения. Именно с этой части мы и начнем наш курс статей по машинному зрению в OpenCV.

Цветовой фильтр.

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

Нам потребуется метод inRange( кадр, цвет_1, цвет_2 ) :

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

Так как цвет объекта не равномерен то используется диапазон оттенков от цвет_1 до цвет_2. Что бы понять это наглядно сразу пишем код :

import cv2
import numpy as np

def nothing(*arg):
    pass
cv2.namedWindow("result")  # создаем главное окно
cv2.namedWindow("settings")  # создаем окно настроек

cap = cv2.VideoCapture(0)
# создаем 6 бегунков для настройки начального и конечного цвета фильтра
# createTrackbar ('Имя', 'Имя окна', 'начальное значение','максимальное значение','вызов функции при изменение бегунка'
cv2.createTrackbar('hue_1', 'settings', 0, 255, nothing)
cv2.createTrackbar('satur_1', 'settings', 0, 255, nothing)
cv2.createTrackbar('value_1', 'settings', 0, 255, nothing)
cv2.createTrackbar('hue_2', 'settings', 255, 255, nothing)
cv2.createTrackbar('satur_2', 'settings', 255, 255, nothing)
cv2.createTrackbar('value_2', 'settings', 255, 255, nothing)

while True:
    flag, img = cap.read()
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)# HSV формат изображения

    # считываем значения бегунков
    h1 = cv2.getTrackbarPos('hue_1', 'settings')
    s1 = cv2.getTrackbarPos('satur_1', 'settings')
    v1 = cv2.getTrackbarPos('value_1', 'settings')
    h2 = cv2.getTrackbarPos('hue_2', 'settings')
    s2 = cv2.getTrackbarPos('satur_2', 'settings')
    v2 = cv2.getTrackbarPos('value_2', '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()

Поиск цветного объекта.

На  этапе мы будем используем алгоритм вычисления моментов.

Момент изображения — это суммарная характеристика цветового пятна, представляющая собой сумму всех точек (пикселей) этого пятна. При этом, имеется множество подвидов моментов, характеризующие разные свойства изображения. Например, момент нулевого порядка m00 — это количество всех точек, составляющих пятно. Момент первого порядка m10 представляет собой сумму X координат точек, а m01 — сумму Y координат. Имеются также моменты m11, m20, m02, m22 и т.д. Однако, стандартные функции OpenCV написаны на языках более низкого уровня, чем python, и работают быстрее. Воспользуемся стандартной функцией для вычисления моментов кадра.

moments( кадр, двоичный )

Аргумент кадр представляет собой нашу картинку. Аргумент двоичный определяет то, как алгоритм будет вычислять вес каждой точки. Напомню, что предыдущий метод inRange дал нам черно-белую картинку, в которой пиксели могут быть черными, белыми, а могут быть и серыми. Так вот, если аргумент двоичный равен 1, то вес всех точек с цветом, отличным от нуля будет равен единице. В противном случае, вес черной точки будет равен 0, а белой точки — 255. Функция moments вернет нам массив моментов вплоть до третьего порядка. Но для вычисления координат центра пятна нам потребуются только моменты первого порядка m01 и m10, а также момент m00.

# вычисляем моменты изображения
moments = cv2.moments(image, 1)
sum_y = moments['m01']
sum_x = moments['m10']
sum_pixel = moments['m00'] 
 # будем реагировать только на те моменты,
 # которые содержать больше 100 пикселей
if sum_pixel > 100:
    x = int(sum_x / sum_pixel)         
    y = int(sum_y / sum_pixel)         
    cv2.circle(img, (x, y), 10, (0,0,255), -1) 

Откроем для примера какую нибудь картинку и найдем на ней объект:

import cv2
import numpy as np

cv2.namedWindow("result")
image = cv2.imread("gren_33.jpg")
# HSV фильтр для зеленых объектов из прошлого урока
hsv_min = np.array((65, 107, 2), np.uint8)
hsv_max = np.array((95, 255, 255), np.uint8)
color_yellow = (0,255,255)
# преобразуем RGB картинку в HSV модель
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# применяем цветовой фильтр
thresh = cv2.inRange(hsv, hsv_min, hsv_max)
# вычисляем моменты изображения
# вычисляем моменты изображения
moments = cv2.moments(thresh, 1)
sum_y = moments['m01']
sum_x = moments['m10']
sum_pixel = moments['m00']
 # будем реагировать только на те моменты,
 # которые содержать больше 50 пикселей
if sum_pixel > 50:
    x = int(sum_x / sum_pixel)
    y = int(sum_y / sum_pixel)
    #Рисуем круг в центр светового питна.
    cv2.circle(image, (x, y), 10, (0, 0, 255), -1)
    # Пишем координаты
    cv2.putText(image, "%d-%d" % (x, y), (x + 50, y - 50), cv2.FONT_HERSHEY_SIMPLEX, 1, color_yellow, 2)

cv2.imshow('result', image)
cv2.imshow('filter', thresh)

ch = cv2.waitKey(0)
cv2.destroyAllWindows()

Ну и на закуску код который находит и рисует траекторию движения объекта на видео:

import cv2
import numpy as np

def callback(*arg):
    print(arg)

def createPath( img ):
    h, w = img.shape[:2]
    return np.zeros((h, w, 3), np.uint8)

cv2.namedWindow( "result" )

cap = cv2.VideoCapture(0)
hsv_min = np.array((0, 116, 151), np.uint8)
hsv_max = np.array((12, 255, 255), np.uint8)

lastx = 0
lasty = 0
path_color = (0, 0, 255)

flag, img = cap.read()
path = createPath(img)

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

    moments = cv2.moments(thresh, 1)
    M01 = moments['m01']
    M10 = moments['m10']
    M00 = moments['m00']

    if M00 > 50:
        x = int(M10 / M00)
        y = int(M01 / M00)
        cv2.circle(img, (x, y), 10, (0, 0, 255), -1)

    if lastx > 0 and lasty > 0:
        cv2.line(path, (lastx, lasty), (x, y), path_color, 5)
    lastx = x
    lasty = y

    # накладываем линию траектории поверх изображения
    img = cv2.add(img, path)

    cv2.imshow('result', img)

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

cap.release()