广度优先的连通域求取算法,参考了http://blog.youkuaiyun.com/cui134/article/details/39610819文章。与我们平时用到的深度优先连通域算法的区别在于后者一般包含迭代求解过程,在数据量很大时可能出现栈溢出的情况;前者基于广度搜索,不存在迭代过程。
把代码和效果图贴出来,以后可能会用到。
#include <opencv2/opencv.hpp>
#include "highgui.h"
#include <math.h>
using namespace std;
using namespace cv;
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 Point 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 isQueueEmpty(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(isQueueEmpty(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(isQueueEmpty(q))
return false;
else
{
*e = q->data[q->front];
//队首标记循环加1
q->front = (q->front+1+q->Qsize) % q->Qsize;
}
return true;
}
void BFS( Mat& G,Mat& Label_Image, int x, int y ,uint8 num)
{
Mat Color_src(G.size(), CV_8UC1);//白色表示未被搜索过,黑色表示搜索完毕,灰色表示正在搜索
Point *u=&Point(0,0);
int i,j,m,n;
Queue Q;
initQueue(&Q,10);
//给所有点标记为白色
Color_src.setTo(Scalar(WHITE));
Color_src.at<unsigned char>(y,x) = GRAY;
enQueue( &Q, Point( x, y));
while( !isQueueEmpty(&Q) )
{
if( deQueue( &Q, u ) )
{
Label_Image.at<unsigned char>(u->y,u->x) = num;
if( u->x==0|| u->x==G.cols||u->y==0|| u->y==G.rows )//不处理边界点
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(G.at<unsigned char>(n,m)==0 ){}
else
{
if(WHITE==Color_src.at<unsigned char>(n,m))
{
Label_Image.at<unsigned char>(n,m) = num;
Color_src.at<unsigned char>(n,m) = GRAY;
enQueue( &Q, Point( m, n ));
}
}
}
}
Color_src.at<unsigned char>(u->y, u->x) = BLACK;
}
}
else break;
}
clearQueue(&Q);
}
void bwLabel(Mat img, Mat L_src ,Mat dst)
{
int i,j;
char s[5];
uint8 Label_value=0;
for( j=0; j<img.rows; j++ )
{
for( i=0; i<img.cols; i++ )
{
uint8 Label=L_src.at<unsigned char>(j,i);
uint8 value=img.at<unsigned char>(j,i);
if(Label==0&&value!=0)
{
Label_value++;
itoa( Label_value,s,10);
putText(dst, s, Point(i,j), FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255));
BFS(img, L_src, i,j,Label_value);//以i,j为种子点标记同一目标
}
}
}
}
int main()
{
Mat src, src_gray, L_src;
int i,j,w,h;
src = imread( "1.bmp" );//读取原图
L_src.create(src.size(), CV_8UC1);
L_src.setTo(0);
w = src.cols;
h = src.rows;
cvtColor( src, src_gray, CV_BGR2GRAY );
long long t = getTickCount();
bwLabel(src_gray,L_src,src);//对图像进行标记
cout<<"time: "<<(getTickCount()-t)/getTickFrequency();
namedWindow("1",CV_WINDOW_AUTOSIZE);
imshow("1",src);//标记后的图像
namedWindow("2",CV_WINDOW_AUTOSIZE);
imshow("2",src_gray);//原图
cvWaitKey(0);
return 0;
}
算法效果如下图所示: