1. 图像细化原理
Zhang-Suen 算法原理
Zhang-Suen 算法每运行一次, 需要遍历所有的不为0
的像素。在对每个像素(P1)进行删除或保留的判断时,我们需要关注其周围的8个邻居像素(P2, P3, P4, P5, P6, P7, P8)的值。其中 P2 到 P8 的顺序是算法规定,用于后面判断。
1.1.1. 算法的每次运行需要进行`两个子阶段`, 满足某个阶段的全部要求,才能将`P1`像素待置零。最后执行完把待置零的像素全部置零。
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f40eb0fbbcc71d079234551de93b606a.png)
1.2. 细化算法过程详解
我们对一副二值图像进行骨架提取,就是删除不需要的轮廓点,只保留其骨架点。假设一个像素点,我们定义该点为p1,则它的八邻域点p2->p9位置如下图所示,该算法考虑p1点邻域的实际情况,以便决定是否删除p1点。假设我们处理的为二值图像,背景为黑色,值为0,要细化的前景物体像素值为1。
算法的描述如下。
首先复制源图像到目地图像,然后建立一个临时图像,接着执行下面操作:
-
把目地图像复制给临时图像,对临时图像进行一次扫描,对于不为0的点,如果满足以下四个条件,则在目地图像中删除该点(就是设置该像素为0),这里p2,…,p9是对应位置的像素灰度值(其为1或者0)。
a:
2<= p2+p3+p4+p5+p6+p7+p8+p9<=6
大于等于2会保证p1点不是端点或孤立点,因为删除端点和孤立点是不合理的,小于等于6保证p1点是一个边界点,而不是一个内部点。等于0时候,周围没有等于1的像素,所以p1为孤立点,等于1的时候,周围只有1个灰度等于1的像素,所以是端点(注:端点是周围有且只能有1个值为1的像素)。
b:p2->p9的排列顺序中,0->1模式的数量为1,比如下面的图中,有p2->p3 => 0->1, p6->p7=>0->1,所以该像素0->1模式的数量为2。
之所以要01模式数量为1,是要保证删除当前像素点后的连通性。比如下面的图中,0->1模式数量大于1,如果删除当前点p1,则连通性不能保证。
c: P2*p4*p6 = 0
d: p4*p6*p8 = 0
在第一次子迭代中,只是移去东南的边界点,而不考虑西北的边界点,注意p4,p6出现了2次,就是说它们有一个为0,则c,d就满足。
-
接下来,把目地图像再次复制到临时图像,接着对临时图像进行一次扫描,如果不为0的点它的八邻域满足以下4个条件,则在目地图像中删除该点(就是设置该像素为0)
a. 2<= p2+p3+p4+p5+p6+p7+p8+p9<=6
b. p2->p9的排列顺序中,01模式的数量(这里假设二值图非零值为1)为1。
c. p2p4p8 = 0
d. p2p6p8 = 0
第二次迭代则相反,会移去西北的边界点,注意p2,p8出现了2次,就是说它们有一个为0,则c,d就满足。
执行完上面两个步骤后,就完成了一次细化算法,我们可以多次迭代执行上述过程,得到最终的骨架图。
1.2.1. 细化实例编程
1.首先对图像进行二值化,白色为255,黑色为0。
2.设置一个3*3的领域S模板。
3.S模板中各个位置上的取值取决于模板所对应图像中不同位置的像素,如果S模板某一个位置上所对应的像素值为白,模板上该位置赋为0,否则赋为1。
4.循环所有的前景像素点,对符合如下条件的像素点标记为删除:
2 ≤ N(p1) ≤ 6——N(p1)表示跟P1相邻的8个像素点中,为前景像素点的个数
S(P1) = 1——S(P1)表示将p2-p9-p2之间按序前后分别成对值为0、1的个数
P2 * P4 * P6 = 0
P4 * P6 * P8 = 0
5.循环所有的前景像素点,对符合如下条件的像素点标记为删除:
2 ≤ N(p1) ≤ 6——N(p1)表示跟P1相邻的8个像素点中,为前景像素点的个数
S(P1) = 1——S(P1)表示将p2-p9-p2之间按序前后分别成对值为0、1的个数
P2 * P4 * P8 = 0
P2 * P6 * P8 = 0
6.如果没有满足的点,则结束细化过程。
下面:
Image_Use为目标图像:高120,长180,处理时不考虑边界(四边)
//背景为黑色,值为0,要细化的前景物体像素值为1。
int temp[3][3];
int count = 0,flinsh_flag = 0;
while(1){
flinsh_flag = 0;
for(int i = 1;i<120-1;i++)
{
for(int j = 1;j<180-1;j++)
{
if(Image_Use[i][j] == 255) continue;
//第一步初始化模板
memset(temp, 0, sizeof(temp));
count = 0;
//第二步根据模板所对应的像素点,对模板进行赋值
//如果S模板某一个位置上所对应的像素值为白,模板上该位置赋为0,否则赋为1
if(Image_Use[i][j-1] == 0) temp[1][0] = 1;
if(Image_Use[i][j+1] == 0) temp[1][2] = 1;
if(Image_Use[i-1][j-1] == 0) temp[0][0] = 1;
if(Image_Use[i-1][j+1] == 0) temp[0][2] = 1;
if(Image_Use[i-1][j] == 0) temp[0][1] = 1;
if(Image_Use[i+1][j-1] == 0) temp[2][0] = 1;
if(Image_Use[i+1][j+1] == 0) temp[2][2] = 1;
if(Image_Use[i+1][j] == 0) temp[2][1] = 1;
//
for(int x = 0;x<3;x++)
for(int y = 0;y<3;y++)
{
if(x == 1 && y == 1) continue;
if(temp[x][y] == 1)
count ++;
}
if(count>=2&&count<=6)
{
int ap = 0;
if (temp[0][1] == 0 && temp[0][2] == 1) ++ap;
if (temp[0][2] == 0 && temp[1][2] == 1) ++ap;
if (temp[1][2] == 0 && temp[2][2] == 1) ++ap;
if (temp[2][2] == 0 && temp[2][1] == 1) ++ap;
if (temp[2][1] == 0 && temp[2][0] == 1) ++ap;
if (temp[2][0] == 0 && temp[1][0] == 1) ++ap;
if (temp[1][0] == 0 && temp[0][0] == 1) ++ap;
if (temp[0][0] == 0 && temp[0][1] == 1) ++ap;
if(ap == 1&&((temp[0][1]*temp[1][2]*temp[2][1])== 0)&&((temp[1][2]*temp[2][1]*temp[1][0])== 0))
{
Image_Use[i][j] = 255;
flinsh_flag ++;
}
}
}
}
if(flinsh_flag == 0)
{
break;
}
flinsh_flag = 0;
for(int i = 1;i<120-1;i++)
{
for(int j = 1;j<180-1;j++)
{
if(Image_Use[i][j] == 255) continue;
//第一步初始化模板
memset(temp, 0, sizeof(temp));
count = 0;
//第二步根据模板所对应的像素点,对模板进行赋值
//如果S模板某一个位置上所对应的像素值为白,模板上该位置赋为0,否则赋为1
if(Image_Use[i][j-1] == 0) temp[1][0] = 1;
if(Image_Use[i][j+1] == 0) temp[1][2] = 1;
if(Image_Use[i-1][j-1] == 0) temp[0][0] = 1;
if(Image_Use[i-1][j+1] == 0) temp[0][2] = 1;
if(Image_Use[i-1][j] == 0) temp[0][1] = 1;
if(Image_Use[i+1][j-1] == 0) temp[2][0] = 1;
if(Image_Use[i+1][j+1] == 0) temp[2][2] = 1;
if(Image_Use[i+1][j] == 0) temp[2][1] = 1;
//
for(int x = 0;x<3;x++)
for(int y = 0;y<3;y++)
{
if(x == 1 && y == 1) continue;
if(temp[x][y] == 1)
count ++;
}
if(count>=2&&count<=6)
{
int ap = 0;
if (temp[0][1] == 0 && temp[0][2] == 1) ++ap;
if (temp[0][2] == 0 && temp[1][2] == 1) ++ap;
if (temp[1][2] == 0 && temp[2][2] == 1) ++ap;
if (temp[2][2] == 0 && temp[2][1] == 1) ++ap;
if (temp[2][1] == 0 && temp[2][0] == 1) ++ap;
if (temp[2][0] == 0 && temp[1][0] == 1) ++ap;
if (temp[1][0] == 0 && temp[0][0] == 1) ++ap;
if (temp[0][0] == 0 && temp[0][1] == 1) ++ap;
if(ap == 1&&((temp[0][1]*temp[1][2]*temp[1][0])== 0)&&((temp[0][1]*temp[2][1]*temp[1][0]) == 0))
{
Image_Use[i][j] = 255;
flinsh_flag++;
}
}
}
}
if(flinsh_flag == 0)
{
break;
}
}