Время прочтения: 4 мин.
Мы часто сталкиваемся с необходимостью детектирования различных объектов на фотографиях или видео. Довольно эффективно данную проблему можно решить, используя библиотеки OpenCV. В данной статье рассмотрим один из алгоритмов детектирования на примере обнаружения дорожных знаков.
Принцип работы программы заключается в следующем: на вход подается изображение, где предварительно мы ожидаем детектировать дорожный знак. При этом работа с видео такая же, так как в таком случае видео покадрово передается в программу и дальнейший алгоритм не отличается. Обычно все изображения, привычные нашему глазу, находятся в формате RGB. С ним неудобно работать, так как падение тени или свечение солнца будут сильно искажать оттенки цвета. А работа программы основана именно на определении цвета. Поэтому первым шагом будет переход в цветовое пространство HSV. Здесь мы уже будем накладывать цветовой фильтр, который будет разрешать только те цвета, которые встречаются в дорожных знаках. Чтобы в контурах не было разрывов, сгладим разрешенные пиксели. Далее рисуем контуры вокруг разрешенных пикселей. Чтобы не было много лишних, можно поставить ограничения по площади контура, например, в данном случае контур должен находиться в пределе от 400 до 10000 пикселей.
def detectionRED(frame, spisok):
""" В качестве аргумента функция принимает кадр, переводит в цветовой формат hsv,
накладывает маску только красного цвета, сглаживает области и находит контуры.
Цикл оставляет контуры, подходящие по размеру, и отрисовывает их.
Функция возвращает кадр, с нарисованными контурами"""
cv.imshow('img', frame)
frame_hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
cv.imshow('frame0', frame_hsv)
frame_mask = cv.inRange(frame_hsv, (0, 162, 67), (255, 255, 255))
cv.imshow('frame1', frame_mask)
frame_dilate = cv.dilate(frame_mask, None, iterations=2)
cv.imshow('frame2', frame_dilate)
contours, hierarchy = cv.findContours(frame_dilate.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
sort = spisok
for i in contours:
area = cv.contourArea(i)
if 400 < area < 10000: # можно регулировать дальность
sort.append(i)
cv.drawContours(frame, sort, -1, (0, 23, 44), 2)
return frame, sort
Поэтапно все действия с изображением представлены на фото ниже.
Остается только вырезать дорожные знаки с изображения и сохранить отдельным файлом.
def detectionIMAGE(image_name):
""" Функция принимает в качестве аргумента название изображения. Передает в функции
отрисовывания контуров и возвращает изображение с нарисованными контурами.
Для выхода нажмите кнопку "Esc". """
start_time = time.time()
image = cv.imread(image_name)
print(type(image))
cadr = cv.imread(image_name)
spisok = []
frame, sort = dtRED(image, sort)
while True:
cv.drawContours(frame, sort, -1, (0, 234, 135), 2)
cv.imshow('frame', frame)
(x, y, w, h) = cv.boundingRect(sort[0])
detect = cadr[y:y + h, x:x + w]
cv.imshow('q', detect)
(x, y, w, h) = cv.boundingRect(sort[1])
detect = cadr[y:y + h, x:x + w]
cv.imshow('r', detect)
(x, y, w, h) = cv.boundingRect(sort[2])
detect = cadr[y:y + h, x:x + w]
cv.imshow('u', detect)
end_time = time.time()
if cv.waitKey(1) == 27:
break
print('Время выполнения:', end_time - start_time)
return frame
Считаю результат очень хорошим, потому что даже в ночное время, программа отлично отработала и смогла детектировать знак, находящийся на заднем фоне. К примеру, нейросеть, обученная на датасете OpenImageV4, на этом изображении смогла найти только знаки на переднем фоне. При этом, в нашем случае не требуется высоких вычислительных мощностей и ядер CUDA и, соответственно, время работы сильно уменьшается, особенно на маломощных и портативных устройствах. Время работы программы составило меньше секунды.
При этом нейросеть, построенная на архитектуре SSD, работала значительно дольше и распознала два знака как один.
Данный алгоритм можно использовать не только для детектирования дорожных знаков, но и для других целей. Например, детектирование ценников в магазине, документов, книг, табличек, обладающих определенным цветом. Увеличить точность можно, оставляя только четырехугольные предметы, либо вводя другие специфичные конкретным объектам фильтры.