http://www.mrluoyi.com/blog/2012/03/connected-domain/#respond
寻找一副二值图像中的连通域。全图遍历+DFS深度优先搜索。
示例输入:
5 5
0 0 0 0 1
0 1 1 0 1
0 1 0 0 1
1 1 1 0 1
1 1 0 0 1
示例输出:
The number of connected domains is: 2
Connected domains are labeled as below:
0 0 0 0 2
0 3 3 0 2
0 3 0 0 2
3 3 3 0 2
3 3 0 0 2
第一种办法比较直观,是递归的办法,代码如下:
#include<stdio.h>
//by Yi Luo (03/08/2012)
#define max 500
int I[max][max];
int n, m;// size = n*m
int num = 0;
const int direction[][2]={{1,0},{0,-1},{0,1},{-1,0}};
bool check(int x, int y){
if (x >= 0 && x < n && y >= 0 && y < m && I[x][y] == 1)
{
return true;
}else
{
return false;
}
}
bool DFS(int x, int y, int label){
if (1 != I[x][y])
{
return false;
}else
{
I[x][y] = label;
for (int i = 0; i < 4; i++)
{
if (check(x + direction[i][0], y + direction[i][1]))
{
DFS(x + direction[i][0], y + direction[i][1], label);
}
}
}return true;
}
int main(void)
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i)
{
for (int j=0; j< m; j++)
{
scanf("%d", &I[i][j]);
}
}
int label = 2;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
if (DFS(i, j, label))
{
label++;
}
}
}
num = label - 2;
printf("\n\nThe number of connected domains is: %d\n", num);
printf("Connected domains are labeled as below:\n\n");
for (int i = 0; i < n; ++i)
{
for (int j=0; j< m; j++)
{
printf("%d ", I[i][j]);
}
printf("\n");
}
getchar();
getchar();
return 0;
}
刚刚又学到了一种新的办法,就是直接扫描的办法,比较巧妙。对于四领域的情况,主要是考察每个前景点的左边、上边两个点的情况:
1、如果左边和上边都有标记过的前景点,则选择二者标号更小的作为当前点的标记,并将之前所有的大号改成小号;
2、如果只有左边是标记过的点,则将当前点和其左边点标号一致;
3、如果只有上边是标记过的点,则和上面的点标号一致;
4、如果左边和上边都没有标记过的点,则当前前景点新开一个标号。
代码如下:
#include<stdio.h>
//by Yi Luo (03/09/2012)
#define size 500
int I[size][size];//二值图像矩阵
int n, m, min, max;// size = n*m
int num = 0;//连通域数目
bool check(int x, int y){
if (x >= 0 && x <= n && y >= 0 && y <= m && I[x][y] != 0)
{
return true;
}else
{
return false;
}
}
int main(void)
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i)
{
for (int j=0; j< m; j++)
{
scanf("%d", &I[i][j]);
} //读入二值图像矩阵
}
int label = 1;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
if (check(i, j))//如果当前位置是合法前景
{
if (check(i, j-1))
{
if (check(i-1, j))//如果该点的左面、上面均是标注过的前景
{
min = I[i-1][j];
max = I[i][j-1];
if (max < min) {
min = I[i][j-1];
max = I[i-1][j];
for (int t = j; t >= 0; t--)
{
for (int w = i; w >=0; w--)
{
if (I[w][t] == max) { I[w][t] = min; }
}
}
label--;
}
I[i][j] = min;
} else //如果只有该点的左面是标注过的前景
{
I[i][j] = I[i][j-1];
}
} else if (check(i-1, j)) //如果只有该点的上面是标注过的前景
{
I[i][j] = I[i-1][j];
} else //如果该点的左面,上面都不是标注过的前景
{
label ++;
I[i][j] = label;
}
}
}
}
num = label - 1;
printf("\n\nThe number of connected domains is: %d\n", num);
printf("Connected domains are labeled as below:\n\n");
for (int i = 0; i < n; ++i)
{
for (int j=0; j< m; j++)
{
printf("%d ", I[i][j]);
} //输出新的标注结果
printf("\n");
}
getchar();
getchar();
return 0;
}