Обнаружить объект заданного цвета — одна из базовых задач машинного зрения. Именно с этой части мы и начнем наш курс статей по машинному зрению в 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()
Большое спасибо за замечательную статью!