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();
}

原圖:

處理之后

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM