Выбор линий из линий Hough
Я использую Hough Lines для обнаружения углов этого изображения. Я планирую найти пересечение линий в углу. Это изображение.
К сожалению, Hough возвращает много строк для каждой строки, которую я ожидаю
Как настроить Hough Lines, чтобы на изображении было только четыре строки, каждая из которых соответствует реальной строке?
4 ответа
Соберите пересечение всей линии
for (int i = 0; i < lines.size(); i++)
{
for (int j = i + 1; j < lines.size(); j++)
{
cv::Point2f pt = computeIntersectionOfTwoLine(lines[i], lines[j]);
if (pt.x >= 0 && pt.y >= 0 && pt.x < image.cols && pt.y < image.rows)
{
corners.push_back(pt);
}
}
}
Вы можете Google алгоритм, чтобы найти пересечение двух линий. Как только вы соберете все точки пересечения, вы можете легко определить минимальный максимум, который даст вам верхнюю левую и нижнюю правую точки. Из этих двух точек вы можете легко получить прямоугольник.
Здесь сортировка 2-точечного массива, чтобы найти четыре угла & http://opencv-code.com/tutorials/automatic-perspective-correction-for-quadrilateral-objects/ См. Эти две ссылки.
Несмотря на то, что преобразование OpenCV действительно могло бы использовать некоторое лучшее Не Максимальное Подавление. Без этого вы получите этот феномен дублирующихся линий. К сожалению, я не знаю простого способа настроить это, кроме как реализовать собственное преобразование. (Что является допустимым вариантом. Хотя преобразование довольно просто)
К счастью, это легко исправить в постобработке:
Для не вероятностного грубого преобразования OpenCv будет возвращать строки в порядке их достоверности, с самой сильной линией первой. Так что просто возьмите первые четыре строки, которые сильно различаются по Ро или Тета.
- Итак, добавьте первую строку, найденную HoughLines в новый список: strong_lines
- для каждой строки, найденной HoughLines:
- проверить, находятся ли его rho и theta близко к любой strong_line (например, rho находится в пределах 50 пикселей, а theta находится в пределах 10° от другой линии)
- если нет, внесите его в список strong_lines
- если вы нашли 4 strong_lines, перерыв
Я реализовал подход, описанный HugoRune, и хотя я хотел бы поделиться своим кодом в качестве примера того, как я это реализовал. Я использовал допуск 5 градусов и 10 пикселей.
strong_lines = np.zeros([4,1,2])
minLineLength = 2
maxLineGap = 10
lines = cv2.HoughLines(edged,1,np.pi/180,10, minLineLength, maxLineGap)
n2 = 0
for n1 in range(0,len(lines)):
for rho,theta in lines[n1]:
if n1 == 0:
strong_lines[n2] = lines[n1]
n2 = n2 + 1
else:
if rho < 0:
rho*=-1
theta-=np.pi
closeness_rho = np.isclose(rho,strong_lines[0:n2,0,0],atol = 10)
closeness_theta = np.isclose(theta,strong_lines[0:n2,0,1],atol = np.pi/36)
closeness = np.all([closeness_rho,closeness_theta],axis=0)
if not any(closeness) and n2 < 4:
strong_lines[n2] = lines[n1]
n2 = n2 + 1
РЕДАКТИРОВАТЬ: код был обновлен, чтобы отразить комментарий относительно отрицательного значения Rho
Вот полное решение, написанное на python 2.7.x с использованием OpenCV 2.4. Он основан на идеях из этой ветки.
Метод: Обнаружить все линии. Предположим, что функция Hough сначала возвращает строки с наивысшим рейтингом. Отфильтруйте линии, чтобы оставить те, которые разделены некоторым минимальным расстоянием и / или углом.
Изображение всех линий Хафа: https://i.ibb.co/t3JFncJ/all-lines.jpg
Отфильтрованные строки: https://i.ibb.co/yQLNxXT/filtered-lines.jpg
Код: http://codepad.org/J57oVIzs
"""
Detect the best 4 lines for a rounded rectangle.
"""
import numpy as np
import cv2
input_image = cv2.imread("image.jpg")
def drawLines(img, lines):
"""
Draw lines on an image
"""
for line in lines:
for rho,theta in line:
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
cv2.line(img, (x1,y1), (x2,y2), (0,0,255), 1)
input_image_grey = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
edged = input_image_grey
rho = 1 # 1 pixel
theta = 1.0*0.017 # 1 degree
threshold = 100
lines = cv2.HoughLines(edged, rho, theta, threshold)
# Fix negative angles
num_lines = lines.shape[1]
for i in range(0, num_lines):
line = lines[0,i,:]
rho = line[0]
theta = line[1]
if rho < 0:
rho *= -1.0
theta -= np.pi
line[0] = rho
line[1] = theta
# Draw all Hough lines in red
img_with_all_lines = np.copy(input_image)
drawLines(img_with_all_lines, lines)
cv2.imshow("Hough lines", img_with_all_lines)
cv2.waitKey()
cv2.imwrite("all_lines.jpg", img_with_all_lines)
# Find 4 lines with unique rho & theta:
num_lines_to_find = 4
filtered_lines = np.zeros([1, num_lines_to_find, 2])
if lines.shape[1] < num_lines_to_find:
print("ERROR: Not enough lines detected!")
# Save the first line
filtered_lines[0,0,:] = lines[0,0,:]
print("Line 1: rho = %.1f theta = %.3f" % (filtered_lines[0,0,0], filtered_lines[0,0,1]))
idx = 1 # Index to store the next unique line
# Initialize all rows the same
for i in range(1,num_lines_to_find):
filtered_lines[0,i,:] = filtered_lines[0,0,:]
# Filter the lines
num_lines = lines.shape[1]
for i in range(0, num_lines):
line = lines[0,i,:]
rho = line[0]
theta = line[1]
# For this line, check which of the existing 4 it is similar to.
closeness_rho = np.isclose(rho, filtered_lines[0,:,0], atol = 10.0) # 10 pixels
closeness_theta = np.isclose(theta, filtered_lines[0,:,1], atol = np.pi/36.0) # 10 degrees
similar_rho = np.any(closeness_rho)
similar_theta = np.any(closeness_theta)
similar = (similar_rho and similar_theta)
if not similar:
print("Found a unique line: %d rho = %.1f theta = %.3f" % (i, rho, theta))
filtered_lines[0,idx,:] = lines[0,i,:]
idx += 1
if idx >= num_lines_to_find:
print("Found %d unique lines!" % (num_lines_to_find))
break
# Draw filtered lines
img_with_filtered_lines = np.copy(input_image)
drawLines(img_with_filtered_lines, filtered_lines)
cv2.imshow("Filtered lines", img_with_filtered_lines)
cv2.waitKey()
cv2.imwrite("filtered_lines.jpg", img_with_filtered_lines)
Вышеупомянутый подход (предложенный @HugoRune's и реализованный @Onamission21) является правильным, но имеет небольшую ошибку. cv2.HoughLines
может возвращать отрицательные значения Ро и Тета до Пи. Обратите внимание, например, что линия (r0,0) очень близка к линии (-r0,pi-epsilon), но они не будут найдены в вышеупомянутом тесте на близость. Я просто лечил отрицательный Rhos, применяя rho*=-1, theta-=pi
до близости расчетов.