参考:
https://blog.csdn.net/qq_37059483/article/details/78018539
https://blog.csdn.net/xjt2015/article/details/51283387
http://www.cnblogs.com/ronny/p/img_aly_01.html
http://www.cnblogs.com/tiandsp/archive/2012/12/06/2805276.html
1 #include <opencv2opencv.hpp> 2 #include <iostream> 3 #include <vector> 4 #include <stack> 5 using namespace std; 6 using namespace cv; 7 8 typedef struct _Feather 9 { 10 int label; // 连通域的label值 11 int area; // 连通域的面积 12 Rect boundingbox; // 连通域的外接矩形框 13 } Feather; 14 15 /* 16 Input: 17 src: 待检测连通域的二值化图像 18 Output: 19 dst: 标记后的图像 20 featherList: 连通域特征的清单 21 return: 22 连通域数量。 23 */ 24 int bwLabel(Mat & src, Mat & dst, vector<Feather> & featherList) 25 { 26 int rows = src.rows; 27 int cols = src.cols; 28 29 int labelValue = 0; 30 Point seed, neighbor; 31 stack<Point> pointStack; // 堆栈 32 33 int area = 0; // 用于计算连通域的面积 34 35 int leftBoundary = 0; // 连通域的左边界,即外接最小矩形的左边框,横坐标值,依此类推 36 int rightBoundary = 0; 37 int topBoundary = 0; 38 int bottomBoundary = 0; 39 Rect box; // 外接矩形框 40 Feather feather; 41 42 featherList.clear(); // 清除数组 43 44 dst.release(); 45 dst = src.clone(); 46 for (int i = 0; i < rows; i++) 47 { 48 uchar *pRow = dst.ptr<uchar>(i); 49 for (int j = 0; j < cols; j++) 50 { 51 //【注:一旦连通域赋值为1;遍历当前行的下一行时候,就会自动跳过下面if判断】 52 if (pRow[j] == 255) 53 { 54 area = 0; 55 labelValue++; // labelValue最大为254,最小为1. 56 seed = Point(j, i); //(731,49) // Point(横坐标,纵坐标)(x,y) 57 //int p = dst.ptr<uchar>(i)[j]; 58 dst.at<uchar>(seed) = labelValue;//1 59 pointStack.push(seed);//坐标压栈 60 61 area++; 62 leftBoundary = seed.x;//731 63 rightBoundary = seed.x;//731 64 topBoundary = seed.y;//49 65 bottomBoundary = seed.y;//49 66 67 while (!pointStack.empty()) 68 { 69 //<1>右邻像素点 70 neighbor = Point(seed.x + 1, seed.y); 71 if ((seed.x != (cols - 1)) && (dst.at<uchar>(neighbor) == 255)) 72 { 73 dst.at<uchar>(neighbor) = labelValue;//【注:】赋值便签1,保证不走回头路和分类连通域 74 pointStack.push(neighbor); 75 76 area++; 77 if (rightBoundary < neighbor.x)//更新边界 78 rightBoundary = neighbor.x; 79 } 80 //<2>下邻像素点 81 neighbor = Point(seed.x, seed.y + 1); 82 if ((seed.y != (rows - 1)) && (dst.at<uchar>(neighbor) == 255)) 83 { 84 dst.at<uchar>(neighbor) = labelValue; 85 pointStack.push(neighbor); 86 87 area++; 88 if (bottomBoundary < neighbor.y) 89 bottomBoundary = neighbor.y; 90 91 } 92 //<3>左邻像素点 93 neighbor = Point(seed.x - 1, seed.y); 94 //int testx = seed.x; 95 //int testp = dst.at<uchar>(neighbor); 96 if ((seed.x != 0) && (dst.at<uchar>(neighbor) == 255)) 97 { 98 dst.at<uchar>(neighbor) = labelValue; 99 pointStack.push(neighbor); 100 101 area++; 102 if (leftBoundary > neighbor.x) 103 leftBoundary = neighbor.x; 104 } 105 //<4>上邻边界点 106 neighbor = Point(seed.x, seed.y - 1); 107 if ((seed.y != 0) && (dst.at<uchar>(neighbor) == 255)) 108 { 109 dst.at<uchar>(neighbor) = labelValue; 110 pointStack.push(neighbor); 111 112 area++; 113 if (topBoundary > neighbor.y) 114 topBoundary = neighbor.y; 115 } 116 117 seed = pointStack.top(); 118 pointStack.pop(); 119 } 120 box = Rect(leftBoundary, topBoundary, rightBoundary - leftBoundary, bottomBoundary - topBoundary); 121 rectangle(src, box, 255); 122 feather.area = area; 123 feather.boundingbox = box; 124 feather.label = labelValue; 125 featherList.push_back(feather); 126 } 127 } 128 } 129 return labelValue; 130 } 131 132 int main(int argc, char *argv[]) 133 { 134 Mat src(imread("test3.jpg", 0)); 135 if (src.empty()) 136 exit(-1); 137 threshold(src, src, 127, 255, THRESH_BINARY); // 二值化图像 138 vector<Feather> featherList; // 存放连通域特征 139 Mat dst; 140 cout << "连通域数量: " << bwLabel(src, dst, featherList) << endl; 141 142 // 为了方便观察,可以将label“放大” 143 for (int i = 0; i < dst.rows; i++) 144 { 145 uchar *p = dst.ptr<uchar>(i); 146 for (int j = 0; j < dst.cols; j++) 147 { 148 p[j] = 30 * p[j]; 149 } 150 } 151 152 cout << "标号" << " " << "面积" << endl; 153 for (vector<Feather>::iterator it = featherList.begin(); it < featherList.end(); it++) 154 { 155 cout << it->label << " " << it->area << endl; 156 rectangle(dst, it->boundingbox, 255); 157 } 158 cv::namedWindow("dst", WINDOW_NORMAL); 159 imshow("src", src); 160 imshow("dst", dst); 161 162 waitKey(); 163 destroyAllWindows(); 164 165 system("pause"); 166 return 0; 167 }