Reading Time: 7 minutes

Самое частое что надо сделать с помощью компьютерного зрения – это определить наличие и место определенного объекта в кадре. Для этого есть много разных методик, как очень простых так и очень сложных, но самой базовой считается определение объекта по цвету. В данной статье мы рассмотрим алгоритм поиска по цвету и потренируемся найти то, что любит весь интернет – котика ? Весь код будет написан на OpenCV 3 и Python 3. В случае работы на Python 2 код будет работать с минимальными изменениями.

Кот в зелени

Начнем наш поиск семейства кошачьих с такого кадра:

Рыжий кот

Рыжий кот

Как мы можем видеть, в данном кадре есть большой и очень заметный кот. Мы его видим, но как его может увидеть компьютер? Для него данное изображение лишь массив точек с разными значениями цвета. Смотрим что мы в данный момент имеем:

import cv2
import numpy as np
cat_image = cv2.imread('kot-ryzhiy-trava-cvety.jpg')
print(type(cat_image), cat_image.shape)

<class 'numpy.ndarray'> (380, 596, 3)

В данном коде мы загрузили изображение и отобразили что оно из себя представляет:3 массива из 380 строк и 596 столбцов. Как мы можем в этом найти кота? Для начала мы можем оценить глазами, что цвет кота отличается от цвета окружения – он ярко рыжий и отлично виден в траве. Как мы можем выделить этот цвет в OpenCV3? Для этого существует специальная функция cv2.inRange(), она принимает на вход изображение и диапазон цвета, который мы хотим выделить. На выходе мы получаем черно-белое изображение, где белым выделены пиксели, цвета которых попадали в диапазон а черным – с цветом вне требуемого. Попробуем выделить на изображении кота:

low_red = (17,50,110)
high_red = (101,140,180)
only_cat = cv2.inRange(cat_image, low_red, high_red)
cv2.imshow('only car', only_cat)
cv2.waitKey(0)

Разберем данный код:

  • Строки 1-2: Для нахождения цвета мы должны задать диапазон, который мы будем искать. В случае с цветовым пространством BGR для верхней и нижней границы мы используем кортеж из трех значений: (Синий(Blue), Зеленый(Green), Красный(Red)) в формате uint8(0-255). Как вы можете отметить – данные наборы очень сложно, если вы не дизайнер, перевести в человеческий язык. Для данного примера я воспользовался программой Digital Color Meter для определения точных значений.
  • Строка 3: Используем функцию cv2.inRange(). Передаем ей изображение кота и два кортежа с определением верхней и нижней границы цвета. На выходе получаем черно-белое изображение, где пиксели со значением 255 соответствуют пикселям с цветом в искомом диапазоне, а значение 0 – всем остальным.
  • Строки 4-5: Стандартный вывод изображение средствами OpenCV3.

На выходе мы получаем такое изображение:

Находим кота в BGR

Находим кота в BGR

Кот в BGR – не кот. Выбираем цветовое пространство.

С данным изображением мы уже можем работать, но что-то в нем не то. Кот выделен не целиком, и есть много лишних точек. На самом деле, мы научимся даже из такого получать много полезной информации, но в данный момент мы сделаем одно очень важное и правильное улучшение. Как вы могли заметить – мы работаем с изображением в пространстве BGR. У данного пространства, кроме не очевидной для человеческого восприятия записи цвета, есть еще один недостаток в нашем случае. Чтобы его понять, рассмотрим что из себя представляет BGR:

Куб RGB(BGR)

Куб RGB(BGR)

Пространство представляет из себя куб с длинной стороны 256, где любой цвет задается координатами соответствующей точки на данном кубе. Данная концепция получила широкое распространение из-за того, что в случае формирования цвета на экране устройства, такого как телефон или монитор компьютера, изображение формируется за счет точек трех цветов – синего, зеленого, и красного. Теперь перейдем к нашей функции cv2.inRange(). Она принимает набор из двух цветов – точек на данном кубе. Что в таком случае мы будем считать за цвет, который удовлетворяет нашим критериям? Для ответа на данный вопрос нарисуем схематично тот же самый куб RGB:

Схема RGB(BGR)

Схема RGB(BGR)

На данной схеме становится понятно, что пространство между двумя заданными точками цвета также является кубом RGB. Таким образом, наша функция находит вложенный цветовой куб в полном кубе RGB. Кроме того, что это может непонятно звучать, это и работает не самым лучшим образом: представим что мы хотим найти оттенки пурпурного цвета, в таком случае мы размещаем наш вложенный куб как можно ближе к пурпурному. Но при таком размещении, мы так или иначе затрагиваем все остальные цвета, из-за того, что стороны куба параллельны основному. Как в таком случае поступить?

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

Переход RGB - HSV

Переход RGB – HSV

В данной модели мы переходим к следующим координатам – цветовой тон, насыщенность и светлота. Именно в данных понятиях люди обычно описывают цвета. Под цветовым тоном (Hue) имеют в виду именно цвет (длину волны). Насыщенность(Saturation) характеризует близость цвета к белому (розовый ближе к белому чем красный). Яркость(Intensity) описывается всех сложнее и в модели HSI ее можно описать как общую яркость точки или цвета.

Все серые цвета (лежащие на диагонали куба) при этом проецируются в центральную точку. Чтобы с помощью этой модели можно было закодировать все цвета, доступные в RGB-модели, необходимо добавить вертикальную ось яркости (или интенсивности) (I). В итоге получается шестигранный конус:

Конус HSL

Конус HSL

В данном конусе диапазон выбранных цветов будет представлять из себя конический слой. Его главные для нас атрибуты: он лежит по оттенку в заданных пределах, в отличии от BGR, его светлота и интенсивность также определяются просто и очевидно в описании цветового предела. Благодаря таким удобным и полезным свойствам данное цветовое пространство заслужило большую любовь как среди дизайнеров, так и особенно среди общества разработчиков компьютерного зрения. В OpenCV3 данное цветовое пространство имеет обозначение HSV. Переведем наше изображение в него и снова найдем котейку:

cat_hsv = cv2.cvtColor(cat_image, cv2.COLOR_BGR2HSV) #Преобразуем в HSV
cat_color_low = (7,40,60) #Данный цвет это темный ненасыщенный красный, близкий к бордовому
cat_color_high = (18,255,200) #Данный цвет это светлый насыщенный оранджевый
only_cat_hsv = cv2.inRange(cat_hsv, cat_color_low, cat_color_high)
cv2.imshow('cat_color_hsv', only_cat_hsv)
cv2.waitKey(0)

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

Кот в HSV

Кот в HSV

Как мы видим – шума стало значительно меньше, а кот определился значительно лучше.

Отметим кота в кадре стандартными средствами OpenCV3

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

moments = cv2.moments(only_cat_hsv, 1) # получим моменты 
x_moment = moments['m01']
y_moment = moments['m10']
area = moments['m00']
x = int(x_moment / area) # Получим координаты x,y кота
y = int(y_moment / area) # и выведем текст на изображение
cv2.putText(cat_image, "Cat!", (x,y), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255), 2) 

cv2.imshow('cat_found', cat_image)
cv2.waitKey(0)

Результатом данного кода будет найденный отмеченный большой рыжий кот:

Кот найден

Кот найден

Итог

Сегодня мы научились находить объекты на фоне с различным цветом, разобрались в каком пространстве лучше заниматься поиском и почему и таки нашли кота в траве. Поздравляю с большим шагом в поиске объектов и способности быстро отличать красные объекты от зеленых ? Данная техника при всех ее простоте используется в очень широком диапазоне задач, таких как автопилоты автомобилей, определители содержания видео и фото, контентные фильтры и много много всего прочего. Отличительные ее качества – простота, скорость и возможность быстро найти объекты, которые специально сделаны так, чтобы их видели издалека.

Следующие способы нахождения объектов – по форме, ключевым точкам и сверточными нейронными сетями. Мы начнем разбор с самых простых и постепенно придем к самым сложным. Оставайтесь с нами и добавляйте сайт в закладки! Хорошего программирования и выходных!


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.