圖像的細(xì)化主要是針對(duì)二值圖而言
所謂骨架,可以理解為圖像的中軸,,,一個(gè)長(zhǎng)方形的骨架,是它的長(zhǎng)方向上的中軸線,
圓的骨架是它的圓心,,直線的骨架是它自身,孤立點(diǎn)的骨架也是自身,。
骨架的獲取主要有兩種方法:
(1)基于烈火模擬
設(shè)想在同一時(shí)刻,,將目標(biāo)的邊緣線都點(diǎn)燃,火的前沿以勻速向內(nèi)部蔓延,,當(dāng)前沿相交時(shí)火焰熄滅,,
火焰熄滅點(diǎn)的結(jié)合就是骨架。
(2)基于最大圓盤(pán)
目標(biāo)的骨架是由目標(biāo)內(nèi)所有內(nèi)切圓盤(pán)的圓心組成
我們來(lái)看看典型的圖形的骨架(用粗線表示)
細(xì)化的算法有很多種,,但比較常用的算法是查表法
細(xì)化是從原來(lái)的圖中去掉一些點(diǎn),,但仍要保持原來(lái)的形狀。
實(shí)際上是保持原圖的骨架,。
判斷一個(gè)點(diǎn)是否能去掉是以8個(gè)相鄰點(diǎn)(八連通)的情況來(lái)作為判據(jù)的,,具體判據(jù)為:
1,內(nèi)部點(diǎn)不能刪除
2,,鼓勵(lì)點(diǎn)不能刪除
3,,直線端點(diǎn)不能刪除
4,如果P是邊界點(diǎn),,去掉P后,,如果連通分量不增加,則P可刪除
看看上面那些點(diǎn),。
第一個(gè)點(diǎn)不能去除,,因?yàn)樗莾?nèi)部點(diǎn)
第二個(gè)點(diǎn)不能去除,,它也是內(nèi)部點(diǎn)
第三個(gè)點(diǎn)不能去除,刪除后會(huì)使原來(lái)相連的部分?jǐn)嚅_(kāi)
第四個(gè)點(diǎn)可以去除,,這個(gè)點(diǎn)不是骨架
第五個(gè)點(diǎn)不可以去除,,它是直線的端點(diǎn)
第六個(gè)點(diǎn)不可以去除,它是直線的端點(diǎn)
對(duì)于所有的這樣的點(diǎn),,我們可以做出一張表,,來(lái)判斷這樣的點(diǎn)能不能刪除
我們對(duì)于黑色的像素點(diǎn),對(duì)于它周?chē)?個(gè)點(diǎn),,我們賦予不同的價(jià)值,,
若周?chē)澈谏覀冋J(rèn)為其價(jià)值為0,,為白色則取九宮格中對(duì)應(yīng)的價(jià)值
對(duì)于前面那幅圖中第一個(gè)點(diǎn),,也就是內(nèi)部點(diǎn),它周?chē)狞c(diǎn)都是黑色,,所以他的總價(jià)值是0,,對(duì)應(yīng)于索引表的第一項(xiàng)
前面那幅圖中第二點(diǎn),它周?chē)腥齻€(gè)白色點(diǎn),,它的總價(jià)值為1+4+32=37,,對(duì)應(yīng)于索引表中第三十八項(xiàng)
我們用這種方法,把所有點(diǎn)的情況映射到0~255的索引表中
我們掃描原圖,,對(duì)于黑色的像素點(diǎn),,根據(jù)周?chē)它c(diǎn)的情況計(jì)算它的價(jià)值,然后查看索引表中對(duì)應(yīng)項(xiàng)來(lái)決定是否要保留這一點(diǎn)
我們很容易寫(xiě)出程序
import cv
def Thin(image,array): h = image.height w = image.width iThin = cv.CreateImage(cv.GetSize(image),8,1) cv.Copy(image,iThin) for i in range(h): for j in range(w): if image[i,j] == 0: a = [1]*9 for k in range(3): for l in range(3): if -1<(i-1+k)<h and -1<(j-1+l)<w and iThin[i-1+k,j-1+l]==0: a[k*3+l] = 0 sum = a[0]*1+a[1]*2+a[2]*4+a[3]*8+a[5]*16+a[6]*32+a[7]*64+a[8]*128 iThin[i,j] = array[sum]*255 return iThin def Two(image): w = image.width h = image.height size = (w,h) iTwo = cv.CreateImage(size,8,1) for i in range(h): for j in range(w): iTwo[i,j] = 0 if image[i,j] < 200 else 255 return iTwo
array = [0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\ 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\ 1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,\ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\ 1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,\ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\ 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\ 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,\ 1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,\ 1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,0,\ 1,1,0,0,1,1,1,0,1,1,0,0,1,0,0,0] image = cv.LoadImage('pic3.jpg',0) iTwo = Two(image) iThin = Thin(iTwo,array) cv.ShowImage('image',image) cv.ShowImage('iTwo',iTwo) cv.ShowImage('iThin',iThin) cv.WaitKey(0)
我們來(lái)看看運(yùn)行效果:
(下圖最左邊的原圖若涉及版權(quán)問(wèn)題,,請(qǐng)作者與我聯(lián)系,,謝謝)
效果差強(qiáng)人意,總覺(jué)得有點(diǎn)不對(duì)頭,,但又說(shuō)不出哪里不對(duì)
好吧,我們來(lái)看看最簡(jiǎn)單的事例
按照前面的分析,,我們應(yīng)該得到一條豎著的線,,但實(shí)際上我們得到了一條橫線
我們?cè)趶纳系较拢瑥淖蟮接覓呙璧臅r(shí)候,,遇到第一個(gè)點(diǎn),,我們查表可以刪除,遇到第二個(gè)點(diǎn),,我們查表也可以刪除,,整個(gè)第一行都可以刪除
于是我們查看第二行時(shí),和第一行一樣,,它也被整個(gè)刪除了,。這樣一直到最后一行,,于是我們得到最后的結(jié)果是一行直線
解決的辦法是:
在每行水平掃描的過(guò)程中,先判斷每一點(diǎn)的左右鄰居,,如果都是黑點(diǎn),,則該點(diǎn)不做處理。另外,,如果某個(gè)黑店被刪除了,,則跳過(guò)它的右鄰居,處理下一點(diǎn),。對(duì)矩形這樣做完一遍,,水平方向會(huì)減少兩像素。
然后我們?cè)俑拇怪狈较驋呙?,方法一樣?/p>
這樣做一次水平掃描和垂直掃描,,原圖會(huì)“瘦”一圈
多次重復(fù)上面的步驟,知道圖形不在變化為止
這一改進(jìn)讓算法的復(fù)雜度的運(yùn)行時(shí)間增大一個(gè)數(shù)量級(jí)
我們來(lái)看看改進(jìn)后的算法:
import cv
def VThin(image,array): h = image.height w = image.width NEXT = 1 for i in range(h): for j in range(w): if NEXT == 0: NEXT = 1 else: M = image[i,j-1]+image[i,j]+image[i,j+1] if 0<j<w-1 else 1 if image[i,j] == 0 and M != 0: a = [0]*9 for k in range(3): for l in range(3): if -1<(i-1+k)<h and -1<(j-1+l)<w and image[i-1+k,j-1+l]==255: a[k*3+l] = 1 sum = a[0]*1+a[1]*2+a[2]*4+a[3]*8+a[5]*16+a[6]*32+a[7]*64+a[8]*128 image[i,j] = array[sum]*255 if array[sum] == 1: NEXT = 0 return image def HThin(image,array): h = image.height w = image.width NEXT = 1 for j in range(w): for i in range(h): if NEXT == 0: NEXT = 1 else: M = image[i-1,j]+image[i,j]+image[i+1,j] if 0<i<h-1 else 1 if image[i,j] == 0 and M != 0: a = [0]*9 for k in range(3): for l in range(3): if -1<(i-1+k)<h and -1<(j-1+l)<w and image[i-1+k,j-1+l]==255: a[k*3+l] = 1 sum = a[0]*1+a[1]*2+a[2]*4+a[3]*8+a[5]*16+a[6]*32+a[7]*64+a[8]*128 image[i,j] = array[sum]*255 if array[sum] == 1: NEXT = 0 return image def Xihua(image,array,num=10): iXihua = cv.CreateImage(cv.GetSize(image),8,1) cv.Copy(image,iXihua) for i in range(num): VThin(iXihua,array) HThin(iXihua,array) return iXihua
def Two(image): w = image.width h = image.height size = (w,h) iTwo = cv.CreateImage(size,8,1) for i in range(h): for j in range(w): iTwo[i,j] = 0 if image[i,j] < 200 else 255 return iTwo
array = [0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\ 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\ 1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,\ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\ 1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,1,\ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\ 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,\ 0,0,1,1,0,0,1,1,1,1,0,1,1,1,0,1,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,\ 1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,\ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,\ 1,1,0,0,1,1,0,0,1,1,0,1,1,1,0,0,\ 1,1,0,0,1,1,1,0,1,1,0,0,1,0,0,0] image = cv.LoadImage('pic3.jpg',0) iTwo = Two(image) iThin = Xihua(iTwo,array) cv.ShowImage('image',image) cv.ShowImage('iTwo',iTwo) cv.ShowImage('iThin',iThin) cv.WaitKey(0)
我們?cè)谡{(diào)用函數(shù)的時(shí)候可以控制掃描的次數(shù),,而不是判斷是否掃描完成
好啦,,我們來(lái)看看運(yùn)行效果吧。
效果確實(shí)比剛才好多了
我們來(lái)看看對(duì)復(fù)雜圖形的效果
(上圖中左圖若有版權(quán)問(wèn)題,,請(qǐng)與我聯(lián)系,,謝謝)
好啦,圖像的細(xì)化就講到這里了
之后一段時(shí)間要準(zhǔn)備考研,,雖然并不認(rèn)為自己還來(lái)得及復(fù)習(xí),,但總得做出點(diǎn)姿態(tài)
呵呵,可能之后兩個(gè)星期不再更新
|