two Pass方法连通域检测

原理:

Two-Pass方法检测连通域的原理可参见这篇博客:http://blog.csdn.net/lichengyu/article/details/13986521

参考下面动图,一目了然。

代码:

代码中标记图的数据类型要注意,如果first pass中标记数多于255,就不要用uchar类型,我直接设置为int类型。

  1 #include "opencv2/imgproc/imgproc.hpp"
  2 #include "opencv2/highgui/highgui.hpp"
  3 #include <map>
  4 #include <cassert>
  5 #include <iostream>
  6 
  7 using namespace std;
  8 
  9 const int max_size = 1000;
 10 int parent[max_size] = { 0 };
 11 
 12 // 找到label x的根节点
 13 int Find(int x, int parent[])
 14 {
 15     assert(x < max_size);
 16     int i = x;
 17     while (0 != parent[i])
 18         i = parent[i];
 19     return i;
 20 }
 21 
 22 // 将label x 和 label y合并到同一个连通域
 23 void Union(int x, int y, int parent[])
 24 {
 25     assert(x < max_size && y < max_size);
 26     int i = x;
 27     int j = y;
 28     while (0 != parent[i])
 29         i = parent[i];
 30     while (0 != parent[j])
 31         j = parent[j];
 32     if (i != j)
 33         parent[i] = j;
 34 }
 35 
 36 cv::Mat twoPassConnectComponent(cv::Mat &binaryImg)
 37 {
 38     int imgW = binaryImg.cols;
 39     int imgH = binaryImg.rows;
 40     int channel = binaryImg.channels();
 41     int type = binaryImg.type();
 42     // first pass
 43     int label = 0;
 44 
 45     cv::Mat dst = cv::Mat::zeros(cv::Size(imgW, imgH), CV_32SC1);
 46     for (int y = 0; y < imgH; y++)
 47     {
 48         for (int x = 0; x < imgW; x++)
 49         {
 50             if (binaryImg.at<uchar>(y, x) != 0)
 51             {
 52                 int left = (x - 1 < 0) ? 0 : dst.at<int>(y, x - 1);
 53                 int up = (y - 1 < 0) ? 0 : dst.at<int>(y - 1, x);
 54 
 55                 if (left != 0 || up != 0)
 56                 {
 57                     if (left != 0 && up != 0)
 58                     {
 59                         dst.at<int>(y, x) = min(left, up);
 60                         if (left <= up)
 61                             Union(up, left, parent);
 62                         else if (up<left)
 63                             Union(left, up, parent);
 64                     }
 65                     else
 66                         dst.at<int>(y, x) = max(left, up);
 67                 }
 68                 else
 69                 {
 70                     dst.at<int>(y, x) = ++label;
 71                 }
 72             }
 73         }
 74     }
 75 
 76     //second pass 
 77     for (int y = 0; y < imgH; y++)
 78     {
 79         for (int x = 0; x < imgW; x++)
 80         {
 81             if (binaryImg.at<uchar>(y, x) != 0)
 82                 dst.at<int>(y, x) = Find(dst.at<int>(y, x), parent);
 83         }
 84     }
 85 
 86     return dst;
 87 }
 88 
 89 cv::Scalar getRandomColor()
 90 {
 91     uchar r = 255 * (rand() / (1.0 + RAND_MAX));
 92     uchar g = 255 * (rand() / (1.0 + RAND_MAX));
 93     uchar b = 255 * (rand() / (1.0 + RAND_MAX));
 94     return cv::Scalar(b, g, r);
 95 }
 96 
 97 cv::Mat showColorLabel(cv::Mat label)
 98 {
 99     int imgW = label.cols;
100     int imgH = label.rows;
101     std::map<int, cv::Scalar> colors;
102 
103     cv::Mat colorLabel = cv::Mat::zeros(imgH, imgW, CV_8UC3);
104     int *pLabel = (int*)label.data;
105     uchar *pColorLabel = colorLabel.data;
106     for (int i = 0; i < imgH; i++)
107     {
108         for (int j = 0; j < imgW; j++)
109         {
110             int idx = (i*imgW + j) * 3;
111             int pixelValue = pLabel[i*imgW + j];
112             if (pixelValue > 0)
113             {
114                 if (colors.count(pixelValue) <= 0)
115                 {
116                     colors[pixelValue] = getRandomColor();
117                 }
118                 cv::Scalar color = colors[pixelValue];
119                 pColorLabel[idx + 0] = color[0];
120                 pColorLabel[idx + 1] = color[1];
121                 pColorLabel[idx + 2] = color[2];
122             }
123         }
124     }
125 
126     return colorLabel;
127 }
128 
129 int main() 
130 {
131     // 加载图像
132     string imageName = "data/source_images/logo.png";
133     cv::Mat image = cv::imread(imageName, 1);
134     if (!image.data) 
135     {
136         cout << "No image data" << endl;
137         getchar();
138         return -1;
139     }
140     //转为灰度图
141     cv::cvtColor(image, image, CV_RGB2GRAY);
142     //阈值化,情景为255,背景为0
143     cv::Mat threshImg;
144     cv::threshold(image, threshImg, 200, 255, cv::THRESH_BINARY);
145     cv::bitwise_not(threshImg, threshImg);
146 
147     //连通域检测 two Pass方法标记图像
148     cv::Mat labelImg = twoPassConnectComponent(threshImg);
149 
150     //不同连通区域用不同颜色表示
151     cv::Mat colorLabelImg=showColorLabel(labelImg);
152 
153     //显示
154     cv::imshow("thresh", threshImg);
155     cv::imshow("label", labelImg*20);
156     cv::imshow("colorLabel", colorLabelImg);
157     cv::waitKey(0);
158 }
View Code

结果:

使用OpenCV的logo为素材图,如下:

(1)转为灰度图然后阈值化

(2)寻找连通域

(3)不同连通区域不同颜色显示

    

 

   

 

 封装后的代码见我的码云code:https://gitee.com/rxdj/twoPassMethod.git

 

posted @ 2018-01-13 23:36  一度逍遥  阅读(4515)  评论(0编辑  收藏  举报