OpenCV二值图求最大连通区域算法(广度优先算法 BFS)

Posted jxlutech

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV二值图求最大连通区域算法(广度优先算法 BFS)相关的知识,希望对你有一定的参考价值。

#include <iostream>
#include <opencv2\opencv.hpp>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <windows.h>
#include <math.h> 
#include <queue>

using namespace std;using namespace cv;

#define WHITE 1
#define GRAY 2
#define BLACK 3
#define INFINITE 255

typedef CvPoint ElemType;

typedef struct

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

typedef struct

    CvPoint regionPoint;//该连通区域起点的坐标
    int regionId;//第i个连通区域的标号
    int pointNum;//第i个连通区域的像素点的总个数
connectRegionNumSet;

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

int main() 
    IplImage * src = cvLoadImage("ff.jpg");
    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);
    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 << "连通区域的数目:" << calConnectRegionNumsBfs(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);//设置字体//参数从左到右:字体初始化,字体格式,字体宽度,字体高度,字体倾斜度,字体粗细,字体笔画类型
    int max_pointNum = 0; //最大连通区域的像素点个数
    int max_regionId = 0;
    for (int i = 0; i<regionSet.size(); i++)
    
        cout << "" << i << "个连通区域的起点坐标 =(" << regionSet[i].regionPoint.x << "," << regionSet[i].regionPoint.y << ")" << ",像素总点数=" << regionSet[i].pointNum << endl;
        cout << "ID:"<<regionSet[i].regionId << 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));
        //找到最大连通区域,并标记----------------------------------------------
        if (max_pointNum < regionSet[i].pointNum)
        
            max_regionId = i;
            max_pointNum = regionSet[i].pointNum;
        
    
    cout << "" << max_regionId << "个连通区域最大" <<"像素总点数=" << regionSet[max_regionId].pointNum << endl;
    cvNamedWindow("src");
    cvShowImage("src", src);
    //cvShowImage("srcGray", srcGray);
    cvWaitKey(0);
    cvReleaseImage(&src);
    cvReleaseImage(&srcGray);
    cvDestroyAllWindows();


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

    int regionId = 0;//管理连通区域标号的变量
    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;//开始时默认都未访问
        
    
    queue<CvPoint> q;
    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) //找到下一个连通区域的起点,即像素值非零且未被访问过的点
                int eachRegionAcc = 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;
                q.push(cvPoint(x, y));
                while (!q.empty())
                
                    foundValidPoint = q.front();
                    q.pop();
                    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 (int t = minX; t <= maxX; t++)
                        
                            if (validPicture[k][t].pixelValue && !validPicture[k][t].vSign)//validPicture[k][t]如果没有访问过
                            
                                validPicture[k][t].vSign = true;//标志为已访问,防止死循环
                                q.push(cvPoint(t, k));
                                eachRegionAcc++;//相邻点的数目加1                                                        
                            
                        
                    
                
                if (eachRegionAcc > 1) //要求:连通区域的点数至少要有两个
                    regionSet[regionSet.size() - 1].pointNum = eachRegionAcc;
                else
                //单个像素点不算,如果单个像素点也算,去掉该else语句即可
                    regionSet.pop_back();//上述默认的即将生成的连通区域不符合要求,出栈
                    regionId--;
                
            
        
    
    return regionSet.size();

 

以上是关于OpenCV二值图求最大连通区域算法(广度优先算法 BFS)的主要内容,如果未能解决你的问题,请参考以下文章

使用OpenCV查找二值图中最大连通区域

S0.4 二值图与阈值化

opencv mser算法框出图片文字区域

OpenCV(26)图像分割 -- 距离变换与分水岭算法(硬币检测扑克牌检测车道检测)

自己写的二值图的轮廓图算法

OpenCV技巧 | 二值图孔洞填充方法与实现(附源码)