Время прочтения: 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, работала значительно дольше и распознала два знака как один.

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