Использование OpenCV и Python для отслеживания объектов по цвету

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

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

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

Теория среднего сдвига

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

Алгоритм помещает ядро в каждую точку данных и суммирует их вместе, чтобы получить оценку плотности ядра (KDE). KDE будет иметь пики и спады, которые соответственно соответствуют местам с высокой и низкой плотностью точек данных.

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

Так как это связано с компьютерным зрением? Представьте себе радугу, как на изображении ниже:

Изображение можно разбить на цветовое пространство HSV, по сути превратив его в большой набор данных. При создании KDE каждый из цветов радуги и синего неба создает вершину. При итеративном смещении точек каждый цвет радуги станет более отчетливым за счет устранения тонких переходов между цветами. Следовательно, компьютер легче распознает каждую полосу.

Этот процесс представляет собой разновидность сегментации изображения.

Если продолжить концепцию, если необходимо идентифицировать определенный цвет радуги, алгоритм среднего сдвига делает его более заметным. Если бы этот цвет нужно было отслеживать, каждый кадр видео радуги проходил бы алгоритм в реальном времени.

Для более реалистичного примера изобразите теннисный матч сверху вниз. Площадка, игроки и ярко-желтый мяч будут иметь пик KDE. Если мяч необходимо отслеживать, алгоритм среднего сдвига сделает массу ярко-желтых пикселей более определенной и будет отслеживать ее в каждом кадре при перемещении между игроками.

Интуиция за кулачковым переключением

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

К сожалению, если объект сдвигается в размере или меняет ориентацию, поле отслеживания не учитывает это изменение. Алгоритм кулачкового сдвига (непрерывно адаптивный средний сдвиг) решает эту проблему.

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

Сильные и слабые стороны среднего сдвига и кулачкового сдвига

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

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

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

Реализация Cam Shift

Поскольку кулачковый сдвиг является более надежным из двух алгоритмов, его реализация на Python и OpenCV будет объяснена более подробно. Однако краткое описание реализации среднего сдвига можно найти здесь.

import numpy as np
import cv2

Нужны только две библиотеки - NumPy и OpenCV. Первый помогает в эффективных вычислениях, а второй предоставляет множество инструментов, используемых в компьютерном зрении.

# initialize video from the webcam
video = cv2.VideoCapture(0)
# Read the video
cap, frame = video.read()

Первая строка просто активирует веб-камеру (если она есть). Метод чтения фактически захватывает две важные части информации:

  1. переменная cap, которая указывает, успешно ли камера захватила кадр
  2. переменная кадра, которая является фактическим неподвижным изображением или кадром из канала камеры
# set up initial coordinates for the tracking window
x, y = 0, 0
# Set up initial size of the tracking window
height, width = 25, 25
track_window = (x,y,width,height)
# set up region of interest (roi)
roi = frame[y:y + height, x:x + width]

В следующем разделе устанавливается начальная область интереса (ROI), которая представляет собой просто область экрана, на которой находится отслеживаемый объект.

Переменные x и y - это координаты в кадре. В этом примере используются координаты (0, 0), потому что их легко найти, когда камера начнет потоковую передачу.

Высота и ширина указывают размер области интереса. Очень важно, чтобы размер области интереса хотя бы приблизительно соответствовал размеру отслеживаемого объекта.

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

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

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

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

# apply mask
hsv_frame =  cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv_frame, np.array((0, 20, 20)), np.array((180, 250, 250)))
hist_frame = cv2.calcHist([hsv_frame], [0], mask, [180], [0,180])
cv2.normalize(hist_frame, hist_frame, 0, 255, cv2.NORM_MINMAX)

Следующая пара строк преобразует кадр в цветовое пространство HSV и применяет «маску», которая просто ищет значения пикселей, которые соответствуют определенному диапазону.

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

# Setup the termination criteria: 10 iteration 1 pxl movement
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )

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

Вышеупомянутая строка останавливает итерации, если произойдет одно из двух:

  1. центроид ROI не сдвинулся, или
  2. есть более 10 итераций

Первый критерий имеет интуитивный смысл. Если объект отслеживается и не движется, запускать алгоритм не нужно. Рентабельность инвестиций не меняется, поэтому ее не нужно пересчитывать. Это приведет только к потере вычислительной мощности.

Второй критерий больше касается производительности. Теоретически, чем больше итераций, тем точнее будет рентабельность инвестиций; однако для выполнения большего количества итераций требуется больше времени. Установка более высокого предела, чем 10, может создать лучшую ограничивающую рамку, но результаты будут запаздывать.

while True:
    
    cap, frame = video.read()
    if cap == True:
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        dst = cv2.calcBackProject([hsv],[0],hist_frame,[0,180],1)

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

Следующая строка, как и раньше, читает данные с камеры. Следующий оператор if проверяет, успешно ли камера захватила кадр.

Последние две строки преобразуют кадр в цветовое пространство HSV и используют обратную проекцию гистограммы, чтобы найти, какие пиксели лучше всего соответствуют цвету объекта, захваченного в исходной области интереса.

        # apply camshift to get the new location
        ret, track_window = cv2.CamShift(dst, track_window, term_crit)

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

       # Draw a box around the ROI
        pts = cv2.boxPoints(ret)
        pts = np.int0(pts)
        img2 = cv2.polylines(frame,[pts],True, 255,2)

Наконец, нарисуйте рамку вокруг области интереса, чтобы ее визуально обозначить.

        cv2.imshow('img2',img2)
        # Use the q button to quit the operation
        if cv2.waitKey(60) & 0xff == ord('q'):
            break
    else:
        break
cv2.destroyAllWindows()
video.release()

Изображение отображается в реальном времени в первой строке. Следующая строка просто создает условие выхода. Если буква «q» нажата на клавиатуре, цикл разрывается и видео выходит.

Точно так же, если камера внезапно не может успешно снимать видео, это также разорвет цикл.

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

Если все пойдет хорошо, окончательный результат должен выглядеть примерно так:

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

Выводы

Алгоритмы среднего сдвига и кулачкового сдвига - невероятно полезные и мощные инструменты для использования при отслеживании объектов. Последний, в частности, может смещать свою область интереса при повороте объекта или изменении расстояния от камеры. Результат - надежный метод отслеживания.

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

Либо они хорошо работают в контролируемой, хорошо освещенной среде и предлагают эффективный метод отслеживания объектов.