1. 背景知識
1.1 單元數組與結構
單元數組其實第二章有所接觸,它提供了一種將各種類型的對象(如數字、字符、矩陣和其他單元數組)組合在一個變量名下的方法。假設有三個實體:(1)一幅238*315的 uint8類圖像f;(2)一個以3*2數組的行的形式出現的二維坐標序列b;(3)一個包含兩個字符名稱char_array={‘area’,’centroid’}的單元數組。這三個相異實體可以使用單元數組組織到一個變量C中:C={f,b,char_array}
C={f,a,char_array};
>> C
C =
1 列
[238x315 uint8]
2 列
[3x2 double]
3 列
如果要獲得具體值,可以用花括號,如果要獲得變量的描述,可以用圓括號。
單元數組包含的是變量的副本,而不是指向這些變量的指針。
假設我們要寫一個函數,該函數輸出一幅圖像的平均亮度、維數、行的平均亮度和列的平均亮度,則可以使用標准方法按一下形式寫函數。
代碼如下:
function[AI,dim,AIrows,AIcols]=image_states(f)
dim=size(f);
AI=mean2(f);
AIrows=mean(f,2);
AIcols=mean(f);
1.2 本章使用的其他一些MATLAB和IPT函數
函數imfill對於二值圖像和灰度圖像作用不同。在輸入二值圖像的背景像素上從參數指定的點的開始,執行填充操作(即將背景像素值設為1)。
代碼實例:
b=imread('finger.jpg');
>> fb=logical(b);
>> gb=imfill(fb,4,'holes');
subplot(1,2,1),imshow(b);
>> title('原圖');
title('將location設為4的結果');
效果圖像:
函數find和bwlabel一起使用,返回構成某個指定對象的像素的坐標向量。若[gb,num]=bwlabel(fB)產生了多個連接區域,則使用以下語法可以獲得第二個區域的坐標:
[r,c]=find(g==2)
在本章中,區域或者邊界的二維坐標被組織成np*2數組的形式,其中每行是一個(x,y)坐標對,np是區域或邊界中的點的數目。在某些情況下,有必要對數組進行排序。為此,可使用函數sortrows。
代碼示例:
>> S=[1 2;7 3;2 4];
>> z=sortrows(S)
z =
1 2
2 4
7 3
該函數按升序對數組S的行進行排序。參量S必須是矩陣或列向量。若想對數組S的行排序,又要去除重復行,則可使用函數unique。
代碼示例:
S=[1 2;6 5;1 2;4 3];
>> z=[1 2;4 3;6 5];
>> m=[3;4;2];
>> n=[1;3;1;2];
[z,m,n]=unique(S,'rows')
z =
1 2
4 3
6 5
m =
1
4
2
n =
1
3
1
2
z是排序后無重復行的數組,m和n是z=S(m,:),S=z(n,:)。注意z是按升序排列的,m指示最初數組的哪些行被保留。通常,有必要對數組行進行向上、向下或側移指定位置數的移位操作,為此我們使用函數circshift:
代碼如下:
z=cirshift(S,[ud lr])
z=circshift(S,[1,2])
z =
4 3
1 2
6 5
1 2
S =
1 2
6 5
1 2
4 3
若ud是S向上或向下移位的元素數。若ud為正,則移位操作向下;否則向上。類似的,若lr為正,則移位操作向右移動lr個元素;否則向左。
1.3 一些基本的m函數
函數boundaries
邊界已被表示為np*2數組,數組中的每一行表示一個二維的坐標對。大部分這種函數會自動地將大小為2*np的坐標數組轉換為大小為np*2的數組。
代碼實例:
f2=logical(b);
>> B=boundaries(f2);
>> d=cellfun('length',B);
>> [max_d,k]=max(d);
v=B{k(1)};
向量v包含輸入圖像中最長邊界的坐標,k是對應的區域數;數組v的大小為np*2。若最大邊界多於一條,則最后一條命令簡單地選出最大長度邊界中的第一個。因為使用函數boundaries計算出的每個邊界中第一個點和最后一個點相同,所以行v(1,:)和行v(end,:)相同。
而函數bound2eight會從b中去除一些像素。函數bound2im生成一幅二值圖像g,該圖像的大小為M*N,邊界點為1,背景值為0。當對邊界進行操作時,計算連接兩點的一條直線的整數坐標是一個基本工具。
2. 表示
2.1 鏈碼
鏈碼通過一個指定長度與方向的直線段的連接序列來表示一個編號方案加以編碼,如圖
典型情況下,這一表示建立在線段的4連接或者8連接之上。每條線段的方向通過一個編號方案加以編碼,基於這種方式的鏈碼稱為Freeman鏈碼。一條邊界的鏈碼取決於起點。然而,代碼可以通過將起點處理為方向數的循環序列和重新定義起點的方法進行歸一化,因此,產生的數字序列形成一個最小幅值的整數。有一個函數fchcode,該函數計算一個保存在數組b中的np*2個已排序邊界點集的Freeman鏈碼。下面的例子是獲得對象的邊界的鏈碼和一階差分。
使用9*9平均掩模的處理結果g:
b=imread('E:\matlab書中資源\素材圖片\dipum_images_ch11\Fig1102(a)(noisy_circular_stroke).tif');
>> imshow(b);
>> h=fspecial('average',9);
>> g=imfilter(b,h,'replicate');
把g經閾值處理后獲得的二值圖像
g=im2bw(g,0.5);
獲取該圖像的邊界使用函數boundaries。
B=boundaries(g);
接下來的代碼為
>> cn=connectpoly(s(:,1),s(:,2));
>> g2=bound2im(cn,M,N,min(cn(:,1)),min(cn(:,2)));
>> subplot(2,3,6),imshow(g2);
>> title('連接點后的結果');
d=cellfun('length',B);
>> [max_d,k]=max(d);
>> b=B{1};
>> [M N]=size(g);
>> g=bound2im(b,M,N,min(b(:,1)),min(b(:,2)));
>> subplot(2,3,4),imshow(g);
>> title('二值圖像的邊界');
>> [s,su]=bsubsamp(b,50);
g2=bound2im(s,M,N,min(s(:,1)),min(s(:,2)));
>> subplot(2,3,5),imshow(g2);
>> title('二次取樣后的邊界');
>> cn=connectpoly(s(:,1),s(:,2));
n=connectpoly(s(:,1),s(:,2));
>> g2=bound2im(cn,M,N,min(cn(:,1)),min(cn(:,2)));
>> subplot(2,3,6),imshow(g2);
>> title('連接點后的結果');
效果如下:
2.2 使用最小周長多邊形的多邊形近似
假設我們用一組級聯的單元來包圍一條邊界,如下圖,這將有助於邊界的可視化,就像對應於單元條的內外邊界的兩堵牆,對象邊界可以被看成是包圍兩堵牆的區域間的橡皮條。Sklansky方法使用一個所謂的“細胞聯合體”或者“細胞馬賽克”,對我們來說,它是一組用於包圍邊界的方形元素的集合。
查找一個區域的MPP的如下步驟:
獲取細胞聯合體。
獲取細胞聯合體的內部區域。
使用函數boundaries以4連接順時針坐標序列的形式獲得步驟2中的區域的邊界。
使用函數fchcode獲得該4連接序列的Freeman鏈碼。
從鏈碼中獲得凸頂點(黑點)與凹頂點(白頂點)。
使用黑點作為頂點構造一個初始多邊形,在進一步的分析中刪除位於該多邊形之外的任何白頂點(在多邊形邊界上的白頂點將保留)。
用剩余的黑白點作為頂點構造一個多邊形。
刪除所有凹頂點的黑點。
重復步驟7與8,直到變化停止。此時,所有角度為180度的頂點均將刪除。剩下的點就是該MPP的頂點。
MPP算法實現中用到的一些M函數
我們可使用函數qtdecomp作為獲得包圍邊界的細胞聯合體的第一步。通常,我們考慮區域B,它由1和背景0的組成。
代碼實例:
b=bwperim(b,8);
Q=qtdecomp(b,0,2);
R=imfill(bf,'holes')&~bf;
b=boundaries(b,4,'cw');
b=b{1};
函數inpolygon用在函數minperpoly中,以便決定一個點是否在多邊形的外部、邊界上或者多邊形的內部,其語法為
in=inpolygon(x,y,xv,yv)
其中,x和y是包含待測點的x和y坐標的向量,而xv和yv是包含按順時針或逆時針順序安排的多邊形頂點的x和y坐標的向量。數組IN是一個向量,其長度等於待測點數。若點在多邊形邊界的內部或邊界上,其值為1;若點在邊界外部,則其值為0。
下面是計算MPP的M函數
我們使用函數minperpoly。語法為:
[x,y]=minperpoly(B,cellsize)
其中,B是一幅輸入二值圖像,它包含單個區域或邊界,而cellsize是細胞聯合體中用於形成邊界的方形單元的大小。列向量x和y包含MPP頂點的x坐標和y坐標。
代碼實例:
b=boundaries(c,4,'cw');
>> b=b{1};
>> [M,N]=size(c);
>> xmin=min(b(:,1));
>> ymin=min(b(:,2));
>> bim=bound2im(b,M,N,xmin,ymin);
[x,y]=minperpoly(c,2);
>> b2=connectpoly(x,y);
>> B2=bound2im(b2,M,N,xmin,ymin);
subplot(2,3,1),imshow(c);
>> title('原圖');
>> subplot(2,3,2),imshow(bim);
>> title('連接邊界');
實例圖像:
2.3 標記
標記是邊界的一維函數的表示,它可以通過多種方法生成。其中最簡單的方法就是作為角度的函數畫出從一個內部點(如質心)到邊界的距離。附錄中有個函數signature可用於查找給定邊界的標記,語法為:
[st,angle,x0,y0]=signature(b,x0,y0)
其中,b是一個大小為np*2的數組,它包含有一條按順時針或逆時針排列邊界的xy坐標。作為不斷增加的angle函數的標記的幅值輸出在st中。函數signature使用MATLAB函數cart2pol將笛卡爾坐標轉換成極坐標,語法為
[theta,rho]=cart2pol(x,y)
其中,x和y是包含笛卡爾坐標點的坐標的向量。向量theta和rho包含對應極坐標下的長度和角度。若x和y是行向量,則theta和rho也是行向量;若x和y是列向量,則theta和rho也是列向量。
2.4 邊界判斷
將一條邊界分解為片段通常是很有用的。分解降低了邊界的復雜度,從而簡化了描述過程。當邊界包含一個或者多個攜帶形狀信息的重要凹面時,這種方法尤其具有吸引力。在這種情況下,使用由邊界包圍的區域凸殼對邊界的魯棒分解是一種有力的工具。
2.5 骨骼
用於表示平面區域結構形狀的一種重要方法是將其簡化為一幅圖形。這一簡化可以通過一種細化(也成為骨骼化)算法獲取區域骨骼來完成。
如第九章所述,IPT通過函數bwmorph來生成二值圖像B中所有區域的骨骼:
代碼實例:
f=imread('E:\matlab書中資源\素材圖片\dipum_images_ch11\Fig1113(a)(chromo_original).tif');
>> f=im2double(f);
>> h=fspecial('gaussian',25,15);
>> g=imfilter(f,h,'replicate');
>> subplot(2,3,1),imshow(f);
>> subplot(2,3,2),imshow(g);
>> title('使用25*25高斯空間掩模平滑圖像后的結果');
>> g=im2bw(g,1.5*graythresh(g));
>> subplot(2,3,3),imshow(g);
>> title('經閾值處理后的圖像');
>> s=bwmorph(g,'skel',Inf);
>> subplot(2,3,4),imshow(s);
>> title('骨骼');
>> s=bwmorph(g,'spur',8);
>> subplot(2,3,5),imshow(s);
>> s=bwmorph(g,'skel',Inf);
>> s1=bwmorph(s,'spur',8);
subplot(2,3,5),imshow(s1);
>> title('應用8次去除毛刺的算法后的骨骼');
>> s1=bwmorph(s1,'spur',15);
subplot(2,3,6),imshow(s1);
>> title('再應用7次去除刺算法后的骨骼');
效果如下: