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

原图:

处理之后

 

posted on 2019-06-23 14:58  JxLuTech  阅读(1664)  评论(0编辑  收藏  举报