之前发过一篇博文是用python写连通区域标记算法,搜索过程采用的是深度优先搜索,对于一些像素数目较多的目标区域,容易陷入深度递归,造成stack溢出,所以这次用广度优先搜索写了一下,算法中用到了队列数据结构,平台是VS2008+opencv2.3.1,对一副二值图进行连通区域标记。
广度优先搜索的伪代码如下:
连通区域标记的代码如下:
#include "stdafx.h"
#include
#include "highgui.h"
#include
typedef unsigned long uint32;
typedef unsigned int uint16;
typedef unsigned char uint8;
#define WHITE 1
#define GRAY 2
#define BLACK 3
#define NIL 0
#define INFINITE 255
typedef CvPoint ElemType;
typedef struct Queue
{
ElemType* data;
int front;
int rear;
int Qsize;
}Queue;
//初始化通过参数传进来创建队列的大小
bool initQueue(Queue* q,int size)
{
q->front = 0;
q->rear = 0;
q->Qsize = size;
q->data = (ElemType*)malloc(q->Qsize*sizeof(ElemType));
if( NULL == q->data)
return false;
return true;
}
//销毁队列,释放内存
void destroyQueue(Queue* q)
{
q->front = 0;
q->rear = 0;
q->Qsize = 0 ;
free((q->data));
q->data = NULL;
}
//清空队列
void clearQueue(Queue* q)
{
q->front = 0;
q->rear = 0;
}
//判断队列是否为空
bool is_empty(Queue *q)
{
if(q->front == q->rear)
{
printf("the queue is empty! \n");
return true;
}
else
{
return false;
}
}
//返回队首元素
bool getHead(Queue *q,ElemType *e)
{
if(is_empty(q))
{
printf("can not get the head element! \n");
return false;
}
else
{
*e = q->data[q->front];
return true;
}
}
//返回队列长度:在循环队列中
int Qlength(Queue *q)
{
return (q->rear-q->front+q->Qsize)%q->Qsize;
}
//入队
bool enQueue(Queue *q,ElemType e)
{
//如果队列已满,重新分配内存
if(q->rear == q->Qsize-1)
{
q->data = (ElemType*)realloc(q->data,2*q->Qsize*sizeof(ElemType));
if(q->data == NULL)
return false;
else
q->Qsize *= 2;
}
//先赋值,然后队尾循环加1
q->data[q->rear] = e;
q->rear = (q->rear+1)%q->Qsize;
return true;
}
//出队
bool deQueue(Queue *q,ElemType *e)
{
if(is_empty(q))
return false;
else
{
*e = q->data[q->front];
//队首标记循环加1
q->front = (q->front+1+q->Qsize) % q->Qsize;
}
return true;
}
void BFS( IplImage* G, IplImage* Label_Image, int x, int y ,uint8 num)
{
IplImage* Color_src,*D_src;//白色表示未被搜索过,黑色表示搜索完毕,灰色表示正在搜索
//Queue *Q;
CvPoint *u=&cvPoint(0,0);
int i,j,m,n;
Queue Q;
initQueue(&Q,10);
//return;
Color_src = cvCreateImage( cvGetSize(G), 8, 1 );
D_src = cvCreateImage( cvGetSize(G), 8, 1 );
//给所有点标记为白色
for( j=0; jheight; j++ )
{
for( i=0; iwidth; i++ )
{
cvSetReal2D(Color_src,j,i,WHITE);
cvSetReal2D(D_src,j,i,INFINITE);
}
}
cvSetReal2D( Color_src, y, x, GRAY );
cvSetReal2D( D_src, y, x, 0 );
enQueue( &Q, cvPoint( x, y ));
while( !is_empty(&Q) )
{
if( deQueue( &Q, u ) )
{
cvSetReal2D( Label_Image,u->y, u->x, num );
if( u->x==0|| u->x==G->width||u->y==0|| u->y==G->height )//不处理边界点
continue;
else
{
for( n=u->y - 1;n<=u->y+1; n++ )//八邻域
{
for( m=u->x - 1;m<=u->x+1; m++ )
{
if( m==u->x && n==u->y ){}
else if( cvGetReal2D(G,n,m)==0 ){}
else
{
if(WHITE==cvGetReal2D(Color_src,n,m))
{
cvSetReal2D( Label_Image,n, m, num );//标记图像
cvSetReal2D( Color_src, n, m, GRAY );
uint8 dis = cvGetReal2D(D_src,u->y, u->x)+1;//该点距离种子点的距离
if(dis>255) dis=255;
cvSetReal2D(D_src,n,m,dis);
enQueue( &Q, cvPoint( m, n ));
}
}
}
}
cvSetReal2D( Color_src, u->y, u->x, BLACK);
}
}
else break;
}
clearQueue(&Q);
cvReleaseImage(&Color_src);
cvReleaseImage(&D_src);
}
void bwLabel(IplImage* img, IplImage* L_src ,IplImage* dst)
{
int i,j;
char s[5];
uint8 Label_value=0;
CvFont font;
cvInitFont( &font,CV_FONT_HERSHEY_SIMPLEX,1,0.5);
for( j=0; jheight; j++ )
{
for( i=0; iwidth; i++ )
{
uint8 Label=cvGetReal2D(L_src,j,i);
uint8 value=cvGetReal2D(img,j,i);
if(Label==0&&value!=0)
{
Label_value++;
itoa( Label_value,s,10);
cvPutText(dst, s, cvPoint(i,j),&font,cvScalar(255,255,255));
BFS(img, L_src, i,j,Label_value);//以i,j为种子点标记同一目标
}
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
IplImage* src,*src_gray,*L_src;
int i,j,w,h;
src = cvLoadImage( "bwlabel.bmp" );//读取原图
src_gray = cvCreateImage( cvGetSize(src), 8, 1 );
L_src = cvCreateImage( cvGetSize(src), 8, 1 );
cvSetZero(L_src);//对标记图像清零
w = cvGetSize(src).width;
h = cvGetSize(src).height;
cvCvtColor( src, src_gray, CV_BGR2GRAY );
bwLabel(src_gray,L_src,src);//对图像进行标记
cvNamedWindow("1",CV_WINDOW_AUTOSIZE);
cvShowImage("1",src);//标记后的图像
cvNamedWindow("2",CV_WINDOW_AUTOSIZE);
cvShowImage("2",src_gray);//原图
cvWaitKey(0);
cvReleaseImage(&src);
cvReleaseImage(&src_gray);
cvDestroyWindow("1");
return 0;
}