Python圖像處理之Hough變換檢測(cè)直線(xiàn)
一、 前言
霍夫變換是一種特征檢測(cè)(feature extraction),被廣泛應(yīng)用在圖像分析(image analysis)、計(jì)算機(jī)視覺(jué)(computer vision)以及數(shù)位影像處理(digital image processing)。由RichardDuda和PeterHart在公元1972年發(fā)明,并稱(chēng)之為廣義霍夫變換(generalizedHoughtransform),廣義霍夫變換和更早前1962年的PaulHough的專(zhuān)利有關(guān)。經(jīng)典的霍夫變換是偵測(cè)圖片中的直線(xiàn),之后,霍夫變換不僅能識(shí)別直線(xiàn),也能夠識(shí)別任何形狀,常見(jiàn)的有圓形、橢圓形。1981年,因?yàn)镈anaH.Ballard的一篇期刊論文"Generalizing the Hough transform to detect arbitrary shapes",讓霍夫變換開(kāi)始流行于計(jì)算機(jī)視覺(jué)界。霍夫變換是用來(lái)辨別找出物件中的特征,例如:線(xiàn)條。他的算法流程大致如下,給定一個(gè)物件、要辨別的形狀的種類(lèi),算法會(huì)在參數(shù)空間(parameter space)中執(zhí)行投票來(lái)決定物體的形狀,而這是由累加空間(accumulator space)里的局部最大值(local maximum)來(lái)決定。
二、Hough 變換
一條直線(xiàn)可由兩個(gè)點(diǎn)A=(x1?,y1?)和B=(x2?,y2?)確定(笛卡爾坐標(biāo))
另一方面,y=kx+b也可以寫(xiě)成關(guān)于(k,q)的函數(shù)表達(dá)式(霍夫空間):
空間變換過(guò)程如下圖:
變換后的空間成為霍夫空間。即:笛卡爾坐標(biāo)系中一條直線(xiàn),對(duì)應(yīng)霍夫空間的一個(gè)點(diǎn)。
反過(guò)來(lái)同樣成立(霍夫空間的一條直線(xiàn),對(duì)應(yīng)笛卡爾坐標(biāo)系的一個(gè)點(diǎn)):
笛卡爾坐標(biāo)系中兩個(gè)點(diǎn)對(duì)應(yīng)霍夫空間兩條線(xiàn):
如果笛卡爾坐標(biāo)系三個(gè)點(diǎn)共線(xiàn),對(duì)應(yīng)的霍夫空間的三條線(xiàn)相交于一點(diǎn)
霍夫變換的后處理的基本方式:選擇由盡可能多直線(xiàn)匯成的點(diǎn)。但是,按照直角坐標(biāo)系表示的話(huà)會(huì)出現(xiàn)下圖的情況:當(dāng)圖像空間中點(diǎn)共的線(xiàn)垂直于x軸時(shí),斜率無(wú)限大,在霍夫空間無(wú)法找到交點(diǎn)。因而,人們最終引入了極坐標(biāo)的表示法。
極坐標(biāo)下的霍夫直線(xiàn)檢測(cè)原理與直角坐標(biāo)系下完全一致,唯一需要重新推導(dǎo)的是與霍夫空間的極坐標(biāo)參數(shù)函數(shù):
化簡(jiǎn)便可得到:
r=xcosθ+ysinθ
如果對(duì)于一個(gè)給定點(diǎn)(x0?,y0?),意味著每一對(duì)(r,θ)代表一條通過(guò)點(diǎn)(xθ?,yθ?)的直線(xiàn)。我們?cè)跇O坐標(biāo)對(duì)極徑極角平面繪出所有通過(guò)它的直線(xiàn), 將得到一條正弦曲線(xiàn). 例如, 對(duì)于給定點(diǎn)(x0?=8和y0?=6) 我們可以繪出下圖 (在平面):
極坐標(biāo)與笛卡爾坐標(biāo)的轉(zhuǎn)換公式,從極坐標(biāo)轉(zhuǎn)換(r,θ)在笛卡爾坐標(biāo)系(x,y):
從笛卡兒坐標(biāo)轉(zhuǎn)換 (x,y) 到極坐標(biāo)(r,θ):
在極坐標(biāo)系下,其實(shí)是一樣的:極坐標(biāo)的點(diǎn)→霍夫空間的直線(xiàn),只不過(guò)霍夫空間不再是[k,q]的參數(shù),而是(r,θ)。
三、直線(xiàn)檢測(cè)
通過(guò)上面的介紹可知,畫(huà)出x−y坐標(biāo)空間中的點(diǎn)在參數(shù)空間中對(duì)應(yīng)的曲線(xiàn),然后計(jì)算參數(shù)空間中曲線(xiàn)的交點(diǎn),就能求得待求的參數(shù)。但還有一個(gè)問(wèn)題,當(dāng)參數(shù)空間中的曲線(xiàn)存在多個(gè)交點(diǎn)時(shí),如何挑選出最有可能的解呢?
具體計(jì)算時(shí),可將參數(shù)空間劃分為所謂的累加單元A(θ,ρ)。如圖下圖所示,對(duì)于x−y平面的每一個(gè)非背景點(diǎn)(xk?,yk?),令 θ等于每個(gè)可取的細(xì)分值,根據(jù)θ=−xk?θ+yk?計(jì)算出對(duì)應(yīng)的ρ值,每計(jì)算出一組 A(θ,ρ),則令A(yù)(θ,ρ)=A(θ,ρ)+1。計(jì)算所有結(jié)果后,找到A(θ,ρ)的峰值對(duì)應(yīng)的θ和ρ,即可檢測(cè)直線(xiàn)。(θmin?,θmax?)和(ρmin?,ρmax?)是期望的參數(shù)范圍:0°≤θ≤180°和−D≤θ≤D, D是圖像對(duì)角線(xiàn)的長(zhǎng)度。 θ和ρ的細(xì)分?jǐn)?shù)量決定了檢測(cè)結(jié)果的精度。
投票過(guò)程可以觀看下面的GIF,
左邊上青色的點(diǎn)代表圖像上的像素點(diǎn),黃色的代表對(duì)各個(gè)點(diǎn)不同角度搜索。右半邊是投票盤(pán),顏色越淺代表票數(shù)越多。
四、代碼實(shí)現(xiàn)
1.hough檢測(cè)
def lines_detector_hough(img,ThetaDim=None, DistStep=None, threshold=None, halfThetaWindowSize=2, halfDistWindowSize=None): ''' :param img: 經(jīng)過(guò)邊緣檢測(cè)得到的二值圖 :param ThetaDim: hough空間中theta軸的刻度數(shù)量(將[0,pi)均分為多少份),反應(yīng)theta軸的粒度,越大粒度越細(xì) :param DistStep: hough空間中dist軸的劃分粒度,即dist軸的最小單位長(zhǎng)度 :param threshold: 投票表決認(rèn)定存在直線(xiàn)的起始閾值 :return: 返回檢測(cè)出的所有直線(xiàn)的參數(shù)(theta,dist)和對(duì)應(yīng)的索引值, ''' row,col= edge.shape if ThetaDim == None: ThetaDim = 90 if DistStep == None: DistStep = 1 # 計(jì)算距離分段數(shù)量 MaxDist = np.sqrt(row ** 2 + col ** 2) DistDim = int(np.ceil(MaxDist / DistStep)) if halfDistWindowSize == None: halfDistWindowSize = int(DistDim /50) # 建立投票 accumulator = np.zeros((ThetaDim, DistDim)) # theta的范圍是[0,pi). 在這里將[0,pi)進(jìn)行了線(xiàn)性映射.類(lèi)似的,也對(duì)Dist軸進(jìn)行了線(xiàn)性映射 # sinTheta = [np.sin(t * np.pi / ThetaDim) for t in range(ThetaDim)] cosTheta = [np.cos(t * np.pi / ThetaDim) for t in range(ThetaDim)] #計(jì)算距離(rho) for i in range(row): for j in range(col): if not edge[i, j] == 0: for k in range(ThetaDim): accumulator[k][int(round((i * cosTheta[k] + j * sinTheta[k]) * DistDim / MaxDist))] += 1 M = accumulator.max() #--------------------------------------- #非極大抑制 if threshold == None: threshold = int(M * 1.369/ 10) result = np.array(np.where(accumulator > threshold)) # 閾值化 #獲得對(duì)應(yīng)的索引值 temp = [[], []] for i in range(result.shape[1]): eight_neiborhood = accumulator[ max(0, result[0, i] - halfThetaWindowSize + 1):min(result[0, i] + halfThetaWindowSize, accumulator.shape[0]), max(0, result[1, i] - halfDistWindowSize + 1):min(result[1, i] + halfDistWindowSize, accumulator.shape[1])] if (accumulator[result[0, i], result[1, i]] >= eight_neiborhood).all(): temp[0].append(result[0, i]) temp[1].append(result[1, i]) #記錄原圖所檢測(cè)的坐標(biāo)點(diǎn)(x,y) result_temp= np.array(temp) #------------------------------------------------------------- result = result_temp.astype(np.float64) result[0] = result[0] * np.pi / ThetaDim result[1] = result[1] * MaxDist / DistDim return result,result_temp
2.畫(huà)直線(xiàn)代碼
def drawLines(lines, edge, color=(255, 0, 0), err=3): ''' :param lines: 檢測(cè)后的直線(xiàn)參數(shù) :param edge: 原圖 :param color: 直線(xiàn)的顏色 :param err:檢測(cè)的可接受的誤差值 :return: 無(wú) ''' if len(edge.shape) == 2: result = np.dstack((edge, edge, edge)) else: result = edge Cos = np.cos(lines[0]) Sin = np.sin(lines[0]) for i in range(edge.shape[0]): for j in range(edge.shape[1]): e = np.abs(lines[1] - i * Cos - j * Sin) if (e < err).any(): result[i, j] = color plt.imshow(result, cmap='gray') plt.axis('off') plt.show()
3.畫(huà)hough空間代碼
def data_img(data): ''' :param data: 直線(xiàn)上含有的點(diǎn)(x,y) :return: 輸出hough空間圖像 ''' fig = plt.figure() # 新建畫(huà)布 ax = axisartist.Subplot(fig, 111) # 使用axisartist.Subplot方法創(chuàng)建一個(gè)繪圖區(qū)對(duì)象ax fig.add_axes(ax) ax.axis[:].set_visible(False) # 隱藏原來(lái)的實(shí)線(xiàn)矩形 ax.axis["x"] = ax.new_floating_axis(0, 0, axis_direction="bottom") # 添加x軸 ax.axis["y"] = ax.new_floating_axis(1, 0, axis_direction="bottom") # 添加y軸 ax.axis["x"].set_axisline_style("->", size=1.0) # 給x坐標(biāo)軸加箭頭 ax.axis["y"].set_axisline_style("->", size=1.0) # 給y坐標(biāo)軸加箭頭 t = np.arange(-np.pi / 2, np.pi / 2, 0.1) ax.annotate(text='x', xy=(2 * math.pi, 0), xytext=(2 * math.pi, 0.1)) # 標(biāo)注x軸 ax.annotate(text='y', xy=(0, 1.0), xytext=(-0.5, 1.0)) # 標(biāo)注y軸 for i in range(data.shape[1]): rho = data[0][i] * np.cos(t) + data[1][i] * np.sin(t) plt.plot(t, rho) plt.show()
4.檢測(cè)結(jié)果
以上就是Python圖像處理之Hough變換檢測(cè)直線(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于Python檢測(cè)直線(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
淺談Keras的Sequential與PyTorch的Sequential的區(qū)別
這篇文章主要介紹了淺談Keras的Sequential與PyTorch的Sequential的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06django基礎(chǔ)之?dāng)?shù)據(jù)庫(kù)操作方法(詳解)
下面小編就為大家?guī)?lái)一篇django基礎(chǔ)之?dāng)?shù)據(jù)庫(kù)操作方法(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05python中input()與raw_input()的區(qū)別分析
這篇文章主要介紹了python中input()與raw_input()的區(qū)別,需要的朋友可以參考下2016-02-02Python編程實(shí)戰(zhàn)之Oracle數(shù)據(jù)庫(kù)操作示例
這篇文章主要介紹了Python編程實(shí)戰(zhàn)之Oracle數(shù)據(jù)庫(kù)操作,結(jié)合具體實(shí)例形式分析了Python的Oracle數(shù)據(jù)庫(kù)模塊cx_Oracle包安裝、Oracle連接及操作技巧,需要的朋友可以參考下2017-06-06python Socket網(wǎng)絡(luò)編程實(shí)現(xiàn)C/S模式和P2P
這篇文章主要介紹了python Socket網(wǎng)絡(luò)編程實(shí)現(xiàn)C/S模式和P2P,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Python中實(shí)現(xiàn)定時(shí)任務(wù)常見(jiàn)的幾種方式
在Python中,實(shí)現(xiàn)定時(shí)任務(wù)是一個(gè)常見(jiàn)的需求,無(wú)論是在自動(dòng)化腳本、數(shù)據(jù)處理、系統(tǒng)監(jiān)控還是其他許多應(yīng)用場(chǎng)景中,Python提供了多種方法來(lái)實(shí)現(xiàn)定時(shí)任務(wù),包括使用標(biāo)準(zhǔn)庫(kù)、第三方庫(kù)以及系統(tǒng)級(jí)別的工具,本文將詳細(xì)介紹幾種常見(jiàn)的Python定時(shí)任務(wù)實(shí)現(xiàn)方式2024-08-08python將MongoDB里的ObjectId轉(zhuǎn)換為時(shí)間戳的方法
這篇文章主要介紹了python將MongoDB里的ObjectId轉(zhuǎn)換為時(shí)間戳的方法,涉及Python操作MongoDB及字符串轉(zhuǎn)換的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03