计算以下边界的阶数为20的形状数及其相应的近似多边形。
1.链码
链码用于表示由顺次连接的具有指定长度和方向的直线段组成的边界。通常,这种表示基于这些线段的4连接或8连接。每个线段的方向使用科数字编号方案编码,如下图所示。以这种方向性数字序列表示的编码称为佛雷曼链码。
2.边界追踪
假设: (1)处理的是一值图像,其目标和背景点分别标为1和0; (2)图像已使用值为0的边界填充,因而消除了目标与图像边界合并的可能性。为方便起见,我们仅限于讨论单个区域。通过单独地处理各个区域,该方法可扩展到多个不相交的区域。给定一个二值区域R 或其边界,追踪R的边界或给定边界的算法由如下步骤组成:
1).令起始点b0为图像中左上角标记为1的点。使用c0表示b0西侧的邻点。很明显,c0总是背景点。从c0开始按顺时针方向考察b0的8个邻点。令b1表示所遇到的值为1的第一个邻点,并直接令c1(背景)是序列中b1之前的点。存储b0和b1的位置,以便在步骤5中使用。
2).令b=b1和c=c1。
3).从c开始按顺时针方向行进,令b的8个邻点为n1,n2,…,n8.
4).令b=nk和c=nk-1。
5).重复步骤(3)和步骤(4),直到b=b0且找到的下一个边界点为b1。算法停止时,所找到的b点的序列就构成了排列后的边界点的集合。
3.形状数
链码边界的-次差分取决于起始点。一条基于四方向编码的边界的形状数,定义为最小量级的一一次差分。形状数的阶n定义为其表示的数字个数。此外,对于闭合边界,n为偶数,其值限制了不同形状的数量。一次差分是通过将链码作为循环序列来计算得到的。尽管链码的-次差分与旋转无关,但总体而言,已编码的边界取决于网格的方向。归一化网格方向的一种方法是,使用基本矩形的边对准链码网格。
4.代码
%Write by 长安 Rjex
I=imread('1.png');
I=rgb2gray(I);
imshow(I);
%K为阈值化后的图形
K=imbinarize(I,graythresh(I));
[m,n]=size(K);
%% 对图像边界进行提取,此处使用8领域边界追踪算法%找到左上角第一个点
L=find(K'==1,1);
flag_i=ceil(L/n);
flag_j=mod(L,n);
endflag_i = flag_i; %标记第一个点
endflag_j = flag_j;
clear L;
%B为边界图,对左上角第一个边界点置为1
B = zeros(m,n);
B(flag_i,flag_j)=1;
oflag_i=flag_i;
oflag_j=flag_j-1;
C(:,:,1)=[-1,-1,-1,0,+1,+1,+1,0];
C(:,:,2)=[-1,0,+1,+1,+1,0,-1,-1];
k=oflag_i-flag_i;
l=oflag_j-flag_j;
for i = 1:8
%自适应矩阵
h=find(C(:,:,1)==k&C(:,:,2)==l);
C2_left(:,:,1) = [C(1,h:end,1) C(1,1:h-1,1)];
C2_left(:,:,2) = [C(1,h:end,2) C(1,1:h-1,2)];
oflag_i=flag_i+C2_left(1,i,1);
oflag_j=flag_j+C2_left(1,i,2);
if oflag_i==0||oflag_j ==0
continue;
end
%对检测点和边缘进行迭代
if K(oflag_i,oflag_j)==1
t(1) = flag_i;
t(2) = flag_j;
flag_i= oflag_i;
flag_j= oflag_j;
oflag_i = t(1)+C2_left(1,i-1,1);
oflag_j = t(2)+C2_left(1,i-1,2);
B(flag_i,flag_j)=1;
break;
end
end
endflag_i2 = flag_i; %标记第二个点
endflag_j2 = flag_j;
%使得程序在刚开始执行时跳过结束判定程序
begin_flag = 0;
%对剩余的边界追踪
while flag_i~=endflag_i2||flag_j~=endflag_j2||t(1)~=endflag_i||t(2)~=endflag_j||begin_flag==0
begin_flag=1;
k=oflag_i-flag_i;
l=oflag_j-flag_j;
%自适应矩阵
h=find(C(:,:,1)==k&C(:,:,2)==l);
C2_left(:,:,1) = [C(1,h:end,1) C(1,1:h-1,1)];
C2_left(:,:,2) = [C(1,h:end,2) C(1,1:h-1,2)];
for i = 1:8
oflag_i=flag_i+C2_left(1,i,1);
oflag_j=flag_j+C2_left(1,i,2);
if oflag_i==0||oflag_j ==0
continue;
end
%对检测点和边缘进行迭代
if K(oflag_i,oflag_j)==1
t(1) = flag_i;
t(2) = flag_j;
flag_i= oflag_i;
flag_j= oflag_j;
oflag_i = t(1)+C2_left(1,i-1,1);
oflag_j = t(2)+C2_left(1,i-1,2);
B(flag_i,flag_j)=1;
break;
end
end
end
clear begin_flag C2_left endflag_i endflag_j endflag_i2 endflag_j2 flag_i flag_j
clear oflag_i oflag_j t l h i k C
figure(1);
imshow(B);
%%
%%对图像进行重取样
N = 20; %图像的阶数为20
chainCode=zeros(1,N);
E = zeros(2,N+1); %E来存储重取样后点的矩阵
%计算偏心率以得到适合的矩形
flag_i = m;flag_j = n;
endflag_i=1;
endflag_j=1;
for i = 1:m
for j = 1:n
if B(i,j)==1
if i < flag_i
flag_i = i;
end
if j < flag_j
flag_j = j;
end
if i > endflag_i
endflag_i = i;
end
if j > endflag_j
endflag_j = j;
end
end
end
end
max_i = endflag_i - flag_i;
max_j = endflag_j - flag_j;
flag = max_i/max_j;
%此处选择5*5模板
newk = round(max_i/5);
D = zeros(m,n);
for i = 1:m
for j = 1:n
if B(i,j)==1
D(ceil(i/newk)*newk,ceil(j/newk)*newk)=1;
end
end
end
figure(2)
imshow(D);
%%
%计算图像的链码%找到左上角第一个点
L=find(D'==1,1);
flag_i=ceil(L/n);
flag_j=mod(L,n);
newe=1;E(1)=flag_i;
E(2)=flag_j;
begin_flag = 0;
endflag_i = flag_i; %标记第一个点
endflag_j = flag_j;
clear L
C(:,:,1)=[0,-newk,0,+newk];
C(:,:,2)=[-newk,0,+newk,0];
C2(:,:,1)=[0,-newk,0,+newk];%用来计算链码
C2(:,:,2)=[+newk,0,-newk,0];
%左上角第一个点的左边点
oflag_i=flag_i;
oflag_j=flag_j-newk;
k=oflag_i-flag_i;
l=oflag_j-flag_j;
for i = 1:4
%自适应矩阵
h=find(C(:,:,1)==k&C(:,:,2)==l);
C2_left(:,:,1) = [C(1,h:end,1) C(1,1:h-1,1)];
C2_left(:,:,2) = [C(1,h:end,2) C(1,1:h-1,2)];
oflag_i=flag_i+C2_left(1,i,1);
oflag_j=flag_j+C2_left(1,i,2);
if oflag_i==0||oflag_j ==0||oflag_j ==0||oflag_j>n
continue;
end
%对检测点和边缘进行迭代
if D(oflag_i,oflag_j)==1
t(1) = flag_i; %t为前一个点
t(2) = flag_j;
flag_i= oflag_i; %此处的flag_i和flag_j为后一个点
flag_j= oflag_j;
newe = newe+1;
E(1,newe) = flag_i;
E(2,newe) = flag_j;
oflag_i = flag_i+C2_left(1,i-1,1);
oflag_j = flag_j+C2_left(1,i-1,2);
break;
end
end
newi=1;
newk=flag_i-t(1);
newl=flag_j-t(2);
chainCode(1)=find(C2(:,:,1)==newk&C2(:,:,2)==newl)-1;
while begin_flag==0||endflag_i ~= flag_i||endflag_j ~= flag_j
begin_flag =1;
k=oflag_i-flag_i;
l=oflag_j-flag_j;
for i = 1:4
%自适应矩阵
h=find(C(:,:,1)==k&C(:,:,2)==l);
C2_left(:,:,1) = [C(1,h:end,1) C(1,1:h-1,1)];
C2_left(:,:,2) = [C(1,h:end,2) C(1,1:h-1,2)];
oflag_i=flag_i+C2_left(1,i,1);
oflag_j=flag_j+C2_left(1,i,2);
if oflag_i==0||oflag_i>m||oflag_j ==0||oflag_j>n
continue;
end
%对检测点和边缘进行迭代
if D(oflag_i,oflag_j)==1
t(1) = flag_i; %t为前一个点
t(2) = flag_j;
flag_i= oflag_i; %此处的flag_i和flag_j为后一个点
flag_j= oflag_j;
newe = newe+1;
E(1,newe) = flag_i;
E(2,newe) = flag_j;
%对下一个要找的点进行迭代
if i==1
oflag_i = flag_i+C2_left(1,i,1);
oflag_j = flag_j+C2_left(1,i,2);
else
oflag_i = flag_i+C2_left(1,i-1,1);
oflag_j = flag_j+C2_left(1,i-1,2);
end
newk=flag_i-t(1);
newl=flag_j-t(2);
newi=newi+1;
chainCode(newi)=find(C2(:,:,1)==newk&C2(:,:,2)==newl)-1;
break;
end
end
end
chainCode
clear begin_flag C2 C2_left endflag_i endflag_j flag flag_i flag_j max_i max_j
clear newi newk newl oflag_i oflag_j t i j k h l C newe
%%
%求一阶差分
difference=zeros(1,N);
newChainCode=[chainCode(N) chainCode];
for i=1:N
difference(i)=newChainCode(i+1)-newChainCode(i);
if difference(i)<0
difference(i)=difference(i)+4;
end
end
difference
clear newChainCode i
%%
%求形状数
flag = 1e21;
newflag = 0;
for i=1:N
newdifference=[difference(i+1:end) difference(1:i)];
t=0;
for j=1:N
t = t+newdifference(j)*(10^(N-j));
end
if t<flag
newflag = i;
flag = t;
end
end
shapeNo = [difference(newflag+1:end) difference(1:newflag)];
shapeNo
clear newdifference newflag t flag i j
%%
%其相应的近似多边形
figure(3),imshow(D);
hold on;
plot(E(2,:),E(1,:),'w-');
hold off;
5.输出结果
边界追踪算法得到的边界如下所示:
直接获得该边界的链码会生成一个变化较小的较长序列,且不能表示该边界的形状,为降低可变性,在得到其边界的链码前,通常需要对改边界重取样。由于要找阶为20的矩形,求得偏心率为0.9508,最匹配的基本矩形为55,故对图像做55的网络,做适当重取样后的图像如下所示:
从左上角开始对图像求链码,求得链码为:
其一次差分为:
使用该链码的故一次差分来计算形状数为:
阶数为20的近似多边形