Как мне преобразовать значения аккумулятора [Преобразование Хафа] обратно в линию на холсте?

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

from math import hypot, pi, cos, sin
from PIL import Image
import numpy as np
import cv2 as cv
import math

def hough(img):

    thetaAxisSize = 460 #Width of the hough space image
    rAxisSize = 360 #Height of the hough space image
    rAxisSize= int(rAxisSize/2)*2 #we make sure that this number is even

    img = im.load()
    w, h = im.size

    houghed_img = Image.new("L", (thetaAxisSize, rAxisSize), 0) #legt Bildgroesse fest
    pixel_houghed_img = houghed_img.load()

    max_radius = hypot(w, h)
    d_theta = pi / thetaAxisSize
    d_rho = max_radius / (rAxisSize/2) 


    #Accumulator
    for x in range(0, w):
        for y in range(0, h):

            treshold = 255
            col = img[x, y]
            if col >= treshold: #determines for each pixel at (x,y) if there is enough evidence of a straight line at that pixel.

                for vx in range(0, thetaAxisSize):
                    theta = d_theta * vx #angle between the x axis and the line connecting the origin with that closest point.
                    rho = x*cos(theta) + y*sin(theta) #distance from the origin to the closest point on the straight line
                    vy = rAxisSize/2 + int(rho/d_rho+0.5) #Berechne Y-Werte im hough space image
                    pixel_houghed_img[vx, vy] += 1 #voting

    return houghed_imgcode here

А затем вызовите функцию следующим образом:

im = Image.open("img3.pgm").convert("L")
houghed_img = hough(im)
houghed_img.save("ho.bmp")
houghed_img.show()

Результат вроде нормальный:

Аккумулятор

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

Поэтому я сначала ищу самые высокие значения в массиве пикселей и беру значения X и Y максимальных значений, которые я нашел. Насколько я понимаю, значения X и Y - это мои ро и тета. Я нахожу максимумы так:

def find_maxima(houghed_img):

    w, h = houghed_img.size
    max_radius = hypot(w, h)
    pixel_houghed_img = houghed_img.load()
    max1, max2, max3 = 0, 0, 0
    x1position, x2position, x3position = 0, 0, 0
    y1position, y2position, y3position = 0, 0, 0
    rho1, rho2, rho3 = 0, 0, 0
    theta1, theta2, theta3 = 0, 0, 0

    for x in range(1, w):
        for y in range(1, h):
            value = pixel_houghed_img[x, y]

            if(value > max1):

                max1 = value
                x1position = x
                y1position = y
                rho1 = x
                theta1 = y

            elif(value > max2):

                max2 = value
                x2position = x
                x3position = y
                rho2 = x
                theta2 = y

            elif(value > max3):

                max3 = value
                x3position = x
                y3position = y
                rho3 = x
                theta3 = y

    print('max', max1, max2, max3)
    print('rho', rho1, rho2, rho3)
    print('theta', theta1, theta2, theta3)

    # Results of the print:
    # ('max', 255, 255, 255)
    # ('rho', 1, 1, 1)
    # ('theta', 183, 184, 186)
    return rho1, theta1, rho2, theta2, rho3, theta3    

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

img_copy = np.ones(im.size)

rho1, theta1, rho2, theta2, rho3, theta3 = find_maxima(houghed_img)

a1 = math.cos(theta1)
b1 = math.sin(theta1)
x01 = a1 * rho1
y01 = b1 * rho1
pt11 = (int(x01 + 1000*(-b1)), int(y01 + 1000*(a1)))
pt21 = (int(x01 - 1000*(-b1)), int(y01 - 1000*(a1)))
cv.line(img_copy, pt11, pt21, (0,0,255), 3, cv.LINE_AA)

a2 = math.cos(theta2)
b2 = math.sin(theta2)
x02 = a2 * rho2
y02 = b2 * rho2
pt12 = (int(x02 + 1000*(-b2)), int(y02 + 1000*(a2)))
pt22 = (int(x02 - 1000*(-b2)), int(y02 - 1000*(a2)))
cv.line(img_copy, pt12, pt22, (0,0,255), 3, cv.LINE_AA)

a3 = math.cos(theta3)
b3 = math.sin(theta3)
x03 = a3 * rho3
y03 = b3 * rho3
pt13 = (int(x03 + 1000*(-b3)), int(y03 + 1000*(a3)))
pt23 = (int(x03 - 1000*(-b3)), int(y03 - 1000*(a3)))
cv.line(img_copy, pt13, pt23, (0,0,255), 3, cv.LINE_AA)

cv.imshow('lines', img_copy)
cv.waitKey(0)
cv.destroyAllWindows()

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

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

Поэтому я предполагаю, что я либо делаю что-то не так, когда объявляю значения rho и theta в функции find_maxima (), что означает, что с этим что-то не так:

   max1 = value
   x1position = x
   y1position = y
   rho1 = x
   theta1 = y

ИЛИ что я делаю что-то не так при переводе значений rho и theta обратно в строку.

Буду очень признателен, если кто-нибудь сможет мне с этим помочь!

Edit1: В качестве запроса найдите исходное изображение, где я хочу найти строки снизу:

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

Edit2: Благодаря участию @Alessandro Jacopson и @Cris Luegno я смог внести некоторые изменения, которые определенно вселили в меня надежду!

В моем def hough (img): я устанавливал порог 255, что означает, что я голосовал только за белые пиксели, что неверно, поскольку я хочу смотреть на черные пиксели, поскольку эти пиксели будут указывать на линии, а не на белый фон моего изображения. Итак, расчет аккумулятора в def hough (img): теперь выглядит так:

#Accumulator
    for x in range(0, w):
        for y in range(0, h):

            treshold = 0
            col = img[x, y]
            if col <= treshold: #determines for each pixel at (x,y) if there is enough evidence of a straight line at that pixel.

                for vx in range(0, thetaAxisSize):
                    theta = d_theta * vx #angle between the x axis and the line connecting the origin with that closest point.
                    rho = x*cos(theta) + y*sin(theta) #distance from the origin to the closest point on the straight line
                    vy = rAxisSize/2 + int(rho/d_rho+0.5) #Berechne Y-Werte im hough space image
                    pixel_houghed_img[vx, vy] += 1 #voting

    return houghed_img

Это приводит к следующему Accumulator и следующим значениям rho и thea при использовании функции find_maxima ():

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

# Results of the prints: (now top 8 instead of top 3)
# ('max', 155, 144, 142, 119, 119, 104, 103, 98)
# ('rho', 120, 264, 157, 121, 119, 198, 197, 197)
# ('theta', 416, 31, 458, 414, 417, 288, 291, 292)

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

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

Так что эти результаты намного лучше, но что-то, похоже, все еще не так. У меня есть сильное подозрение, что здесь все еще что-то не так:

for x in range(1, w):
    for y in range(1, h):
        value = pixel_houghed_img[x, y]

        if(value > max1):

            max1 = value
            x1position = x
            y1position = y
            rho1 = value
            theta1 = x

Здесь я устанавливаю rho и theta равными [0 ... w] соответственно [0 ... h]. Я думаю, что это неправильно, поскольку в заданном пространстве значения X и почему Y не равны 0, 1,2,3 ... поскольку мы находимся в другом пространстве. Итак, я предполагаю, что мне нужно умножить X и Y на что-то, чтобы вернуть их в некоторое пространство. Но это всего лишь предположение, может вы, ребята, придумаете что-нибудь еще?

Еще раз большое спасибо Алессандро и Крису за то, что помогли мне здесь!

Edit3: Рабочий код, спасибо @Cris Luengo

from math import hypot, pi, cos, sin
from PIL import Image
import numpy as np
import cv2 as cv
import math

def hough(img):

    img = im.load()
    w, h = im.size

    thetaAxisSize = w #Width of the hough space image
    rAxisSize = h #Height of the hough space image
    rAxisSize= int(rAxisSize/2)*2 #we make sure that this number is even

    houghed_img = Image.new("L", (thetaAxisSize, rAxisSize), 0) #legt Bildgroesse fest
    pixel_houghed_img = houghed_img.load()

    max_radius = hypot(w, h)
    d_theta = pi / thetaAxisSize
    d_rho = max_radius / (rAxisSize/2) 

    #Accumulator
    for x in range(0, w):
        for y in range(0, h):

            treshold = 0
            col = img[x, y]
            if col <= treshold: #determines for each pixel at (x,y) if there is enough evidence of a straight line at that pixel.

                for vx in range(0, thetaAxisSize):
                    theta = d_theta * vx #angle between the x axis and the line connecting the origin with that closest point.
                    rho = x*cos(theta) + y*sin(theta) #distance from the origin to the closest point on the straight line
                    vy = rAxisSize/2 + int(rho/d_rho+0.5) #Berechne Y-Werte im hough space image
                    pixel_houghed_img[vx, vy] += 1 #voting

    return houghed_img, rAxisSize, d_rho, d_theta

def find_maxima(houghed_img, rAxisSize, d_rho, d_theta):

    w, h = houghed_img.size
    pixel_houghed_img = houghed_img.load()
    maxNumbers = 9
    ignoreRadius = 10
    maxima = [0] * maxNumbers
    rhos = [0] * maxNumbers
    thetas = [0] * maxNumbers

    for u in range(0, maxNumbers):

        print('u:', u)
        value = 0 
        xposition = 0
        yposition = 0

        #find maxima in the image
        for x in range(0, w):
            for y in range(0, h):

                if(pixel_houghed_img[x,y] > value):

                    value = pixel_houghed_img[x, y]
                    xposition = x
                    yposition = y

        #Save Maxima, rhos and thetas
        maxima[u] = value
        rhos[u] = (yposition - rAxisSize/2) * d_rho
        thetas[u] = xposition * d_theta

        pixel_houghed_img[xposition, yposition] = 0

        #Delete the values around the found maxima
        radius = ignoreRadius

        for vx2 in range (-radius, radius): #checks the values around the center
            for vy2 in range (-radius, radius): #checks the values around the center
                x2 = xposition + vx2 #sets the spectated position on the shifted value 
                y2 = yposition + vy2

                if not(x2 < 0 or x2 >= w):
                    if not(y2 < 0 or y2 >= h):

                        pixel_houghed_img[x2, y2] = 0
                        print(pixel_houghed_img[x2, y2])

    print('max', maxima)
    print('rho', rhos)
    print('theta', thetas)

    return maxima, rhos, thetas

im = Image.open("img5.pgm").convert("L")
houghed_img, rAxisSize, d_rho, d_theta = hough(im)
houghed_img.save("houghspace.bmp")
houghed_img.show()

img_copy = np.ones(im.size)

maxima, rhos, thetas = find_maxima(houghed_img, rAxisSize, d_rho, d_theta)

for t in range(0, len(maxima)):
    a = math.cos(thetas[t])
    b = math.sin(thetas[t])
    x = a * rhos[t]
    y = b * rhos[t]
    pt1 = (int(x + 1000*(-b)), int(y + 1000*(a)))
    pt2 = (int(x - 1000*(-b)), int(y - 1000*(a)))
    cv.line(img_copy, pt1, pt2, (0,0,255), 3, cv.LINE_AA)

cv.imshow('lines', img_copy)
cv.waitKey(0)
cv.destroyAllWindows()

Исходное изображение:

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

Аккумулятор:

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

Успешное обнаружение линии:

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


person Leonard Michalas    schedule 24.06.2018    source источник
comment
Ваши 3 максимума со значением 255 выглядят подозрительно. Может у вас аккумулятор типа uint8? Вы насыщаете аккумуляторные бункеры?   -  person Cris Luengo    schedule 25.06.2018
comment
Привет, Крис, я знал, что снова могу на тебя рассчитывать! Большое тебе спасибо! Вы заставили меня задуматься своим вводом, и я думаю, что обнаружил ошибку в своем коде относительно 255 значений. Однако что-то по-прежнему не так ... См. Мой обновленный вопрос.   -  person Leonard Michalas    schedule 25.06.2018


Ответы (2)


Эта часть вашего кода кажется неправильной:

max1 = value
x1position = x
y1position = y
rho1 = value
theta1 = x

Если x и y - две координаты в пространстве параметров, они будут соответствовать rho и theta. Устанавливать rho равным значению не имеет смысла. Я также не знаю, почему вы храните x1position и y1position, поскольку вы не используете эти переменные.

Затем вам нужно преобразовать эти координаты обратно в фактические значения rho и theta, инвертируя преобразование, которое вы выполняете при написании:

theta = d_theta * vx #angle between the x axis and the line connecting the origin with that closest point.
rho = x*cos(theta) + y*sin(theta) #distance from the origin to the closest point on the straight line
vy = rAxisSize/2 + int(rho/d_rho+0.5) #Berechne Y-Werte im hough space image

Обратное будет:

rho = (y - rAxisSize/2) * d_rho
theta = x * d_theta
person Cris Luengo    schedule 26.06.2018
comment
Большое спасибо, Крис! Результат все еще не идеален, но теперь он правильно определяет некоторые линии! - person Leonard Michalas; 26.06.2018
comment
@Leonard: Да, чтобы улучшить поиск по максимальным значениям, сначала найдите одно наибольшее значение, очистите часть пространства Хафа вокруг него, затем найдите следующее наибольшее значение и т. Д. Таким образом вы предотвратите множественные совпадения для одной строки. - person Cris Luengo; 26.06.2018
comment
Большое спасибо! Вы так сильно мне помогли, я очень ценю это. Теперь он работает отлично! Я добавил свой оптимизированный и рабочий код в конец своего вопроса для вашего интереса и, возможно, для будущих поколений, которые заинтересованы в проведении серьезных преобразований. - person Leonard Michalas; 26.06.2018

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

Затем вы написали, что:

# Results of the print:
# ('max', 255, 255, 255)
# ('rho', 1, 1, 1)
# ('theta', 183, 184, 186)

так что rho одинаково для трех строк, а theta не сильно отличается от 183 до 186; так что три линии почти равны друг другу, и этот факт не зависит от метода, который вы используете, чтобы получить уравнение линии и нарисовать его.

Согласно руководству Hough Line Transform, оно Мне кажется, что ваш метод нахождения двух точек на прямой верен. Это то, что предлагает учебник, и мне кажется, что это эквивалентно вашему коду:

lines = cv2.HoughLines(edges,1,np.pi/180,200)
for rho,theta in lines[0]:
    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),2)

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

Для простоты посмотрите, что происходит только в одном измерении. Ожидается, что алгоритм поиска пиков найдет три местоположения пика в x=-1, x=0 и x=1, а значения пиков должны быть близки к 0,25, 0,5 и 1.

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

import numpy as np
import matplotlib.pyplot as plt


x = np.linspace(-2, 2, 1000)
y = np.exp(-(x-1)**2/0.01)+.5*np.exp(-(x)**2/0.01)+.25*np.exp(-(x+1)**2/0.01)

max1, max2, max3 = 0, 0, 0
m1 = np.zeros(1000)
m2 = np.zeros(1000)
m3 = np.zeros(1000)
x1position, x2position, x3position = 0, 0, 0
for i in range(0,1000):
    value = y[i]

    if(value > max1):

        max1 = value
        x1position = x[i]

    elif(value > max2):

        max2 = value
        x2position = x[i]

    elif(value > max3):

        max3 = value
        x3position = x[i]

    m1[i] = max1
    m2[i] = max2
    m3[i] = max3



print('xposition',x1position, x2position, x3position )
print('max', max1, max2, max3)

plt.figure()
plt.subplot(4,1,1)
plt.plot(x, y)
plt.ylabel('$y$')
plt.subplot(4,1,2)
plt.plot(x, m1)
plt.ylabel('$max_1$')
plt.subplot(4,1,3)
plt.plot(x, m2)
plt.ylabel('$max_2$')
plt.subplot(4,1,4)
plt.plot(x, m3)
plt.xlabel('$x$')
plt.ylabel('$max_3$')
plt.show()

выход

('xposition', 0.99899899899899891, 1.0030030030030028, 1.0070070070070072)
('max', 0.99989980471948192, 0.99909860379824966, 0.99510221871862647)

и это не то, что ожидалось.

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

Чтобы обнаружить несколько пиков в 2D-поле, вам следует взглянуть, например, на этот Обнаружение пиков в 2D-массив

person Alessandro Jacopson    schedule 24.06.2018