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 ); }