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

Я ищу процедуру для точного определения углов искаженного прямоугольника с помощью OpenCV в Python.

Я пробовал решать различные предложения с помощью поиска в Google, но через синусоидальное наложение прямой линии (см. изображение с порогом) я, вероятно, не могу обнаружить углы. Я пробовал findContours и HoughLines до сих пор без хороших результатов. К сожалению, я не понимаю C-код от Сюй Бина в как найти положение угла размытия с помощью opencv?

Это мое исходное изображение:

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

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

contours, hierarchy = cv2.findContours(g_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
box = cv2.minAreaRect(contour)
box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
box = np.array(box, dtype="float")
box = perspective.order_points(box)

Я получаю только следующий результат с дополнительным рисунком:

Я думал, что подгонка строк будет хорошим способом решить проблему, но, к сожалению, я не смог заставить HoughLines работать, и после просмотра OpenCV Python - Как реализовать RANSAC для обнаружения прямых линий? RANSAC также сложно применить для моей проблемы.

Любая помощь высоко ценится.


person Robin2505    schedule 06.11.2019    source источник
comment
Возможно, поэтому какая-то предварительная обработка сначала. Преобразуйте свое изображение в оттенки серого и пороговое значение. Используйте некоторую морфологию, чтобы очистить его, если это необходимо. Затем получите свой контур и т. д.   -  person fmw42    schedule 07.11.2019


Ответы (2)


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

Псевдокод

  1. Измените размер, если хотите, но не обязательно
  2. Преобразовать в оттенки серого
  3. Применение размытия или двусторонней фильтрации
  4. Примените порог Оцу, чтобы получить бинарное изображение
  5. Найдите контур, из которого состоит прямоугольник
  6. Примерный контур в виде прямоугольника
  7. Точки сближения - это углы вашего прямоугольника!

Код для этого

  1. Изменение размера:
    Функция принимает новую ширину и высоту, поэтому я просто делаю изображение в 5 раз больше, чем сейчас.
img = cv2.resize(img, (img.shape[0] * 5, img.shape[1] * 5))

измененный размер

  1. Преобразование оттенков серого:
    просто преобразование в оттенки серого из цветового пространства OpenCV по умолчанию BGR.
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

серый

  1. Размытие/двусторонняя фильтрация.
    Вы можете использовать любое количество методов, чтобы еще больше смягчить это изображение, если это необходимо. Может быть, размытие по Гауссу или, как предположила Натанси, двусторонний фильтр, но не нужно и то, и другое.
# choose one, or a different function
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
blurred = cv2.bilateralFilter(gray, 9, 75, 75)

размыто

  1. Порог Оцу
    Используя функцию порога, передайте 0 и 255 в качестве аргументов для порогового значения и максимального значения. Мы передаем 0, потому что мы используем технику пороговой обработки cv2.THRESH_OTSU, которая определяет значение для нас. Это возвращается вместе с самим порогом, но я просто установил его на _, потому что он нам не нужен.
_, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU)

молоть

  1. Поиск контура
    Контуры — это гораздо больше, чем я объясню здесь, не стесняйтесь проверить документы. Нам важно знать, что он возвращает список контуров вместе с иерархией. Нам не нужна иерархия, поэтому она устанавливается на _, и нам нужен только один найденный контур, поэтому мы устанавливаем contour = contours[0].
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour = contours[0]

контур

  1. Примерный контур в виде прямоугольника
    Сначала вычисляем периметр контура. Затем мы аппроксимируем ее функцией cv2.approxPolyDP и сообщаем ей максимальное расстояние между исходной кривой и ее аппроксимацией функцией 0.05 * perimeter. Возможно, вам придется поиграть с десятичной дробью для лучшего приближения.
    approx — это пустой массив формы (num_points, 1, 2), который в данном случае равен (4, 1, 2), потому что он нашел 4 угла прямоугольника.

Не стесняйтесь читать больше в документы.

perimeter = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.05 * perimeter, True)
  1. Найдите свой перекошенный прямоугольник!
    Готово! Вот как можно нарисовать эти точки. Сначала мы рисуем круги, зацикливая их, затем захватывая координаты x и y, а затем рисуем сам прямоугольник.
# drawing points
for point in approx:
    x, y = point[0]
    cv2.circle(img, (x, y), 3, (0, 255, 0), -1)

# drawing skewed rectangle
cv2.drawContours(img, [approx], -1, (0, 255, 0))

готовый продукт

Готовый код

import cv2

img = cv2.imread("rect.png")

img = cv2.resize(img, (img.shape[0] * 5, img.shape[1] * 5))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.bilateralFilter(gray, 9, 75, 75)
_, thresh = cv2.threshold(blurred, 0, 255, cv2.THRESH_OTSU)

contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contour = contours[0]

perimeter = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.05 * perimeter, True)

for point in approx:
    x, y = point[0]
    cv2.circle(img, (x, y), 3, (0, 255, 0), -1)
cv2.drawContours(img, [approx], -1, (0, 255, 0))
person Ani Aggarwal    schedule 14.02.2021

Для обнаружения углов вы можете использовать cv2.goodFeaturesToTrack(). Функция принимает четыре параметра

corners = cv2.goodFeaturesToTrack(image, maxCorners, qualityLevel, minDistance)
  • image — ввод 8-битного или 32-битного полутонового одноканального изображения с плавающей запятой
  • maxCorners - Максимальное количество углов для возврата
  • qualityLevel - Минимально допустимый уровень качества углов от 0 до 1. Все углы ниже уровня качества отбраковываются
  • minDistance - Минимально возможное евклидово расстояние между углами

Теперь, когда мы знаем, как находить углы, нам нужно найти повернутый прямоугольник и применить функцию. Вот подход:


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

Затем мы находим искаженный прямоугольник, находя контуры с помощью cv2.findContours(), а затем получаем повернутую ограничивающую рамку, выделенную зеленым цветом. Мы рисуем эту ограничивающую рамку на маске

Теперь, когда у нас есть маска, мы просто используем cv2.goodFeaturesToTrack(), чтобы найти углы на маске.

Вот результат исходного входного изображения и координаты (x, y) для каждого угла.

Угловые точки

(377.0, 375.0)
(81.0, 344.0)
(400.0, 158.0)
(104.0, 127.0)

Код

import cv2
import numpy as np
import imutils

# Resize image, blur, and Otsu's threshold
image = cv2.imread('1.png')
resize = imutils.resize(image, width=500)
mask = np.zeros(resize.shape, dtype=np.uint8)
gray = cv2.cvtColor(resize, cv2.COLOR_BGR2GRAY)
blur = cv2.bilateralFilter(gray,9,75,75)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Find distorted rectangle contour and draw onto a mask
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
rect = cv2.minAreaRect(cnts[0])
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(resize,[box],0,(36,255,12),2)
cv2.fillPoly(mask, [box], (255,255,255))

# Find corners on the mask
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
corners = cv2.goodFeaturesToTrack(mask, maxCorners=4, qualityLevel=0.5, minDistance=150)

for corner in corners:
    x,y = corner.ravel()
    cv2.circle(resize,(x,y),8,(155,20,255),-1)
    print("({}, {})".format(x,y))

cv2.imshow('resize', resize)
cv2.imshow('thresh', thresh)
cv2.imshow('mask', mask)
cv2.waitKey()
person nathancy    schedule 07.11.2019
comment
Получить желаемый зеленый прямоугольник сложно, поскольку изображение настолько размыто, что при получении бинарного изображения с помощью пороговой обработки или обнаружения края Канни разделение между объектом и фоном неоднозначно. Потенциальные вещи, которые вы можете попробовать, - это применить двусторонний фильтр и поэкспериментировать с обнаружением хитрых краев. - person nathancy; 07.11.2019