OpenCV на python: поиск цветного объекта

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

Возьмем, к примеру, робота-футболиста. Чтобы забить гол, ему нужно сначала найти мяч на поле. Как правило, мяч в таких соревнованиях имеет цвет, отличный от цвета поля.

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

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

На этом уроке мы разберем алгоритм детектирования и вычисления координат цветового пятна в кадре. То есть будем водить объектом какого-то конкретного цвета перед камерой, а программа будет выдавать нам координаты этого объекта относительно краев видеокадра.

Алгоритм поиска цветового пятна в OpenCV. Моменты изображения

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

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

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

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

Например, момент нулевого порядка m00 — это количество всех точек, составляющих пятно. Момент первого порядка m10 представляет собой сумму X координат точек, а m01 — сумму Y координат. Имеются также моменты m11, m20, m02, m22 и т.д.

Формула для вычисления моментов очень проста, и мы можем посчитать пиксели вручную. Однако, стандартные функции OpenCV написаны на языках более низкого уровня, чем python, и работают быстрее. Воспользуемся стандартной функцией для вычисления моментов кадра:

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

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

Функция moments вернет нам массив моментов вплоть до третьего порядка. Но для вычисления координат центра пятна нам потребуются только моменты первого порядка m01 и m10, а также момент m00.

dM01 = moments['m01']
dM10 = moments['m10']
dArea = moments['m00']

Чтобы получить координаты X и Y искомого пятна, нам следует поделить полученные моменты m10 и m01 на нулевой момент m00. Таким образом мы найдем средние координаты X и Y всех точек, а это и есть центр пятна.

x = int(dM10 / dArea)
y = int(dM01 / dArea)

Программа для поиска цветового пятна на Raspberry Pi и 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 фильтр для зеленых объектов из прошлого урока
hsv_min = np.array((53, 55, 147), np.uint8)
hsv_max = np.array((83, 160, 255), np.uint8)

while True:
    flag, img = cap.read()
    # преобразуем RGB картинку в HSV модель
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV )
    # применяем цветовой фильтр
    thresh = cv2.inRange(hsv, hsv_min, hsv_max)

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

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

cap.release()
cv2.destroyAllWindows()

Запускаем программу и пробуем водить перед камерой зеленым объектом.

Примечание! Функция moment не может различить одно пятно в кадре или несколько. Так что, если в кадре окажется, например, два зеленых объекта, то они интерпретируются как один большой, и центр будет где-то между ними.

Программа для поиска цветового пятна на Raspberry Pi и python. Подсветка траектории

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

import cv2
import numpy as np
import video

if __name__ == '__main__':
    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 = video.create_capture(0)
hsv_min = np.array((53, 55, 147), np.uint8)
hsv_max = np.array((83, 160, 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)
    dM01 = moments['m01']
    dM10 = moments['m10']
    dArea = moments['m00']

    if dArea > 100:
        x = int(dM10 / dArea)
        y = int(dM01 / dArea)
        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()
cv2.destroyAllWindows()

Пример работы программы на Raspberry Pi с обычной веб-камерой.

Программа для поиска цветового пятна на Raspberry Pi и 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, 55, 147), np.uint8)
hsv_max = np.array((83, 160, 255), np.uint8)

color_yellow = (0,255,255)

while True:
    flag, img = cap.read()
    img = cv2.flip(img,1) # отражение кадра вдоль оси Y
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV )
    thresh = cv2.inRange(hsv, hsv_min, hsv_max)

    moments = cv2.moments(thresh, 1)
    dM01 = moments['m01']
    dM10 = moments['m10']
    dArea = moments['m00']

    if dArea > 100:
        x = int(dM10 / dArea)
        y = int(dM01 / dArea)
        cv2.circle(img, (x, y), 5, color_yellow, 2)
        cv2.putText(img, "%d-%d" % (x,y), (x+10,y-10), 
                    cv2.FONT_HERSHEY_SIMPLEX, 1, color_yellow, 2)

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

cap.release()
cv2.destroyAllWindows()

Запускаем программу и проверяем работу алгоритма. Снова используем Raspberry Pi. Как видно на видео, мощности Raspberry Pi вполне хватает для выполнения поиска цветного объекта с помощью моментов.

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


Изменено:

OpenCV на python: поиск цветного объекта: 33 комментария

  1. У меня работает только с USB камеры. Как задействовать штатную камеру подключаемую к разъему MIPI Camera Serial Interface (CSI-2) ?

  2. Доброго дня! Не подскажете ли вы мне, как можно изменять разрешение съёмки и частоту кадров применительно к этой программе (-ам)? Я видел похожий код от зарубежного автора, но там была лишь строчка изменяющая разрешение (представлена ниже), но о регулировке частоты нигде и ничего не находится.
    (cap = cv2.VideoCapture(0)
    # Reduce the size of video to 320×240 so rpi can process faster
    cap.set(3,320)
    cap.set(4,240))

    • Частота кадров зависит от вычислительной мощности вашего компьютера и размера кадра. Уменьшая размер, вы тем самым ускоряете обработку каждого кадра, соответственно, увеличиваете частоту видеопотока на выходе.

      • Да если бы это было так просто.. Я задаю разрешение 720p и загрузка процессора становится равной 60%, меняю на 480p и загрузка уменьшается на 5-10% (нет чёткой зависимости), при этом прироста частоты кадров не наблюдается (на 1-2, может, 3 больше становится, но это несерьёзно). Может ли проблема быть в самом OpenCV (в его неэффективных алгоритмах)?

        • В OpenCv можно уменьшить частоту камеры, задается как у вас в set (точно не помню как называется, там для этого макрос есть, вродебы на CAP начинается). Так же можно через waitKey(задержка) задать, где задержка — число в миллисекундах. Но это повлияет на всю работы программы.

  3. Подскажите пожалуйста как можно сделать чтобы при появлении в кадре «зеленого обьекта» загорелся светодиод на определненное время?

  4. у меня распознаются объекты на видео и отмечаются.. как записать их координаты?

    • Как вариант, можно воспользоваться функциями python для работы с файлами. Типа:
      file = open(«C:\\my_file.txt», «a»)
      file.write(«x=» + x + «,y=» + y)
      file.close()

  5. В этом кусочке:

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

    PyCharm ругается на то, что не определены переменные x/y
    Не подскажете, они определены где-то ранее, просто я не понял?

    • Скорее всего при копировании кода из браузера скопировался скрытый символ, который все портит.

  6. Здравствуйте. У меня проблема. Возникает ошибка
    flag, img = cap.read()
    AttributeError: ‘NoneType’ object has no attribute ‘read’

    Хотя драйвер камеры правильный и в прошлом уроке всё работало.

    • измените значения захвата,

      cap = video.create_capture(0)

      на 1 возможно камера занята чем-то. или перезагрузите компьютер.

  7. Здравствуйте. Прошла прошлый урок со встроенной распберри камерой и настроенным драйвером. Соответственно, кусок с настройкой вставлен в то жже начало программы. В первой-же задаче возникает
    flag, img = cap.read()
    AttributeError: ‘NoneType’ object has no attribute ‘read’
    Подскажите пожалуйста, что делать в этом случае.
    Если что, Код вот тут:
    https://pp.userapi.com/c844416/v844416612/1fd172/1A-_CJ7y8w0.jpg

    • Видимо скрипт не находит камеру #0. Попробуйте cap=video.create_capture(1)

      • Нее. То же самое….
        File «redline_col.py», line 28, in
        flag, img = cap.read()
        AttributeError: ‘NoneType’ object has no attribute ‘read’

  8. Здравствуйте, а есть идеи как найти 2 цветовых пятна ? Функция moment не может различить одно пятно в кадре или несколько.

  9. Подскажите пожалуйста вариант, если в кадре находится несколько объектов одного цвета

  10. Присоединяюсь ко всем выше сказанным =))
    Подскажите пожалуйста, как вычислять координаты 2-х и более цветных пятен одного цвета.
    Спасибо!!!!!

    • Накладываете цветовой фильтр, потом обнаруживаете прямоугольники/эллипсы объектов. Выделяете по площади — получаете нужное количество объектов.

  11. Здравствуйте. Могли бы вы добавить уроки по openCV в отдельный блок статей? Сейчас вкладка блоки не работает, и полагаю такой блок уже есть, но проверить это я не могу

  12. Пишу всем у кого ошибка !

    Import Video
    cap = video.create_capture(0)
    ^^^^^
    NameError: name ‘video’ is not defined

    Используйте cap = cv2.VideoCapture(0) и все заработает

  13. Выводит ошибку к первому тексту программы
    ch = cv2.waitKey(5)
    IdentificationError: unexpected indent

    • Проверьте все отступы, возможно после копирования с сайта что-то сдвинулось. В Python тело оператора выделяется отступами, а не фигурными скобками как в языке Си (в том же Arduino IDE).

Добавить комментарий для 1223 Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.