2015-11-21 2 views
0

Я использую OpenCV HoughlinesP для поиска горизонтальных и вертикальных линий. Он не находит никаких строк большую часть времени. Даже когда он находит строки, он даже не близок к фактическому изображению.Python OpenCV HoughLinesP не удалось обнаружить линии

import cv2 
import numpy as np 

img = cv2.imread('image_with_edges.jpg') 
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 


flag,b = cv2.threshold(gray,0,255,cv2.THRESH_OTSU) 

element = cv2.getStructuringElement(cv2.MORPH_CROSS,(1,1)) 
cv2.erode(b,element) 

edges = cv2.Canny(b,10,100,apertureSize = 3) 

lines = cv2.HoughLinesP(edges,1,np.pi/2,275, minLineLength = 100, maxLineGap = 200)[0].tolist() 

for x1,y1,x2,y2 in lines: 
    for index, (x3,y3,x4,y4) in enumerate(lines): 

    if y1==y2 and y3==y4: # Horizontal Lines 
     diff = abs(y1-y3) 
    elif x1==x2 and x3==x4: # Vertical Lines 
     diff = abs(x1-x3) 
    else: 
     diff = 0 

    if diff < 10 and diff is not 0: 
     del lines[index] 

    gridsize = (len(lines) - 2)/2 

    cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2) 
    cv2.imwrite('houghlines3.jpg',img) 

входного изображения: input image

Вывод изображения: (см красная линия): enter image description here

@ljetibo Попробуйте это: c_6.jpg

ответ

7

Там совсем немного неправильно здесь поэтому я только начинаю с самого начала.

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

В руководстве упоминается, что

cv2.threshold (SRC, молоти, MAXVAL, тип [, ДСТ]) → RetVal, ДСТ

специальное значение THRESH_OTSU может быть объединен с одним из выше значений. В этом случае функция определяет оптимальное пороговое значение с использованием алгоритма Otsu и использует его вместо заданного thresh.

Я знаю, это немного сбивает с толку, потому что вы не actully объединить THRESH_OTSU с любым из других методов (THRESH_BINARY и т.д ...), к сожалению, руководство может быть. Фактически этот метод предполагает, что существует «передний план» и «фон», которые следуют бимодальной гистограмме, а затем применяют THRESH_BINARY.

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

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

Image after OP's thresholding

После этого момента, не трудно догадаться, что идет не так. Но давайте идти дальше, что вы пытаетесь достичь, я полагаю, что-то вроде этого:

flag,b = cv2.threshold(gray,160,255,cv2.THRESH_BINARY) 

Image with manually guessed threshold.

Тогда вы идете, и попытаться подорвать имидж. Я не уверен, почему вы это делаете, было ваше намерение «смело» использовать линии или было намерение удалить шум. В любом случае вы никогда не назначали результат эрозии.Numpy массивы, что является способом представлены изображения, изменчивы, но это не так, синтаксис работы:

cv2.erode(src, kernel, [optionalOptions]) → dst 

Таким образом, вы должны написать:

b = cv2.erode(b,element) 

Хорошо, теперь для элемента и как эрозионные работы. Эрозия перетаскивает ядро ​​над изображением. Ядро - простая матрица с 1 и 0 в ней. Один из элементов этой матрицы, обычно центрированный один, называется якорем. Якорь - это элемент, который будет заменен в конце операции. Когда вы создали

cv2.getStructuringElement(cv2.MORPH_CROSS, (1, 1)) 

то, что вы создали, фактически является матрицей 1x1 (1 столбец, 1 строка). Это делает эрозию совершенно бесполезной.

Что такое эрозия, во-первых, извлекает все значения яркости пикселей из исходного изображения, где элемент ядра, перекрывающий сегмент изображения, имеет «1». Затем он находит минимальное значение полученных пикселей и заменяет якорь этим значением.

Что это значит, в вашем случае, вы перетаскиваете матрицу [1] на изображение, сравниваете, если яркость пикселя исходного изображения больше, равна или меньше самого себя, а затем вы заменяете его самим.

Если вы намеревались удалить «шум», то, вероятно, лучше использовать прямоугольное ядро ​​над изображением. Подумайте об этом так: «шум» - это то, что «не вписывается» в окружение. Поэтому, если вы сравните свой центральный пиксель с его окружением, и вы обнаружите, что он не подходит, это, скорее всего, шум.

Кроме того, я сказал, что он заменяет якорь минимальным значением, полученным ядром. Численно минимальное значение равно 0, что совпадение показывает, как черный изображен на изображении. Это означает, что в вашем случае преимущественно белого изображения эрозия «раздувает» черные пиксели. Erosion заменит 255 значащих белых пикселей на 0 значных черных пикселей, если они находятся в пределах досягаемости ядра. В любом случае он не должен иметь форму (1,1), когда-либо.

>>> cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) 
array([[0, 1, 0], 
     [1, 1, 1], 
     [0, 1, 0]], dtype=uint8) 

Если мы разрушим второе изображение прямоугольным ядром 3x3, мы получим изображение ниже.

Eroded threshed image.

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

Canny edges

Хорошо, теперь мы ищем ТОЧНО вертикальные и РОВНО горизонтальных линий ТОЛЬКО. Конечно, нет таких линий, кроме от меридиана на левой части изображения (в том, что, как это называется?) И конец изображение, которое вы получите после того, как вы сделали это право было бы это:

enter image description here

сейчас так как вы никогда не описывали свою точную идею, и я полагаю, что вам нужны параллели и меридианы, у вас будет больше удачи на картах с меньшим масштабом, потому что это не линии для начала, они кривые. Кроме того, есть ли какая-то конкретная причина для получения вероятности Hough? «Регулярный» Хью не хватает?

Извините за слишком длинный пост, надеюсь, что это поможет.


Текст был добавлен в качестве запроса для разъяснения с ОП 24 ноября. потому что нет никакого способа подогнать ответ в ограниченный комментарий с символом.

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

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

  1. Использовать алгоритм RANSAC. Разработайте формулу, описывающую характер длинной. и лат. линий в зависимости от рассматриваемой карты. То есть кривые широты будут почти идеальными прямыми линиями на карте, когда вы приближаетесь к экватору, а экватор - совершенно прямая линия, но будет очень изогнутым, напоминающим сегменты круга, когда вы находитесь на высоких широтах (около полюсов). SciPy уже имеет RANSAC, реализованный как класс, все, что вам нужно сделать, это найти и программно определить модель, которую вы хотите попробовать подогнать к кривым. Конечно, есть всегда полезный текст 4dummies here. Это проще всего, потому что все, что вам нужно сделать, это математика.
  2. Немного сложнее было бы создать прямоугольную сетку, а затем попытаться использовать cv findHomography, чтобы деформировать сетку на место на изображении. Для различных геометрических преобразований вы можете сделать сетку, которую вы можете проверить OpenCv manual. Это своего рода хакерский подход и может работать хуже, чем 1. потому что он зависит от того, что вы можете воссоздать сетку с достаточным количеством деталей и объектов на ней, которые могут идентифицировать структуры на изображении, который вы пытаетесь деформировать его. Это требует, чтобы вы выполняли аналогичную математику до 1. и просто немного кодирования, чтобы составить конечное решение из нескольких различных функций.
  3. На самом деле это сделать. Существуют математически опрятные способы описания кривых как список касательных линий на кривой. Вы можете попытаться подобрать кучу более коротких HoughLines к вашему сегменту изображения или изображения, а затем попытаться сгруппировать все найденные строки и определить, предположив, что они касаются кривой, если они действительно следуют кривой желаемой формы или они случайны. См. this paper по этому вопросу. Из всех подходов это один из самых сложных, потому что он требует довольно много соло-кодирования и некоторой математики о методе.

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

Чтобы показать вам, что вы можете сделать только с преобразованием Хаф проверить ниже:

import cv2 
import numpy as np 

def draw_lines(hough, image, nlines): 
    n_x, n_y=image.shape 
    #convert to color image so that you can see the lines 
    draw_im = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) 

    for (rho, theta) in hough[0][:nlines]: 
     try: 
     x0 = np.cos(theta)*rho 
     y0 = np.sin(theta)*rho 
     pt1 = (int(x0 + (n_x+n_y)*(-np.sin(theta))), 
       int(y0 + (n_x+n_y)*np.cos(theta))) 
     pt2 = (int(x0 - (n_x+n_y)*(-np.sin(theta))), 
       int(y0 - (n_x+n_y)*np.cos(theta))) 
     alph = np.arctan((pt2[1]-pt1[1])/(pt2[0]-pt1[0])) 
     alphdeg = alph*180/np.pi 
     #OpenCv uses weird angle system, see: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html 
     if abs(np.cos(alph - 180)) > 0.8: #0.995: 
      cv2.line(draw_im, pt1, pt2, (255,0,0), 2) 
     if rho>0 and abs(np.cos(alphdeg - 90)) > 0.7: 
      cv2.line(draw_im, pt1, pt2, (0,0,255), 2)  
     except: 
     pass 
    cv2.imwrite("/home/dino/Desktop/3HoughLines.png", draw_im, 
      [cv2.IMWRITE_PNG_COMPRESSION, 12]) 

img = cv2.imread('a.jpg') 
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 

flag,b = cv2.threshold(gray,160,255,cv2.THRESH_BINARY) 
cv2.imwrite("1tresh.jpg", b) 

element = np.ones((3,3)) 
b = cv2.erode(b,element) 
cv2.imwrite("2erodedtresh.jpg", b) 

edges = cv2.Canny(b,10,100,apertureSize = 3) 
cv2.imwrite("3Canny.jpg", edges) 

hough = cv2.HoughLines(edges, 1, np.pi/180, 200) 
draw_lines(hough, b, 100) 

Как видно из сильфона изображения, прямые линии только долготу. Широта не так прямая, поэтому для каждой широты у вас есть несколько обнаруженных линий, которые ведут себя как касательные на линии. Синие нарисованные линии рисуются if abs(np.cos(alph - 180)) > 0.8:, тогда как красные линии тянутся на rho>0 and abs(np.cos(alphdeg - 90)) > 0.7. Обратите особое внимание при сравнении исходного изображения с изображением с нарисованными на нем линиями. Сходство сверхъестественное (хе-хе, получи его?), Но поскольку они не линии, многие из них только похожи на мусор.(особенно, что самая высокая обнаруженная линия широты, которая кажется слишком «угловой», но на самом деле эти линии делают идеальную касательную к широтной линии в ее самой толстой точке, точно так же, как требует алгоритм hough). Признайте, что существует ограничения для обнаружения кривых с помощью алгоритма обнаружения линии

Best possible detected lines.

+0

спасибо за большие объяснения. Ты жжешь! – user914425

+0

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

+0

@ user914425; проверьте добавленный текст в нижней части столбца ниже горизонтальной линии. Нет места, чтобы сделать это в комментариях. – ljetibo

Смежные вопросы