From 45b4f4f32bf2aec782432c9755e2e5b03a7bd152 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 24 Aug 2012 23:57:17 -0700 Subject: [PATCH 01/16] connectedComponents: warning free version --- .../include/opencv2/imgproc/imgproc.hpp | 5 + modules/imgproc/src/connectedcomponents.cpp | 365 ++++++++++++++++++ samples/cpp/connected_components.cpp | 32 +- 3 files changed, 384 insertions(+), 18 deletions(-) create mode 100644 modules/imgproc/src/connectedcomponents.cpp diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 63f5218140..3d80cfee4e 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1091,6 +1091,11 @@ enum { TM_SQDIFF=0, TM_SQDIFF_NORMED=1, TM_CCORR=2, TM_CCORR_NORMED=3, TM_CCOEFF CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, OutputArray result, int method ); +//! computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total +//number of labels [0, N-1] where 0 represents the background label. +CV_EXPORTS_W uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity = 8); + + //! mode of the contour retrieval algorithm enum { diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp new file mode 100644 index 0000000000..cc83f97481 --- /dev/null +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -0,0 +1,365 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// Intel License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000, Intel Corporation, all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of Intel Corporation may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +// 2011 Jason Newton +//M*/ +// +#include "precomp.hpp" + +namespace cv{ + namespace connectedcomponents{ + using std::vector; + + //Find the root of the tree of node i + template + inline static + LabelT findRoot(const vector &P, LabelT i){ + LabelT root = i; + while(P[root] < root){ + root = P[root]; + } + return root; + } + + //Make all nodes in the path of node i point to root + template + inline static + void setRoot(vector &P, LabelT i, LabelT root){ + while(P[i] < i){ + LabelT j = P[i]; + P[i] = root; + i = j; + } + P[i] = root; + } + + //Find the root of the tree of the node i and compress the path in the process + template + inline static + LabelT find(vector &P, LabelT i){ + LabelT root = findRoot(P, i); + setRoot(P, i, root); + return root; + } + + //unite the two trees containing nodes i and j and return the new root + template + inline static + LabelT set_union(vector &P, LabelT i, LabelT j){ + LabelT root = findRoot(P, i); + if(i != j){ + LabelT rootj = findRoot(P, j); + if(root > rootj){ + root = rootj; + } + setRoot(P, j, root); + } + setRoot(P, i, root); + return root; + } + + //Flatten the Union Find tree and relabel the components + template + inline static + LabelT flattenL(vector &P){ + LabelT k = 1; + for(size_t i = 1; i < P.size(); ++i){ + if(P[i] < i){ + P[i] = P[P[i]]; + }else{ + P[i] = k; k = k + 1; + } + } + return k; + } + + ////Flatten the Union Find tree - inconsistent labels + //void flatten(int P[], int size){ + // for(int i = 1; i < size; ++i){ + // P[i] = P[P[i]]; + // } + //} + const int G4[2][2] = {{-1, 0}, {0, -1}};//b, d neighborhoods + const int G8[4][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}};//a, b, c, d neighborhoods + //Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant + //using decision trees + //Kesheng Wu, et al + template + struct LabelingImpl{ + LabelT operator()(Mat &L, const Mat &I){ + const int rows = L.rows; + const int cols = L.cols; + size_t nPixels = size_t(rows) * cols; + vector P; P.push_back(0); + LabelT l = 1; + //scanning phase + for(int r_i = 0; r_i < rows; ++r_i){ + for(int c_i = 0; c_i < cols; ++c_i){ + if(!I.at(r_i, c_i)){ + L.at(r_i, c_i) = 0; + continue; + } + if(connectivity == 8){ + const int a = 0; + const int b = 1; + const int c = 2; + const int d = 3; + + bool T[4]; + + for(size_t i = 0; i < 4; ++i){ + int gr = r_i + G8[i][0]; + int gc = c_i + G8[i][1]; + T[i] = false; + if(gr >= 0 && gr < rows && gc >= 0 && gc < cols){ + if(I.at(gr, gc)){ + T[i] = true; + } + } + } + + //decision tree + if(T[b]){ + //copy(b) + L.at(r_i, c_i) = L.at(r_i + G8[b][0], c_i + G8[b][1]); + }else{//not b + if(T[c]){ + if(T[a]){ + //copy(c, a) + L.at(r_i, c_i) = set_union(P, L.at(r_i + G8[c][0], c_i + G8[c][1]), L.at(r_i + G8[a][0], c_i + G8[a][1])); + }else{ + if(T[d]){ + //copy(c, d) + L.at(r_i, c_i) = set_union(P, L.at(r_i + G8[c][0], c_i + G8[c][1]), L.at(r_i + G8[d][0], c_i + G8[d][1])); + }else{ + //copy(c) + L.at(r_i, c_i) = L.at(r_i + G8[c][0], c_i + G8[c][1]); + } + } + }else{//not c + if(T[a]){ + //copy(a) + L.at(r_i, c_i) = L.at(r_i + G8[a][0], c_i + G8[a][1]); + }else{ + if(T[d]){ + //copy(d) + L.at(r_i, c_i) = L.at(r_i + G8[d][0], c_i + G8[d][1]); + }else{ + //new label + L.at(r_i, c_i) = l; + P.push_back(l);//P[l] = l; + l = l + 1; + } + } + } + } + }else{ + //B & D only + const int b = 0; + const int d = 1; + assert(connectivity == 4); + bool T[2]; + for(size_t i = 0; i < 2; ++i){ + int gr = r_i + G4[i][0]; + int gc = c_i + G4[i][1]; + T[i] = false; + if(gr >= 0 && gr < rows && gc >= 0 && gc < cols){ + if(I.at(gr, gc)){ + T[i] = true; + } + } + } + + if(T[b]){ + if(T[d]){ + //copy(d, b) + L.at(r_i, c_i) = set_union(P, L.at(r_i + G4[d][0], c_i + G4[d][1]), L.at(r_i + G4[b][0], c_i + G4[b][1])); + }else{ + //copy(b) + L.at(r_i, c_i) = L.at(r_i + G4[b][0], c_i + G4[b][1]); + } + }else{ + if(T[d]){ + //copy(d) + L.at(r_i, c_i) = L.at(r_i + G4[d][0], c_i + G4[d][1]); + }else{ + //new label + L.at(r_i, c_i) = l; + P.push_back(l);//P[l] = l; + l = l + 1; + } + } + + } + } + } + + //analysis + LabelT nLabels = flattenL(P); + + //assign final labels + for(size_t r = 0; r < rows; ++r){ + for(size_t c = 0; c < cols; ++c){ + L.at(r, c) = P[L.at(r, c)]; + } + } + + return nLabels; + }//End function LabelingImpl operator() + + };//End struct LabelingImpl +}//end namespace connectedcomponents + +//L's type must have an appropriate depth for the number of pixels in I +uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity){ + CV_Assert(L.rows == I.rows); + CV_Assert(L.cols == I.cols); + CV_Assert(L.channels() == 1 && I.channels() == 1); + CV_Assert(connectivity == 8 || connectivity == 4); + + int lDepth = L.depth(); + int iDepth = I.depth(); + using connectedcomponents::LabelingImpl; + //warn if L's depth is not sufficient? + + if(lDepth == CV_8U){ + if(iDepth == CV_8U || iDepth == CV_8S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_16U || iDepth == CV_16S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_32S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_32F){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_64F){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + } + }else if(lDepth == CV_16U){ + if(iDepth == CV_8U || iDepth == CV_8S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_16U || iDepth == CV_16S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_32S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_32F){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_64F){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + } + }else if(lDepth == CV_32S){ + if(iDepth == CV_8U || iDepth == CV_8S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_16U || iDepth == CV_16S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_32S){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_32F){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + }else if(iDepth == CV_64F){ + if(connectivity == 4){ + return (uint64_t) LabelingImpl()(L, I); + }else{ + return (uint64_t) LabelingImpl()(L, I); + } + } + } + + CV_Error(CV_StsUnsupportedFormat, "unsupported label/image type"); + return -1; +} + + +} + diff --git a/samples/cpp/connected_components.cpp b/samples/cpp/connected_components.cpp index c915bcda4a..6d3357fb6e 100644 --- a/samples/cpp/connected_components.cpp +++ b/samples/cpp/connected_components.cpp @@ -11,25 +11,21 @@ int threshval = 100; static void on_trackbar(int, void*) { Mat bw = threshval < 128 ? (img < threshval) : (img > threshval); - - vector > contours; - vector hierarchy; - - findContours( bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); - - Mat dst = Mat::zeros(img.size(), CV_8UC3); - - if( !contours.empty() && !hierarchy.empty() ) - { - // iterate through all the top-level contours, - // draw each connected component with its own random color - int idx = 0; - for( ; idx >= 0; idx = hierarchy[idx][0] ) - { - Scalar color( (rand()&255), (rand()&255), (rand()&255) ); - drawContours( dst, contours, idx, color, CV_FILLED, 8, hierarchy ); - } + Mat labelImage(img.size(), CV_32S); + int nLabels = connectedComponents(labelImage, bw, 8); + Vec3b colors[nLabels]; + colors[0] = Vec3b(0, 0, 0);//background + for(int label = 1; label < nLabels; ++label){ + colors[label] = Vec3b( (rand()&255), (rand()&255), (rand()&255) ); } + Mat dst(img.size(), CV_8UC3); + for(int r = 0; r < dst.rows; ++r){ + for(int c = 0; c < dst.cols; ++c){ + int label = labelImage.at(r, c); + Vec3b &pixel = dst.at(r, c); + pixel = colors[label]; + } + } imshow( "Connected Components", dst ); } From 4c0cb2576d588b641b3496628eb2c07cfeedb7b6 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 25 Aug 2012 02:31:52 -0700 Subject: [PATCH 02/16] connectedComponents: peep-hole optimizations, mostly surrouding the fact that cv::Mat::at is expensive in a tight-loop -also added a "blobstats" version --- .../include/opencv2/imgproc/imgproc.hpp | 17 +- modules/imgproc/src/connectedcomponents.cpp | 354 ++++++++++++------ samples/cpp/connected_components.cpp | 2 +- 3 files changed, 252 insertions(+), 121 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 3d80cfee4e..0cb761b40f 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1091,9 +1091,24 @@ enum { TM_SQDIFF=0, TM_SQDIFF_NORMED=1, TM_CCORR=2, TM_CCORR_NORMED=3, TM_CCOEFF CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, OutputArray result, int method ); + +struct CV_EXPORTS ConnectedComponentStats +{ + int32_t lower_x; + int32_t lower_y; + int32_t upper_x; + int32_t upper_y; + double centroid_x; + double centroid_y; + uint64_t integral_x; + uint64_t integral_y; + uint32_t area; +}; //! computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total -//number of labels [0, N-1] where 0 represents the background label. +//number of labels [0, N-1] where 0 represents the background label. L's value type determines the label type, an important +//consideration based on the total number of labels or alternatively the total number of pixels. CV_EXPORTS_W uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity = 8); +CV_EXPORTS_W uint64_t connectedComponents(Mat &L, const Mat &I, std::vector &statsv, int connectivity = 8); //! mode of the contour retrieval algorithm diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index cc83f97481..50a1ca1e0d 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -41,15 +41,81 @@ //M*/ // #include "precomp.hpp" +#include namespace cv{ namespace connectedcomponents{ - using std::vector; + + template + struct NoOp{ + NoOp(){ + } + void init(const LabelT labels){ + (void) labels; + } + inline + void operator()(int r, int c, LabelT l){ + (void) r; + (void) c; + (void) l; + } + void finish(){} + }; + template + struct CCStatsOp{ + std::vector &statsv; + CCStatsOp(std::vector &_statsv): statsv(_statsv){ + } + inline + void init(const LabelT nlabels){ + statsv.clear(); + cv::ConnectedComponentStats stats = cv::ConnectedComponentStats(); + stats.lower_x = std::numeric_limits::max(); + stats.lower_y = std::numeric_limits::max(); + stats.upper_x = std::numeric_limits::min(); + stats.upper_y = std::numeric_limits::min(); + stats.centroid_x = 0; + stats.centroid_y = 0; + stats.integral_x = 0; + stats.integral_y = 0; + stats.area = 0; + statsv.resize(nlabels, stats); + } + void operator()(int r, int c, LabelT l){ + ConnectedComponentStats &stats = statsv[l]; + if(c > stats.upper_x){ + stats.upper_x = c; + }else{ + if(c < stats.lower_x){ + stats.lower_x = c; + } + } + if(r > stats.upper_y){ + stats.upper_y = r; + }else{ + if(r < stats.lower_y){ + stats.lower_y = r; + } + } + stats.integral_x += c; + stats.integral_y += r; + stats.area++; + } + void finish(){ + for(size_t l = 0; l < statsv.size(); ++l){ + ConnectedComponentStats &stats = statsv[l]; + stats.lower_x = std::min(stats.lower_x, stats.upper_x); + stats.lower_y = std::min(stats.lower_y, stats.upper_y); + stats.centroid_x = stats.integral_x / double(stats.area); + stats.centroid_y = stats.integral_y / double(stats.area); + } + } + }; //Find the root of the tree of node i template inline static - LabelT findRoot(const vector &P, LabelT i){ + LabelT findRoot(const LabelT *P, LabelT i){ LabelT root = i; while(P[root] < root){ root = P[root]; @@ -60,7 +126,7 @@ namespace cv{ //Make all nodes in the path of node i point to root template inline static - void setRoot(vector &P, LabelT i, LabelT root){ + void setRoot(LabelT *P, LabelT i, LabelT root){ while(P[i] < i){ LabelT j = P[i]; P[i] = root; @@ -72,7 +138,7 @@ namespace cv{ //Find the root of the tree of the node i and compress the path in the process template inline static - LabelT find(vector &P, LabelT i){ + LabelT find(LabelT *P, LabelT i){ LabelT root = findRoot(P, i); setRoot(P, i, root); return root; @@ -81,7 +147,7 @@ namespace cv{ //unite the two trees containing nodes i and j and return the new root template inline static - LabelT set_union(vector &P, LabelT i, LabelT j){ + LabelT set_union(LabelT *P, LabelT i, LabelT j){ LabelT root = findRoot(P, i); if(i != j){ LabelT rootj = findRoot(P, j); @@ -97,9 +163,9 @@ namespace cv{ //Flatten the Union Find tree and relabel the components template inline static - LabelT flattenL(vector &P){ + LabelT flattenL(LabelT *P, LabelT length){ LabelT k = 1; - for(size_t i = 1; i < P.size(); ++i){ + for(LabelT i = 1; i < length; ++i){ if(P[i] < i){ P[i] = P[P[i]]; }else{ @@ -109,137 +175,155 @@ namespace cv{ return k; } - ////Flatten the Union Find tree - inconsistent labels - //void flatten(int P[], int size){ - // for(int i = 1; i < size; ++i){ - // P[i] = P[P[i]]; - // } - //} - const int G4[2][2] = {{-1, 0}, {0, -1}};//b, d neighborhoods - const int G8[4][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}};//a, b, c, d neighborhoods //Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant //using decision trees //Kesheng Wu, et al - template + //Note: rows are encoded as position in the "rows" array to save lookup times + //reference for 4-way: {{-1, 0}, {0, -1}};//b, d neighborhoods + const int G4[2][2] = {{1, 0}, {0, -1}};//b, d neighborhoods + //reference for 8-way: {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}};//a, b, c, d neighborhoods + const int G8[4][2] = {{1, -1}, {1, 0}, {1, 1}, {0, -1}};//a, b, c, d neighborhoods + template, int connectivity = 8> struct LabelingImpl{ - LabelT operator()(Mat &L, const Mat &I){ + LabelT operator()(Mat &L, const Mat &I, StatsOp &sop){ const int rows = L.rows; const int cols = L.cols; - size_t nPixels = size_t(rows) * cols; - vector P; P.push_back(0); - LabelT l = 1; + size_t Plength = (size_t(rows + 3 - 1)/3) * (size_t(cols + 3 - 1)/3); + if(connectivity == 4){ + Plength = 4 * Plength;//a quick and dirty upper bound, an exact answer exists if you want to find it + //the 4 comes from the fact that a 3x3 block can never have more than 4 unique labels + } + LabelT *P = (LabelT *) fastMalloc(sizeof(LabelT) * Plength); + P[0] = 0; + LabelT lunique = 1; //scanning phase for(int r_i = 0; r_i < rows; ++r_i){ - for(int c_i = 0; c_i < cols; ++c_i){ - if(!I.at(r_i, c_i)){ - L.at(r_i, c_i) = 0; - continue; - } - if(connectivity == 8){ - const int a = 0; - const int b = 1; - const int c = 2; - const int d = 3; - - bool T[4]; - - for(size_t i = 0; i < 4; ++i){ - int gr = r_i + G8[i][0]; - int gc = c_i + G8[i][1]; - T[i] = false; - if(gr >= 0 && gr < rows && gc >= 0 && gc < cols){ - if(I.at(gr, gc)){ - T[i] = true; - } - } + LabelT *Lrow = (LabelT *)(L.data + L.step.p[0] * r_i); + LabelT *Lrow_prev = (LabelT *)(((char *)Lrow) - L.step.p[0]); + const PixelT *Irow = (PixelT *)(I.data + I.step.p[0] * r_i); + const PixelT *Irow_prev = (const PixelT *)(((char *)Irow) - I.step.p[0]); + LabelT *Lrows[2] = { + Lrow, + Lrow_prev + }; + const PixelT *Irows[2] = { + Irow, + Irow_prev + }; + if(connectivity == 8){ + const int a = 0; + const int b = 1; + const int c = 2; + const int d = 3; + const bool T_a_r = (r_i - G8[a][0]) >= 0; + const bool T_b_r = (r_i - G8[b][0]) >= 0; + const bool T_c_r = (r_i - G8[c][0]) >= 0; + for(int c_i = 0; Irows[0] != Irow + cols; ++Irows[0], c_i++){ + if(!*Irows[0]){ + Lrow[c_i] = 0; + continue; } + Irows[1] = Irow_prev + c_i; + Lrows[0] = Lrow + c_i; + Lrows[1] = Lrow_prev + c_i; + const bool T_a = T_a_r && (c_i + G8[a][1]) >= 0 && *(Irows[G8[a][0]] + G8[a][1]); + const bool T_b = T_b_r && *(Irows[G8[b][0]] + G8[b][1]); + const bool T_c = T_c_r && (c_i + G8[c][1]) < cols && *(Irows[G8[c][0]] + G8[c][1]); + const bool T_d = (c_i + G8[d][1]) >= 0 && *(Irows[G8[d][0]] + G8[d][1]); //decision tree - if(T[b]){ + if(T_b){ //copy(b) - L.at(r_i, c_i) = L.at(r_i + G8[b][0], c_i + G8[b][1]); + *Lrows[0] = *(Lrows[G8[b][0]] + G8[b][1]); }else{//not b - if(T[c]){ - if(T[a]){ + if(T_c){ + if(T_a){ //copy(c, a) - L.at(r_i, c_i) = set_union(P, L.at(r_i + G8[c][0], c_i + G8[c][1]), L.at(r_i + G8[a][0], c_i + G8[a][1])); + *Lrows[0] = set_union(P, *(Lrows[G8[c][0]] + G8[c][1]), *(Lrows[G8[a][0]] + G8[a][1])); }else{ - if(T[d]){ + if(T_d){ //copy(c, d) - L.at(r_i, c_i) = set_union(P, L.at(r_i + G8[c][0], c_i + G8[c][1]), L.at(r_i + G8[d][0], c_i + G8[d][1])); + *Lrows[0] = set_union(P, *(Lrows[G8[c][0]] + G8[c][1]), *(Lrows[G8[d][0]] + G8[d][1])); }else{ //copy(c) - L.at(r_i, c_i) = L.at(r_i + G8[c][0], c_i + G8[c][1]); + *Lrows[0] = *(Lrows[G8[c][0]] + G8[c][1]); } } }else{//not c - if(T[a]){ + if(T_a){ //copy(a) - L.at(r_i, c_i) = L.at(r_i + G8[a][0], c_i + G8[a][1]); + *Lrows[0] = *(Lrows[G8[a][0]] + G8[a][1]); }else{ - if(T[d]){ + if(T_d){ //copy(d) - L.at(r_i, c_i) = L.at(r_i + G8[d][0], c_i + G8[d][1]); + *Lrows[0] = *(Lrows[G8[d][0]] + G8[d][1]); }else{ //new label - L.at(r_i, c_i) = l; - P.push_back(l);//P[l] = l; - l = l + 1; + *Lrows[0] = lunique; + P[lunique] = lunique; + lunique = lunique + 1; } } } } - }else{ - //B & D only - const int b = 0; - const int d = 1; - assert(connectivity == 4); - bool T[2]; - for(size_t i = 0; i < 2; ++i){ - int gr = r_i + G4[i][0]; - int gc = c_i + G4[i][1]; - T[i] = false; - if(gr >= 0 && gr < rows && gc >= 0 && gc < cols){ - if(I.at(gr, gc)){ - T[i] = true; - } - } + } + }else{ + //B & D only + assert(connectivity == 4); + const int b = 0; + const int d = 1; + const bool T_b_r = (r_i - G4[b][0]) >= 0; + for(int c_i = 0; Irows[0] != Irow + cols; ++Irows[0], c_i++){ + if(!*Irows[0]){ + Lrow[c_i] = 0; + continue; } - - if(T[b]){ - if(T[d]){ + Irows[1] = Irow_prev + c_i; + Lrows[0] = Lrow + c_i; + Lrows[1] = Lrow_prev + c_i; + const bool T_b = T_b_r && *(Irows[G4[b][0]] + G4[b][1]); + const bool T_d = (c_i + G4[d][1]) >= 0 && *(Irows[G4[d][0]] + G4[d][1]); + if(T_b){ + if(T_d){ //copy(d, b) - L.at(r_i, c_i) = set_union(P, L.at(r_i + G4[d][0], c_i + G4[d][1]), L.at(r_i + G4[b][0], c_i + G4[b][1])); + *Lrows[0] = set_union(P, *(Lrows[G4[d][0]] + G4[d][1]), *(Lrows[G4[b][0]] + G4[b][1])); }else{ //copy(b) - L.at(r_i, c_i) = L.at(r_i + G4[b][0], c_i + G4[b][1]); + *Lrows[0] = *(Lrows[G4[b][0]] + G4[b][1]); } }else{ - if(T[d]){ + if(T_d){ //copy(d) - L.at(r_i, c_i) = L.at(r_i + G4[d][0], c_i + G4[d][1]); + *Lrows[0] = *(Lrows[G4[d][0]] + G4[d][1]); }else{ //new label - L.at(r_i, c_i) = l; - P.push_back(l);//P[l] = l; - l = l + 1; + *Lrows[0] = lunique; + P[lunique] = lunique; + lunique = lunique + 1; } } - } } } //analysis - LabelT nLabels = flattenL(P); + LabelT nLabels = flattenL(P, lunique); + sop.init(nLabels); - //assign final labels - for(size_t r = 0; r < rows; ++r){ - for(size_t c = 0; c < cols; ++c){ - L.at(r, c) = P[L.at(r, c)]; + for(int r_i = 0; r_i < rows; ++r_i){ + LabelT *Lrow_start = (LabelT *)(L.data + L.step.p[0] * r_i); + LabelT *Lrow_end = Lrow_start + cols; + LabelT *Lrow = Lrow_start; + for(int c_i = 0; Lrow != Lrow_end; ++Lrow, ++c_i){ + const LabelT l = P[*Lrow]; + *Lrow = l; + sop(r_i, c_i, l); } } + sop.finish(); + fastFree(P); + return nLabels; }//End function LabelingImpl operator() @@ -247,7 +331,8 @@ namespace cv{ }//end namespace connectedcomponents //L's type must have an appropriate depth for the number of pixels in I -uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity){ +template +uint64_t connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsOp &sop){ CV_Assert(L.rows == I.rows); CV_Assert(L.cols == I.cols); CV_Assert(L.channels() == 1 && I.channels() == 1); @@ -261,98 +346,102 @@ uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity){ if(lDepth == CV_8U){ if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_16U || iDepth == CV_16S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_32S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_32F){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_64F){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } } }else if(lDepth == CV_16U){ if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_16U || iDepth == CV_16S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_32S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_32F){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_64F){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } } }else if(lDepth == CV_32S){ + //note that signed types don't really make sense here and not being able to use uint32_t matters for scientific projects + //OpenCV: how should we proceed? .at typechecks in debug mode if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_16U || iDepth == CV_16S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_32S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_32F){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } }else if(iDepth == CV_64F){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I); + return (uint64_t) LabelingImpl()(L, I, sop); } + }else{ + CV_Assert(false); } } @@ -360,6 +449,33 @@ uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity){ return -1; } +uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity){ + int lDepth = L.depth(); + if(lDepth == CV_8U){ + connectedcomponents::NoOp sop; return connectedComponents_sub1(L, I, connectivity, sop); + }else if(lDepth == CV_16U){ + connectedcomponents::NoOp sop; return connectedComponents_sub1(L, I, connectivity, sop); + }else if(lDepth == CV_32S){ + connectedcomponents::NoOp sop; return connectedComponents_sub1(L, I, connectivity, sop); + }else{ + CV_Assert(false); + return 0; + } +} + +uint64_t connectedComponents(Mat &L, const Mat &I, std::vector &statsv, int connectivity){ + int lDepth = L.depth(); + if(lDepth == CV_8U){ + connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(L, I, connectivity, sop); + }else if(lDepth == CV_16U){ + connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(L, I, connectivity, sop); + }else if(lDepth == CV_32S){ + connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(L, I, connectivity, sop); + }else{ + CV_Assert(false); + return 0; + } +} } diff --git a/samples/cpp/connected_components.cpp b/samples/cpp/connected_components.cpp index 6d3357fb6e..7b362dfd66 100644 --- a/samples/cpp/connected_components.cpp +++ b/samples/cpp/connected_components.cpp @@ -12,7 +12,7 @@ static void on_trackbar(int, void*) { Mat bw = threshval < 128 ? (img < threshval) : (img > threshval); Mat labelImage(img.size(), CV_32S); - int nLabels = connectedComponents(labelImage, bw, 8); + uint64_t nLabels = connectedComponents(labelImage, bw, 8); Vec3b colors[nLabels]; colors[0] = Vec3b(0, 0, 0);//background for(int label = 1; label < nLabels; ++label){ From 85880397c411b58b665a877b2eef5b1dd0cb1863 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 5 Nov 2012 12:02:53 -0800 Subject: [PATCH 03/16] connectedcomponents: use opencv integral types, add to docs, fix up things for a python export --- ...uctural_analysis_and_shape_descriptors.rst | 42 +++++++++++++++++++ .../include/opencv2/imgproc/imgproc.hpp | 24 +++++------ modules/imgproc/src/connectedcomponents.cpp | 12 +++++- modules/python/src2/cv2.cpp | 12 +++++- 4 files changed, 75 insertions(+), 15 deletions(-) diff --git a/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst b/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst index 55cea58d51..ad5c22cbb6 100644 --- a/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst +++ b/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst @@ -118,6 +118,48 @@ These values are proved to be invariants to the image scale, rotation, and refle .. seealso:: :ocv:func:`matchShapes` +connectedComponents +----------- +computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total +number of labels [0, N-1] where 0 represents the background label. L's value type determines the label type, an important +consideration based on the total number of labels or alternatively the total number of pixels. + +.. ocv:function:: uint64 connectedComponents(Mat &L, const Mat &I, int connectivity = 8) + +.. ocv:function:: uint64 connectedComponentsWithStats(Mat &L, const Mat &I, std::vector &statsv, int connectivity = 8) + + :param L: destitination Labeled image + + :param I: the image to be labeled + + :param connectivity: 8 or 4 for 8-way or 4-way connectivity respectively + + :param statsv: statistics for each label, including the background label + +Statistics information such as bounding box, area, and centroid is exported via the ``ConnectComponentStats`` structure defined as: :: + + class CV_EXPORTS ConnectedComponentStats + { + public: + //! lower left corner column + int lower_x; + //! lower left corner row + int lower_y; + //! upper right corner column + int upper_x; + //! upper right corner row + int upper_y; + //! centroid column + double centroid_x; + //! centroid row + double centroid_y; + //! sum of all columns where the image was non-zero + uint64 integral_x; + //! sum of all rows where the image was non-zero + uint64 integral_y; + //! count of all non-zero pixels + unsigned int area; + }; findContours ---------------- diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 0cb761b40f..3c9d787106 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1091,24 +1091,24 @@ enum { TM_SQDIFF=0, TM_SQDIFF_NORMED=1, TM_CCORR=2, TM_CCORR_NORMED=3, TM_CCOEFF CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, OutputArray result, int method ); - struct CV_EXPORTS ConnectedComponentStats { - int32_t lower_x; - int32_t lower_y; - int32_t upper_x; - int32_t upper_y; - double centroid_x; - double centroid_y; - uint64_t integral_x; - uint64_t integral_y; - uint32_t area; + int lower_x;//!< lower left corner column + int lower_y;//!< lower left corner row + int upper_x;//!< upper right corner column + int upper_y;//!< upper right corner row + double centroid_x;//!< centroid column + double centroid_y;//!< centroid row + uint64 integral_x;//!< sum of all columns where the image was non-zero + uint64 integral_y;//!< sum of all rows where the image was non-zero + unsigned int area;//!< count of all non-zero pixels }; + //! computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total //number of labels [0, N-1] where 0 represents the background label. L's value type determines the label type, an important //consideration based on the total number of labels or alternatively the total number of pixels. -CV_EXPORTS_W uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity = 8); -CV_EXPORTS_W uint64_t connectedComponents(Mat &L, const Mat &I, std::vector &statsv, int connectivity = 8); +CV_EXPORTS_W uint64 connectedComponents(CV_OUT Mat &L, const Mat &I, int connectivity = 8); +CV_EXPORTS_W uint64 connectedComponentsWithStats(CV_OUT Mat &L, const Mat &I, CV_OUT std::vector &statsv, int connectivity = 8); //! mode of the contour retrieval algorithm diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index 50a1ca1e0d..8e75ce7bdd 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -43,6 +43,16 @@ #include "precomp.hpp" #include +//It's 2012 and we still let compilers get by without defining standard integer types... +typedef schar int8_t; +typedef uchar uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef int64 int64_t; +typedef uint64 uint64_t; + namespace cv{ namespace connectedcomponents{ @@ -463,7 +473,7 @@ uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity){ } } -uint64_t connectedComponents(Mat &L, const Mat &I, std::vector &statsv, int connectivity){ +uint64_t connectedComponentsWithStats(Mat &L, const Mat &I, std::vector &statsv, int connectivity){ int lDepth = L.depth(); if(lDepth == CV_8U){ connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(L, I, connectivity, sop); diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 28cf00eac2..5dbbbb4409 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -123,6 +123,7 @@ typedef Ptr Ptr_FeatureDetector; typedef Ptr Ptr_DescriptorExtractor; typedef Ptr Ptr_Feature2D; typedef Ptr Ptr_DescriptorMatcher; +typedef vector vector_ConnectedComponentStats; typedef SimpleBlobDetector::Params SimpleBlobDetector_Params; @@ -410,7 +411,7 @@ static bool pyopencv_to(PyObject* obj, bool& value, const char* name = " Date: Thu, 22 Nov 2012 21:26:52 -0800 Subject: [PATCH 04/16] adjust output type to return int32... it should at least be unsigned but this breaks python bindings; remove non-8bit input type support, not worth the binary size --- .../include/opencv2/imgproc/imgproc.hpp | 4 +- modules/imgproc/src/connectedcomponents.cpp | 97 +++---------------- 2 files changed, 15 insertions(+), 86 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 3c9d787106..5e3da2658e 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1107,8 +1107,8 @@ struct CV_EXPORTS ConnectedComponentStats //! computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total //number of labels [0, N-1] where 0 represents the background label. L's value type determines the label type, an important //consideration based on the total number of labels or alternatively the total number of pixels. -CV_EXPORTS_W uint64 connectedComponents(CV_OUT Mat &L, const Mat &I, int connectivity = 8); -CV_EXPORTS_W uint64 connectedComponentsWithStats(CV_OUT Mat &L, const Mat &I, CV_OUT std::vector &statsv, int connectivity = 8); +CV_EXPORTS_W int connectedComponents(CV_OUT Mat &L, const Mat &I, int connectivity = 8); +CV_EXPORTS_W int connectedComponentsWithStats(CV_OUT Mat &L, const Mat &I, CV_OUT std::vector &statsv, int connectivity = 8); //! mode of the contour retrieval algorithm diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index 8e75ce7bdd..db118f5ccb 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -50,8 +50,6 @@ typedef short int16_t; typedef unsigned short uint16_t; typedef int int32_t; typedef unsigned int uint32_t; -typedef int64 int64_t; -typedef uint64 uint64_t; namespace cv{ namespace connectedcomponents{ @@ -342,7 +340,7 @@ namespace cv{ //L's type must have an appropriate depth for the number of pixels in I template -uint64_t connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsOp &sop){ +int connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsOp &sop){ CV_Assert(L.rows == I.rows); CV_Assert(L.cols == I.cols); CV_Assert(L.channels() == 1 && I.channels() == 1); @@ -356,99 +354,31 @@ uint64_t connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsO if(lDepth == CV_8U){ if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_16U || iDepth == CV_16S){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_32S){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_32F){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_64F){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(L, I, sop); } + }else{ + CV_Assert(false); } }else if(lDepth == CV_16U){ if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_16U || iDepth == CV_16S){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_32S){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_32F){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_64F){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(L, I, sop); } + }else{ + CV_Assert(false); } }else if(lDepth == CV_32S){ //note that signed types don't really make sense here and not being able to use uint32_t matters for scientific projects //OpenCV: how should we proceed? .at typechecks in debug mode if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_16U || iDepth == CV_16S){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(L, I, sop); }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_32S){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_32F){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); - } - }else if(iDepth == CV_64F){ - if(connectivity == 4){ - return (uint64_t) LabelingImpl()(L, I, sop); - }else{ - return (uint64_t) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(L, I, sop); } }else{ CV_Assert(false); @@ -459,7 +389,7 @@ uint64_t connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsO return -1; } -uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity){ +int connectedComponents(Mat &L, const Mat &I, int connectivity){ int lDepth = L.depth(); if(lDepth == CV_8U){ connectedcomponents::NoOp sop; return connectedComponents_sub1(L, I, connectivity, sop); @@ -473,7 +403,7 @@ uint64_t connectedComponents(Mat &L, const Mat &I, int connectivity){ } } -uint64_t connectedComponentsWithStats(Mat &L, const Mat &I, std::vector &statsv, int connectivity){ +int connectedComponentsWithStats(Mat &L, const Mat &I, std::vector &statsv, int connectivity){ int lDepth = L.depth(); if(lDepth == CV_8U){ connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(L, I, connectivity, sop); @@ -488,4 +418,3 @@ uint64_t connectedComponentsWithStats(Mat &L, const Mat &I, std::vector Date: Tue, 27 Nov 2012 02:25:52 -0800 Subject: [PATCH 05/16] A few changes to comply with upstream requirements for merge. -Change input/output order from (out Labeled, in Image) -> (in Image, Out Labeled) and convert to Input/OutputArrays in the process. -Adopt OutputArray for statistics export so that the algorithm is "wrapper friendly" and not requiring a new struct in language bindings at the expense of using doubles for everything and slowing statistics computation down.. --- .../include/opencv2/imgproc/imgproc.hpp | 17 +-- modules/imgproc/src/connectedcomponents.cpp | 105 +++++++++--------- modules/python/src2/cv2.cpp | 1 - samples/cpp/connected_components.cpp | 2 +- 4 files changed, 59 insertions(+), 66 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 5e3da2658e..8920ed2a12 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1091,24 +1091,13 @@ enum { TM_SQDIFF=0, TM_SQDIFF_NORMED=1, TM_CCORR=2, TM_CCORR_NORMED=3, TM_CCOEFF CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, OutputArray result, int method ); -struct CV_EXPORTS ConnectedComponentStats -{ - int lower_x;//!< lower left corner column - int lower_y;//!< lower left corner row - int upper_x;//!< upper right corner column - int upper_y;//!< upper right corner row - double centroid_x;//!< centroid column - double centroid_y;//!< centroid row - uint64 integral_x;//!< sum of all columns where the image was non-zero - uint64 integral_y;//!< sum of all rows where the image was non-zero - unsigned int area;//!< count of all non-zero pixels -}; +enum { CC_STAT_LEFT=0, CC_STAT_TOP=1, CC_STAT_WIDTH=2, CC_STAT_HEIGHT=3, CC_STAT_CX=4, CC_STAT_CY=5, CC_STAT_AREA=6, CC_STAT_INTEGRAL_X=7, CC_STAT_INTEGRAL_Y=8, CC_STAT_MAX = 9}; //! computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total //number of labels [0, N-1] where 0 represents the background label. L's value type determines the label type, an important //consideration based on the total number of labels or alternatively the total number of pixels. -CV_EXPORTS_W int connectedComponents(CV_OUT Mat &L, const Mat &I, int connectivity = 8); -CV_EXPORTS_W int connectedComponentsWithStats(CV_OUT Mat &L, const Mat &I, CV_OUT std::vector &statsv, int connectivity = 8); +CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8); +CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, int connectivity = 8); //! mode of the contour retrieval algorithm diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index db118f5ccb..b52a6c7293 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -71,51 +71,54 @@ namespace cv{ }; template struct CCStatsOp{ - std::vector &statsv; - CCStatsOp(std::vector &_statsv): statsv(_statsv){ + cv::Mat statsv; + CCStatsOp(OutputArray _statsv): statsv(_statsv.getMat()){ } inline void init(const LabelT nlabels){ - statsv.clear(); - cv::ConnectedComponentStats stats = cv::ConnectedComponentStats(); - stats.lower_x = std::numeric_limits::max(); - stats.lower_y = std::numeric_limits::max(); - stats.upper_x = std::numeric_limits::min(); - stats.upper_y = std::numeric_limits::min(); - stats.centroid_x = 0; - stats.centroid_y = 0; - stats.integral_x = 0; - stats.integral_y = 0; - stats.area = 0; - statsv.resize(nlabels, stats); + statsv = cv::Mat(nlabels, CC_STAT_MAX, cv::DataType::type); + for(int l = 0; l < (int) nlabels; ++l){ + double *row = &statsv.at(l, 0); + row[CC_STAT_LEFT] = std::numeric_limits::max(); + row[CC_STAT_TOP] = std::numeric_limits::max(); + row[CC_STAT_WIDTH] = std::numeric_limits::min(); + row[CC_STAT_HEIGHT] = std::numeric_limits::min(); + row[CC_STAT_CX] = 0; + row[CC_STAT_CY] = 0; + row[CC_STAT_AREA] = 0; + row[CC_STAT_INTEGRAL_X] = 0; + row[CC_STAT_INTEGRAL_Y] = 0; + } } void operator()(int r, int c, LabelT l){ - ConnectedComponentStats &stats = statsv[l]; - if(c > stats.upper_x){ - stats.upper_x = c; + double *row = &statsv.at(l, 0); + if(c > row[CC_STAT_WIDTH]){ + row[CC_STAT_WIDTH] = c; }else{ - if(c < stats.lower_x){ - stats.lower_x = c; + if(c < row[CC_STAT_LEFT]){ + row[CC_STAT_LEFT] = c; } } - if(r > stats.upper_y){ - stats.upper_y = r; + if(r > row[CC_STAT_HEIGHT]){ + row[CC_STAT_HEIGHT] = r; }else{ - if(r < stats.lower_y){ - stats.lower_y = r; + if(r < row[CC_STAT_TOP]){ + row[CC_STAT_TOP] = r; } } - stats.integral_x += c; - stats.integral_y += r; - stats.area++; + row[CC_STAT_INTEGRAL_X] += c; + row[CC_STAT_INTEGRAL_Y] += r; + row[CC_STAT_AREA]++; } void finish(){ - for(size_t l = 0; l < statsv.size(); ++l){ - ConnectedComponentStats &stats = statsv[l]; - stats.lower_x = std::min(stats.lower_x, stats.upper_x); - stats.lower_y = std::min(stats.lower_y, stats.upper_y); - stats.centroid_x = stats.integral_x / double(stats.area); - stats.centroid_y = stats.integral_y / double(stats.area); + for(int l = 0; l < statsv.rows; ++l){ + double *row = &statsv.at(l, 0); + row[CC_STAT_LEFT] = std::min(row[CC_STAT_LEFT], row[CC_STAT_WIDTH]); + row[CC_STAT_WIDTH] = row[CC_STAT_WIDTH] - row[CC_STAT_LEFT] + 1; + row[CC_STAT_TOP] = std::min(row[CC_STAT_TOP], row[CC_STAT_HEIGHT]); + row[CC_STAT_HEIGHT] = row[CC_STAT_HEIGHT] - row[CC_STAT_TOP] + 1; + row[CC_STAT_CX] = row[CC_STAT_INTEGRAL_X] / double(row[CC_STAT_AREA]); + row[CC_STAT_CY] = row[CC_STAT_INTEGRAL_Y] / double(row[CC_STAT_AREA]); } } }; @@ -193,7 +196,11 @@ namespace cv{ const int G8[4][2] = {{1, -1}, {1, 0}, {1, 1}, {0, -1}};//a, b, c, d neighborhoods template, int connectivity = 8> struct LabelingImpl{ - LabelT operator()(Mat &L, const Mat &I, StatsOp &sop){ + LabelT operator()(InputArray _I, OutputArray _L, StatsOp &sop){ + cv::Mat I = _I.getMat(); + cv::Mat L = _L.getMat(); + CV_Assert(L.rows == I.rows); + CV_Assert(L.cols == I.cols); const int rows = L.rows; const int cols = L.cols; size_t Plength = (size_t(rows + 3 - 1)/3) * (size_t(cols + 3 - 1)/3); @@ -340,9 +347,7 @@ namespace cv{ //L's type must have an appropriate depth for the number of pixels in I template -int connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsOp &sop){ - CV_Assert(L.rows == I.rows); - CV_Assert(L.cols == I.cols); +int connectedComponents_sub1(InputArray I, OutputArray L, int connectivity, StatsOp &sop){ CV_Assert(L.channels() == 1 && I.channels() == 1); CV_Assert(connectivity == 8 || connectivity == 4); @@ -354,9 +359,9 @@ int connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsOp &so if(lDepth == CV_8U){ if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (int) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(I, L, sop); }else{ - return (int) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(I, L, sop); } }else{ CV_Assert(false); @@ -364,9 +369,9 @@ int connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsOp &so }else if(lDepth == CV_16U){ if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (int) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(I, L, sop); }else{ - return (int) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(I, L, sop); } }else{ CV_Assert(false); @@ -376,9 +381,9 @@ int connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsOp &so //OpenCV: how should we proceed? .at typechecks in debug mode if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (int) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(I, L, sop); }else{ - return (int) LabelingImpl()(L, I, sop); + return (int) LabelingImpl()(I, L, sop); } }else{ CV_Assert(false); @@ -389,28 +394,28 @@ int connectedComponents_sub1(Mat &L, const Mat &I, int connectivity, StatsOp &so return -1; } -int connectedComponents(Mat &L, const Mat &I, int connectivity){ +int connectedComponents(InputArray I, OutputArray L, int connectivity){ int lDepth = L.depth(); if(lDepth == CV_8U){ - connectedcomponents::NoOp sop; return connectedComponents_sub1(L, I, connectivity, sop); + connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); }else if(lDepth == CV_16U){ - connectedcomponents::NoOp sop; return connectedComponents_sub1(L, I, connectivity, sop); + connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); }else if(lDepth == CV_32S){ - connectedcomponents::NoOp sop; return connectedComponents_sub1(L, I, connectivity, sop); + connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); }else{ CV_Assert(false); return 0; } } -int connectedComponentsWithStats(Mat &L, const Mat &I, std::vector &statsv, int connectivity){ +int connectedComponentsWithStats(InputArray I, OutputArray L, OutputArray statsv, int connectivity){ int lDepth = L.depth(); if(lDepth == CV_8U){ - connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(L, I, connectivity, sop); + connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(I, L, connectivity, sop); }else if(lDepth == CV_16U){ - connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(L, I, connectivity, sop); + connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(I, L, connectivity, sop); }else if(lDepth == CV_32S){ - connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(L, I, connectivity, sop); + connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(I, L, connectivity, sop); }else{ CV_Assert(false); return 0; diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 5dbbbb4409..bc52f308c9 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -123,7 +123,6 @@ typedef Ptr Ptr_FeatureDetector; typedef Ptr Ptr_DescriptorExtractor; typedef Ptr Ptr_Feature2D; typedef Ptr Ptr_DescriptorMatcher; -typedef vector vector_ConnectedComponentStats; typedef SimpleBlobDetector::Params SimpleBlobDetector_Params; diff --git a/samples/cpp/connected_components.cpp b/samples/cpp/connected_components.cpp index 7b362dfd66..781ffec046 100644 --- a/samples/cpp/connected_components.cpp +++ b/samples/cpp/connected_components.cpp @@ -12,7 +12,7 @@ static void on_trackbar(int, void*) { Mat bw = threshval < 128 ? (img < threshval) : (img > threshval); Mat labelImage(img.size(), CV_32S); - uint64_t nLabels = connectedComponents(labelImage, bw, 8); + int nLabels = connectedComponents(bw, labelImage, 8); Vec3b colors[nLabels]; colors[0] = Vec3b(0, 0, 0);//background for(int label = 1; label < nLabels; ++label){ From 6a4d881a78f88f6f068d9fd179f054611add2ad7 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 8 Dec 2012 21:57:49 -0800 Subject: [PATCH 06/16] use vector instead of non-standard stack allocation. also correct program argument borkage --- samples/cpp/connected_components.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/cpp/connected_components.cpp b/samples/cpp/connected_components.cpp index 781ffec046..617752b425 100644 --- a/samples/cpp/connected_components.cpp +++ b/samples/cpp/connected_components.cpp @@ -13,7 +13,7 @@ static void on_trackbar(int, void*) Mat bw = threshval < 128 ? (img < threshval) : (img > threshval); Mat labelImage(img.size(), CV_32S); int nLabels = connectedComponents(bw, labelImage, 8); - Vec3b colors[nLabels]; + std::vector colors(nLabels); colors[0] = Vec3b(0, 0, 0);//background for(int label = 1; label < nLabels; ++label){ colors[label] = Vec3b( (rand()&255), (rand()&255), (rand()&255) ); @@ -41,14 +41,14 @@ static void help() const char* keys = { - "{@image |stuff.jpg|image for converting to a grayscale}" + "{@image|stuff.jpg|image for converting to a grayscale}" }; int main( int argc, const char** argv ) { help(); CommandLineParser parser(argc, argv, keys); - string inputImage = parser.get(1); + string inputImage = parser.get("@image"); img = imread(inputImage.c_str(), 0); if(img.empty()) From e70b3ef598870739db85cfad4a420c89a6097968 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Thu, 29 Nov 2012 02:21:06 -0800 Subject: [PATCH 07/16] use a ltype parameter to determine result Label image type; export stats with differening types over different outputarrays --- .../include/opencv2/imgproc/imgproc.hpp | 6 +- modules/imgproc/src/connectedcomponents.cpp | 82 ++++++++++++------- 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 8920ed2a12..3c682452ba 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1091,13 +1091,13 @@ enum { TM_SQDIFF=0, TM_SQDIFF_NORMED=1, TM_CCORR=2, TM_CCORR_NORMED=3, TM_CCOEFF CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, OutputArray result, int method ); -enum { CC_STAT_LEFT=0, CC_STAT_TOP=1, CC_STAT_WIDTH=2, CC_STAT_HEIGHT=3, CC_STAT_CX=4, CC_STAT_CY=5, CC_STAT_AREA=6, CC_STAT_INTEGRAL_X=7, CC_STAT_INTEGRAL_Y=8, CC_STAT_MAX = 9}; +enum { CC_STAT_LEFT=0, CC_STAT_TOP=1, CC_STAT_WIDTH=2, CC_STAT_HEIGHT=3, CC_STAT_AREA=4, CC_STAT_MAX = 5}; //! computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total //number of labels [0, N-1] where 0 represents the background label. L's value type determines the label type, an important //consideration based on the total number of labels or alternatively the total number of pixels. -CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8); -CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, int connectivity = 8); +CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8, int ltype=CV_32S); +CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, OutputArray centroids, int connectivity = 8, int ltype=CV_32S); //! mode of the contour retrieval algorithm diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index b52a6c7293..6418337c53 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -69,29 +69,42 @@ namespace cv{ } void finish(){} }; + struct Point2ui64{ + uint64_t x, y; + Point2ui64(uint64_t _x, uint64_t _y):x(_x), y(_y){} + }; template struct CCStatsOp{ + OutputArray _mstatsv; cv::Mat statsv; - CCStatsOp(OutputArray _statsv): statsv(_statsv.getMat()){ + OutputArray _mcentroidsv; + cv::Mat centroidsv; + std::vector integrals; + + CCStatsOp(OutputArray _statsv, OutputArray _centroidsv): _mstatsv(_statsv), _mcentroidsv(_centroidsv){ } inline void init(const LabelT nlabels){ - statsv = cv::Mat(nlabels, CC_STAT_MAX, cv::DataType::type); + _mstatsv.create(cv::Size(nlabels, CC_STAT_MAX), cv::DataType::type); + statsv = _mstatsv.getMat(); + _mcentroidsv.create(cv::Size(nlabels, 2), cv::DataType::type); + centroidsv = _mcentroidsv.getMat(); + for(int l = 0; l < (int) nlabels; ++l){ - double *row = &statsv.at(l, 0); + unsigned int *row = (unsigned int *) &statsv.at(l, 0); row[CC_STAT_LEFT] = std::numeric_limits::max(); row[CC_STAT_TOP] = std::numeric_limits::max(); row[CC_STAT_WIDTH] = std::numeric_limits::min(); row[CC_STAT_HEIGHT] = std::numeric_limits::min(); - row[CC_STAT_CX] = 0; - row[CC_STAT_CY] = 0; + //row[CC_STAT_CX] = 0; + //row[CC_STAT_CY] = 0; row[CC_STAT_AREA] = 0; - row[CC_STAT_INTEGRAL_X] = 0; - row[CC_STAT_INTEGRAL_Y] = 0; } + integrals.resize(nlabels, Point2ui64(0, 0)); } void operator()(int r, int c, LabelT l){ - double *row = &statsv.at(l, 0); + int *row = &statsv.at(l, 0); + unsigned int *urow = (unsigned int *) row; if(c > row[CC_STAT_WIDTH]){ row[CC_STAT_WIDTH] = c; }else{ @@ -106,19 +119,23 @@ namespace cv{ row[CC_STAT_TOP] = r; } } - row[CC_STAT_INTEGRAL_X] += c; - row[CC_STAT_INTEGRAL_Y] += r; - row[CC_STAT_AREA]++; + urow[CC_STAT_AREA]++; + Point2ui64 &integral = integrals[l]; + integral.x += c; + integral.y += r; } void finish(){ for(int l = 0; l < statsv.rows; ++l){ - double *row = &statsv.at(l, 0); + unsigned int *row = (unsigned int *) &statsv.at(l, 0); row[CC_STAT_LEFT] = std::min(row[CC_STAT_LEFT], row[CC_STAT_WIDTH]); row[CC_STAT_WIDTH] = row[CC_STAT_WIDTH] - row[CC_STAT_LEFT] + 1; row[CC_STAT_TOP] = std::min(row[CC_STAT_TOP], row[CC_STAT_HEIGHT]); row[CC_STAT_HEIGHT] = row[CC_STAT_HEIGHT] - row[CC_STAT_TOP] + 1; - row[CC_STAT_CX] = row[CC_STAT_INTEGRAL_X] / double(row[CC_STAT_AREA]); - row[CC_STAT_CY] = row[CC_STAT_INTEGRAL_Y] / double(row[CC_STAT_AREA]); + + Point2ui64 &integral = integrals[l]; + double *centroid = ¢roidsv.at(l, 0); + centroid[0] = double(integral.x) / row[CC_STAT_AREA]; + centroid[1] = double(integral.y) / row[CC_STAT_AREA]; } } }; @@ -196,9 +213,7 @@ namespace cv{ const int G8[4][2] = {{1, -1}, {1, 0}, {1, 1}, {0, -1}};//a, b, c, d neighborhoods template, int connectivity = 8> struct LabelingImpl{ - LabelT operator()(InputArray _I, OutputArray _L, StatsOp &sop){ - cv::Mat I = _I.getMat(); - cv::Mat L = _L.getMat(); + LabelT operator()(const cv::Mat &I, cv::Mat &L, StatsOp &sop){ CV_Assert(L.rows == I.rows); CV_Assert(L.cols == I.cols); const int rows = L.rows; @@ -347,7 +362,8 @@ namespace cv{ //L's type must have an appropriate depth for the number of pixels in I template -int connectedComponents_sub1(InputArray I, OutputArray L, int connectivity, StatsOp &sop){ +static +int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, StatsOp &sop){ CV_Assert(L.channels() == 1 && I.channels() == 1); CV_Assert(connectivity == 8 || connectivity == 4); @@ -394,13 +410,15 @@ int connectedComponents_sub1(InputArray I, OutputArray L, int connectivity, Stat return -1; } -int connectedComponents(InputArray I, OutputArray L, int connectivity){ - int lDepth = L.depth(); - if(lDepth == CV_8U){ +int connectedComponents(InputArray _I, OutputArray _L, int connectivity, int ltype){ + const cv::Mat I = _I.getMat(); + _L.create(I.size(), CV_MAT_TYPE(ltype)); + cv::Mat L = _L.getMat(); + if(ltype == CV_8U){ connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); - }else if(lDepth == CV_16U){ + }else if(ltype == CV_16U){ connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); - }else if(lDepth == CV_32S){ + }else if(ltype == CV_32S){ connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); }else{ CV_Assert(false); @@ -408,14 +426,16 @@ int connectedComponents(InputArray I, OutputArray L, int connectivity){ } } -int connectedComponentsWithStats(InputArray I, OutputArray L, OutputArray statsv, int connectivity){ - int lDepth = L.depth(); - if(lDepth == CV_8U){ - connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(I, L, connectivity, sop); - }else if(lDepth == CV_16U){ - connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(I, L, connectivity, sop); - }else if(lDepth == CV_32S){ - connectedcomponents::CCStatsOp sop(statsv); return connectedComponents_sub1(I, L, connectivity, sop); +int connectedComponentsWithStats(InputArray _I, OutputArray _L, OutputArray statsv, OutputArray centroids, int connectivity, int ltype){ + const cv::Mat I = _I.getMat(); + _L.create(I.size(), CV_MAT_TYPE(ltype)); + cv::Mat L = _L.getMat(); + if(ltype == CV_8U){ + connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); + }else if(ltype == CV_16U){ + connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); + }else if(ltype == CV_32S){ + connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); }else{ CV_Assert(false); return 0; From d094e4bdbe04340f80b95c8e1f281576fdcb4a1f Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 8 Dec 2012 22:06:15 -0800 Subject: [PATCH 08/16] drop support for 8bit output for size cost relative to utility --- modules/imgproc/src/connectedcomponents.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index 6418337c53..dd1665f65d 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -414,9 +414,7 @@ int connectedComponents(InputArray _I, OutputArray _L, int connectivity, int lty const cv::Mat I = _I.getMat(); _L.create(I.size(), CV_MAT_TYPE(ltype)); cv::Mat L = _L.getMat(); - if(ltype == CV_8U){ - connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); - }else if(ltype == CV_16U){ + if(ltype == CV_16U){ connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); }else if(ltype == CV_32S){ connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); @@ -430,9 +428,7 @@ int connectedComponentsWithStats(InputArray _I, OutputArray _L, OutputArray stat const cv::Mat I = _I.getMat(); _L.create(I.size(), CV_MAT_TYPE(ltype)); cv::Mat L = _L.getMat(); - if(ltype == CV_8U){ - connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); - }else if(ltype == CV_16U){ + if(ltype == CV_16U){ connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); }else if(ltype == CV_32S){ connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); From 63debf5032df775a52136be92c12e6ef0f5d11a4 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Mon, 10 Dec 2012 02:21:45 -0800 Subject: [PATCH 09/16] connectedcomponents test case --- .../imgproc/test/test_connectedcomponents.cpp | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 modules/imgproc/test/test_connectedcomponents.cpp diff --git a/modules/imgproc/test/test_connectedcomponents.cpp b/modules/imgproc/test/test_connectedcomponents.cpp new file mode 100644 index 0000000000..c428cc0742 --- /dev/null +++ b/modules/imgproc/test/test_connectedcomponents.cpp @@ -0,0 +1,108 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include "test_precomp.hpp" +#include + +using namespace cv; +using namespace std; + +class CV_ConnectedComponentsTest : public cvtest::BaseTest +{ +public: + CV_ConnectedComponentsTest(); + ~CV_ConnectedComponentsTest(); +protected: + void run(int); +}; + +CV_ConnectedComponentsTest::CV_ConnectedComponentsTest() {} +CV_ConnectedComponentsTest::~CV_ConnectedComponentsTest() {} + +void CV_ConnectedComponentsTest::run( int /* start_from */) +{ + string exp_path = string(ts->get_data_path()) + "connectedcomponents/ccomp_exp.png"; + Mat exp = imread(exp_path, 0); + Mat orig = imread(string(ts->get_data_path()) + "connectedcomponents/concentric_circles.png", 0); + + if (orig.empty()) + { + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + Mat bw = orig > 128; + Mat labelImage; + int nLabels = connectedComponents(bw, labelImage, 8, CV_32S); + + for(int r = 0; r < labelImage.rows; ++r){ + for(int c = 0; c < labelImage.cols; ++c){ + int l = labelImage.at(r, c); + bool pass = l >= 0 && l <= nLabels; + if(!pass){ + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + } + } + + if( exp.empty() || orig.size() != exp.size() ) + { + imwrite(exp_path, labelImage); + exp = labelImage; + } + + if (0 != norm(labelImage > 0, exp > 0, NORM_INF)) + { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + return; + } + if (nLabels != norm(labelImage, NORM_INF)+1) + { + ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH ); + return; + } + ts->set_failed_test_info(cvtest::TS::OK); +} + +TEST(Imgproc_ConnectedComponents, regression) { CV_ConnectedComponentsTest test; test.safe_run(); } + From 68e77ac051866d0a8745a7e782b35663a6c633b3 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 15 Dec 2012 03:36:51 -0800 Subject: [PATCH 10/16] use opencv's integer type convension --- modules/imgproc/src/connectedcomponents.cpp | 34 ++++++++------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index dd1665f65d..e0ad063646 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -43,14 +43,6 @@ #include "precomp.hpp" #include -//It's 2012 and we still let compilers get by without defining standard integer types... -typedef schar int8_t; -typedef uchar uint8_t; -typedef short int16_t; -typedef unsigned short uint16_t; -typedef int int32_t; -typedef unsigned int uint32_t; - namespace cv{ namespace connectedcomponents{ @@ -70,8 +62,8 @@ namespace cv{ void finish(){} }; struct Point2ui64{ - uint64_t x, y; - Point2ui64(uint64_t _x, uint64_t _y):x(_x), y(_y){} + uint64 x, y; + Point2ui64(uint64 _x, uint64 _y):x(_x), y(_y){} }; template struct CCStatsOp{ @@ -375,9 +367,9 @@ int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, Sta if(lDepth == CV_8U){ if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (int) LabelingImpl()(I, L, sop); + return (int) LabelingImpl()(I, L, sop); }else{ - return (int) LabelingImpl()(I, L, sop); + return (int) LabelingImpl()(I, L, sop); } }else{ CV_Assert(false); @@ -385,21 +377,21 @@ int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, Sta }else if(lDepth == CV_16U){ if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (int) LabelingImpl()(I, L, sop); + return (int) LabelingImpl()(I, L, sop); }else{ - return (int) LabelingImpl()(I, L, sop); + return (int) LabelingImpl()(I, L, sop); } }else{ CV_Assert(false); } }else if(lDepth == CV_32S){ - //note that signed types don't really make sense here and not being able to use uint32_t matters for scientific projects + //note that signed types don't really make sense here and not being able to use unsigned matters for scientific projects //OpenCV: how should we proceed? .at typechecks in debug mode if(iDepth == CV_8U || iDepth == CV_8S){ if(connectivity == 4){ - return (int) LabelingImpl()(I, L, sop); + return (int) LabelingImpl()(I, L, sop); }else{ - return (int) LabelingImpl()(I, L, sop); + return (int) LabelingImpl()(I, L, sop); } }else{ CV_Assert(false); @@ -415,9 +407,9 @@ int connectedComponents(InputArray _I, OutputArray _L, int connectivity, int lty _L.create(I.size(), CV_MAT_TYPE(ltype)); cv::Mat L = _L.getMat(); if(ltype == CV_16U){ - connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); + connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); }else if(ltype == CV_32S){ - connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); + connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); }else{ CV_Assert(false); return 0; @@ -429,9 +421,9 @@ int connectedComponentsWithStats(InputArray _I, OutputArray _L, OutputArray stat _L.create(I.size(), CV_MAT_TYPE(ltype)); cv::Mat L = _L.getMat(); if(ltype == CV_16U){ - connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); + connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); }else if(ltype == CV_32S){ - connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); + connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); }else{ CV_Assert(false); return 0; From ad0bfdfb25f379fa269e557c1c8565f1d10ba992 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sat, 15 Dec 2012 03:37:24 -0800 Subject: [PATCH 11/16] disable windows build warning for connectedcomponents template argument comparisons --- modules/imgproc/src/connectedcomponents.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index e0ad063646..97da8824d1 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -43,6 +43,10 @@ #include "precomp.hpp" #include +#if defined _MSC_VER +#pragma warning(disable: 4127) +#endif + namespace cv{ namespace connectedcomponents{ From 1eae455acb9e3275657bd6ec8e36a92c30e2ccbd Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Sat, 15 Dec 2012 23:14:50 +0400 Subject: [PATCH 12/16] fixed build warnings from VS; hopefully, fixes Android build too --- .../include/opencv2/imgproc/imgproc.hpp | 17 ++- modules/imgproc/src/connectedcomponents.cpp | 118 +++++++----------- 2 files changed, 57 insertions(+), 78 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 6be7ef6d1c..284b25482a 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1104,12 +1104,17 @@ CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, enum { CC_STAT_LEFT=0, CC_STAT_TOP=1, CC_STAT_WIDTH=2, CC_STAT_HEIGHT=3, CC_STAT_AREA=4, CC_STAT_MAX = 5}; -//! computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total -//number of labels [0, N-1] where 0 represents the background label. L's value type determines the label type, an important -//consideration based on the total number of labels or alternatively the total number of pixels. -CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8, int ltype=CV_32S); -CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, OutputArray centroids, int connectivity = 8, int ltype=CV_32S); - +// computes the connected components labeled image of boolean image I +// with 4 or 8 way connectivity - returns N, the total +// number of labels [0, N-1] where 0 represents the background label. +// L's value type determines the label type, an important +// consideration based on the total number of labels or +// alternatively the total number of pixels. +CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, + int connectivity = 8, int ltype=CV_32S); +CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels, + OutputArray stats, OutputArray centroids, + int connectivity = 8, int ltype=CV_32S); //! mode of the contour retrieval algorithm enum diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index 97da8824d1..d6bd125b0b 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -43,22 +43,16 @@ #include "precomp.hpp" #include -#if defined _MSC_VER -#pragma warning(disable: 4127) -#endif - namespace cv{ namespace connectedcomponents{ - template struct NoOp{ NoOp(){ } - void init(const LabelT labels){ - (void) labels; + void init(int /*labels*/){ } inline - void operator()(int r, int c, LabelT l){ + void operator()(int r, int c, int l){ (void) r; (void) c; (void) l; @@ -69,38 +63,35 @@ namespace cv{ uint64 x, y; Point2ui64(uint64 _x, uint64 _y):x(_x), y(_y){} }; - template + struct CCStatsOp{ - OutputArray _mstatsv; + const _OutputArray* _mstatsv; cv::Mat statsv; - OutputArray _mcentroidsv; + const _OutputArray* _mcentroidsv; cv::Mat centroidsv; std::vector integrals; - CCStatsOp(OutputArray _statsv, OutputArray _centroidsv): _mstatsv(_statsv), _mcentroidsv(_centroidsv){ + CCStatsOp(OutputArray _statsv, OutputArray _centroidsv): _mstatsv(&_statsv), _mcentroidsv(&_centroidsv){ } inline - void init(const LabelT nlabels){ - _mstatsv.create(cv::Size(nlabels, CC_STAT_MAX), cv::DataType::type); - statsv = _mstatsv.getMat(); - _mcentroidsv.create(cv::Size(nlabels, 2), cv::DataType::type); - centroidsv = _mcentroidsv.getMat(); + void init(int nlabels){ + _mstatsv->create(cv::Size(nlabels, CC_STAT_MAX), cv::DataType::type); + statsv = _mstatsv->getMat(); + _mcentroidsv->create(cv::Size(nlabels, 2), cv::DataType::type); + centroidsv = _mcentroidsv->getMat(); for(int l = 0; l < (int) nlabels; ++l){ - unsigned int *row = (unsigned int *) &statsv.at(l, 0); - row[CC_STAT_LEFT] = std::numeric_limits::max(); - row[CC_STAT_TOP] = std::numeric_limits::max(); - row[CC_STAT_WIDTH] = std::numeric_limits::min(); - row[CC_STAT_HEIGHT] = std::numeric_limits::min(); - //row[CC_STAT_CX] = 0; - //row[CC_STAT_CY] = 0; + int *row = (int *) &statsv.at(l, 0); + row[CC_STAT_LEFT] = INT_MAX; + row[CC_STAT_TOP] = INT_MAX; + row[CC_STAT_WIDTH] = INT_MIN; + row[CC_STAT_HEIGHT] = INT_MIN; row[CC_STAT_AREA] = 0; } integrals.resize(nlabels, Point2ui64(0, 0)); } - void operator()(int r, int c, LabelT l){ + void operator()(int r, int c, int l){ int *row = &statsv.at(l, 0); - unsigned int *urow = (unsigned int *) row; if(c > row[CC_STAT_WIDTH]){ row[CC_STAT_WIDTH] = c; }else{ @@ -115,14 +106,14 @@ namespace cv{ row[CC_STAT_TOP] = r; } } - urow[CC_STAT_AREA]++; + row[CC_STAT_AREA]++; Point2ui64 &integral = integrals[l]; integral.x += c; integral.y += r; } void finish(){ for(int l = 0; l < statsv.rows; ++l){ - unsigned int *row = (unsigned int *) &statsv.at(l, 0); + int *row = &statsv.at(l, 0); row[CC_STAT_LEFT] = std::min(row[CC_STAT_LEFT], row[CC_STAT_WIDTH]); row[CC_STAT_WIDTH] = row[CC_STAT_WIDTH] - row[CC_STAT_LEFT] + 1; row[CC_STAT_TOP] = std::min(row[CC_STAT_TOP], row[CC_STAT_HEIGHT]); @@ -130,8 +121,9 @@ namespace cv{ Point2ui64 &integral = integrals[l]; double *centroid = ¢roidsv.at(l, 0); - centroid[0] = double(integral.x) / row[CC_STAT_AREA]; - centroid[1] = double(integral.y) / row[CC_STAT_AREA]; + double area = ((unsigned*)row)[CC_STAT_AREA]; + centroid[0] = double(integral.x) / area; + centroid[1] = double(integral.y) / area; } } }; @@ -207,11 +199,12 @@ namespace cv{ const int G4[2][2] = {{1, 0}, {0, -1}};//b, d neighborhoods //reference for 8-way: {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}};//a, b, c, d neighborhoods const int G8[4][2] = {{1, -1}, {1, 0}, {1, 1}, {0, -1}};//a, b, c, d neighborhoods - template, int connectivity = 8> + template struct LabelingImpl{ - LabelT operator()(const cv::Mat &I, cv::Mat &L, StatsOp &sop){ + LabelT operator()(const cv::Mat &I, cv::Mat &L, int connectivity, StatsOp &sop){ CV_Assert(L.rows == I.rows); CV_Assert(L.cols == I.cols); + CV_Assert(connectivity == 8 || connectivity == 4); const int rows = L.rows; const int cols = L.cols; size_t Plength = (size_t(rows + 3 - 1)/3) * (size_t(cols + 3 - 1)/3); @@ -295,7 +288,6 @@ namespace cv{ } }else{ //B & D only - assert(connectivity == 4); const int b = 0; const int d = 1; const bool T_b_r = (r_i - G4[b][0]) >= 0; @@ -368,70 +360,52 @@ int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, Sta using connectedcomponents::LabelingImpl; //warn if L's depth is not sufficient? + CV_Assert(iDepth == CV_8U || iDepth == CV_8S); + if(lDepth == CV_8U){ - if(iDepth == CV_8U || iDepth == CV_8S){ - if(connectivity == 4){ - return (int) LabelingImpl()(I, L, sop); - }else{ - return (int) LabelingImpl()(I, L, sop); - } - }else{ - CV_Assert(false); - } + return (int) LabelingImpl()(I, L, connectivity, sop); }else if(lDepth == CV_16U){ - if(iDepth == CV_8U || iDepth == CV_8S){ - if(connectivity == 4){ - return (int) LabelingImpl()(I, L, sop); - }else{ - return (int) LabelingImpl()(I, L, sop); - } - }else{ - CV_Assert(false); - } + return (int) LabelingImpl()(I, L, connectivity, sop); }else if(lDepth == CV_32S){ //note that signed types don't really make sense here and not being able to use unsigned matters for scientific projects //OpenCV: how should we proceed? .at typechecks in debug mode - if(iDepth == CV_8U || iDepth == CV_8S){ - if(connectivity == 4){ - return (int) LabelingImpl()(I, L, sop); - }else{ - return (int) LabelingImpl()(I, L, sop); - } - }else{ - CV_Assert(false); - } + return (int) LabelingImpl()(I, L, connectivity, sop); } CV_Error(CV_StsUnsupportedFormat, "unsupported label/image type"); return -1; } -int connectedComponents(InputArray _I, OutputArray _L, int connectivity, int ltype){ +} + +int cv::connectedComponents(InputArray _I, OutputArray _L, int connectivity, int ltype){ const cv::Mat I = _I.getMat(); - _L.create(I.size(), CV_MAT_TYPE(ltype)); + _L.create(I.size(), CV_MAT_DEPTH(ltype)); cv::Mat L = _L.getMat(); + connectedcomponents::NoOp sop; if(ltype == CV_16U){ - connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); + return connectedComponents_sub1(I, L, connectivity, sop); }else if(ltype == CV_32S){ - connectedcomponents::NoOp sop; return connectedComponents_sub1(I, L, connectivity, sop); + return connectedComponents_sub1(I, L, connectivity, sop); }else{ - CV_Assert(false); + CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s"); return 0; } } -int connectedComponentsWithStats(InputArray _I, OutputArray _L, OutputArray statsv, OutputArray centroids, int connectivity, int ltype){ +int cv::connectedComponentsWithStats(InputArray _I, OutputArray _L, OutputArray statsv, + OutputArray centroids, int connectivity, int ltype) +{ const cv::Mat I = _I.getMat(); - _L.create(I.size(), CV_MAT_TYPE(ltype)); + _L.create(I.size(), CV_MAT_DEPTH(ltype)); cv::Mat L = _L.getMat(); + connectedcomponents::CCStatsOp sop(statsv, centroids); if(ltype == CV_16U){ - connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); + return connectedComponents_sub1(I, L, connectivity, sop); }else if(ltype == CV_32S){ - connectedcomponents::CCStatsOp sop(statsv, centroids); return connectedComponents_sub1(I, L, connectivity, sop); + return connectedComponents_sub1(I, L, connectivity, sop); }else{ - CV_Assert(false); + CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s"); return 0; } } - -} From 5f86eb8d63f1684088940d36015ee0bb79436484 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Dec 2012 00:44:32 -0800 Subject: [PATCH 13/16] proper dimensions from cv::Size in output stats/centroid --- modules/imgproc/src/connectedcomponents.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index d6bd125b0b..c07249b474 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -75,9 +75,9 @@ namespace cv{ } inline void init(int nlabels){ - _mstatsv->create(cv::Size(nlabels, CC_STAT_MAX), cv::DataType::type); + _mstatsv->create(cv::Size(CC_STAT_MAX, nlabels), cv::DataType::type); statsv = _mstatsv->getMat(); - _mcentroidsv->create(cv::Size(nlabels, 2), cv::DataType::type); + _mcentroidsv->create(cv::Size(2, nlabels), cv::DataType::type); centroidsv = _mcentroidsv->getMat(); for(int l = 0; l < (int) nlabels; ++l){ From e1b60aa4e326b900b63596c19c50ad97a8de98eb Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Sun, 16 Dec 2012 22:44:36 -0800 Subject: [PATCH 14/16] drop usage of macros... the type is already there! --- modules/imgproc/src/connectedcomponents.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index c07249b474..69655869ca 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -380,7 +380,7 @@ int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, Sta int cv::connectedComponents(InputArray _I, OutputArray _L, int connectivity, int ltype){ const cv::Mat I = _I.getMat(); - _L.create(I.size(), CV_MAT_DEPTH(ltype)); + _L.create(I.size(), ltype); cv::Mat L = _L.getMat(); connectedcomponents::NoOp sop; if(ltype == CV_16U){ @@ -397,7 +397,7 @@ int cv::connectedComponentsWithStats(InputArray _I, OutputArray _L, OutputArray OutputArray centroids, int connectivity, int ltype) { const cv::Mat I = _I.getMat(); - _L.create(I.size(), CV_MAT_DEPTH(ltype)); + _L.create(I.size(), ltype); cv::Mat L = _L.getMat(); connectedcomponents::CCStatsOp sop(statsv, centroids); if(ltype == CV_16U){ From 31857082f4bbfb124b39399e8381efa2a93f0956 Mon Sep 17 00:00:00 2001 From: Vadim Pisarevsky Date: Mon, 17 Dec 2012 21:33:21 +0400 Subject: [PATCH 15/16] probably fixed build problems on Android --- modules/imgproc/src/connectedcomponents.cpp | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index 69655869ca..4fee0aacd1 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -378,32 +378,32 @@ int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, Sta } -int cv::connectedComponents(InputArray _I, OutputArray _L, int connectivity, int ltype){ - const cv::Mat I = _I.getMat(); - _L.create(I.size(), ltype); - cv::Mat L = _L.getMat(); +int cv::connectedComponents(InputArray _img, OutputArray _labels, int connectivity, int ltype){ + const cv::Mat img = _img.getMat(); + _labels.create(img.size(), CV_MAT_DEPTH(ltype)); + cv::Mat labels = _labels.getMat(); connectedcomponents::NoOp sop; if(ltype == CV_16U){ - return connectedComponents_sub1(I, L, connectivity, sop); + return connectedComponents_sub1(img, labels, connectivity, sop); }else if(ltype == CV_32S){ - return connectedComponents_sub1(I, L, connectivity, sop); + return connectedComponents_sub1(img, labels, connectivity, sop); }else{ CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s"); return 0; } } -int cv::connectedComponentsWithStats(InputArray _I, OutputArray _L, OutputArray statsv, +int cv::connectedComponentsWithStats(InputArray _img, OutputArray _labels, OutputArray statsv, OutputArray centroids, int connectivity, int ltype) { - const cv::Mat I = _I.getMat(); - _L.create(I.size(), ltype); - cv::Mat L = _L.getMat(); + const cv::Mat img = _img.getMat(); + _labels.create(img.size(), CV_MAT_DEPTH(ltype)); + cv::Mat labels = _labels.getMat(); connectedcomponents::CCStatsOp sop(statsv, centroids); if(ltype == CV_16U){ - return connectedComponents_sub1(I, L, connectivity, sop); + return connectedComponents_sub1(img, labels, connectivity, sop); }else if(ltype == CV_32S){ - return connectedComponents_sub1(I, L, connectivity, sop); + return connectedComponents_sub1(img, labels, connectivity, sop); }else{ CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s"); return 0; From 4cb25e9584e3349b41b85b183c44fd81028187c7 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Wed, 19 Dec 2012 14:55:46 -0800 Subject: [PATCH 16/16] update documentation to latest connected components interface --- ...uctural_analysis_and_shape_descriptors.rst | 54 ++++++++----------- .../include/opencv2/imgproc/imgproc.hpp | 6 +-- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst b/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst index ad5c22cbb6..9f45301c57 100644 --- a/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst +++ b/modules/imgproc/doc/structural_analysis_and_shape_descriptors.rst @@ -119,47 +119,37 @@ These values are proved to be invariants to the image scale, rotation, and refle .. seealso:: :ocv:func:`matchShapes` connectedComponents ------------ -computes the connected components labeled image of boolean image I with 4 or 8 way connectivity - returns N, the total -number of labels [0, N-1] where 0 represents the background label. L's value type determines the label type, an important -consideration based on the total number of labels or alternatively the total number of pixels. +----------------------- +computes the connected components labeled image of boolean image ``image`` with 4 or 8 way connectivity - returns N, the total number of labels [0, N-1] where 0 represents the background label. ltype specifies the output label image type, an important consideration based on the total number of labels or alternatively the total number of pixels in the source image. -.. ocv:function:: uint64 connectedComponents(Mat &L, const Mat &I, int connectivity = 8) +.. ocv:function:: int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8, int ltype=CV_32S) -.. ocv:function:: uint64 connectedComponentsWithStats(Mat &L, const Mat &I, std::vector &statsv, int connectivity = 8) +.. ocv:function:: int connectedComponentsWithStats(InputArray image, OutputArray labels, OutputArray stats, OutputArray centroids, int connectivity = 8, int ltype=CV_32S) - :param L: destitination Labeled image + :param image: the image to be labeled - :param I: the image to be labeled + :param labels: destination labeled image :param connectivity: 8 or 4 for 8-way or 4-way connectivity respectively - :param statsv: statistics for each label, including the background label + :param ltype: output image label type. Currently CV_32S and CV_16U are supported. -Statistics information such as bounding box, area, and centroid is exported via the ``ConnectComponentStats`` structure defined as: :: + :param statsv: statistics output for each label, including the background label, see below for available statistics. Statistics are accessed via statsv(label, COLUMN) where available columns are defined below. + + * **CC_STAT_LEFT** The leftmost (x) coordinate which is the inclusive start of the bounding box in the horizontal + direction. + + * **CC_STAT_TOP** The topmost (y) coordinate which is the inclusive start of the bounding box in the vertical + direction. + + * **CC_STAT_WIDTH** The horizontal size of the bounding box + + * **CC_STAT_HEIGHT** The vertical size of the bounding box + + * **CC_STAT_AREA** The total area (in pixels) of the connected component + + :param centroids: floating point centroid (x,y) output for each label, including the background label - class CV_EXPORTS ConnectedComponentStats - { - public: - //! lower left corner column - int lower_x; - //! lower left corner row - int lower_y; - //! upper right corner column - int upper_x; - //! upper right corner row - int upper_y; - //! centroid column - double centroid_x; - //! centroid row - double centroid_y; - //! sum of all columns where the image was non-zero - uint64 integral_x; - //! sum of all rows where the image was non-zero - uint64 integral_y; - //! count of all non-zero pixels - unsigned int area; - }; findContours ---------------- diff --git a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp index 284b25482a..34df8f2fcf 100644 --- a/modules/imgproc/include/opencv2/imgproc/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc/imgproc.hpp @@ -1104,12 +1104,12 @@ CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ, enum { CC_STAT_LEFT=0, CC_STAT_TOP=1, CC_STAT_WIDTH=2, CC_STAT_HEIGHT=3, CC_STAT_AREA=4, CC_STAT_MAX = 5}; -// computes the connected components labeled image of boolean image I +// computes the connected components labeled image of boolean image ``image`` // with 4 or 8 way connectivity - returns N, the total // number of labels [0, N-1] where 0 represents the background label. -// L's value type determines the label type, an important +// ltype specifies the output label image type, an important // consideration based on the total number of labels or -// alternatively the total number of pixels. +// alternatively the total number of pixels in the source image. CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels, int connectivity = 8, int ltype=CV_32S); CV_EXPORTS_W int connectedComponentsWithStats(InputArray image, OutputArray labels,