diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 0f4cff0f4f..e57dbb9370 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3703,8 +3703,10 @@ image with 4 or 8 way connectivity - returns N, the total number of labels [0, N 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. ccltype specifies the connected components labeling algorithm to use, currently -Grana's (BBDT) and Wu's (SAUF) algorithms are supported, see the cv::ConnectedComponentsAlgorithmsTypes +Grana (BBDT) and Wu's (SAUF) algorithms are supported, see the cv::ConnectedComponentsAlgorithmsTypes for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not. +This function uses parallel version of both Grana and Wu's algorithms if at least one allowed +parallel framework is enabled and if the rows of the image are at least twice the number returned by getNumberOfCPUs. @param image the 8-bit single-channel image to be labeled @param labels destination labeled image @@ -3735,7 +3737,8 @@ consideration based on the total number of labels or alternatively the total num the source image. ccltype specifies the connected components labeling algorithm to use, currently Grana's (BBDT) and Wu's (SAUF) algorithms are supported, see the cv::ConnectedComponentsAlgorithmsTypes for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not. - +This function uses parallel version of both Grana and Wu's algorithms (statistics included) if at least one allowed +parallel framework is enabled and if the rows of the image are at least twice the number returned by getNumberOfCPUs. @param image the 8-bit single-channel image to be labeled @param labels destination labeled image diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index 9ffcf719f7..1887f3be13 100644 --- a/modules/imgproc/src/connectedcomponents.cpp +++ b/modules/imgproc/src/connectedcomponents.cpp @@ -42,6 +42,7 @@ // 2016 Federico Bolelli // 2016 Lorenzo Baraldi // 2016 Roberto Vezzani +// 2016 Michele Cancilla //M*/ // #include "precomp.hpp" @@ -53,30 +54,50 @@ namespace cv{ struct NoOp{ NoOp(){ } + + inline void init(int /*labels*/){ } + + inline + void initElement(const int /*nlabels*/){ + } + inline void operator()(int r, int c, int l){ - (void) r; - (void) c; - (void) l; + (void)r; + (void)c; + (void)l; } - void finish(){} + + void finish(){ + } + + inline + void setNextLoc(const int /*nextLoc*/){ + } + + inline static + void mergeStats(const cv::Mat& /*imgLabels*/, NoOp * /*sopArray*/, NoOp& /*sop*/, const int& /*nLabels*/){ + } + }; struct Point2ui64{ uint64 x, y; - Point2ui64(uint64 _x, uint64 _y):x(_x), y(_y){} - }; + Point2ui64(uint64 _x, uint64 _y) :x(_x), y(_y){} + }; struct CCStatsOp{ - const _OutputArray* _mstatsv; + const _OutputArray *_mstatsv; cv::Mat statsv; - const _OutputArray* _mcentroidsv; + const _OutputArray *_mcentroidsv; cv::Mat centroidsv; std::vector integrals; + int _nextLoc; + + CCStatsOp(){} + CCStatsOp(OutputArray _statsv, OutputArray _centroidsv) : _mstatsv(&_statsv), _mcentroidsv(&_centroidsv){} - CCStatsOp(OutputArray _statsv, OutputArray _centroidsv): _mstatsv(&_statsv), _mcentroidsv(&_centroidsv){ - } inline void init(int nlabels){ _mstatsv->create(cv::Size(CC_STAT_MAX, nlabels), cv::DataType::type); @@ -84,8 +105,8 @@ namespace cv{ _mcentroidsv->create(cv::Size(2, nlabels), cv::DataType::type); centroidsv = _mcentroidsv->getMat(); - for(int l = 0; l < (int) nlabels; ++l){ - int *row = (int *) &statsv.at(l, 0); + for (int l = 0; l < (int)nlabels; ++l){ + 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; @@ -94,30 +115,76 @@ namespace cv{ } integrals.resize(nlabels, Point2ui64(0, 0)); } + + inline + void initElement(const int nlabels){ + statsv = cv::Mat(nlabels, CC_STAT_MAX, cv::DataType::type); + for (int l = 0; l < (int)nlabels; ++l){ + int *row = (int *)statsv.ptr(l); + 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, int l){ - int *row = &statsv.at(l, 0); + int *row =& statsv.at(l, 0); row[CC_STAT_LEFT] = MIN(row[CC_STAT_LEFT], c); row[CC_STAT_WIDTH] = MAX(row[CC_STAT_WIDTH], c); row[CC_STAT_TOP] = MIN(row[CC_STAT_TOP], r); row[CC_STAT_HEIGHT] = MAX(row[CC_STAT_HEIGHT], r); row[CC_STAT_AREA]++; - Point2ui64 &integral = integrals[l]; + Point2ui64& integral = integrals[l]; integral.x += c; integral.y += r; } + void finish(){ - for(int l = 0; l < statsv.rows; ++l){ - int *row = &statsv.at(l, 0); + for (int l = 0; l < statsv.rows; ++l){ + int *row =& statsv.at(l, 0); row[CC_STAT_WIDTH] = row[CC_STAT_WIDTH] - row[CC_STAT_LEFT] + 1; row[CC_STAT_HEIGHT] = row[CC_STAT_HEIGHT] - row[CC_STAT_TOP] + 1; - Point2ui64 &integral = integrals[l]; + Point2ui64& integral = integrals[l]; double *centroid = ¢roidsv.at(l, 0); double area = ((unsigned*)row)[CC_STAT_AREA]; centroid[0] = double(integral.x) / area; centroid[1] = double(integral.y) / area; } } + + inline + void setNextLoc(const int nextLoc){ + _nextLoc = nextLoc; + } + + inline static + void mergeStats(const cv::Mat& imgLabels, CCStatsOp *sopArray, CCStatsOp& sop, const int& nLabels){ + const int h = imgLabels.rows; + + if (sop._nextLoc != h){ + for (int nextLoc = sop._nextLoc; nextLoc < h; nextLoc = sopArray[nextLoc]._nextLoc){ + //merge between sopNext and sop + for (int l = 0; l < nLabels; ++l){ + int *rowNext = (int*)sopArray[nextLoc].statsv.ptr(l); + if (rowNext[CC_STAT_AREA] > 0){ //if changed merge all the stats + int *rowMerged = (int*)sop.statsv.ptr(l); + rowMerged[CC_STAT_LEFT] = MIN(rowMerged[CC_STAT_LEFT], rowNext[CC_STAT_LEFT]); + rowMerged[CC_STAT_WIDTH] = MAX(rowMerged[CC_STAT_WIDTH], rowNext[CC_STAT_WIDTH]); + rowMerged[CC_STAT_TOP] = MIN(rowMerged[CC_STAT_TOP], rowNext[CC_STAT_TOP]); + rowMerged[CC_STAT_HEIGHT] = MAX(rowMerged[CC_STAT_HEIGHT], rowNext[CC_STAT_HEIGHT]); + rowMerged[CC_STAT_AREA] += rowNext[CC_STAT_AREA]; + + sop.integrals[l].x += sopArray[nextLoc].integrals[l].x; + sop.integrals[l].y += sopArray[nextLoc].integrals[l].y; + } + } + } + } + } }; //Find the root of the tree of node i @@ -125,7 +192,7 @@ namespace cv{ inline static LabelT findRoot(const LabelT *P, LabelT i){ LabelT root = i; - while(P[root] < root){ + while (P[root] < root){ root = P[root]; } return root; @@ -135,7 +202,7 @@ namespace cv{ template inline static void setRoot(LabelT *P, LabelT i, LabelT root){ - while(P[i] < i){ + while (P[i] < i){ LabelT j = P[i]; P[i] = root; i = j; @@ -157,9 +224,9 @@ namespace cv{ inline static LabelT set_union(LabelT *P, LabelT i, LabelT j){ LabelT root = findRoot(P, i); - if(i != j){ + if (i != j){ LabelT rootj = findRoot(P, j); - if(root > rootj){ + if (root > rootj){ root = rootj; } setRoot(P, j, root); @@ -173,782 +240,676 @@ namespace cv{ inline static LabelT flattenL(LabelT *P, LabelT length){ LabelT k = 1; - for(LabelT i = 1; i < length; ++i){ - if(P[i] < i){ + for (LabelT i = 1; i < length; ++i){ + if (P[i] < i){ P[i] = P[P[i]]; - }else{ + } + else{ P[i] = k; k = k + 1; } } return k; } + template + inline static + void flattenL(LabelT *P, const int start, const int nElem, LabelT& k){ + for (int i = start; i < start + nElem; ++i){ + if (P[i] < i){//node that point to root + P[i] = P[P[i]]; + } + else{ //for root node + P[i] = k; + k = k + 1; + } + } + } + + //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 LabelingWuParallel{ + + class FirstScan8Connectivity : public cv::ParallelLoopBody{ + const cv::Mat& img_; + cv::Mat& imgLabels_; + LabelT *P_; + int *chunksSizeAndLabels_; + + public: + FirstScan8Connectivity(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, int *chunksSizeAndLabels) + : img_(img), imgLabels_(imgLabels), P_(P), chunksSizeAndLabels_(chunksSizeAndLabels){} + + FirstScan8Connectivity& operator=(const FirstScan8Connectivity& ) { return *this; } + + void operator()(const cv::Range& range) const{ + + int r = range.start; + chunksSizeAndLabels_[r] = range.end; + + LabelT label = LabelT((r + 1) / 2) * LabelT((imgLabels_.cols + 1) / 2) + 1; + + const LabelT firstLabel = label; + const int w = img_.cols; + const int limitLine = r, startR = r; + + // Rosenfeld Mask + // +-+-+-+ + // |p|q|r| + // +-+-+-+ + // |s|x| + // +-+-+ + for (; r != range.end; ++r) + { + PixelT const * const img_row = img_.ptr(r); + PixelT const * const img_row_prev = (PixelT *)(((char *)img_row) - img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels_.step.p[0]); + for (int c = 0; c < w; ++c) { + +#define condition_p c > 0 && r > limitLine && img_row_prev[c - 1] > 0 +#define condition_q r > limitLine && img_row_prev[c] > 0 +#define condition_r c < w - 1 && r > limitLine && img_row_prev[c + 1] > 0 +#define condition_s c > 0 && img_row[c - 1] > 0 +#define condition_x img_row[c] > 0 + + if (condition_x){ + if (condition_q){ + //copy q + imgLabels_row[c] = imgLabels_row_prev[c]; + } + else{ + //not q + if (condition_r){ + if (condition_p){ + //concavity p->x->r. Merge + imgLabels_row[c] = set_union(P_, imgLabels_row_prev[c - 1], imgLabels_row_prev[c + 1]); + } + else{ //not p and q + if (condition_s){ + //step s->x->r. Merge + imgLabels_row[c] = set_union(P_, imgLabels_row[c - 1], imgLabels_row_prev[c + 1]); + } + else{ //not p, q and s + //copy r + imgLabels_row[c] = imgLabels_row_prev[c + 1]; + } + } + } + else{ + //not r and q + if (condition_p){ + //copy p + imgLabels_row[c] = imgLabels_row_prev[c - 1]; + } + else{//not r,q and p + if (condition_s){ + imgLabels_row[c] = imgLabels_row[c - 1]; + } + else{ + //new label + imgLabels_row[c] = label; + P_[label] = label; + label = label + 1; + } + } + } + } + } + else{ + //x is a background pixel + imgLabels_row[c] = 0; + } + } + } + //write in the follower memory location + chunksSizeAndLabels_[startR + 1] = label - firstLabel; + } +#undef condition_p +#undef condition_q +#undef condition_r +#undef condition_s +#undef condition_x + }; + + class FirstScan4Connectivity : public cv::ParallelLoopBody{ + const cv::Mat& img_; + cv::Mat& imgLabels_; + LabelT *P_; + int *chunksSizeAndLabels_; + + public: + FirstScan4Connectivity(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, int *chunksSizeAndLabels) + : img_(img), imgLabels_(imgLabels), P_(P), chunksSizeAndLabels_(chunksSizeAndLabels){} + + FirstScan4Connectivity& operator=(const FirstScan4Connectivity& ) { return *this; } + + void operator()(const cv::Range& range) const{ + + int r = range.start; + chunksSizeAndLabels_[r] = range.end; + + LabelT label = LabelT((r * imgLabels_.cols + 1) / 2 + 1); + + const LabelT firstLabel = label; + const int w = img_.cols; + const int limitLine = r, startR = r; + + // Rosenfeld Mask + // +-+-+-+ + // |-|q|-| + // +-+-+-+ + // |s|x| + // +-+-+ + for (; r != range.end; ++r){ + PixelT const * const img_row = img_.ptr(r); + PixelT const * const img_row_prev = (PixelT *)(((char *)img_row) - img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels_.step.p[0]); + for (int c = 0; c < w; ++c) { + +#define condition_q r > limitLine && img_row_prev[c] > 0 +#define condition_s c > 0 && img_row[c - 1] > 0 +#define condition_x img_row[c] > 0 + + if (condition_x){ + if (condition_q){ + if (condition_s){ + //step s->x->q. Merge + imgLabels_row[c] = set_union(P_, imgLabels_row[c - 1], imgLabels_row_prev[c]); + } + else{ + //copy q + imgLabels_row[c] = imgLabels_row_prev[c]; + } + } + else{ + if (condition_s){ // copy s + imgLabels_row[c] = imgLabels_row[c - 1]; + } + else{ + //new label + imgLabels_row[c] = label; + P_[label] = label; + label = label + 1; + } + } + } + else{ + //x is a background pixel + imgLabels_row[c] = 0; + } + } + } + //write in the following memory location + chunksSizeAndLabels_[startR + 1] = label - firstLabel; + } +#undef condition_q +#undef condition_s +#undef condition_x + }; + + class SecondScan : public cv::ParallelLoopBody{ + cv::Mat& imgLabels_; + const LabelT *P_; + StatsOp& sop_; + StatsOp *sopArray_; + LabelT& nLabels_; + public: + SecondScan(cv::Mat& imgLabels, const LabelT *P, StatsOp& sop, StatsOp *sopArray, LabelT& nLabels) + : imgLabels_(imgLabels), P_(P), sop_(sop), sopArray_(sopArray), nLabels_(nLabels){} + + SecondScan& operator=(const SecondScan& ) { return *this; } + + void operator()(const cv::Range& range) const{ + + int r = range.start; + const int rowBegin = r; + const int rowEnd = range.end; + + if (rowBegin > 0){ + sopArray_[rowBegin].initElement(nLabels_); + sopArray_[rowBegin].setNextLoc(rowEnd); //_nextLoc = rowEnd; + + for (; r < rowEnd; ++r) { + LabelT * img_row_start = imgLabels_.ptr(r); + LabelT * const img_row_end = img_row_start + imgLabels_.cols; + for (int c = 0; img_row_start != img_row_end; ++img_row_start, ++c){ + *img_row_start = P_[*img_row_start]; + sopArray_[rowBegin](r, c, *img_row_start); + } + } + } + else{ + //the first thread uses sop in order to make less merges + sop_.setNextLoc(rowEnd); + for (; r < rowEnd; ++r) { + LabelT * img_row_start = imgLabels_.ptr(r); + LabelT * const img_row_end = img_row_start + imgLabels_.cols; + for (int c = 0; img_row_start != img_row_end; ++img_row_start, ++c){ + *img_row_start = P_[*img_row_start]; + sop_(r, c, *img_row_start); + } + } + } + } + }; + + inline static + void mergeLabels8Connectivity(cv::Mat& imgLabels, LabelT *P, const int *chunksSizeAndLabels){ + + // Merge Mask + // +-+-+-+ + // |p|q|r| + // +-+-+-+ + // |x| + // +-+ + const int w = imgLabels.cols, h = imgLabels.rows; + + for (int r = chunksSizeAndLabels[0]; r < h; r = chunksSizeAndLabels[r]){ + + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0]); + + for (int c = 0; c < w; ++c){ + +#define condition_p c > 0 && imgLabels_row_prev[c - 1] > 0 +#define condition_q imgLabels_row_prev[c] > 0 +#define condition_r c < w - 1 && imgLabels_row_prev[c + 1] > 0 +#define condition_x imgLabels_row[c] > 0 + + if (condition_x){ + if (condition_p){ + //merge of two label + imgLabels_row[c] = set_union(P, imgLabels_row_prev[c - 1], imgLabels_row[c]); + } + if (condition_r){ + //merge of two label + imgLabels_row[c] = set_union(P, imgLabels_row_prev[c + 1], imgLabels_row[c]); + } + if (condition_q){ + //merge of two label + imgLabels_row[c] = set_union(P, imgLabels_row_prev[c], imgLabels_row[c]); + } + } + } + } +#undef condition_p +#undef condition_q +#undef condition_r +#undef condition_x + } + + inline static + void mergeLabels4Connectivity(cv::Mat& imgLabels, LabelT *P, const int *chunksSizeAndLabels){ + + // Merge Mask + // +-+-+-+ + // |-|q|-| + // +-+-+-+ + // |x| + // +-+ + const int w = imgLabels.cols, h = imgLabels.rows; + + for (int r = chunksSizeAndLabels[0]; r < h; r = chunksSizeAndLabels[r]){ + + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0]); + + for (int c = 0; c < w; ++c){ + +#define condition_q imgLabels_row_prev[c] > 0 +#define condition_x imgLabels_row[c] > 0 + + if (condition_x){ + if (condition_q){ + //merge of two label + imgLabels_row[c] = set_union(P, imgLabels_row_prev[c], imgLabels_row[c]); + } + } + } + } +#undef condition_q +#undef condition_x + } + + LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){ + CV_Assert(img.rows == imgLabels.rows); + CV_Assert(img.cols == imgLabels.cols); + CV_Assert(connectivity == 8 || connectivity == 4); + + const int nThreads = cv::getNumberOfCPUs(); + cv::setNumThreads(nThreads); + + const int h = img.rows; + const int w = img.cols; + + //A quick and dirty upper bound for the maximimum number of labels. + //Following formula comes from the fact that a 2x2 block in 4-way connectivity + //labeling can never have more than 2 new labels and 1 label for background. + //Worst case image example pattern: + //1 0 1 0 1... + //0 1 0 1 0... + //1 0 1 0 1... + //............ + //Obviously, 4-way connectivity upper bound is also good for 8-way connectivity labeling + const size_t Plength = (size_t(h) * size_t(w) + 1) / 2 + 1; + + //Array used to store info and labeled pixel by each thread. + //Different threads affect different memory location of chunksSizeAndLabels + int *chunksSizeAndLabels = (int *)cv::fastMalloc(h * sizeof(int)); + + //Tree of labels + LabelT *P = (LabelT *)cv::fastMalloc(Plength * sizeof(LabelT)); + //First label is for background + P[0] = 0; + + cv::Range range(0, h); + LabelT nLabels = 1; + + if (connectivity == 8){ + //First scan, each thread works with chunk of img.rows/nThreads rows + //e.g. 300 rows, 4 threads -> each chunks is composed of 75 rows + cv::parallel_for_(range, FirstScan8Connectivity(img, imgLabels, P, chunksSizeAndLabels), nThreads); + + //merge labels of different chunks + mergeLabels8Connectivity(imgLabels, P, chunksSizeAndLabels); + + for (int i = 0; i < h; i = chunksSizeAndLabels[i]){ + flattenL(P, int((i + 1) / 2) * int((w + 1) / 2) + 1, chunksSizeAndLabels[i + 1], nLabels); + } + } + else{ + //First scan, each thread works with chunk of img.rows/nThreads rows + //e.g. 300 rows, 4 threads -> each chunks is composed of 75 rows + cv::parallel_for_(range, FirstScan4Connectivity(img, imgLabels, P, chunksSizeAndLabels), nThreads); + + //merge labels of different chunks + mergeLabels4Connectivity(imgLabels, P, chunksSizeAndLabels); + + for (int i = 0; i < h; i = chunksSizeAndLabels[i]){ + flattenL(P, int(i * w + 1) / 2 + 1, chunksSizeAndLabels[i + 1], nLabels); + } + } + + //Array for statistics dataof threads + StatsOp *sopArray = new StatsOp[h]; + + sop.init(nLabels); + //Second scan + cv::parallel_for_(range, SecondScan(imgLabels, P, sop, sopArray, nLabels), nThreads); + StatsOp::mergeStats(imgLabels, sopArray, sop, nLabels); + sop.finish(); + + delete[] sopArray; + cv::fastFree(chunksSizeAndLabels); + cv::fastFree(P); + return nLabels; + } + };//End struct LabelingWuParallel + + //Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant //using decision trees //Kesheng Wu, et al - //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 struct LabelingWu{ - 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; - //A quick and dirty upper bound for the maximimum number of labels. The 4 comes from - //the fact that a 3x3 block can never have more than 4 unique labels for both 4 & 8-way - const size_t Plength = 4 * (size_t(rows + 3 - 1)/3) * (size_t(cols + 3 - 1)/3); - 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){ - LabelT * const Lrow = L.ptr(r_i); - LabelT * const Lrow_prev = (LabelT *)(((char *)Lrow) - L.step.p[0]); - const PixelT * const Irow = I.ptr(r_i); - const PixelT * const 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]); + LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){ + CV_Assert(imgLabels.rows == img.rows); + CV_Assert(imgLabels.cols == img.cols); + CV_Assert(connectivity == 8 || connectivity == 4); - //decision tree - if(T_b){ - //copy(b) - *Lrows[0] = *(Lrows[G8[b][0]] + G8[b][1]); - }else{//not b - if(T_c){ - if(T_a){ - //copy(c, a) - *Lrows[0] = set_union(P, *(Lrows[G8[c][0]] + G8[c][1]), *(Lrows[G8[a][0]] + G8[a][1])); - }else{ - if(T_d){ - //copy(c, d) - *Lrows[0] = set_union(P, *(Lrows[G8[c][0]] + G8[c][1]), *(Lrows[G8[d][0]] + G8[d][1])); - }else{ - //copy(c) - *Lrows[0] = *(Lrows[G8[c][0]] + G8[c][1]); + const int h = img.rows; + const int w = img.cols; + + //A quick and dirty upper bound for the maximimum number of labels. + //Following formula comes from the fact that a 2x2 block in 4-way connectivity + //labeling can never have more than 2 new labels and 1 label for background. + //Worst case image example pattern: + //1 0 1 0 1... + //0 1 0 1 0... + //1 0 1 0 1... + //............ + //Obviously, 4-way connectivity upper bound is also good for 8-way connectivity labeling + const size_t Plength = (size_t(h) * size_t(w) + 1) / 2 + 1; + //array P for equivalences resolution + LabelT *P = (LabelT *)fastMalloc(sizeof(LabelT) *Plength); + //first label is for background pixels + P[0] = 0; + LabelT lunique = 1; + + if (connectivity == 8){ + for (int r = 0; r < h; ++r){ + // Get row pointers + PixelT const * const img_row = img.ptr(r); + PixelT const * const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]); + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0]); + + for (int c = 0; c < w; ++c){ + +#define condition_p c>0 && r>0 && img_row_prev[c - 1]>0 +#define condition_q r>0 && img_row_prev[c]>0 +#define condition_r c < w - 1 && r > 0 && img_row_prev[c + 1] > 0 +#define condition_s c > 0 && img_row[c - 1] > 0 +#define condition_x img_row[c] > 0 + + if (condition_x){ + if (condition_q){ + //x <- q + imgLabels_row[c] = imgLabels_row_prev[c]; + } + else{ + // q = 0 + if (condition_r){ + if (condition_p){ + // x <- merge(p,r) + imgLabels_row[c] = set_union(P, imgLabels_row_prev[c - 1], imgLabels_row_prev[c + 1]); + } + else{ + // p = q = 0 + if (condition_s){ + // x <- merge(s,r) + imgLabels_row[c] = set_union(P, imgLabels_row[c - 1], imgLabels_row_prev[c + 1]); + } + else{ + // p = q = s = 0 + // x <- r + imgLabels_row[c] = imgLabels_row_prev[c + 1]; + } + } + } + else{ + // r = q = 0 + if (condition_p){ + // x <- p + imgLabels_row[c] = imgLabels_row_prev[c - 1]; + } + else{ + // r = q = p = 0 + if (condition_s){ + imgLabels_row[c] = imgLabels_row[c - 1]; + } + else{ + //new label + imgLabels_row[c] = lunique; + P[lunique] = lunique; + lunique = lunique + 1; + } + } } } - }else{//not c - if(T_a){ - //copy(a) - *Lrows[0] = *(Lrows[G8[a][0]] + G8[a][1]); - }else{ - if(T_d){ - //copy(d) - *Lrows[0] = *(Lrows[G8[d][0]] + G8[d][1]); - }else{ + } + else{ + //x is a background pixel + imgLabels_row[c] = 0; + } + } + } +#undef condition_p +#undef condition_q +#undef condition_r +#undef condition_s +#undef condition_x + } + else{ + for (int r = 0; r < h; ++r){ + PixelT const * const img_row = img.ptr(r); + PixelT const * const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]); + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0]); + for (int c = 0; c < w; ++c) { + +#define condition_q r > 0 && img_row_prev[c] > 0 +#define condition_s c > 0 && img_row[c - 1] > 0 +#define condition_x img_row[c] > 0 + + if (condition_x){ + if (condition_q){ + if (condition_s){ + //Merge s->x->q + imgLabels_row[c] = set_union(P, imgLabels_row[c - 1], imgLabels_row_prev[c]); + } + else{ + //copy q + imgLabels_row[c] = imgLabels_row_prev[c]; + } + } + else{ + if (condition_s){ + // copy s + imgLabels_row[c] = imgLabels_row[c - 1]; + } + else{ //new label - *Lrows[0] = lunique; + imgLabels_row[c] = lunique; P[lunique] = lunique; lunique = lunique + 1; } } } - } - } - }else{ - //B & D only - 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; - } - 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) - *Lrows[0] = set_union(P, *(Lrows[G4[d][0]] + G4[d][1]), *(Lrows[G4[b][0]] + G4[b][1])); - }else{ - //copy(b) - *Lrows[0] = *(Lrows[G4[b][0]] + G4[b][1]); - } - }else{ - if(T_d){ - //copy(d) - *Lrows[0] = *(Lrows[G4[d][0]] + G4[d][1]); - }else{ - //new label - *Lrows[0] = lunique; - P[lunique] = lunique; - lunique = lunique + 1; + else{ + //x is a background pixel + imgLabels_row[c] = 0; } } } +#undef condition_q +#undef condition_s +#undef condition_x } - } - //analysis - LabelT nLabels = flattenL(P, lunique); - sop.init(nLabels); + //analysis + LabelT nLabels = flattenL(P, lunique); + sop.init(nLabels); - for(int r_i = 0; r_i < rows; ++r_i){ - LabelT *Lrow_start = L.ptr(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); + for (int r = 0; r < h; ++r) { + LabelT * img_row_start = imgLabels.ptr(r); + LabelT * const img_row_end = img_row_start + w; + for (int c = 0; img_row_start != img_row_end; ++img_row_start, ++c){ + *img_row_start = P[*img_row_start]; + sop(r, c, *img_row_start); + } } - } - sop.finish(); - fastFree(P); + sop.finish(); + fastFree(P); - return nLabels; - }//End function LabelingWu operator() + return nLabels; + }//End function LabelingWu operator() };//End struct LabelingWu + // Based on "Optimized Block-based Connected Components Labeling with Decision Trees", Costantino Grana et al // Only for 8-connectivity template - struct LabelingGrana{ - LabelT operator()(const cv::Mat &img, cv::Mat &imgLabels, int connectivity, StatsOp &sop){ - CV_Assert(img.rows == imgLabels.rows); - CV_Assert(img.cols == imgLabels.cols); - CV_Assert(connectivity == 8 || connectivity == 4); + struct LabelingGranaParallel{ - const int h = img.rows; - const int w = img.cols; + class FirstScan : public cv::ParallelLoopBody{ + private: + const cv::Mat& img_; + cv::Mat& imgLabels_; + LabelT *P_; + int *chunksSizeAndLabels_; - //A quick and dirty upper bound for the maximimum number of labels. - const size_t Plength = img.rows*img.cols / 4; - LabelT *P = (LabelT *)fastMalloc(sizeof(LabelT)* Plength); - P[0] = 0; - LabelT lunique = 1; + public: + FirstScan(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, int *chunksSizeAndLabels) + : img_(img), imgLabels_(imgLabels), P_(P), chunksSizeAndLabels_(chunksSizeAndLabels){} - // First scan - for (int r = 0; r(r); - const PixelT* const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]); - const PixelT* const img_row_prev_prev = (PixelT *)(((char *)img_row_prev) - img.step.p[0]); - const PixelT* const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); - LabelT* const imgLabels_row = imgLabels.ptr(r); - LabelT* const imgLabels_row_prev_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0] - imgLabels.step.p[0]); - for (int c = 0; c < w; c += 2) { + FirstScan& operator=(const FirstScan&) { return *this; } - // We work with 2x2 blocks - // +-+-+-+ - // |P|Q|R| - // +-+-+-+ - // |S|X| - // +-+-+ + void operator()(const cv::Range& range) const{ - // The pixels are named as follows - // +---+---+---+ - // |a b|c d|e f| - // |g h|i j|k l| - // +---+---+---+ - // |m n|o p| - // |q r|s t| - // +---+---+ + int r = range.start; + r += (r % 2); - // Pixels a, f, l, q are not needed, since we need to understand the - // the connectivity between these blocks and those pixels only metter - // when considering the outer connectivities + chunksSizeAndLabels_[r] = range.end + (range.end % 2); - // A bunch of defines used to check if the pixels are foreground, - // without going outside the image limits. - #define condition_b c-1>=0 && r-2>=0 && img_row_prev_prev[c-1]>0 - #define condition_c r-2>=0 && img_row_prev_prev[c]>0 - #define condition_d c+1=0 && img_row_prev_prev[c+1]>0 - #define condition_e c+2=0 && img_row_prev_prev[c+2]>0 + LabelT label = LabelT((r + 1) / 2) * LabelT((imgLabels_.cols + 1) / 2) + 1; - #define condition_g c-2>=0 && r-1>=0 && img_row_prev[c-2]>0 - #define condition_h c-1>=0 && r-1>=0 && img_row_prev[c-1]>0 - #define condition_i r-1>=0 && img_row_prev[c]>0 - #define condition_j c+1=0 && img_row_prev[c+1]>0 - #define condition_k c+2=0 && img_row_prev[c+2]>0 + const LabelT firstLabel = label; + const int h = img_.rows, w = img_.cols; + const int limitLine = r + 1, startR = r; - #define condition_m c-2>=0 && img_row[c-2]>0 - #define condition_n c-1>=0 && img_row[c-1]>0 - #define condition_o img_row[c]>0 - #define condition_p c+10 + for (; r < range.end; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_prev = (PixelT *)(((char *)img_row) - img_.step.p[0]); + const PixelT * const img_row_prev_prev = (PixelT *)(((char *)img_row_prev) - img_.step.p[0]); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_prev_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels_.step.p[0] - imgLabels_.step.p[0]); + for (int c = 0; c < w; c += 2) { - #define condition_r c-1>=0 && r+10 - #define condition_s r+10 - #define condition_t c+10 + // We work with 2x2 blocks + // +-+-+-+ + // |P|Q|R| + // +-+-+-+ + // |S|X| + // +-+-+ - // This is a decision tree which allows to choose which action to - // perform, checking as few conditions as possible. - // Actions: the blocks label are provisionally stored in the top left - // pixel of the block in the labels image + // The pixels are named as follows + // +---+---+---+ + // |a b|c d|e f| + // |g h|i j|k l| + // +---+---+---+ + // |m n|o p| + // |q r|s t| + // +---+---+ - if (condition_o) { - if (condition_n) { - if (condition_j) { - if (condition_i) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_c) { - if (condition_h) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_g) { - if (condition_b) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - } - else { - if (condition_p) { - if (condition_k) { - if (condition_d) { - if (condition_i) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_c) { - if (condition_h) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_g) { - if (condition_b) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - } - else { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - } - } - else { - if (condition_r) { - if (condition_j) { - if (condition_m) { - if (condition_h) { - if (condition_i) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_c) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - } - else { - if (condition_g) { - if (condition_b) { - if (condition_i) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_c) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - } - else { - if (condition_i) { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - else { - if (condition_h) { - if (condition_c) { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - else { - //Action_14: Merge labels of block P, Q and S - imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]), imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - } - } - else { - if (condition_p) { - if (condition_k) { - if (condition_m) { - if (condition_h) { - if (condition_d) { - if (condition_i) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_c) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - else { - if (condition_d) { - if (condition_g) { - if (condition_b) { - if (condition_i) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_c) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - else { - if (condition_i) { - if (condition_g) { - if (condition_b) { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - else { - //Action_16: labels of block Q, R and S - imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_16: labels of block Q, R and S - imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - } - } - else { - if (condition_i) { - if (condition_d) { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - else { - //Action_16: labels of block Q, R and S - imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); - continue; - } - } - else { - if (condition_h) { - if (condition_d) { - if (condition_c) { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - else { - //Action_15: Merge labels of block P, R and S - imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_15: Merge labels of block P, R and S - imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - } - } - else { - if (condition_h) { - if (condition_m) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - // ACTION_9 Merge labels of block P and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]); - continue; - } - } - else { - if (condition_i) { - if (condition_m) { - if (condition_g) { - if (condition_b) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - } - } - } - else { - if (condition_h) { - if (condition_m) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - // ACTION_9 Merge labels of block P and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]); - continue; - } - } - else { - if (condition_i) { - if (condition_m) { - if (condition_g) { - if (condition_b) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - else { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - } - } - } - } - else { - if (condition_j) { - if (condition_i) { - //Action_4: Assign label of block Q - imgLabels_row[c] = imgLabels_row_prev_prev[c]; - continue; - } - else { - if (condition_h) { - if (condition_c) { - //Action_4: Assign label of block Q - imgLabels_row[c] = imgLabels_row_prev_prev[c]; - continue; - } - else { - //Action_7: Merge labels of block P and Q - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]); - continue; - } - } - else { - //Action_4: Assign label of block Q - imgLabels_row[c] = imgLabels_row_prev_prev[c]; - continue; - } - } - } - else { - if (condition_p) { - if (condition_k) { - if (condition_i) { - if (condition_d) { - //Action_5: Assign label of block R - imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; - continue; - } - else { - // ACTION_10 Merge labels of block Q and R - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]); - continue; - } - } - else { - if (condition_h) { - if (condition_d) { - if (condition_c) { - //Action_5: Assign label of block R - imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; - continue; - } - else { - //Action_8: Merge labels of block P and R - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]); - continue; - } - } - else { - //Action_8: Merge labels of block P and R - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]); - continue; - } - } - else { - //Action_5: Assign label of block R - imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; - continue; - } - } - } - else { - if (condition_i) { - //Action_4: Assign label of block Q - imgLabels_row[c] = imgLabels_row_prev_prev[c]; - continue; - } - else { - if (condition_h) { - //Action_3: Assign label of block P - imgLabels_row[c] = imgLabels_row_prev_prev[c - 2]; - continue; - } - else { - //Action_2: New label (the block has foreground pixels and is not connected to anything else) - imgLabels_row[c] = lunique; - P[lunique] = lunique; - lunique = lunique + 1; - continue; - } - } - } - } - else { - if (condition_i) { - //Action_4: Assign label of block Q - imgLabels_row[c] = imgLabels_row_prev_prev[c]; - continue; - } - else { - if (condition_h) { - //Action_3: Assign label of block P - imgLabels_row[c] = imgLabels_row_prev_prev[c - 2]; - continue; - } - else { - //Action_2: New label (the block has foreground pixels and is not connected to anything else) - imgLabels_row[c] = lunique; - P[lunique] = lunique; - lunique = lunique + 1; - continue; - } - } - } - } - } - } - } - else { - if (condition_s) { - if (condition_p) { + // Pixels a, f, l, q are not needed, since we need to understand the + // the connectivity between these blocks and those pixels only metter + // when considering the outer connectivities + + // A bunch of defines used to check if the pixels are foreground, + // without going outside the image limits. + +#define condition_b c-1>=0 && r > limitLine && img_row_prev_prev[c-1]>0 +#define condition_c r > limitLine && img_row_prev_prev[c]>0 +#define condition_d c+1 limitLine && img_row_prev_prev[c+1]>0 +#define condition_e c+2 limitLine && img_row_prev_prev[c+2]>0 + +#define condition_g c-2>=0 && r > limitLine - 1 && img_row_prev[c-2]>0 +#define condition_h c-1>=0 && r > limitLine - 1 && img_row_prev[c-1]>0 +#define condition_i r > limitLine - 1 && img_row_prev[c]>0 +#define condition_j c+1 limitLine - 1 && img_row_prev[c+1]>0 +#define condition_k c+2 limitLine - 1 && img_row_prev[c+2]>0 + +#define condition_m c-2>=0 && img_row[c-2]>0 +#define condition_n c-1>=0 && img_row[c-1]>0 +#define condition_o img_row[c]>0 +#define condition_p c+10 + +#define condition_r c-1>=0 && r+10 +#define condition_s r+10 +#define condition_t c+10 + + // This is a decision tree which allows to choose which action to + // perform, checking as few conditions as possible. + // Actions are available after the tree. + + if (condition_o) { if (condition_n) { if (condition_j) { if (condition_i) { @@ -972,25 +933,1787 @@ namespace cv{ } else { //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); continue; } } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_p) { + if (condition_k) { + if (condition_d) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + if (condition_h) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + } + else { + if (condition_r) { + if (condition_j) { + if (condition_m) { + if (condition_h) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_g) { + if (condition_b) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_i) { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + else { + if (condition_h) { + if (condition_c) { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + else { + //Action_14: Merge labels of block P_, Q and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + } + else { + if (condition_p) { + if (condition_k) { + if (condition_m) { + if (condition_h) { + if (condition_d) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_d) { + if (condition_g) { + if (condition_b) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_i) { + if (condition_g) { + if (condition_b) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + } + else { + if (condition_i) { + if (condition_d) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_h) { + if (condition_d) { + if (condition_c) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + else { + //Action_15: Merge labels of block P_, R and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_15: Merge labels of block P_, R and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + } + else { + if (condition_h) { + if (condition_m) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + // ACTION_9 Merge labels of block P_ and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_i) { + if (condition_m) { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + } + } + else { + if (condition_h) { + if (condition_m) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + // ACTION_9 Merge labels of block P_ and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_i) { + if (condition_m) { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + } + } + } + else { + if (condition_j) { + if (condition_i) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + if (condition_h) { + if (condition_c) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + //Action_7: Merge labels of block P_ and Q + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]); + continue; + } + } + else { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + } + } + else { + if (condition_p) { + if (condition_k) { + if (condition_i) { + if (condition_d) { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + else { + // ACTION_10 Merge labels of block Q and R + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + if (condition_h) { + if (condition_d) { + if (condition_c) { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + else { + //Action_8: Merge labels of block P_ and R + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + //Action_8: Merge labels of block P_ and R + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + } + } + else { + if (condition_i) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + if (condition_h) { + //Action_3: Assign label of block P_ + imgLabels_row[c] = imgLabels_row_prev_prev[c - 2]; + continue; + } + else { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = label; + P_[label] = label; + label = label + 1; + continue; + } + } + } + } + else { + if (condition_i) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + if (condition_h) { + //Action_3: Assign label of block P_ + imgLabels_row[c] = imgLabels_row_prev_prev[c - 2]; + continue; + } + else { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = label; + P_[label] = label; + label = label + 1; + continue; + } + } + } + } + } + } + } + else { + if (condition_s) { + if (condition_p) { + if (condition_n) { + if (condition_j) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + if (condition_h) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_k) { + if (condition_d) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + if (condition_h) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + } + else { + if (condition_r) { + if (condition_j) { + if (condition_m) { + if (condition_h) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_g) { + if (condition_b) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_k) { + if (condition_d) { + if (condition_m) { + if (condition_h) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_g) { + if (condition_b) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_i) { + if (condition_m) { + if (condition_h) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_i) { + if (condition_m) { + if (condition_h) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + } + } + else { + if (condition_j) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + if (condition_k) { + if (condition_i) { + if (condition_d) { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + else { + // ACTION_10 Merge labels of block Q and R + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + } + else { + if (condition_i) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = label; + P_[label] = label; + label = label + 1; + continue; + } + } + } + } + } + } + else { + if (condition_r) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_n) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = label; + P_[label] = label; + label = label + 1; + continue; + } + } + } + } + else { + if (condition_p) { + if (condition_j) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + if (condition_k) { + if (condition_i) { + if (condition_d) { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + else { + // ACTION_10 Merge labels of block Q and R + imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + } + else { + if (condition_i) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = label; + P_[label] = label; + label = label + 1; + continue; + } + } + } + } + else { + if (condition_t) { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = label; + P_[label] = label; + label = label + 1; + continue; + } + else { + // Action_1: No action (the block has no foreground pixels) + imgLabels_row[c] = 0; + continue; + } + } + } + } + } + } + //write in the follower memory location + chunksSizeAndLabels_[startR + 1] = label - firstLabel; + } +#undef condition_k +#undef condition_j +#undef condition_i +#undef condition_h +#undef condition_g +#undef condition_e +#undef condition_d +#undef condition_c +#undef condition_b + }; + + class SecondScan : public cv::ParallelLoopBody{ + private: + const cv::Mat& img_; + cv::Mat& imgLabels_; + LabelT *P_; + StatsOp& sop_; + StatsOp *sopArray_; + LabelT& nLabels_; + + public: + SecondScan(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, StatsOp& sop, StatsOp *sopArray, LabelT& nLabels) + : img_(img), imgLabels_(imgLabels), P_(P), sop_(sop), sopArray_(sopArray), nLabels_(nLabels){} + + SecondScan& operator=(const SecondScan& ) { return *this; } + + void operator()(const cv::Range& range) const{ + + int r = range.start; + r += (r % 2); + const int rowBegin = r; + const int rowEnd = range.end + range.end % 2; + + if (rowBegin > 0){ + sopArray_[rowBegin].initElement(nLabels_); + sopArray_[rowBegin].setNextLoc(rowEnd); //_nextLoc = rowEnd; + + if (imgLabels_.rows& 1){ + if (imgLabels_.cols& 1){ + //Case 1: both rows and cols odd + for (; r < rowEnd; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]); + // Get rows pointer + for (int c = 0; c < imgLabels_.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P_[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sopArray_[rowBegin](r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sopArray_[rowBegin](r, c, 0); + } + if (c + 1 < imgLabels_.cols) { + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sopArray_[rowBegin](r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sopArray_[rowBegin](r, c + 1, 0); + } + if (r + 1 < imgLabels_.rows) { + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sopArray_[rowBegin](r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sopArray_[rowBegin](r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sopArray_[rowBegin](r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sopArray_[rowBegin](r + 1, c + 1, 0); + } + } + } + else if (r + 1 < imgLabels_.rows) { + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sopArray_[rowBegin](r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sopArray_[rowBegin](r + 1, c, 0); + } + } + } + else { + imgLabels_row[c] = 0; + sopArray_[rowBegin](r, c, 0); + if (c + 1 < imgLabels_.cols) { + imgLabels_row[c + 1] = 0; + sopArray_[rowBegin](r, c + 1, 0); + if (r + 1 < imgLabels_.rows) { + imgLabels_row_fol[c] = 0; + imgLabels_row_fol[c + 1] = 0; + sopArray_[rowBegin](r + 1, c, 0); + sopArray_[rowBegin](r + 1, c + 1, 0); + } + } + else if (r + 1 < imgLabels_.rows) { + imgLabels_row_fol[c] = 0; + sopArray_[rowBegin](r + 1, c, 0); + } + } + } + } + }//END Case 1 + else{ + //Case 2: only rows odd + for (; r < rowEnd; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]); + // Get rows pointer + for (int c = 0; c < imgLabels_.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P_[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sopArray_[rowBegin](r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sopArray_[rowBegin](r, c, 0); + } + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sopArray_[rowBegin](r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sopArray_[rowBegin](r, c + 1, 0); + } + if (r + 1 < imgLabels_.rows) { + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sopArray_[rowBegin](r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sopArray_[rowBegin](r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sopArray_[rowBegin](r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sopArray_[rowBegin](r + 1, c + 1, 0); + } + } + } + else { + imgLabels_row[c] = 0; + imgLabels_row[c + 1] = 0; + sopArray_[rowBegin](r, c, 0); + sopArray_[rowBegin](r, c + 1, 0); + if (r + 1 < imgLabels_.rows) { + imgLabels_row_fol[c] = 0; + imgLabels_row_fol[c + 1] = 0; + sopArray_[rowBegin](r + 1, c, 0); + sopArray_[rowBegin](r + 1, c + 1, 0); + } + } + } + } + }// END Case 2 + } + else{ + if (imgLabels_.cols& 1){ + //Case 3: only cols odd + for (; r < rowEnd; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]); + // Get rows pointer + for (int c = 0; c < imgLabels_.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P_[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sopArray_[rowBegin](r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sopArray_[rowBegin](r, c, 0); + } + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sopArray_[rowBegin](r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sopArray_[rowBegin](r + 1, c, 0); + } + if (c + 1 < imgLabels_.cols) { + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sopArray_[rowBegin](r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sopArray_[rowBegin](r, c + 1, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sopArray_[rowBegin](r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sopArray_[rowBegin](r + 1, c + 1, 0); + } + } + } + else{ + imgLabels_row[c] = 0; + imgLabels_row_fol[c] = 0; + sopArray_[rowBegin](r, c, 0); + sopArray_[rowBegin](r + 1, c, 0); + if (c + 1 < imgLabels_.cols) { + imgLabels_row[c + 1] = 0; + imgLabels_row_fol[c + 1] = 0; + sopArray_[rowBegin](r, c + 1, 0); + sopArray_[rowBegin](r + 1, c + 1, 0); + } + } + } + } + }// END case 3 + else{ + //Case 4: nothing odd + for (; r < rowEnd; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]); + // Get rows pointer + for (int c = 0; c < imgLabels_.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P_[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sopArray_[rowBegin](r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sopArray_[rowBegin](r, c, 0); + } + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sopArray_[rowBegin](r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sopArray_[rowBegin](r, c + 1, 0); + } + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sopArray_[rowBegin](r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sopArray_[rowBegin](r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sopArray_[rowBegin](r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sopArray_[rowBegin](r + 1, c + 1, 0); + } + } + else { + imgLabels_row[c] = 0; + imgLabels_row[c + 1] = 0; + imgLabels_row_fol[c] = 0; + imgLabels_row_fol[c + 1] = 0; + sopArray_[rowBegin](r, c, 0); + sopArray_[rowBegin](r, c + 1, 0); + sopArray_[rowBegin](r + 1, c, 0); + sopArray_[rowBegin](r + 1, c + 1, 0); + } + } + }//END case 4 + } + } + } + else{ + //the first thread uses sop in order to make less merges + sop_.setNextLoc(rowEnd); + if (imgLabels_.rows& 1){ + if (imgLabels_.cols& 1){ + //Case 1: both rows and cols odd + for (; r < rowEnd; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]); + // Get rows pointer + for (int c = 0; c < imgLabels_.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P_[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sop_(r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sop_(r, c, 0); + } + if (c + 1 < imgLabels_.cols) { + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sop_(r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sop_(r, c + 1, 0); + } + if (r + 1 < imgLabels_.rows) { + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sop_(r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sop_(r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sop_(r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sop_(r + 1, c + 1, 0); + } + } + } + else if (r + 1 < imgLabels_.rows) { + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sop_(r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sop_(r + 1, c, 0); + } + } + } + else { + imgLabels_row[c] = 0; + sop_(r, c, 0); + if (c + 1 < imgLabels_.cols) { + imgLabels_row[c + 1] = 0; + sop_(r, c + 1, 0); + if (r + 1 < imgLabels_.rows) { + imgLabels_row_fol[c] = 0; + imgLabels_row_fol[c + 1] = 0; + sop_(r + 1, c, 0); + sop_(r + 1, c + 1, 0); + } + } + else if (r + 1 < imgLabels_.rows) { + imgLabels_row_fol[c] = 0; + sop_(r + 1, c, 0); + } + } + } + } + }//END Case 1 + else{ + //Case 2: only rows odd + for (; r < rowEnd; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]); + // Get rows pointer + for (int c = 0; c < imgLabels_.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P_[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sop_(r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sop_(r, c, 0); + } + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sop_(r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sop_(r, c + 1, 0); + } + if (r + 1 < imgLabels_.rows) { + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sop_(r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sop_(r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sop_(r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sop_(r + 1, c + 1, 0); + } + } + } + else { + imgLabels_row[c] = 0; + imgLabels_row[c + 1] = 0; + sop_(r, c, 0); + sop_(r, c + 1, 0); + if (r + 1 < imgLabels_.rows) { + imgLabels_row_fol[c] = 0; + imgLabels_row_fol[c + 1] = 0; + sop_(r + 1, c, 0); + sop_(r + 1, c + 1, 0); + } + } + } + } + }// END Case 2 + } + else{ + if (imgLabels_.cols& 1){ + //Case 3: only cols odd + for (; r < rowEnd; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]); + // Get rows pointer + for (int c = 0; c < imgLabels_.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P_[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sop_(r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sop_(r, c, 0); + } + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sop_(r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sop_(r + 1, c, 0); + } + if (c + 1 < imgLabels_.cols) { + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sop_(r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sop_(r, c + 1, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sop_(r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sop_(r + 1, c + 1, 0); + } + } + } + else{ + imgLabels_row[c] = 0; + imgLabels_row_fol[c] = 0; + sop_(r, c, 0); + sop_(r + 1, c, 0); + if (c + 1 < imgLabels_.cols) { + imgLabels_row[c + 1] = 0; + imgLabels_row_fol[c + 1] = 0; + sop_(r, c + 1, 0); + sop_(r + 1, c + 1, 0); + } + } + } + } + }// END case 3 + else{ + //Case 4: nothing odd + for (; r < rowEnd; r += 2){ + // Get rows pointer + const PixelT * const img_row = img_.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]); + LabelT * const imgLabels_row = imgLabels_.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]); + // Get rows pointer + for (int c = 0; c < imgLabels_.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P_[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sop_(r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sop_(r, c, 0); + } + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sop_(r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sop_(r, c + 1, 0); + } + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sop_(r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sop_(r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sop_(r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sop_(r + 1, c + 1, 0); + } + } + else { + imgLabels_row[c] = 0; + imgLabels_row[c + 1] = 0; + imgLabels_row_fol[c] = 0; + imgLabels_row_fol[c + 1] = 0; + sop_(r, c, 0); + sop_(r, c + 1, 0); + sop_(r + 1, c, 0); + sop_(r + 1, c + 1, 0); + } + } + }//END case 4 + } + } + } + } + }; + + inline static + void mergeLabels(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, int *chunksSizeAndLabels){ + + // Merge Mask + // +---+---+---+ + // |P -|Q -|R -| + // |- -|- -|- -| + // +---+---+---+ + // |X -| + // |- -| + // +---+ + const int w = imgLabels.cols, h = imgLabels.rows; + + for (int r = chunksSizeAndLabels[0]; r < h; r = chunksSizeAndLabels[r]){ + + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_prev_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0] - imgLabels.step.p[0]); + const PixelT * const img_row = img.ptr(r); + const PixelT * const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]); + + for (int c = 0; c < w; c += 2){ + +#define condition_x imgLabels_row[c] > 0 +#define condition_pppr c > 1 && imgLabels_row_prev_prev[c - 2] > 0 +#define condition_qppr imgLabels_row_prev_prev[c] > 0 +#define condition_qppr1 c < w - 1 +#define condition_qppr2 c < w +#define condition_rppr c < w - 2 && imgLabels_row_prev_prev[c + 2] > 0 + + if (condition_x){ + if (condition_pppr){ + //check in img + if (img_row[c] > 0 && img_row_prev[c - 1] > 0) + //assign the same label + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row[c]); + } + if (condition_qppr){ + if (condition_qppr1){ + if ((img_row[c] > 0 && img_row_prev[c] > 0) || (img_row[c + 1] > 0 && img_row_prev[c] > 0) || + (img_row[c] > 0 && img_row_prev[c + 1] > 0) || (img_row[c + 1] > 0 && img_row_prev[c + 1] > 0)){ + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c]); + } + } + else /*if (condition_qppr2)*/{ + if (img_row[c] > 0 && img_row_prev[c] > 0) + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c]); + } + } + if (condition_rppr){ + if (img_row[c + 1] > 0 && img_row_prev[c + 2] > 0) + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c]); + } + } + } + } + } + + LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){ + CV_Assert(img.rows == imgLabels.rows); + CV_Assert(img.cols == imgLabels.cols); + CV_Assert(connectivity == 8); + + const int nThreads = cv::getNumberOfCPUs(); + cv::setNumThreads(nThreads); + + const int h = img.rows; + const int w = img.cols; + + //A quick and dirty upper bound for the maximimum number of labels. + //Following formula comes from the fact that a 2x2 block in 8-connectivity case + //can never have more than 1 new label and 1 label for background. + //Worst case image example pattern: + //1 0 1 0 1... + //0 0 0 0 0... + //1 0 1 0 1... + //............ + const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1; + + //Array used to store info and labeled pixel by each thread. + //Different threads affect different memory location of chunksSizeAndLabels + int *chunksSizeAndLabels = (int *)cv::fastMalloc(h * sizeof(int)); + + //Tree of labels + LabelT *P = (LabelT *)cv::fastMalloc(Plength * sizeof(LabelT)); + //First label is for background + P[0] = 0; + + cv::Range range(0, h); + + //First scan, each thread works with chunk of img.rows/nThreads rows + //e.g. 300 rows, 4 threads -> each chunks is composed of 75 rows + cv::parallel_for_(range, FirstScan(img, imgLabels, P, chunksSizeAndLabels), nThreads); + + //merge labels of different chunks + mergeLabels(img, imgLabels, P, chunksSizeAndLabels); + + LabelT nLabels = 1; + for (int i = 0; i < h; i = chunksSizeAndLabels[i]){ + flattenL(P, LabelT((i + 1) / 2) * LabelT((w + 1) / 2) + 1, chunksSizeAndLabels[i + 1], nLabels); + } + + //Array for statistics data + StatsOp *sopArray = new StatsOp[h]; + sop.init(nLabels); + + //Second scan + cv::parallel_for_(range, SecondScan(img, imgLabels, P, sop, sopArray, nLabels), nThreads); + + StatsOp::mergeStats(imgLabels, sopArray, sop, nLabels); + sop.finish(); + + delete[] sopArray; + cv::fastFree(chunksSizeAndLabels); + cv::fastFree(P); + return nLabels; + } + };//End struct LabelingGranaParallel + + // Based on �Optimized Block-based Connected Components Labeling with Decision Trees�, Costantino Grana et al + // Only for 8-connectivity + template + struct LabelingGrana{ + LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){ + CV_Assert(img.rows == imgLabels.rows); + CV_Assert(img.cols == imgLabels.cols); + CV_Assert(connectivity == 8); + + const int h = img.rows; + const int w = img.cols; + + //A quick and dirty upper bound for the maximimum number of labels. + //Following formula comes from the fact that a 2x2 block in 8-connectivity case + //can never have more than 1 new label and 1 label for background. + //Worst case image example pattern: + //1 0 1 0 1... + //0 0 0 0 0... + //1 0 1 0 1... + //............ + const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1; + + LabelT *P = (LabelT *)fastMalloc(sizeof(LabelT) *Plength); + P[0] = 0; + LabelT lunique = 1; + + // First scan + for (int r = 0; r < h; r += 2) { + // Get rows pointer + const PixelT * const img_row = img.ptr(r); + const PixelT * const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]); + const PixelT * const img_row_prev_prev = (PixelT *)(((char *)img_row_prev) - img.step.p[0]); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_prev_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0] - imgLabels.step.p[0]); + for (int c = 0; c < w; c += 2) { + + // We work with 2x2 blocks + // +-+-+-+ + // |P|Q|R| + // +-+-+-+ + // |S|X| + // +-+-+ + + // The pixels are named as follows + // +---+---+---+ + // |a b|c d|e f| + // |g h|i j|k l| + // +---+---+---+ + // |m n|o p| + // |q r|s t| + // +---+---+ + + // Pixels a, f, l, q are not needed, since we need to understand the + // the connectivity between these blocks and those pixels only metter + // when considering the outer connectivities + + // A bunch of defines used to check if the pixels are foreground, + // without going outside the image limits. +#define condition_b c-1>=0 && r-2>=0 && img_row_prev_prev[c-1]>0 +#define condition_c r-2>=0 && img_row_prev_prev[c]>0 +#define condition_d c+1=0 && img_row_prev_prev[c+1]>0 +#define condition_e c+2=0 && img_row_prev[c-1]>0 + +#define condition_g c-2>=0 && r-1>=0 && img_row_prev[c-2]>0 +#define condition_h c-1>=0 && r-1>=0 && img_row_prev[c-1]>0 +#define condition_i r-1>=0 && img_row_prev[c]>0 +#define condition_j c+1=0 && img_row_prev[c+1]>0 +#define condition_k c+2=0 && img_row_prev[c+2]>0 + +#define condition_m c-2>=0 && img_row[c-2]>0 +#define condition_n c-1>=0 && img_row[c-1]>0 +#define condition_o img_row[c]>0 +#define condition_p c+10 + +#define condition_r c-1>=0 && r+10 +#define condition_s r+10 +#define condition_t c+10 + + // This is a decision tree which allows to choose which action to + // perform, checking as few conditions as possible. + // Actions: the blocks label are provisionally stored in the top left + // pixel of the block in the labels image + + if (condition_o) { + if (condition_n) { + if (condition_j) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + if (condition_h) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } else { //Action_11: Merge labels of block Q and S imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); continue; } } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } } } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } } - else { + } + else { + if (condition_p) { if (condition_k) { if (condition_d) { if (condition_i) { @@ -1044,57 +2767,29 @@ namespace cv{ continue; } } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } } - else { - if (condition_r) { - if (condition_j) { - if (condition_m) { - if (condition_h) { - if (condition_i) { + } + else { + if (condition_r) { + if (condition_j) { + if (condition_m) { + if (condition_h) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { //Action_6: Assign label of block S imgLabels_row[c] = imgLabels_row[c - 2]; continue; } - else { - if (condition_c) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - } - else { - if (condition_g) { - if (condition_b) { - if (condition_i) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - if (condition_c) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } - } - else { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); - continue; - } - } else { //Action_11: Merge labels of block Q and S imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); @@ -1103,16 +2798,72 @@ namespace cv{ } } else { + if (condition_g) { + if (condition_b) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_i) { //Action_11: Merge labels of block Q and S imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); continue; } + else { + if (condition_h) { + if (condition_c) { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + else { + //Action_14: Merge labels of block P, Q and S + imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } } - else { + } + else { + if (condition_p) { if (condition_k) { - if (condition_d) { - if (condition_m) { - if (condition_h) { + if (condition_m) { + if (condition_h) { + if (condition_d) { if (condition_i) { //Action_6: Assign label of block S imgLabels_row[c] = imgLabels_row[c - 2]; @@ -1132,6 +2883,13 @@ namespace cv{ } } else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_d) { if (condition_g) { if (condition_b) { if (condition_i) { @@ -1164,22 +2922,8 @@ namespace cv{ continue; } } - } - else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - } - else { - if (condition_i) { - if (condition_m) { - if (condition_h) { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; - } - else { + else { + if (condition_i) { if (condition_g) { if (condition_b) { //Action_12: Merge labels of block R and S @@ -1198,6 +2942,20 @@ namespace cv{ continue; } } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + } + else { + if (condition_i) { + if (condition_d) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; } else { //Action_16: labels of block Q, R and S @@ -1206,21 +2964,49 @@ namespace cv{ } } else { - //Action_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); - continue; + if (condition_h) { + if (condition_d) { + if (condition_c) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + else { + //Action_15: Merge labels of block P, R and S + imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_15: Merge labels of block P, R and S + imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } } } } else { - if (condition_i) { + if (condition_h) { if (condition_m) { - if (condition_h) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; - } - else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + // ACTION_9 Merge labels of block P and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_i) { + if (condition_m) { if (condition_g) { if (condition_b) { //Action_6: Assign label of block S @@ -1239,6 +3025,53 @@ namespace cv{ continue; } } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + } + } + else { + if (condition_h) { + if (condition_m) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + // ACTION_9 Merge labels of block P and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_i) { + if (condition_m) { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } } else { //Action_11: Merge labels of block Q and S @@ -1254,13 +3087,36 @@ namespace cv{ } } } - else { - if (condition_j) { + } + else { + if (condition_j) { + if (condition_i) { //Action_4: Assign label of block Q imgLabels_row[c] = imgLabels_row_prev_prev[c]; continue; } else { + if (condition_h) { + if (condition_c) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + //Action_7: Merge labels of block P and Q + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]); + continue; + } + } + else { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + } + } + else { + if (condition_p) { if (condition_k) { if (condition_i) { if (condition_d) { @@ -1275,9 +3131,30 @@ namespace cv{ } } else { - //Action_5: Assign label of block R - imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; - continue; + if (condition_h) { + if (condition_d) { + if (condition_c) { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + else { + //Action_8: Merge labels of block P and R + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + //Action_8: Merge labels of block P and R + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } } } else { @@ -1286,6 +3163,34 @@ namespace cv{ imgLabels_row[c] = imgLabels_row_prev_prev[c]; continue; } + else { + if (condition_h) { + //Action_3: Assign label of block P + imgLabels_row[c] = imgLabels_row_prev_prev[c - 2]; + continue; + } + else { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = lunique; + P[lunique] = lunique; + lunique = lunique + 1; + continue; + } + } + } + } + else { + if (condition_i) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + if (condition_h) { + //Action_3: Assign label of block P + imgLabels_row[c] = imgLabels_row_prev_prev[c - 2]; + continue; + } else { //Action_2: New label (the block has foreground pixels and is not connected to anything else) imgLabels_row[c] = lunique; @@ -1298,59 +3203,369 @@ namespace cv{ } } } - else { - if (condition_r) { - //Action_6: Assign label of block S - imgLabels_row[c] = imgLabels_row[c - 2]; - continue; + } + else { + if (condition_s) { + if (condition_p) { + if (condition_n) { + if (condition_j) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + if (condition_h) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_k) { + if (condition_d) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + if (condition_h) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + } + else { + if (condition_r) { + if (condition_j) { + if (condition_m) { + if (condition_h) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_g) { + if (condition_b) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_k) { + if (condition_d) { + if (condition_m) { + if (condition_h) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_g) { + if (condition_b) { + if (condition_i) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_c) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + else { + if (condition_i) { + if (condition_m) { + if (condition_h) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_16: labels of block Q, R and S + imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_12: Merge labels of block R and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + continue; + } + } + } + else { + if (condition_i) { + if (condition_m) { + if (condition_h) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + if (condition_g) { + if (condition_b) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + } + else { + //Action_11: Merge labels of block Q and S + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + continue; + } + } + else { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; + continue; + } + } + } + } + else { + if (condition_j) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + if (condition_k) { + if (condition_i) { + if (condition_d) { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + else { + // ACTION_10 Merge labels of block Q and R + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + } + else { + if (condition_i) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = lunique; + P[lunique] = lunique; + lunique = lunique + 1; + continue; + } + } + } + } + } } else { - if (condition_n) { + if (condition_r) { //Action_6: Assign label of block S imgLabels_row[c] = imgLabels_row[c - 2]; continue; } else { - //Action_2: New label (the block has foreground pixels and is not connected to anything else) - imgLabels_row[c] = lunique; - P[lunique] = lunique; - lunique = lunique + 1; - continue; - } - } - } - } - else { - if (condition_p) { - if (condition_j) { - //Action_4: Assign label of block Q - imgLabels_row[c] = imgLabels_row_prev_prev[c]; - continue; - } - else { - if (condition_k) { - if (condition_i) { - if (condition_d) { - //Action_5: Assign label of block R - imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; - continue; - } - else { - // ACTION_10 Merge labels of block Q and R - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]); - continue; - } - } - else { - //Action_5: Assign label of block R - imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; - continue; - } - } - else { - if (condition_i) { - //Action_4: Assign label of block Q - imgLabels_row[c] = imgLabels_row_prev_prev[c]; + if (condition_n) { + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; continue; } else { @@ -1364,51 +3579,175 @@ namespace cv{ } } else { - if (condition_t) { - //Action_2: New label (the block has foreground pixels and is not connected to anything else) - imgLabels_row[c] = lunique; - P[lunique] = lunique; - lunique = lunique + 1; - continue; + if (condition_p) { + if (condition_j) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + if (condition_k) { + if (condition_i) { + if (condition_d) { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + else { + // ACTION_10 Merge labels of block Q and R + imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]); + continue; + } + } + else { + //Action_5: Assign label of block R + imgLabels_row[c] = imgLabels_row_prev_prev[c + 2]; + continue; + } + } + else { + if (condition_i) { + //Action_4: Assign label of block Q + imgLabels_row[c] = imgLabels_row_prev_prev[c]; + continue; + } + else { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = lunique; + P[lunique] = lunique; + lunique = lunique + 1; + continue; + } + } + } } else { - // Action_1: No action (the block has no foreground pixels) - imgLabels_row[c] = 0; - continue; + if (condition_t) { + //Action_2: New label (the block has foreground pixels and is not connected to anything else) + imgLabels_row[c] = lunique; + P[lunique] = lunique; + lunique = lunique + 1; + continue; + } + else { + // Action_1: No action (the block has no foreground pixels) + imgLabels_row[c] = 0; + continue; + } } } } } + } - } - // Second scan + analysis - LabelT nLabels = flattenL(P, lunique); - sop.init(nLabels); + // Second scan + analysis + LabelT nLabels = flattenL(P, lunique); + sop.init(nLabels); - if (imgLabels.rows & 1){ - if (imgLabels.cols & 1){ - //Case 1: both rows and cols odd - for (int r = 0; r(r); - const PixelT* const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); - LabelT* const imgLabels_row = imgLabels.ptr(r); - LabelT* const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels.step.p[0]); + if (imgLabels.rows & 1){ + if (imgLabels.cols & 1){ + //Case 1: both rows and cols odd + for (int r = 0; r < imgLabels.rows; r += 2) { + // Get rows pointer + const PixelT * const img_row = img.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels.step.p[0]); - for (int c = 0; c0) { - iLabel = P[iLabel]; - if (img_row[c] > 0){ - imgLabels_row[c] = iLabel; - sop(r, c, iLabel); + for (int c = 0; c < imgLabels.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sop(r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sop(r, c, 0); + } + if (c + 1 < imgLabels.cols) { + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sop(r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sop(r, c + 1, 0); + } + if (r + 1 < imgLabels.rows) { + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sop(r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sop(r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sop(r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sop(r + 1, c + 1, 0); + } + } + } + else if (r + 1 < imgLabels.rows) { + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sop(r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sop(r + 1, c, 0); + } + } } - else{ + else { imgLabels_row[c] = 0; sop(r, c, 0); + if (c + 1 < imgLabels.cols) { + imgLabels_row[c + 1] = 0; + sop(r, c + 1, 0); + if (r + 1 < imgLabels.rows) { + imgLabels_row_fol[c] = 0; + imgLabels_row_fol[c + 1] = 0; + sop(r + 1, c, 0); + sop(r + 1, c + 1, 0); + } + } + else if (r + 1 < imgLabels.rows) { + imgLabels_row_fol[c] = 0; + sop(r + 1, c, 0); + } } - if (c + 1(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels.step.p[0]); + + for (int c = 0; c < imgLabels.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sop(r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sop(r, c, 0); + } if (img_row[c + 1] > 0){ imgLabels_row[c + 1] = iLabel; sop(r, c + 1, iLabel); @@ -1417,297 +3756,247 @@ namespace cv{ imgLabels_row[c + 1] = 0; sop(r, c + 1, 0); } - if (r + 1 0){ imgLabels_row_fol[c] = iLabel; sop(r + 1, c, iLabel); - } else{ + } + else{ imgLabels_row_fol[c] = 0; sop(r + 1, c, 0); } - if (img_row_fol[c + 1]>0){ + if (img_row_fol[c + 1] > 0){ imgLabels_row_fol[c + 1] = iLabel; sop(r + 1, c + 1, iLabel); - } else{ + } + else{ imgLabels_row_fol[c + 1] = 0; sop(r + 1, c + 1, 0); } - } - } - else if (r + 10){ - imgLabels_row_fol[c] = iLabel; - sop(r + 1, c, iLabel); - }else{ - imgLabels_row_fol[c] = 0; - sop(r + 1, c, 0); } } - } - else { - imgLabels_row[c] = 0; - sop(r, c, 0); - if (c + 1(r); - const PixelT* const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); - LabelT* const imgLabels_row = imgLabels.ptr(r); - LabelT* const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels.step.p[0]); + if (imgLabels.cols & 1){ + //Case 3: only cols odd + for (int r = 0; r < imgLabels.rows; r += 2) { + // Get rows pointer + const PixelT * const img_row = img.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels.step.p[0]); - for (int c = 0; c0) { - iLabel = P[iLabel]; - if (img_row[c]>0){ - imgLabels_row[c] = iLabel; - sop(r, c, iLabel); - } else{ - imgLabels_row[c] = 0; - sop(r, c, 0); - } - if (img_row[c + 1]>0){ - imgLabels_row[c + 1] = iLabel; - sop(r, c + 1, iLabel); - }else{ - imgLabels_row[c + 1] = 0; - sop(r, c + 1, 0); - } - if (r + 10){ + for (int c = 0; c < imgLabels.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sop(r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sop(r, c, 0); + } + if (img_row_fol[c] > 0){ imgLabels_row_fol[c] = iLabel; sop(r + 1, c, iLabel); - }else{ + } + else{ imgLabels_row_fol[c] = 0; sop(r + 1, c, 0); } - if (img_row_fol[c + 1]>0){ - imgLabels_row_fol[c + 1] = iLabel; - sop(r + 1, c + 1, iLabel); - }else{ + if (c + 1 < imgLabels.cols) { + if (img_row[c + 1] > 0){ + imgLabels_row[c + 1] = iLabel; + sop(r, c + 1, iLabel); + } + else{ + imgLabels_row[c + 1] = 0; + sop(r, c + 1, 0); + } + if (img_row_fol[c + 1] > 0){ + imgLabels_row_fol[c + 1] = iLabel; + sop(r + 1, c + 1, iLabel); + } + else{ + imgLabels_row_fol[c + 1] = 0; + sop(r + 1, c + 1, 0); + } + } + } + else{ + imgLabels_row[c] = 0; + imgLabels_row_fol[c] = 0; + sop(r, c, 0); + sop(r + 1, c, 0); + if (c + 1 < imgLabels.cols) { + imgLabels_row[c + 1] = 0; imgLabels_row_fol[c + 1] = 0; + sop(r, c + 1, 0); sop(r + 1, c + 1, 0); } } } - else { - imgLabels_row[c] = 0; - imgLabels_row[c + 1] = 0; - sop(r, c, 0); - sop(r, c + 1, 0); - if (r + 1(r); - const PixelT* const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); - LabelT* const imgLabels_row = imgLabels.ptr(r); - LabelT* const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels.step.p[0]); + }// END case 3 + else{ + //Case 4: nothing odd + for (int r = 0; r < imgLabels.rows; r += 2) { + // Get rows pointer + const PixelT * const img_row = img.ptr(r); + const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); + LabelT * const imgLabels_row = imgLabels.ptr(r); + LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels.step.p[0]); - for (int c = 0; c0) { - iLabel = P[iLabel]; - if (img_row[c]>0){ - imgLabels_row[c] = iLabel; - sop(r, c, iLabel); - }else{ - imgLabels_row[c] = 0; - sop(r, c, 0); - } - if (img_row_fol[c]>0){ - imgLabels_row_fol[c] = iLabel; - sop(r + 1, c, iLabel); - }else{ - imgLabels_row_fol[c] = 0; - sop(r + 1, c, 0); - } - if (c + 10){ + for (int c = 0; c < imgLabels.cols; c += 2) { + LabelT iLabel = imgLabels_row[c]; + if (iLabel > 0) { + iLabel = P[iLabel]; + if (img_row[c] > 0){ + imgLabels_row[c] = iLabel; + sop(r, c, iLabel); + } + else{ + imgLabels_row[c] = 0; + sop(r, c, 0); + } + if (img_row[c + 1] > 0){ imgLabels_row[c + 1] = iLabel; sop(r, c + 1, iLabel); - }else{ + } + else{ imgLabels_row[c + 1] = 0; sop(r, c + 1, 0); } - if (img_row_fol[c + 1]>0){ + if (img_row_fol[c] > 0){ + imgLabels_row_fol[c] = iLabel; + sop(r + 1, c, iLabel); + } + else{ + imgLabels_row_fol[c] = 0; + sop(r + 1, c, 0); + } + if (img_row_fol[c + 1] > 0){ imgLabels_row_fol[c + 1] = iLabel; sop(r + 1, c + 1, iLabel); - }else{ + } + else{ imgLabels_row_fol[c + 1] = 0; sop(r + 1, c + 1, 0); } } - } - else{ - imgLabels_row[c] = 0; - imgLabels_row_fol[c] = 0; - sop(r, c, 0); - sop(r + 1, c, 0); - if (c + 1(r); - const PixelT* const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]); - LabelT* const imgLabels_row = imgLabels.ptr(r); - LabelT* const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels.step.p[0]); - - for (int c = 0; c0) { - iLabel = P[iLabel]; - if (img_row[c] > 0){ - imgLabels_row[c] = iLabel; - sop(r, c, iLabel); - }else{ + else { imgLabels_row[c] = 0; - sop(r, c, 0); - } - if (img_row[c + 1] > 0){ - imgLabels_row[c + 1] = iLabel; - sop(r, c + 1, iLabel); - }else{ imgLabels_row[c + 1] = 0; - sop(r, c + 1, 0); - } - if (img_row_fol[c] > 0){ - imgLabels_row_fol[c] = iLabel; - sop(r + 1, c, iLabel); - }else{ imgLabels_row_fol[c] = 0; - sop(r + 1, c, 0); - } - if (img_row_fol[c + 1] > 0){ - imgLabels_row_fol[c + 1] = iLabel; - sop(r + 1, c + 1, iLabel); - }else{ imgLabels_row_fol[c + 1] = 0; + sop(r, c, 0); + sop(r, c + 1, 0); + sop(r + 1, c, 0); sop(r + 1, c + 1, 0); } } - else { - imgLabels_row[c] = 0; - imgLabels_row[c + 1] = 0; - imgLabels_row_fol[c] = 0; - imgLabels_row_fol[c + 1] = 0; - sop(r, c, 0); - sop(r, c + 1, 0); - sop(r + 1, c, 0); - sop(r + 1, c + 1, 0); - } } - } - }//END case 4 + }//END case 4 + } + + sop.finish(); + fastFree(P); + + return nLabels; + + } //End function LabelingGrana operator() + };//End struct LabelingGrana + }//end namespace connectedcomponents + + //L's type must have an appropriate depth for the number of pixels in I + template + static + int connectedComponents_sub1(const cv::Mat& I, cv::Mat& L, int connectivity, int ccltype, StatsOp& sop){ + CV_Assert(L.channels() == 1 && I.channels() == 1); + CV_Assert(connectivity == 8 || connectivity == 4); + CV_Assert(ccltype == CCL_GRANA || ccltype == CCL_WU || ccltype == CCL_DEFAULT); + + int lDepth = L.depth(); + int iDepth = I.depth(); + const char *currentParallelFramework = cv::currentParallelFramework(); + const int numberOfCPUs = cv::getNumberOfCPUs(); + + CV_Assert(iDepth == CV_8U || iDepth == CV_8S); + + //Run parallel labeling only if the rows of the image are at least twice the number returned by getNumberOfCPUs + const bool is_parallel = currentParallelFramework != NULL && numberOfCPUs > 1 && L.rows / numberOfCPUs >= 2; + + if (ccltype == CCL_WU || connectivity == 4){ + // Wu algorithm is used + using connectedcomponents::LabelingWu; + using connectedcomponents::LabelingWuParallel; + //warn if L's depth is not sufficient? + if (lDepth == CV_8U){ + //Not supported yet + } + else if (lDepth == CV_16U){ + return (int)LabelingWu()(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 (!is_parallel) + return (int)LabelingWu()(I, L, connectivity, sop); + else + return (int)LabelingWuParallel()(I, L, connectivity, sop); + } + } + else if ((ccltype == CCL_GRANA || ccltype == CCL_DEFAULT) && connectivity == 8){ + // Grana algorithm is used + using connectedcomponents::LabelingGrana; + using connectedcomponents::LabelingGranaParallel; + //warn if L's depth is not sufficient? + if (lDepth == CV_8U){ + //Not supported yet + } + else if (lDepth == CV_16U){ + return (int)LabelingGrana()(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 (!is_parallel) + return (int)LabelingGrana()(I, L, connectivity, sop); + else + return (int)LabelingGranaParallel()(I, L, connectivity, sop); + } } - sop.finish(); - fastFree(P); - - return nLabels; - - } //End function LabelingGrana operator() - }; //End struct LabelingGrana -}//end namespace connectedcomponents - -//L's type must have an appropriate depth for the number of pixels in I -template -static -int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, int ccltype, StatsOp &sop){ - CV_Assert(L.channels() == 1 && I.channels() == 1); - CV_Assert(connectivity == 8 || connectivity == 4); - CV_Assert(ccltype == CCL_GRANA || ccltype == CCL_WU || ccltype == CCL_DEFAULT); - - int lDepth = L.depth(); - int iDepth = I.depth(); - - CV_Assert(iDepth == CV_8U || iDepth == CV_8S); - - if (ccltype == CCL_WU || connectivity == 4){ - // Wu algorithm is used - using connectedcomponents::LabelingWu; - //warn if L's depth is not sufficient? - if (lDepth == CV_8U){ - return (int)LabelingWu()(I, L, connectivity, sop); - } - else if (lDepth == CV_16U){ - return (int)LabelingWu()(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 - return (int)LabelingWu()(I, L, connectivity, sop); - } - }else if ((ccltype == CCL_GRANA || ccltype == CCL_DEFAULT) && connectivity == 8){ - // Grana algorithm is used - using connectedcomponents::LabelingGrana; - //warn if L's depth is not sufficient? - if (lDepth == CV_8U){ - return (int)LabelingGrana()(I, L, connectivity, sop); - } - else if (lDepth == CV_16U){ - return (int)LabelingGrana()(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 - return (int)LabelingGrana()(I, L, connectivity, sop); - } + CV_Error(CV_StsUnsupportedFormat, "unsupported label/image type"); + return -1; } - CV_Error(CV_StsUnsupportedFormat, "unsupported label/image type"); - return -1; -} - } // Simple wrapper to ensure binary and source compatibility (ABI) -int cv::connectedComponents(InputArray _img, OutputArray _labels, int connectivity, int ltype){ - return cv::connectedComponents(_img, _labels, connectivity, ltype, CCL_DEFAULT); +int cv::connectedComponents(InputArray img_, OutputArray _labels, int connectivity, int ltype){ + return cv::connectedComponents(img_, _labels, connectivity, ltype, CCL_DEFAULT); } -int cv::connectedComponents(InputArray _img, OutputArray _labels, int connectivity, int ltype, int ccltype){ - const cv::Mat img = _img.getMat(); +int cv::connectedComponents(InputArray img_, OutputArray _labels, int connectivity, int ltype, int ccltype){ + const cv::Mat img = img_.getMat(); _labels.create(img.size(), CV_MAT_DEPTH(ltype)); cv::Mat labels = _labels.getMat(); connectedcomponents::NoOp sop; @@ -1724,16 +4013,16 @@ int cv::connectedComponents(InputArray _img, OutputArray _labels, int connectivi } // Simple wrapper to ensure binary and source compatibility (ABI) -int cv::connectedComponentsWithStats(InputArray _img, OutputArray _labels, OutputArray statsv, - OutputArray centroids, int connectivity, int ltype) +int cv::connectedComponentsWithStats(InputArray img_, OutputArray _labels, OutputArray statsv, + OutputArray centroids, int connectivity, int ltype) { - return cv::connectedComponentsWithStats(_img, _labels, statsv, centroids, connectivity, ltype, CCL_DEFAULT); + return cv::connectedComponentsWithStats(img_, _labels, statsv, centroids, connectivity, ltype, CCL_DEFAULT); } -int cv::connectedComponentsWithStats(InputArray _img, OutputArray _labels, OutputArray statsv, - OutputArray centroids, int connectivity, int ltype, int ccltype) +int cv::connectedComponentsWithStats(InputArray img_, OutputArray _labels, OutputArray statsv, + OutputArray centroids, int connectivity, int ltype, int ccltype) { - const cv::Mat img = _img.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); diff --git a/modules/video/include/opencv2/video/background_segm.hpp b/modules/video/include/opencv2/video/background_segm.hpp index f4c6e4cf0d..8a2d40b1a7 100644 --- a/modules/video/include/opencv2/video/background_segm.hpp +++ b/modules/video/include/opencv2/video/background_segm.hpp @@ -188,7 +188,7 @@ public: A shadow is detected if pixel is a darker version of the background. The shadow threshold (Tau in the paper) is a threshold defining how much darker the shadow can be. Tau= 0.5 means that if a pixel - is more than twice darker then it is not shadow. See Prati, Mikic, Trivedi and Cucchiarra, + is more than twice darker then it is not shadow. See Prati, Mikic, Trivedi and Cucchiara, *Detecting Moving Shadows...*, IEEE PAMI,2003. */ CV_WRAP virtual double getShadowThreshold() const = 0; @@ -289,7 +289,7 @@ public: A shadow is detected if pixel is a darker version of the background. The shadow threshold (Tau in the paper) is a threshold defining how much darker the shadow can be. Tau= 0.5 means that if a pixel - is more than twice darker then it is not shadow. See Prati, Mikic, Trivedi and Cucchiarra, + is more than twice darker then it is not shadow. See Prati, Mikic, Trivedi and Cucchiara, *Detecting Moving Shadows...*, IEEE PAMI,2003. */ CV_WRAP virtual double getShadowThreshold() const = 0; diff --git a/modules/video/src/bgfg_KNN.cpp b/modules/video/src/bgfg_KNN.cpp index 9dcfe37e78..70f0b43741 100755 --- a/modules/video/src/bgfg_KNN.cpp +++ b/modules/video/src/bgfg_KNN.cpp @@ -236,7 +236,7 @@ protected: // Tau - shadow threshold. The shadow is detected if the pixel is darker //version of the background. Tau is a threshold on how much darker the shadow can be. //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow - //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003. + //See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003. //model data int nLongCounter;//circular counter diff --git a/modules/video/src/bgfg_gaussmix2.cpp b/modules/video/src/bgfg_gaussmix2.cpp index 977a2581f3..51ec9f99b6 100644 --- a/modules/video/src/bgfg_gaussmix2.cpp +++ b/modules/video/src/bgfg_gaussmix2.cpp @@ -386,7 +386,7 @@ protected: // Tau - shadow threshold. The shadow is detected if the pixel is darker //version of the background. Tau is a threshold on how much darker the shadow can be. //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow - //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003. + //See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003. String name_; @@ -461,7 +461,7 @@ struct GaussBGStatModel2Params // Tau - shadow threshold. The shadow is detected if the pixel is darker //version of the background. Tau is a threshold on how much darker the shadow can be. //Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow - //See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003. + //See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003. }; struct GMM @@ -472,7 +472,7 @@ struct GMM // shadow detection performed per pixel // should work for rgb data, could be usefull for gray scale and depth data as well -// See: Prati,Mikic,Trivedi,Cucchiarra,"Detecting Moving Shadows...",IEEE PAMI,2003. +// See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003. CV_INLINE bool detectShadowGMM(const float* data, int nchannels, int nmodes, const GMM* gmm, const float* mean,