霍夫變換原理


---恢復內容開始---

注意事項:

1.由霍夫變換產生的線條的長度是無限的。

2.霍夫變換可很好的解決遮擋問題。(邊緣檢測無法還原物體完整輪廓)

一.原理

坐標變換

將笛卡爾坐標系下的直線方程轉化為極坐標系下的直線方程。

直角坐標表達式(又稱為斜截式):

 

k---斜率;b---y軸截距

極坐標系下的表達式(又稱為法線式):

---直線到原點的距離; ---直線與x軸的夾角

因此笛卡爾坐標系下的一個點,轉化到極坐標系下為一條直線;而笛卡爾坐標系下的一條正弦曲線,轉換至極坐標系下為眾多曲線的交點

因此把笛卡爾坐標系下直線的問題轉化為求極坐標系下交點的問題。

二.程序

2.1 算法流程

1.將圖像轉化為灰度圖,邊緣檢測,轉化為二值化邊緣圖像

2.對圖像進行霍夫變換

3.先使用峰值檢測函數,找到大於閾值的霍夫變換單元

4.將上訴識別出的一組侯選峰,需確定與其相關的起始點和終止點。

5.繪圖

2.1按照霍夫變換的原理,其計算過程大致如下:

import numpy as np
import cv2
def hough_detectline(img):
    thetas=np.deg2rad(np.arange(0,180))    #角度變弧度
    row,cols=img.shape
    diag_len=np.ceil(np.sqrt(row**2+cols**2))   #np.ceil朝正無窮方向取整
    rhos=np.linspace(-(diag_len),diag_len,int(2*diag_len))   #np.linspace生成等差數列
    #默認為50,第三個參數為元素個數,本程序中生成int(2*diag_len)個元素
    cos_t=np.cos(thetas)
    sin_t=np.sin(thetas)
    num_theta=len(thetas)
    #投票
    vote=np.zeros(int(2*diag_len),num_theta,dtype=np.uint64)
    y_inx,x_inx=np.nonzero(img)   #返回img數組中不為0的元素的下標,數組可以為布爾值
    for i in range(len(x_inx)):
        x=x_inx[i]
        y=y_inx[i]
        for j in range(num_theta):
            rho=round(x*cos_t[j]+y*sin_t[j])+diag_len   #round為返回四舍五入值
            if isinstance(rho,int):   #如果rho是int類型,則
                vote[rho,j]+=1
            else:
                vote[int(rho),j]+=1
    return vote,rhos,thetas
image=np.zeros((500,500))
image[10:100,10:100]=np.eye(90)
accumulator,rhos,thetas=hough_detectline(image)
idx=np.argmax(accumulator)   #返回最大值的索引,即得票最多
rho=rhos[int(idx/accumulator.shape[1])]
theta=thetas[idx%accumulator.shape[1]]
k=-np.cos(theta)/np.sin(theta)
b=rho/np.sin(theta)
x=np.float32(np.arange(1,150,2))
y=np.float32(k*x+b)
cv2.imshow("original",image)
cv2.waitKey(0)
for i in range(len(x)-1):
    cv2.circle(image,x[i],y[i],5,(255,0,0),1)   #畫圓
cv2.imshow("hough",image)
cv2.waitKey(0)
print("rho={0:.2f},theta={1:.0f}".format(rho,np.rad2deg(theta)))

該段程序按照霍夫變換規則寫,重在理解各種索引與元素之間的關系。(程序未跑成功,先記錄下來,待深入理解后思考其存在的問題

霍夫變換存在函數,該函數存在於skimage庫中,tranform模塊內,函數為hough_line(img),該函數返回三個值,霍夫變換累加器值、theta和distance,即夾角和距離。

2.2利用skimage庫中,tranform模塊內,函數為hough_line(img)尋找並繪制直線

import skimage.transform as st
import numpy as np
import matplotlib.pyplot as plt

# 構建測試圖片
image = np.zeros((100, 100))  #背景圖
idx = np.arange(25, 75)    #25-74序列
image[idx[::-1], idx] = 255  # 線條\
image[idx, idx] = 255        # 線條/

# hough線變換
h, theta, d = st.hough_line(image)

#生成一個一行三列的窗口(可顯示三張圖片).
fig, (ax0, ax1,ax2) = plt.subplots(1, 3, figsize=(8, 6))
plt.tight_layout()

#顯示原始圖片
ax0.imshow(image, plt.cm.gray)
ax0.set_title('Input image')
ax0.set_axis_off()

#顯示hough變換所得數據
ax1.imshow(np.log(1 + h))
ax1.set_title('Hough transform')
ax1.set_xlabel('Angles (degrees)')
ax1.set_ylabel('Distance (pixels)')
ax1.axis('image')

#顯示檢測出的線條
ax2.imshow(image, 'gray')
row1, col1 = image.shape
for _, angle, dist in zip(*st.hough_line_peaks(h, theta, d)):
    y0 = (dist - 0 * np.cos(angle)) / np.sin(angle)
    y1 = (dist - col1 * np.cos(angle)) / np.sin(angle)
    ax2.plot((0, col1), (y0, y1), '-r')
ax2.axis((0, col1, row1, 0))
ax2.set_title('Detected lines')
ax2.set_axis_off()
plt.show()

 利用cv2庫中函數cv2.HoughLines()函數檢測直線

import cv2
from matplotlib import pyplot as plt
from numpy import *
img=cv2.imread(r"C:\Users\Jimmy\Desktop\qi3.jpg")
plt.subplot(121)
plt.imshow(img)
plt.title('input')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges=cv2.Canny(gray,50,100,apertureSize=3)
lines=cv2.HoughLines(edges,1,np.pi/180,150,5)

lines1=lines[:,0,:]   #3維化為2維
for rho,theta in lines1[:]:
    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,255,0),5)
plt.subplot(122)
plt.imshow(img)
plt.title('output')
plt.show()

該函數返回值為直線的rho和theta

利用優化霍夫變換函數cv2.HoughLinesP()做直線檢測

import cv2
from matplotlib import pyplot as plt
from numpy import *
img=cv2.imread(r"C:\Users\Jimmy\Desktop\qi3.jpg")
plt.subplot(121)
plt.imshow(img)
plt.title('input')
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges=cv2.Canny(gray,50,100,apertureSize=3)
lines=cv2.HoughLinesP(edges,1,np.pi/180,150,minLineLength=60,maxLineGap=10)

lines1=lines[:,0,:]   #3維化為2維
for x1,y1,x2,y2 in lines1[:]:
    cv2.line(img,(x1,y1),(x2,y2),(0,255,0),5)
plt.subplot(122)
plt.imshow(img)
plt.title('output')
plt.show()

該函數直接返回直線的起始點坐標。

 

2.2 霍夫變換提取圓

采用cv2.HoughCircles()函數

輸入參數為(image,method,dp,min_dist,param1,param2,minRadius,maxRadius)

image為需要進行霍夫變換的圖像

method:為檢測方法,一般用CV_HOUGH_GRADIENT,即霍夫梯度法

dp:為檢測內側圓心的累加器圖像的分辨率與輸入圖像之比的倒數。若為1,即累加器和輸入圖像具有相同的分辨率;若為2,則累加器有輸入圖像一半的寬度和高度。

min_dist:兩個圓之間圓心的最小距離。防止重復畫一個圓

param1:默認值100,為傳遞給canny邊緣檢測算子的高閾值,低閾值為其一半

param2:默認值100,表示在檢測階段圓心的累加器閾值,它越小,表示可以檢測到更多不存在的圓,它越大,表示能檢測出來的圓越完美。

minRadius:默認值0,圓半徑的最小值

maxRadius:默認值0,圓半徑的最大值。

import numpy as np
import matplotlib.pyplot as plt
import cv2
img=cv2.imread(r"F:\pycharm\test\iterable\filr\water1.jpg")
cv2.imshow('image',img)
#灰度化
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow('gray',gray)
print(gray.shape)
circles=cv2.HoughCircles(gray,cv2.HOUGH_GRADIENT,
                         1,100,param1=100,param2=31,minRadius=10,maxRadius=100)
#查看返回值類型
print(circles)

circles=np.uint16(np.around(circles))
for i in circles[0,:]:
    cv2.circle(img,(i[0],i[1]),i[2],(0,0,255),2)
    # cv2.circle(gray, (i[0], i[1], i[2]), (0, 0, 255), 2)
cv2.imshow('detected circle',gray)
cv2.waitKey(0)
cv2.destroyAllWindows()

執行上述代碼,可在圖中繪出圓。

2.3在視頻中檢測圓

import cv2
from numpy import *
cap=cv2.VideoCapture(r'F:\pycharm\test\iterable\filr\FLIR0563.mp4')
# fps=cap.get(cv2.CAP_PROP_FPS)
fps=10
size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
    int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
fourcc = cv2.VideoWriter_fourcc('M', 'P', '4', '2')
outVideo = cv2.VideoWriter('F:\\pycharm\\test\\iterable\\filr\\out_new2.avi', fourcc, fps, size)

while(1):
    ret, img = cap.read()
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 灰度圖像
    circles1=cv2.HoughCircles(gray,cv2.HOUGH_GRADIENT,
                             1,100,param1=100,param2=31,minRadius=10,maxRadius=100)
    try:
        circles = circles1[0, :, :]
    except TypeError:
        continue
        # print("NULL")
    else:
        circles = np.uint16(np.around(circles))  # 四舍五入
        for i in circles[:]:
            cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2)  # 畫圓
            # cv2.circle(img, (i[0], i[1]), 2, color=[0, 255, 0], thickness=2)  # 畫圓心

        outVideo.write(img)
#         cv2.imshow('cap', img)
#         if cv2.waitKey(1) & 0xff == ord('q'):
#             break
# cv2.destroyAllWindows()

 

3卡爾坐標系下圓的方程:

由於參數增加,計算復雜度增加

計算步驟:

1.創建累加器空間(提取圓需要3維累加器),初始化累加器

2.對圖像中的每個邊緣點檢測,將可能是同一個圓的值累加

3.搜索局部最大值,檢測圓

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM