From 45b4f4f32bf2aec782432c9755e2e5b03a7bd152 Mon Sep 17 00:00:00 2001 From: Jason Newton Date: Fri, 24 Aug 2012 23:57:17 -0700 Subject: [PATCH 01/11] 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 63f521814..3d80cfee4 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 000000000..cc83f9748 --- /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 c915bcda4..6d3357fb6 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/11] 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 3d80cfee4..0cb761b40 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 cc83f9748..50a1ca1e0 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 6d3357fb6..7b362dfd6 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/11] 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 55cea58d5..ad5c22cbb 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 0cb761b40..3c9d78710 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 50a1ca1e0..8e75ce7bd 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 28cf00eac..5dbbbb440 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/11] 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 3c9d78710..5e3da2658 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 8e75ce7bd..db118f5cc 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); + 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_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); + 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); @@ -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/11] 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 5e3da2658..8920ed2a1 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 db118f5cc..b52a6c729 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 5dbbbb440..bc52f308c 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 7b362dfd6..781ffec04 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/11] 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 781ffec04..617752b42 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/11] 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 8920ed2a1..3c682452b 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 b52a6c729..6418337c5 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/11] 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 6418337c5..dd1665f65 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/11] 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 000000000..c428cc074 --- /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/11] 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 dd1665f65..e0ad06364 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/11] 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 e0ad06364..97da8824d 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{