图像处理中最大连通区域的求解

8 篇文章 0 订阅
5 篇文章 0 订阅

在matlab中有对图像的连通区域进行求解的函数,即bwlabel。但是opencv里好像没有,所以这里自己实现一下,方便以后使用。

 

首先,我回顾一下bwlabel的参数和用法:

 L =bwlabel(BW,n)
   
返回一个和BW大小相同的L矩阵,包含了标记了BW中每个连通区域的类别标签,这些标签的值为12num(连通区域的个数)。n的值为48,表示是按4连通寻找区域,还是8连通寻找,默认为8

[L,num] = bwlabel(BW,n)这里num返回的就是BW中连通区域的个数。

 

 

实现步骤:

(1)将输入图像阈值化,若输入图像为彩色图像,则先灰度化后再阈值化为二值图像。

(2)给二值图像添加一个是否已访问的属性,类型为Bool(避免死循环)

(3)找到第一个非零的像素点,将其入栈并将其是否已访问的属性置为真。

(4)以栈的大小是否为0作为结束条件,寻找栈顶元素相邻的八邻域非零像素点,并将它们入栈,结束后将栈顶元素删除。

(5)当栈为空时,表明一个连通区域已经遍历完成,需继续找到下一个非空且未访问过的像素点作为起点,重复4步骤,直到所有的非零像素点都被访问完成。

(6)当所有的连通区域求解完成之后,将像素点个数最大的连通区域标记出来即可。

 

用到的数据结构

typedef struct{

         boolvSign;//像素点是否被访问过的标记,ture已访问,false表示未访问,给图片添加的一个属性

         intpixelValue;//像素值

}isVisit;

 

 

typedef struct{

         CvPointregionPoint;//该连通区域起点的坐标

         intregionId;//第i个连通区域的标号

         intpointNum;//第i个连通区域的像素点的总个数

}connectRegionNumSet;

输入图像:

 

求解的连通区域:


类似于bwlabel的标记


提取出最大的连通区域:

























--------------------------------------------------------------------------------------------------分割线-----------------------------------------------------------------------------------------------------

别急,代码在最下哦。

只有一个文件main.cpp。


#include<cv.h>

#include<highgui.h>

#include<iostream>

usingnamespace std;

typedefstruct{

         bool vSign;//像素点是否被访问过的标记,ture已访问,false表示未访问,给图片添加的一个属性

         int pixelValue;//像素值

}isVisit;

typedefstruct{

         CvPoint regionPoint;//该连通区域起点的坐标

         int regionId;//第i个连通区域的标号

         int pointNum;//第i个连通区域的像素点的总个数

}connectRegionNumSet;

 

intcalConnectRegionNums(IplImage *srcGray,vector<vector<isVisit>>& validPicture,vector<connectRegionNumSet> &regionSet);

 

intmain(){

         IplImage * src =cvLoadImage("useConnectImage.jpg");//F:/MasterHomeWork/ImageProcess/ConnectNumProgramMy/

         IplImage * srcGray = NULL;

 

         if(src->nChannels==1)

                   goto next;

         srcGray = cvCreateImage(cvSize(src->width,src->height),8,1);

         cvCvtColor(src,srcGray,CV_RGB2GRAY);

next:

         if(!srcGray)

                   cvThreshold(src,srcGray,66,255,CV_THRESH_BINARY);

         else

                   cvThreshold(srcGray,srcGray,66,255,CV_THRESH_BINARY);

 

         cvNamedWindow("srcBinaryGray");

         cvShowImage("srcBinaryGray",srcGray);

        

 

        

         //按照以下方法,申请的是堆heap内存空间,相对而言较大,足够一般用户使用,而且可以动态分配。

         vector<vector<isVisit> >validPoint;//此种申请发放只要电脑内存足够大,图片的尺寸也可足够大

         validPoint.resize(srcGray->height);

         for(int i =0;i<validPoint.size();i++)

                   validPoint[i].resize(srcGray->width);

 

         vector<connectRegionNumSet>regionSet;//存放找到的各个连通区域

         //regionSet.size()为连通区域的个数。

 

         cout<<"连通区域的数目:"<<calConnectRegionNums(srcGray,validPoint,regionSet)<<endl<<endl;//计算连通区域数目

        

         char text[3];//设置连通区域的编号,最小标号为0,最大编号为99

         CvFont font;//设置字体

         //参数从左到右:字体初始化,字体格式,字体宽度,字体高度,字体倾斜度,字体粗细,字体笔画类型

         cvInitFont(&font,CV_FONT_HERSHEY_COMPLEX, 0.6, 0.6, 0, 1, 8);

         for(int i=0;i<regionSet.size();i++){

 

                   cout<<"第"<<i<<"个连通区域的起点坐标 =("<<regionSet[i].regionPoint.x<<","<<regionSet[i].regionPoint.y<<")"<<",像素总点数="<<regionSet[i].pointNum<<endl;

 

                   if(i < 10){//连通区域的个数为个位数

                            text[0] = '0';

                            text[1] = '0'+i;

                   }

                   else{//连通区域的个数为十位数

                            text[0] ='0'+(i)/10;

                            text[1] ='0'+(i)%10;

                   }

                   text[2] = '\0';

                   cvPutText(src,text,regionSet[i].regionPoint,&font,cvScalar(0,0,255));

         }

 

         cvNamedWindow("src");

         cvShowImage("src",src);

         cvShowImage("srcGray",srcGray);

 

         cvWaitKey(0);

         cvReleaseImage(&src);

         cvReleaseImage(&srcGray);

         cvDestroyAllWindows();

}

 

intcalConnectRegionNums(IplImage *srcGray, vector<vector<isVisit>>& validPicture,vector<connectRegionNumSet> &regionSet)

{

         int regionId = 1;//管理连通区域标号的便令

         connectRegionNumSet regionSetTemp;//临时用到的regionSetTemp类型中间变量

         uchar * ptr = (uchar*)(srcGray->imageData);

         for(int y=0; y<srcGray->height;y++){//给图片加上一个是否已访问的属性

                   ptr = (uchar*)(srcGray->imageData+y*srcGray->widthStep);

                   for(int x=0;x<srcGray->width; x++){

                            validPicture[y][x].pixelValue= (int)ptr[x];

                            validPicture[y][x].vSign= false;//开始时默认都未访问

                   }

         }

         vector<CvPoint> stack;//stack(栈),heap(堆)

         CvPoint foundValidPoint;

         for(int y=0; y<srcGray->height;y++){//给图片加上一个是否已访问的属性

                   for(int x=0;x<srcGray->width; x++){

                            if(validPicture[y][x].pixelValue&& !validPicture[y][x].vSign){//找到下一个连通区域的起点,即像素值非零且未被访问过的点

                                     inteachRegionAcc = 1;//表示即将要寻找的连通区域的总像素点个数;

                                     //将validPicture[y][x]点默认为即将生成的连通区域的起点

                                     regionSetTemp.regionPoint= cvPoint(x,y);//x表示列,y表示行

                                     regionSetTemp.regionId= regionId++;

                                     regionSetTemp.pointNum= 1;

                                     regionSet.push_back(regionSetTemp);

                                     //将该点设置为已访问,并对其执行入栈操作

                                     validPicture[y][x].vSign= true;

                                     stack.push_back(cvPoint(x,y));

                                     while(stack.size()){//当栈内为元素时,表示该连通区域的点已经全部访问

                                               foundValidPoint= stack.back();//从栈尾开始寻找八连通的相邻点

                                               stack.pop_back();//上一句已得到栈尾像素点,该点可以出栈了

                                               inti = foundValidPoint.x;//t

                                               intj = foundValidPoint.y;//k

                                               intminY = (j-1<0?0:j-1);

                                               intmaxY = ((j+1>srcGray->height-1?srcGray->height-1:j+1));

                                               intminX = (i-1<0?0:i-1);

                                               intmaxX = (i+1>srcGray->width-1?srcGray->width-1:i+1);

                                               for(intk=minY; k<=maxY; k++){//在八连通范围内(两点之间距离小于根号2的点),表示其相邻点,入栈c

                                                        for(intt=minX; t<=maxX; t++){

                                                                 if(validPicture[k][t].pixelValue&& !validPicture[k][t].vSign){//validPicture[k][t]如果没有访问过

                                                                           validPicture[k][t].vSign= true;//标志为已访问,防止死循环

                                                                           stack.push_back(cvPoint(t,k));//入栈,以便再次迭代

                                                                           eachRegionAcc++;//相邻点的数目加1                                                        

                                                                 }

                                                        }

                                               }

                                     }

                                     if(eachRegionAcc> 1){//要求:连通区域的点数至少要有两个

                                               regionSet[regionSet.size()-1].pointNum= eachRegionAcc;

                                     }

                                     else{//单个像素点不算,如果单个像素点也算,去掉该else语句即可

                                               regionSet.pop_back();//上述默认的即将生成的连通区域不符合要求,出栈

                                               regionId--;

                                     }

                            }

                   }

         }

 

         //找到最大连通区域,并标记-----------------------------------------------

         int max_pointNum = 0; //最大连通区域的像素点个数

         int max_regionId = 0;  //最大连通区域的标号

         for(int i=0;i<regionSet.size();i++)

         {

                   if(max_pointNum <regionSet[i].pointNum)

                   {        max_pointNum = regionSet[i].pointNum;

                            max_regionId =i;

                   }

         }

 

         for(int i=0;i<regionSet.size();i++)

                   if(i!=max_regionId)

         {

                  

                            //标记

                   int x = regionSet[i].regionPoint.x;

                   int y =regionSet[i].regionPoint.y;

 

                   //重置每个像素点为未访问

                   for(int y=0;y<srcGray->height; y++){//给图片加上一个是否已访问的属性

                            for(int x=0;x<srcGray->width; x++){

                                     validPicture[y][x].vSign= false;//开始时默认都未访问

                            }

                   }

 

                   //将该点设置为已访问,并对其执行入栈操作

                   validPicture[y][x].vSign =true;

                   cvSet2D(srcGray,y,x,CV_RGB(0,0,0));

                   stack.push_back(cvPoint(x,y));

                   while(stack.size()){//当栈内为元素时,表示该连通区域的点已经全部访问

                            foundValidPoint =stack.back();//从栈尾开始寻找八连通的相邻点

                            stack.pop_back();//上一句已得到栈尾像素点,该点可以出栈了

                            int i = foundValidPoint.x;//t

                            int j =foundValidPoint.y;//k

                            int minY =(j-1<0?0:j-1);

                            int maxY =((j+1>srcGray->height-1?srcGray->height-1:j+1));

                            int minX =(i-1<0?0:i-1);

                            int maxX =(i+1>srcGray->width-1?srcGray->width-1:i+1);

                            for(int k=minY;k<=maxY; k++){//在八连通范围内(两点之间距离小于根号2的点),表示其相邻点,入栈c

                                     for(intt=minX; t<=maxX; t++){

                                               if(validPicture[k][t].pixelValue&& !validPicture[k][t].vSign){//validPicture[k][t]如果没有访问过

                                                        validPicture[k][t].vSign= true;//标志为已访问,防止死循环

                                                        stack.push_back(cvPoint(t,k));//入栈,以便再次迭代

                                                        cvSet2D(srcGray,k,t,CV_RGB(0,0,0));             

                                               }

                                     }

                            }

                   }

         }

        

         //找到最大连通区域,并标记-----------------------------------------------

 

         return regionSet.size();

 

 

}
  • 10
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值