圖像的細化(骨架抽取)


【原文:http://www.cnblogs.com/xianglan/archive/2011/01/01/1923779.html

圖像的細化主要是針對二值圖而言

所謂骨架,可以理解為圖像的中軸,,一個長方形的骨架,是它的長方向上的中軸線,

圓的骨架是它的圓心,直線的骨架是它自身,孤立點的骨架也是自身。

骨架的獲取主要有兩種方法:

(1)基於烈火模擬

設想在同一時刻,將目標的邊緣線都點燃,火的前沿以勻速向內部蔓延,當前沿相交時火焰熄滅,

火焰熄滅點的結合就是骨架。

(2)基於最大圓盤

目標的骨架是由目標內所有內切圓盤的圓心組成

我們來看看典型的圖形的骨架(用粗線表示)

細化的算法有很多種,但比較常用的算法是查表法

細化是從原來的圖中去掉一些點,但仍要保持原來的形狀。

實際上是保持原圖的骨架。

判斷一個點是否能去掉是以8個相鄰點(八連通)的情況來作為判據的,具體判據為:

1,內部點不能刪除

2,鼓勵點不能刪除

3,直線端點不能刪除

4,如果P是邊界點,去掉P后,如果連通分量不增加,則P可刪除

 

看看上面那些點。

第一個點不能去除,因為它是內部點

第二個點不能去除,它也是內部點

第三個點不能去除,刪除后會使原來相連的部分斷開

第四個點可以去除,這個點不是骨架

第五個點不可以去除,它是直線的端點

第六個點不可以去除,它是直線的端點

 

對於所有的這樣的點,我們可以做出一張表,來判斷這樣的點能不能刪除

我們對於黑色的像素點,對於它周圍的8個點,我們賦予不同的價值,

若周圍某黑色,我們認為其價值為0,為白色則取九宮格中對應的價值

對於前面那幅圖中第一個點,也就是內部點,它周圍的點都是黑色,所以他的總價值是0,對應於索引表的第一項

前面那幅圖中第二點,它周圍有三個白色點,它的總價值為1+4+32=37,對應於索引表中第三十八項

我們用這種方法,把所有點的情況映射到0~255的索引表中

 

我們掃描原圖,對於黑色的像素點,根據周圍八點的情況計算它的價值,然后查看索引表中對應項來決定是否要保留這一點

 

我們很容易寫出程序

 

復制代碼
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)
復制代碼

 

 

我們來看看運行效果:

(下圖最左邊的原圖若涉及版權問題,請作者與我聯系,謝謝)

 

效果差強人意,總覺得有點不對頭,但又說不出哪里不對

好吧,我們來看看最簡單的事例

按照前面的分析,我們應該得到一條豎着的線,但實際上我們得到了一條橫線

我們在從上到下,從左到右掃描的時候,遇到第一個點,我們查表可以刪除,遇到第二個點,我們查表也可以刪除,整個第一行都可以刪除

於是我們查看第二行時,和第一行一樣,它也被整個刪除了。這樣一直到最后一行,於是我們得到最后的結果是一行直線

 

解決的辦法是:

在每行水平掃描的過程中,先判斷每一點的左右鄰居,如果都是黑點,則該點不做處理。另外,如果某個黑店被刪除了,則跳過它的右鄰居,處理下一點。對矩形這樣做完一遍,水平方向會減少兩像素。

然后我們再改垂直方向掃描,方法一樣。

這樣做一次水平掃描和垂直掃描,原圖會“瘦”一圈

多次重復上面的步驟,知道圖形不在變化為止

 

這一改進讓算法的復雜度的運行時間增大一個數量級

我們來看看改進后的算法:

 

復制代碼
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)
復制代碼

 

 

我們在調用函數的時候可以控制掃描的次數,而不是判斷是否掃描完成

 

好啦,我們來看看運行效果吧。

 

效果確實比剛才好多了

 

我們來看看對復雜圖形的效果

 

(上圖中左圖若有版權問題,請與我聯系,謝謝)

 

好啦,圖像的細化就講到這里了


免責聲明!

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



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