Обработка изображений: улучшение алгоритма для детектора логотипа FedEx в реальном времени - PullRequest
15 голосов
/ 02 апреля 2019

Я работал над проектом, включающим обработку изображений для обнаружения логотипа.В частности, цель состоит в том, чтобы разработать автоматизированную систему для детектора грузовых автомобилей / логотипов FedEx в режиме реального времени, которая считывает кадры из потока IP-камеры и отправляет уведомление об обнаружении.Вот пример системы в действии с узнаваемым логотипом, окруженным зеленым прямоугольником.

original frame

Fedex logo

Некоторые ограничения по проекту:

  • Использует сырой OpenCV (без глубокого обучения, искусственного интеллекта или обученных нейронных сетей)
  • Фон изображения может быть шумным
  • Яркость изображения может сильно различаться (утром,день, ночь)
  • Грузовик / логотип FedEx может иметь любую шкалу , поворот или ориентацию, поскольку он может быть припаркован в любом месте на тротуаре
  • Логотип может быть размытым или размытым с разными оттенками в зависимости от времени суток
  • В одном кадре может быть много других автомобилей с аналогичными размерами или цветами
  • Обнаружение в реальном времени (~ 25 кадров в секунду с IP-камеры)
  • IP-камера находится в фиксированном положении, и грузовик FedEx всегда будет в одной и той же ориентации (никогда не назад или вверх ногами)
  • Грузовик FedEx всегда будетбыть "красным" вариантом вместо "зеленого" варианта

Текущая реализация / алгоритм

У меня есть два потока:

  • Thread # 1 - захватывает кадры с IP-камеры с использованием cv2.VideoCapture() иизменяет размеры кадра для дальнейшей обработки.Решили обрабатывать кадры захвата в отдельном потоке, чтобы улучшить FPS за счет уменьшения задержки ввода / вывода, поскольку cv2.VideoCapture() блокирует.Благодаря выделению независимого потока только для захвата кадров, это позволило бы основному потоку обработки всегда иметь доступный кадр для обнаружения.
  • Поток № 2 - Основной поток обработки / обнаружения для обнаружения логотипа FedEx с использованием цветного порогаи обнаружение контура.

Общий псевдоалгоритм

For each frame:
    Find bounding box for purple color of logo
    Find bounding box for red/orange color of logo
    If both bounding boxes are valid/adjacent and contours pass checks:
        Combine bounding boxes
        Draw combined bounding boxes on original frame
        Play sound notification for detected logo

Предел цвета для обнаружения логотипа

ДляУстановив пороговое значение цвета, я определил пороговые значения HSV (низкий, высокий) для фиолетового и красного для определения логотипа.

colors = {
    'purple': ([120,45,45], [150,255,255]),
    'red': ([0,130,0], [15,255,255]) 
}

Чтобы найти координаты ограничивающего прямоугольника для каждого цвета, я следую этому алгоритму:

  • Размытие кадра
  • Эрозия и расширение кадра с помощью ядрадля удаления фонового шума
  • Преобразование кадра из цветового формата BGR в HSV
  • Выполнение маски на кадре с использованием нижней и верхней границ цвета HSV с установленными пороговыми значениями цвета
  • Поиск наибольшегоКонтур в маске и получить ограничивающие координаты

После выполнения маски, я получаю эти изолированные фиолетовый (слева) и красный (справа) разделы логотипа.

image image

Ложные положительные проверки

Теперь, когда у меня есть две маски, я выполняю проверки, чтобы убедиться, что найденная границакоробки на самом деле образуют логотип.Для этого я использую cv2.matchShapes(), который сравнивает два контура и возвращает показатель, показывающий сходство.Чем ниже результат, тем выше совпадение.Кроме того, я использую cv2.pointPolygonTest(), который находит кратчайшее расстояние между точкой на изображении и контуром для дополнительной проверки.Мой ложный положительный процесс включает в себя:

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

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

Результаты

enter image description here enter image description here

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

enter image description here enter image description here

Хотя этот подход к определению цветовых порогов и контуров работал в основных случаях, когда логотип был четким, в некоторых областях его не хватало:

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

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

Ответы [ 2 ]

10 голосов
/ 21 июня 2019

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

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

Example of feature detection

Я сгенерировал вышеупомянутое изображение, используя код, измененный из OpenCV docs документов по обнаружению функции SIFT:

import numpy as np
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('main.jpg',0)  # target Image

# Create the sift object
sift = cv2.xfeatures2d.SIFT_create(700)

# Find keypoints and descriptors directly
kp, des = sift.detectAndCompute(img, None)

# Add the keypoints to the final image
img2 = cv2.drawKeypoints(img, kp, None, (255, 0, 0), 4)

# Show the image
plt.imshow(img2)
plt.show()

При этом вы заметите, что большое количество функций попадает на логотип FedEx (вверху).

Следующее, что я сделал, попытался сопоставить функции из видеопотока с функциями влоготип FedEx.Я сделал это с помощью функции соответствия FLANN.Вы могли бы использовать много подходов (включая грубую силу), но поскольку вы работаете над видео-фидом, это, вероятно, ваш лучший вариант.Приведенный ниже код основан на OpenCV документах о сопоставлении функций:

import numpy as np
import cv2
from matplotlib import pyplot as plt

logo = cv2.imread('logo.jpg', 0) # query Image
img = cv2.imread('main2.jpg',0)  # target Image


# Create the sift object
sift = cv2.xfeatures2d.SIFT_create(700)

# Find keypoints and descriptors directly
kp1, des1 = sift.detectAndCompute(img, None)
kp2, des2 = sift.detectAndCompute(logo,None)

# FLANN parameters
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)   # or pass empty dictionary
flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

# Need to draw only good matches, so create a mask
matchesMask = [[0,0] for i in range(len(matches))]

# ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):
    if m.distance < 0.7*n.distance:
        matchesMask[i]=[1,0]

# Draw lines
draw_params = dict(matchColor = (0,255,0),
                   singlePointColor = (255,0,0),
                   matchesMask = matchesMask,
                   flags = 0)


# Display the matches
img3 = cv2.drawMatchesKnn(img,kp1,logo,kp2,matches,None,**draw_params)
plt.imshow(img3, )
plt.show()

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

Logo Matching

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

Надеюсь, это поможет!

2 голосов
/ 25 июня 2019

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

enter image description here

Сначала мы уменьшаем искажение ствола.

import cv2
img = cv2.imread('fedex.jpg')
margin = 150
# add border as the undistorted image is going to be larger
img = cv2.copyMakeBorder(
                 img, 
                 margin, 
                 margin, 
                 margin, 
                 margin, 
                 cv2.BORDER_CONSTANT, 
                 0)
import numpy as np

width  = img.shape[1]
height = img.shape[0]
distCoeff = np.zeros((4,1), np.float64)

k1 = -4.5e-5;
k2 = 0.0;
p1 = 0.0;
p2 = 0.0;

distCoeff[0,0] = k1;
distCoeff[1,0] = k2;
distCoeff[2,0] = p1;
distCoeff[3,0] = p2;

cam = np.eye(3, dtype=np.float32)

cam[0,2] = width/2.0  # define center x
cam[1,2] = height/2.0 # define center y
cam[0,0] = 12.        # define focal length x
cam[1,1] = 12.        # define focal length y

dst = cv2.undistort(img, cam, distCoeff)

enter image description here

Затем мы преобразуем изображение таким образом, как будто камера направлена ​​прямо на грузовик FedEx.Именно там, где вдоль бордюра припаркован грузовик, логотип FedEx будет иметь практически одинаковый размер и ориентацию.

# use four points for homography estimation, coordinated taken from undistorted image
# 1. top-left corner of F
# 2. bottom-left corner of F
# 3. top-right of E
# 4. bottom-right of E
pts_src = np.array([[1083, 235], [1069, 343], [1238, 301],[1201, 454]])
pts_dst = np.array([[1069, 235],[1069, 320],[1201, 235],[1201, 320]])
h, status = cv2.findHomography(pts_src, pts_dst)
im_out = cv2.warpPerspective(dst, h, (dst.shape[1], dst.shape[0]))
...