CV学习日志:CV开发之寻找连通域

         OpenCV中为人熟知的提取连通域的函数是connectedComponents,但它是针对灰度图像的。其实,OpenCV中还有一个函数可以提取short或ushort图像的连通域且对其源码稍加修改就可以提取任何类型的矩阵的连通域,此函数就是filterSpeckles。它原本是用于后处理StereoBM和StereoSGBM生成的视差图,但我们完全可以对扩展其应用,用于提取任何类型矩阵的连通域。

         filterSpeckles的关键函数有三个:newVal、maxSpeckleSize、maxDiff。

         (1)maxDiff:相邻元素的值小于此值,认为是属于同一连通域

         (2)maxSpeckleSize:连通域的像素个数小于此值,则被滤掉。

         (3)newVal:被滤掉的连通域设置为此值。

         OpenCV中寻找连通域使用的是深度搜索法DFS。这里,我提取其源码并增加了基于广度搜索法BFS的实现。通过对DFS和BFS,DFS效率更高,符合连通域提取原理。

 

         以下是详细代码,依赖于C++14、OpenCV4.x和Spdlog。

  1 #include <opencv2/opencv.hpp>
  2 #include <spdlog/spdlog.h>
  3 using namespace std;
  4 using namespace cv;
  5 #define RANDOM(min, max) (rand() % ((max) - (min) + 1) + (min))
  6 #define ns1970 chrono::time_point_cast<chrono::nanoseconds>(chrono::system_clock::now()).time_since_epoch().count()
  7 class FilterSpeckles
  8 {
  9 public:
 10     static void TestMe(int argc = 0, char** argv = 0)
 11     {
 12         int N = 999;
 13         for (int k = 0; k < N; ++k)
 14         {
 15             //1.GenerateData
 16             int rows = RANDOM(128, 1024);
 17             int cols = RANDOM(128, 1024);
 18             Mat_<uchar> src8u(rows, cols); randu(src8u, 0, UINT8_MAX);
 19             Mat_<short> src16s(rows, cols); randu(src16s, 0, INT16_MAX);
 20             int invalidV = -1;
 21             int speckleSize = RANDOM(128, 1024);
 22             int speckleRange = RANDOM(2, 128);
 23 
 24             //2.CalcByOpenCV
 25             Mat_<uchar> src8u0 = src8u.clone();
 26             Mat_<short> src16s0 = src16s.clone();
 27             int64 t00 = ns1970; cv::filterSpeckles(src8u0, invalidV, speckleSize, speckleRange); t00 = ns1970 - t00;
 28             int64 t01 = ns1970; cv::filterSpeckles(src16s0, invalidV, speckleSize, speckleRange); t01 = ns1970 - t01;
 29 
 30             //3.CalcByBFS
 31             Mat_<uchar> src8u1 = src8u.clone();
 32             Mat_<short> src16s1 = src16s.clone();
 33             FilterSpeckles filter10(src8u1.rows, src8u1.cols, 3);
 34             FilterSpeckles filter11(src16s1.rows, src16s1.cols, 3);
 35             int64 t10 = ns1970; filter10.BFS(src8u1.ptr<uchar>(), invalidV, speckleSize, speckleRange); t10 = ns1970 - t10;
 36             int64 t11 = ns1970; filter11.BFS(src16s1.ptr<short>(), invalidV, speckleSize, speckleRange); t11 = ns1970 - t11;
 37 
 38             //4.CalcByDFS
 39             Mat_<uchar> src8u2 = src8u.clone();
 40             Mat_<short> src16s2 = src16s.clone();
 41             FilterSpeckles filter20(src8u2.rows, src8u2.cols, 3);
 42             FilterSpeckles filter21(src16s2.rows, src16s2.cols, 3);
 43             int64 t20 = ns1970; filter20.DFS(src8u2.ptr<uchar>(), invalidV, speckleSize, speckleRange); t20 = ns1970 - t20;
 44             int64 t21 = ns1970; filter21.DFS(src16s2.ptr<short>(), invalidV, speckleSize, speckleRange); t21 = ns1970 - t21;
 45 
 46             //5.AnalyzeError
 47             double infSrc8u0Src8u1 = norm(src8u0, src8u1);
 48             double infSrc16s0Src16s1 = norm(src16s0, src16s1);
 49             double infSrc8u0Src8u2 = norm(src8u0, src8u2);
 50             double infSrc16s0Src16s2 = norm(src16s0, src16s2);
 51             double sumFilters[4] = { 0, 0, 0, 0 };
 52             for (int i = 0; i < filter10.validAreas.size(); ++i)
 53                 for (int j = 0; j < filter10.validAreas[i].size(); ++j)
 54                     sumFilters[0] += std::abs(filter10.validAreas[i][j].x) + std::abs(filter10.validAreas[i][j].y) + std::abs(filter10.validAreas[i][j].z);
 55             for (int i = 0; i < filter11.validAreas.size(); ++i)
 56                 for (int j = 0; j < filter11.validAreas[i].size(); ++j)
 57                     sumFilters[1] += std::abs(filter11.validAreas[i][j].x) + std::abs(filter11.validAreas[i][j].y) + std::abs(filter11.validAreas[i][j].z);
 58             for (int i = 0; i < filter20.validAreas.size(); ++i)
 59                 for (int j = 0; j < filter20.validAreas[i].size(); ++j)
 60                     sumFilters[2] += std::abs(filter20.validAreas[i][j].x) + std::abs(filter20.validAreas[i][j].y) + std::abs(filter20.validAreas[i][j].z);
 61             for (int i = 0; i < filter21.validAreas.size(); ++i)
 62                 for (int j = 0; j < filter21.validAreas[i].size(); ++j)
 63                     sumFilters[3] += std::abs(filter21.validAreas[i][j].x) + std::abs(filter21.validAreas[i][j].y) + std::abs(filter21.validAreas[i][j].z);
 64             double infFilter10Filter20 = std::abs(sumFilters[0] - sumFilters[2]);
 65             double infFilter11Filter21 = std::abs(sumFilters[1] - sumFilters[3]);
 66 
 67             //6.PrintError
 68             spdlog::info("LoopCount: {}      timeVS: {} vs {}   {} vs {}", k, t10 - t00, t20 - t00, t11 - t01, t21 - t01);
 69             if (infSrc8u0Src8u1 > 0 || infSrc16s0Src16s1 > 0 || infSrc8u0Src8u2 > 0 || infSrc16s0Src16s2 > 0 || infFilter10Filter20 > 0 || infFilter11Filter21 > 0)
 70             {
 71                 spdlog::info("5.1PrintError");
 72                 spdlog::info("infSrc8u0Src8u1: {}", infSrc8u0Src8u1);
 73                 spdlog::info("infSrc16s0Src16s1: {}", infSrc16s0Src16s1);
 74                 spdlog::info("infSrc8u0Src8u2: {}", infSrc8u0Src8u2);
 75                 spdlog::info("infSrc16s0Src16s2: {}", infSrc16s0Src16s2);
 76                 spdlog::info("infFilter10Filter20: {}", infFilter10Filter20);
 77                 spdlog::info("infFilter11Filter21: {}", infFilter11Filter21);
 78                 spdlog::info("Press any key to continue");
 79                 getchar();
 80             }
 81         }
 82     }
 83 
 84 private://ImaSize
 85     int rows = 0;
 86     int cols = 0;
 87     int total = 0;
 88     int saveOption = 0; //0-none, 1-valid, 2-small, 3-both
 89 
 90 private://BufData
 91     int* labeldata = 0;
 92     bool* typedata = 0;
 93     Point3i* dfsdata = 0;
 94     vector<char> buffer = {};
 95 
 96 public://AreaData
 97     vector<Point3i> area = {};//Point2iForXY or intForPos
 98     vector<vector<Point3i>> validAreas = {};
 99     vector<vector<Point3i>> smallAreas = {};
100 
101 public:
102     FilterSpeckles(int rows0, int cols0, int saveOption0)
103     {
104         //1.
105         rows = rows0;
106         cols = cols0;
107         total = rows * cols;
108         saveOption = saveOption0;
109 
110         //2.
111         int bufSize = total * (sizeof(int) + sizeof(bool) + sizeof(Point3i));
112         buffer.resize(bufSize);
113         labeldata = (int*)buffer.data();
114         typedata = (bool*)(labeldata + total);
115         dfsdata = (Point3i*)(typedata + total);
116 
117         //3.
118         area.reserve(total);
119         if (saveOption == 1 || saveOption == 3) validAreas.reserve(total);
120         if (saveOption == 2 || saveOption == 3) smallAreas.reserve(total);
121     }
122     template <typename dp> bool BFS(dp* imadata, int invalidV, int speckleSize, int speckleRange)
123     {
124         validAreas.clear(); smallAreas.clear();
125         memset(labeldata, 0, total * sizeof(labeldata[0]));
126         for (int i = 0, label = 0; i < rows; ++i)
127         {
128             dp* imadataI = imadata + i * cols;
129             int* labeldataI = labeldata + i * cols;
130 
131             for (int j = 0; j < cols; ++j)
132             {
133                 //1.InvalidPoint
134                 if (imadataI[j] == invalidV) continue;
135 
136                 //2.LabeledPoint
137                 if (labeldataI[j])
138                 {
139                     if (typedata[labeldataI[j]])
140                         imadataI[j] = (dp)invalidV;
141                     continue;
142                 }
143 
144                 //3.UnlabeledPoint
145                 labeldataI[j] = ++label;
146                 area.clear(); area.push_back(Point3i(j, i, i * cols + j));
147                 for (int k = 0, ii, jj, cur; k < area.size(); ++k)
148                 {
149                     ii = area[k].y;
150                     jj = area[k].x;
151                     cur = area[k].z;
152                     dp* imadataX = imadata + cur;
153                     int* labeldataX = labeldata + cur;
154 
155                     if (ii < rows - 1 && !labeldataX[+cols] && imadataX[+cols] != invalidV && abs(imadataX[0] - imadataX[+cols]) <= speckleRange)
156                     {
157                         labeldataX[+cols] = label;
158                         area.push_back(Point3i(jj, ii + 1, (ii + 1) * cols + jj));
159                     }
160 
161                     if (ii > 0 && !labeldataX[-cols] && imadataX[-cols] != invalidV && abs(imadataX[0] - imadataX[-cols]) <= speckleRange)
162                     {
163                         labeldataX[-cols] = label;
164                         area.push_back(Point3i(jj, ii - 1, (ii - 1) * cols + jj));
165                     }
166 
167                     if (jj < cols - 1 && !labeldataX[+1] && imadataX[+1] != invalidV && abs(imadataX[0] - imadataX[+1]) <= speckleRange)
168                     {
169                         labeldataX[+1] = label;
170                         area.push_back(Point3i(jj + 1, ii, ii * cols + jj + 1));
171                     }
172 
173                     if (jj > 0 && !labeldataX[-1] && imadataX[-1] != invalidV && abs(imadataX[0] - imadataX[-1]) <= speckleRange)
174                     {
175                         labeldataX[-1] = label;
176                         area.push_back(Point3i(jj - 1, ii, ii * cols + jj - 1));
177                     }
178                 }
179 
180                 //4.MarkLabel
181                 if (area.size() <= speckleSize)
182                 {
183                     typedata[labeldataI[j]] = true;
184                     imadataI[j] = (dp)invalidV;
185                     if (saveOption == 2 || saveOption == 3) smallAreas.push_back(area);
186                 }
187                 else
188                 {
189                     typedata[labeldataI[j]] = false;
190                     if (saveOption == 1 || saveOption == 3) validAreas.push_back(area);
191                 }
192             }
193         }
194         return true;
195     }
196     template <typename dp> bool DFS(dp* imadata, int invalidV, int speckleSize, int speckleRange)
197     {
198         validAreas.clear(); smallAreas.clear();
199         memset(labeldata, 0, total * sizeof(labeldata[0]));
200         for (int i = 0, label = 0; i < rows; ++i)
201         {
202             dp* imadataI = imadata + i * cols;
203             int* labeldataI = labeldata + i * cols;
204 
205             for (int j = 0; j < cols; ++j)
206             {
207                 //1.InvalidPoint
208                 if (imadataI[j] == invalidV) continue;
209 
210                 //2.LabeledPoint
211                 if (labeldataI[j])
212                 {
213                     if (typedata[labeldataI[j]])
214                         imadataI[j] = (dp)invalidV;
215                     continue;
216                 }
217 
218                 //3.UnlabeledPoint
219                 labeldataI[j] = ++label;
220                 dfsdata[0] = Point3i(j, i, i * cols + j);
221                 area.clear(); area.push_back(dfsdata[0]);
222                 for (int k = 0, ii, jj, cur; k >= 0; --k)
223                 {
224                     ii = dfsdata[k].y;
225                     jj = dfsdata[k].x;
226                     cur = dfsdata[k].z;
227                     dp* imadataX = imadata + cur;
228                     int* labeldataX = labeldata + cur;
229 
230                     if (ii < rows - 1 && !labeldataX[+cols] && imadataX[+cols] != invalidV && abs(imadataX[0] - imadataX[+cols]) <= speckleRange)
231                     {
232                         labeldataX[+cols] = label;
233                         dfsdata[k] = Point3i(jj, ii + 1, (ii + 1) * cols + jj);
234                         area.push_back(dfsdata[k++]);
235                     }
236 
237                     if (ii > 0 && !labeldataX[-cols] && imadataX[-cols] != invalidV && abs(imadataX[0] - imadataX[-cols]) <= speckleRange)
238                     {
239                         labeldataX[-cols] = label;
240                         dfsdata[k] = Point3i(jj, ii - 1, (ii - 1) * cols + jj);
241                         area.push_back(dfsdata[k++]);
242                     }
243 
244                     if (jj < cols - 1 && !labeldataX[+1] && imadataX[+1] != invalidV && abs(imadataX[0] - imadataX[+1]) <= speckleRange)
245                     {
246                         labeldataX[+1] = label;
247                         dfsdata[k] = Point3i(jj + 1, ii, ii * cols + jj + 1);
248                         area.push_back(dfsdata[k++]);
249                     }
250 
251                     if (jj > 0 && !labeldataX[-1] && imadataX[-1] != invalidV && abs(imadataX[0] - imadataX[-1]) <= speckleRange)
252                     {
253                         labeldataX[-1] = label;
254                         dfsdata[k] = Point3i(jj - 1, ii, ii * cols + jj - 1);
255                         area.push_back(dfsdata[k++]);
256                     }
257                 }
258 
259                 //4.MarkLabel
260                 if (area.size() <= speckleSize)
261                 {
262                     typedata[labeldataI[j]] = true;
263                     imadataI[j] = (dp)invalidV;
264                     if (saveOption == 2 || saveOption == 3) smallAreas.push_back(area);
265                 }
266                 else
267                 {
268                     typedata[labeldataI[j]] = false;
269                     if (saveOption == 1 || saveOption == 3) validAreas.push_back(area);
270                 }
271             }
272         }
273         return true;
274     }
275 };
276 
277 int main(int argc, char** argv) { FilterSpeckles::TestMe(argc, argv); return 0; }
View Code
原文地址:https://www.cnblogs.com/dzyBK/p/14100478.html