Поиск / отслеживание прямоугольника с использованием OpenCV

Что мне нужно

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

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

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

Что я сделал

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

Короткий вывод процедуры:

  1. Возьмите рамку
  2. Преобразовать этот кадр в HSV
  3. Бинаризуйте его (используя цвет, который пользователь выбрал, и пороговые значения с ползунков)
  4. Применить морфовые операции (размывание и расширение)
  5. Найти контуры
  6. Получите наименьшую ориентированную шкатулку для каждого контура
  7. Возьмите самый большой из этих ограничивающих прямоугольников в результате

Как вы можете заметить, я не использую знания о фактической форме бумаги, просто потому, что не знаю, как правильно использовать эту информацию.

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

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

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

и здесь плохой плохой

Вопрос

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

Обновить

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

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

Как получить размер, поворот и положение прямоугольника?
Функция minAreaRect opencv возвращает объект RotatedRect . Этот объект содержит все необходимые мне данные.

Заметка
Поскольку прямоугольник monoхромный, нет возможности различать верхнюю и нижнюю, левую и правую. Это означает, что rotation всегда находится в диапазоне [0, 180] что отлично подходит для моих целей. Отношение двух сторон прямой всегда w:h > 2:1 . Если бы прямоугольник был квадратом, диапазон колебаний изменился бы на [0, 90] , но здесь это можно считать несущественным.

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

Я расскажу о прогрессе.

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

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

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

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

введите описание изображения здесь

 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, нормированный, открытый):

введите описание изображения здесь

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

 _, 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 

введите описание изображения здесь

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

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

изменения

  • Использование дерева для укрепления моих controllerов («прямоугольников»), как показано ниже.
  • Размещено 2 маркера ArUco на каждом controllerе.

контроллер

Как это устроено

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

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

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

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

Обнаружение в яркой среде

в более темной среде:

Обнаружение в темной среде

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

Обнаружение отсутствующих маркеров

Отказы

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

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