Обнаружение / отслеживание прямоугольников с помощью OpenCV

что мне нужно

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

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

инвариантность вращения необходима в случае, если пользователь наклоняет прямоугольник вдоль его локальной оси X и / или Y. Такое вращение меняет форму бумаги с прямоугольника на трапецию. В этом случае объект ориентированная ограничивающая коробка может использоваться для измерения размера бумаги.

что я сделал

в начале есть шаг калибровки. Окно показывает канал камеры, и пользователь должен щелкнуть по прямоугольнику. При щелчке цвет пикселя, на который указывает мышь, принимается за эталонный цвет. Рамки преобразованы в цветовое пространство HSV для того чтобы улучшить различать цвета. У меня есть 6 ползунков, которые регулируют верхний и Нижний пороги для каждого канала. Эти пороги используются для бинаризации изображения (используя opencv ).
После этого я размываю и расширяю двоичное изображение, чтобы удалить шум и объединить куски nerby (используя opencv erode и dilate функции).
Следующим шагом является поиск контуров (используя opencv findContours функция) в двоичном изображении. Эти контуры используются для обнаружения наименьших ориентированных прямоугольников (используя opencv ). В качестве конечного результата я использую прямоугольник с наибольшей область.

краткое завершение процедуры:

  1. захватить кадр
  2. преобразовать этот кадр в HSV
  3. Binarize его (используя цвет, выбранный пользователем, и пороги из ползунков)
  4. применить morph ops (размывать и расширять)
  5. найти контуры
  6. получить наименьший ориентированный bouding box каждого контура
  7. возьмите самый большой из этих ограничивающих ящиков как результат
<!-Как вы, возможно, заметили, я не пользуюсь знаниями о фактической форме бумаги просто потому, что не знаю, как правильно использовать эту информацию.

я также думал об использовании алгоритмов отслеживания opencv. Но было три причины, которые мешали мне использовать их:--13-->

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

вот-относительно-хороший улов (двоичное изображение после эрозии и расширения) ok

а вот это плохо один bad

Вопрос

как я могу улучшить обнаружение в целом и особенно быть более устойчивым к изменениям освещения?

обновление

здесь некоторые необработанные изображения для тестирования.

вы не можете просто использовать более толстый материал?
Да, я могу, и я уже делаю (к сожалению, я не могу получить доступ к этим частям на данный момент). Однако проблема остается. Даже если я использую такие материалы, как cartboard. Согнуть его не так просто, как бумагу, но все же можно согнуть.

как вы получаете размер, вращение и положение прямоугольника?
The minAreaRect функция opencv возвращает

2 ответов


канал H в пространстве HSV-это оттенок, и он не чувствителен к изменению света. Красный диапазон примерно [150,180].

основываясь на упомянутой информации, я выполняю следующие работы.

  1. измените в пространство HSV, разделите канал H, порог и нормализуйте его.
  2. применить morph ops (открыть)
  3. найти контуры, фильтровать по некоторым свойствам (ширина, высота, площадь, соотношение и так далее).

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

enter image description here

imgname = "src.png"
img = cv2.imread(imgname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

## Split the H channel in HSV, and get the red range
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
h[h<150]=0
h[h>180]=0

## normalize, do the open-morp-op
normed = cv2.normalize(h, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8UC1)
kernel = cv2.getStructuringElement(shape=cv2.MORPH_ELLIPSE, ksize=(3,3))
opened = cv2.morphologyEx(normed, cv2.MORPH_OPEN, kernel)
res = np.hstack((h, normed, opened))
cv2.imwrite("tmp1.png", res)

Теперь мы получаем результат следующим образом (h, normed, opened):

enter image description here

затем найдите контуры и отфильтруйте их.

_, contours, _ = cv2.findContours(opened, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
print(len(contours))

bboxes = []
rboxes = []
cnts = []
dst = img.copy()
for cnt in contours:
    ## Get the stright bounding rect
    bbox = cv2.boundingRect(cnt)
    x,y,w,h = bbox
    if w<30 or h < 30 or w*h < 2000 or w > 500:
        continue

    ## Draw rect
    cv2.rectangle(dst, (x,y), (x+w,y+h), (255,0,0), 1, 16)

    ## Get the rotated rect
    rbox = cv2.minAreaRect(cnt)
    (cx,cy), (w,h), rot_angle = rbox
    print("rot_angle:", rot_angle)  

    ## backup 
    bboxes.append(bbox)
    rboxes.append(rbox)
    cnts.append(cnt)

результат такой:

rot_angle: -2.4540319442749023
rot_angle: -1.8476102352142334

enter image description here

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


Я знаю, что прошло некоторое время с тех пор, как я задал вопрос. Недавно я продолжил тему и решил свою проблему (хотя и не через обнаружение прямоугольника).

изменения

  • использование дерева для укрепления моих контроллеров ("прямоугольники"), как показано ниже.
  • размещен 2 ArUco маркеры на каждом контроллере.

Controller

как это работает

  • преобразовать фрейм в оттенки серого,
  • downsample (для повышения производительности во время обнаружения),
  • выравнивание гистограммы с помощью cv::equalizeHist,
  • найти маркеры с помощью cv::aruco::detectMarkers,
  • коррелируют маркеры (если несколько контроллеров),
  • анализ маркеров (положение и вращение),
  • вычислить результат и применить некоторые исправления ошибок.

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

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

здесь результат обнаружения в ярком среды:

Detection in a bright environment

в более темных условиях:

Detection in a dark environment

и при скрытии одного из маркеров (синяя точка указывает на экстраполированное положение маркера):

Detection of missing markers

сбоев

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

после подхода обнаружения формы я попытался просеять и ORB в сочетании с грубой силой и KNN matcher для извлечения и поиска объектов в кадрах. Оказалось, что одноцветные объекты не обеспечивают много ключевых точек (какой сюрприз). Производительность SIFT была ужасной в любом случае (ок. 10 fps @ 540p). Я нарисовал несколько линий и других фигур на контроллере, что привело к появлению большего количества клавиш. Однако, это не уступило в огромном улучшения.