Steven Wang's Blog
C'est la vie
rss
email
twitter
新浪微博
  • Home
  • About
  • Google Profile
  • 新浪微博

二值图像连通区标记之区域生长法

8 Comments
Posted on 二月 27 2011

连通区标记是最基本的图像处理算法之一,最近的项目中需要一个纯C语言实现的连通区标记算法,本以为如此基础的算法在网上能搜到现成代码,结果大失所望,讲解标记算法思想的文章很多,给出代码实例的却很少,能找到的几段程序,都有各种各样的问题。于是,自己动手丰衣足食,并拿出来与大家分享。

两阶段法是传统的连通区标记算法,在维基百科上有详细的介绍:
Connected Component Labeling
该算法中,第一阶段按从左至右、从上至下的顺序,对整幅图像进行扫描,通过比较每个前景像素的邻域进行连通区标记,并创建等效标记列表。第二阶段的任务是合并等效标记列表,并再次扫描图像以更新标记。算法的优点的是通俗易懂,缺点是需要两次扫描图像,效率不高,并且第二阶段编程实现较为复杂。

区域生长法利用区域生长的思想,一次生长过程可以标记一整个连通区,只需对图像进行一次扫描就能标记出所有连通区。算法描述如下:

Step1、输入待标记图像bitmap,初始化一个与输入图像同样尺寸的标记矩阵labelmap,一个队列queue以及标记计数labelIndex;
Step2、按从左至右、从上至下的顺序扫描bitmap,当扫描到一个未被标记的前景像素p时,labelIndex加1,并在labelmap中标记p(相应点的值赋为labelIndex),同时,扫描p的八邻域点,若存在未被标记的前景像素,则在labelmap中进行标记,并放入queue中,作为区域生长的种子;
Step3、当queue不为空时,从queue中取出一个生长种子点p1,扫描p1的八邻域点,若存在未被标记过的前景像素,则在labelmap中进行标记,并放入queue中;
Step4、重复Step3直至queue为空,一个连通区标记完成;
Step5、转到Step2,直至整幅图像被扫描完毕,得到标记矩阵labelmap和连通区的个数labelIndex。

该算法最坏情况下,将对每个像素点都进行一次八邻域搜索,算法复杂度为O(n)。

C语言实现如下:

//辅助队列
typedef struct QNode
{
	int data;
	struct QNode *next;
}QNode;

typedef struct Queue
{
	struct QNode* first;
	struct QNode* last;
}Queue;

void PushQueue(Queue *queue, int data)
{
	QNode *p = NULL;
	p = (QNode*)malloc(sizeof(QNode));
	p->data = data;
	if(queue->first == NULL)
	{
		queue->first = p;
		queue->last = p;
		p->next = NULL;
	}
	else
	{
		p->next = NULL;
		queue->last->next = p;
		queue->last = p;
	}
}

int PopQueue(Queue *queue)
{
	QNode *p = NULL;
	int data;
	if(queue->first == NULL)
	{
		return -1;
	}
	p = queue->first;
	data = p->data;
	if(queue->first->next == NULL)
	{
		queue->first = NULL;
		queue->last = NULL;
	}
	else
	{
		queue->first = p->next;
	}
	free(p);
	return data;
}

static int NeighborDirection[8][2] = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}};

//搜索并标记当前像素的8邻域
void SearchNeighbor(unsigned char *bitmap, int width, int height, int *labelmap, int labelIndex, int pixelIndex, Queue *queue)
{
	int searchIndex, i, length;
	labelmap[pixelIndex] = labelIndex;
	length = width * height;
	for(i = 0;i < 8;i++)
	{
		searchIndex = pixelIndex + NeighborDirection[i][0] * width + NeighborDirection[i][1];
		if(searchIndex > 0 && searchIndex < length && 
			bitmap[searchIndex] == 255 && labelmap[searchIndex] == 0)
		{
			labelmap[searchIndex] = labelIndex;
			PushQueue(queue, searchIndex);
		}
	}
}

int ConnectedComponentLabeling(unsigned char *bitmap, int width, int height, int *labelmap)
{
	int cx, cy, index, popIndex, labelIndex = 0;
	Queue *queue = NULL;
	queue = (Queue*)malloc(sizeof(Queue));
	queue->first = NULL;
    	queue->last = NULL;
	memset(labelmap, 0, width * height);
	for(cy = 1; cy < height - 1; cy++)
	{
		for(cx = 1; cx < width - 1; cx++)
		{
			index = cy * width + cx;
			if(bitmap[index] == 255 && labelmap[index] == 0)
			{
				labelIndex++;
				SearchNeighbor(bitmap, width, height, labelmap, labelIndex, index, queue);

				popIndex = PopQueue(queue);
				while(popIndex > -1)
				{
					SearchNeighbor(bitmap, width, height, labelmap, labelIndex, popIndex, queue);
					popIndex = PopQueue(queue);
				}
			}
		}
	}
	free(queue);
	return labelIndex;
}

Resources & Reference:
1一种二值图像连通区域标记的新方法.pdf

--End--
作者:Steven Wang | 可以转载, 转载时务必以超链接形式标明文章原始出处和作者信息及版权声明
网址:http://blog.stevenwang.name/connected-component-labeling-rg-545001.html

Tags: 连通区标记 
Categories: 数字图像处理 

8 Comments

  1. amao says:
    二月 27 2011 at 22:47 Reply

    我当时也是卡在第二阶段,要不就是错的,要不就是很复杂。后来还是用区域生长实现的。再后来,发现PIL中有现成的,就用现成的了。

  2. Steven Wang says:
    二月 27 2011 at 22:50 Reply

    @amao, 很多图像处理库都有现成的实现,最近在DSP上做点开发,所以得自己动手来了。

  3. 大米 says:
    二月 27 2011 at 23:48 Reply

    涛哥原来是搞图形学的啊
    看不懂这些算法
    我学的还是太粗浅了

  4. Steven Wang says:
    二月 28 2011 at 09:35 Reply

    @大米, 这个是图像方面的,跟图形学还是有差别,看到你的新博客,现在对Linux关注得很多呀,以后有问题要向你请教了。

  5. 大米 says:
    二月 28 2011 at 12:43 Reply

    @Steven Wang, 哎 再过几年 也许才会有人请教我吧……
    我以后想搞搞运维神马的。

  6. 逍遥 says:
    四月 15 2011 at 22:43 Reply

    你还真是学术男...更新已经完全是专业内容了...

  7. 逍遥 says:
    四月 15 2011 at 22:44 Reply

    另外,Powered by Google App Engine, 由 xuming 提供 Micolog程序,你这个不是wordpress么? GAE常被封,稳定么?

  8. howto says:
    四月 22 2011 at 17:34 Reply

    太专业,C我就会简单的

Leave a Reply



About Me

    Steven Wang
    Student in Computer Software and Theory
    Life@Wuxi, Jiangsu
    Study@Jiangnan University
    more...

Feeds

  • Entries (RSS)
  • Comments (RSS)
  • 订阅到 Google Reader
  • 订阅到 抓虾
  • 订阅到 鲜果
  • 订阅到 QQ

Popular Posts

  • 围着脖子推GTalk机器人V1.0发布(27297)
  • 通过SSH + Chrome + Proxy Switchy!代理上网(19678)
  • 在GAE上部署Micolog博客系统(11307)
  • 围着脖子推V2.0 Beta1版发布 支持Twitter,新浪微博,人人网,嘀咕,做啥 同步更新(11059)
  • 围着脖子推GTalk机器人V1.0更新-增加接收Twitter更新等功能(10662)
  • 围着脖子推更新-增加同步更新网易微博、腾讯微博和搜狐微博(10481)
  • 在Matlab中实现Hough变换检测直线(8448)
  • Micolog主题(theme) —— translucence(7840)

Recent Posts

  • Steven Wang's 2011
  • 工作
  • T400升级Intel SSD
  • Java中的时区转换小结
  • 二值图像连通区标记之区域生长法
  • Steven Wang's 2010
  • 微博提醒应用上线
  • 双喜临门

Recent Comments

  • Queen:加油。...
  • Queen:hi,我来打个招呼,深圳的朋友。...
  • yu :@Steven Wang, p<...
  • ixwebhosting:文章总结的好潇洒,即将对末来学生生活说声...
  • john:希望新的一年更加美好...

Categories

  • Google App Engine(10)
  • 数字图像处理(11)
  • Micolog(7)
  • VPS(6)
  • 围着脖子推(15)
  • 人工神经网络(5)
  • 算法(11)
  • MyLife(16)
  • 媒体检索(4)
  • Others(8)
  • Python(2)

Archives

  • January 2012(1)
  • December 2011(2)
  • May 2011(1)
  • February 2011(1)
  • December 2010(3)
  • November 2010(1)
  • October 2010(1)
  • September 2010(4)
  • August 2010(2)
  • July 2010(5)
  • June 2010(4)
  • May 2010(7)

Blog roll

  • ~Issue
  • Fenng
  • 刘未鹏 | Mind Hacks
  • 林海听松
  • Yu Zheng
  • Johnny Han
  • 静静的安静
  • Dbger
  • land of promise
  • 星星
  • ISHENS|TECH
  • 天天软件园
  • leezhenchong's blog
  • 苏洋博客

  • Home
  • About
  • Google Profile
  • Twitter
  • 新浪微博
  • Login
Powered by Google App Engine  |   Designed by WebTreats  |   由 xuming 提供 Micolog程序