---恢復內容開始---
注意事項:
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.搜索局部最大值,檢測圓