diff --git a/modules/imgproc/src/connectedcomponents.cpp b/modules/imgproc/src/connectedcomponents.cpp index bf53704144..00fa8d0eb4 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" @@ -50,414 +51,860 @@ namespace cv{ namespace connectedcomponents{ - struct NoOp{ - NoOp(){ - } - void init(int /*labels*/){ - } - inline - void operator()(int r, int c, int l){ - (void) r; - (void) c; - (void) l; - } - void finish(){} - }; - struct Point2ui64{ - uint64 x, y; - Point2ui64(uint64 _x, uint64 _y):x(_x), y(_y){} - }; - - struct CCStatsOp{ - const _OutputArray* _mstatsv; - cv::Mat statsv; - const _OutputArray* _mcentroidsv; - cv::Mat centroidsv; - std::vector integrals; - - 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); - statsv = _mstatsv->getMat(); - _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); - 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; + struct NoOp{ + NoOp(){ } - integrals.resize(nlabels, Point2ui64(0, 0)); - } - void operator()(int r, int c, int l){ - 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]; - integral.x += c; - integral.y += r; - } - void finish(){ - for(int l = 0; l < statsv.rows; ++l){ + 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 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){} + }; + + struct CCStatsOp{ + const _OutputArray* _mstatsv; + cv::Mat statsv; + const _OutputArray* _mcentroidsv; + cv::Mat centroidsv; + std::vector integrals; + int _nextLoc; + + CCStatsOp(){} + 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); + statsv = _mstatsv->getMat(); + _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); + 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)); + } + + 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); - 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; - + 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]; - 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; + integral.x += c; + integral.y += r; } - } - }; - //Find the root of the tree of node i - template - inline static - LabelT findRoot(const LabelT *P, LabelT i){ - LabelT root = i; - while(P[root] < root){ - root = P[root]; - } - return root; - } + void finish(){ + 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; - //Make all nodes in the path of node i point to root - template - inline static - void setRoot(LabelT *P, LabelT i, LabelT root){ - while(P[i] < i){ - LabelT j = P[i]; - P[i] = root; - i = j; - } - P[i] = root; - } - - //Find the root of the tree of the node i and compress the path in the process - template - inline static - LabelT find(LabelT *P, LabelT i){ - LabelT root = findRoot(P, i); - setRoot(P, i, root); - return root; - } - - //unite the two trees containing nodes i and j and return the new root - template - inline static - LabelT set_union(LabelT *P, LabelT i, LabelT j){ - LabelT root = findRoot(P, i); - if(i != j){ - LabelT rootj = findRoot(P, j); - if(root > rootj){ - root = rootj; + 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; + } } - setRoot(P, j, root); - } - setRoot(P, i, root); - return root; - } - //Flatten the Union Find tree and relabel the components - template - inline static - LabelT flattenL(LabelT *P, LabelT length){ - LabelT k = 1; - for(LabelT i = 1; i < length; ++i){ - if(P[i] < i){ - P[i] = P[P[i]]; - }else{ - P[i] = k; k = k + 1; - } - } - return k; - } + inline + void setNextLoc(const int nextLoc){ + _nextLoc = nextLoc; + } - //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]); + inline static + void mergeStats(const cv::Mat &imgLabels, CCStatsOp *SopArray, CCStatsOp &sop, const int &nLabels){ + const int h = imgLabels.rows; - //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]); - } - } - }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{ - //new label - *Lrows[0] = lunique; - P[lunique] = lunique; - lunique = lunique + 1; + 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; } } } } } - }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; + + }; + + //Find the root of the tree of node i + template + inline static + LabelT findRoot(const LabelT *P, LabelT i){ + LabelT root = i; + while (P[root] < root){ + root = P[root]; + } + return root; + } + + //Make all nodes in the path of node i point to root + template + inline static + void setRoot(LabelT *P, LabelT i, LabelT root){ + while (P[i] < i){ + LabelT j = P[i]; + P[i] = root; + i = j; + } + P[i] = root; + } + + //Find the root of the tree of the node i and compress the path in the process + template + inline static + LabelT find(LabelT *P, LabelT i){ + LabelT root = findRoot(P, i); + setRoot(P, i, root); + return root; + } + + //unite the two trees containing nodes i and j and return the new root + template + inline static + LabelT set_union(LabelT *P, LabelT i, LabelT j){ + LabelT root = findRoot(P, i); + if (i != j){ + LabelT rootj = findRoot(P, j); + if (root > rootj){ + root = rootj; } - 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; - } + setRoot(P, j, root); + } + setRoot(P, i, root); + return root; + } + + //Flatten the Union Find tree and relabel the components + template + inline static + LabelT flattenL(LabelT *P, LabelT length){ + LabelT k = 1; + for (LabelT i = 1; i < length; ++i){ + if (P[i] < i){ + P[i] = P[P[i]]; + } + 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; } } } - } - //analysis - LabelT nLabels = flattenL(P, lunique); - sop.init(nLabels); + //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 LabelingWu{ +#ifdef HAVE_TBB +# if TBB_VERSION_MAJOR + TBB_VERSION_MINOR >= 2017 - 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); - } - } + class FirstScan8Connectivity{ + const cv::Mat &_img; + cv::Mat &_imgLabels; + LabelT *_P; + int *_chunksSizeAndLabels; - sop.finish(); - fastFree(P); + public: + FirstScan8Connectivity(const cv::Mat &img, cv::Mat &imgLabels, LabelT *P, int *chunksSizeAndLabels) + : _img(img), _imgLabels(imgLabels), _P(P), _chunksSizeAndLabels(chunksSizeAndLabels){} - return nLabels; - }//End function LabelingWu operator() - };//End struct LabelingWu + FirstScan8Connectivity & operator=(const FirstScan8Connectivity &) { return *this; } - // 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); + void operator()(const tbb::blocked_range &range) const{ - const int h = img.rows; - const int w = img.cols; + int r = range.begin(); + _chunksSizeAndLabels[r] = range.end(); - //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; + LabelT label = LabelT(r * _imgLabels.cols / 4 + 1); - // 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) { + const LabelT firstLabel = label; + const int w = _img.cols; + const int limitLine = r, startR = r; - // We work with 2x2 blocks - // +-+-+-+ - // |P|Q|R| - // +-+-+-+ - // |S|X| - // +-+-+ + // 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) { - // 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| - // +---+---+ +#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 - // 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_prev[c+2]>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; + 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 { - //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{ //not p, q and s + //copy r + imgLabels_row[c] = imgLabels_row_prev[c + 1]; } } - 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{ + //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 { - //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{ + //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{ + 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 tbb::blocked_range &range) const{ + + int r = range.begin(); + _chunksSizeAndLabels[r] = range.end(); + + LabelT label = LabelT(r * _imgLabels.cols / 4 + 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{ + 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 tbb::blocked_range &range) const{ + + int r = range.begin(); + const int rowBegin = r; + const int rowEnd = range.end(); + + if (rowBegin > 0){ + _sopArray[rowBegin].initElement(_nLabels); + _sopArray[rowBegin].setNextLoc(rowEnd); //_nextLoc = range.second; + + for (; r < range.end(); ++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 use sop in order to make less merges + _sop.setNextLoc(rowEnd); + for (; r < range.end(); ++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]); } } } - else { - if (condition_p) { - if (condition_k) { - if (condition_d) { + } +#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 = tbb::task_scheduler_init::default_num_threads(); + tbb::task_scheduler_init init(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; + + tbb::blocked_range range(0, h, h / nThreads); + + 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 + tbb::parallel_for(range, FirstScan8Connectivity(img, imgLabels, P, chunksSizeAndLabels), tbb::static_partitioner()); + + //merge labels of different chunks + mergeLabels8Connectivity(imgLabels, P, chunksSizeAndLabels); + } + 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 + tbb::parallel_for(range, FirstScan4Connectivity(img, imgLabels, P, chunksSizeAndLabels), tbb::static_partitioner()); + + //merge labels of different chunks + mergeLabels4Connectivity(imgLabels, P, chunksSizeAndLabels); + } + LabelT nLabels = 1; + + for (int i = 0; i < h; i = chunksSizeAndLabels[i]){ + flattenL(P, i * w / 4 + 1, chunksSizeAndLabels[i + 1], nLabels); + } + + //Array for statistics dataof threads + StatsOp *SopArray = new StatsOp[h]; + + sop.init(nLabels); + //Second scan + tbb::parallel_for(range, SecondScan(imgLabels, P, sop, SopArray, nLabels), tbb::static_partitioner()); + StatsOp::mergeStats(imgLabels, SopArray, sop, nLabels); + sop.finish(); + + delete[] SopArray; + cv::fastFree(chunksSizeAndLabels); + cv::fastFree(P); + return nLabels; + } +#endif +#else + 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); + + 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{ + //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 + imgLabels_row[c] = 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); + + 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); + + return nLabels; + }//End function LabelingWu operator() +#endif + };//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{ +#ifdef HAVE_TBB +# if TBB_VERSION_MAJOR + TBB_VERSION_MINOR >= 2017 + + class FirstScan{ + private: + const cv::Mat &_img; + cv::Mat &_imgLabels; + LabelT *_P; + int *_chunksSizeAndLabels; + + public: + FirstScan(const cv::Mat &img, cv::Mat &imgLabels, LabelT *P, int *chunksSizeAndLabels) + : _img(img), _imgLabels(imgLabels), _P(P), _chunksSizeAndLabels(chunksSizeAndLabels){} + + FirstScan & operator=(const FirstScan&) { return *this; } + + void operator()(const tbb::blocked_range &range) const{ + + int r = range.begin(); + r += (r % 2); + + _chunksSizeAndLabels[r] = range.end() + (range.end() % 2); + + LabelT label = LabelT(r * _imgLabels.cols / 4 + 1); + + const LabelT firstLabel = label; + const int h = _img.rows, w = _img.cols; + const int limitLine = r + 1, startR = r; + + 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) { + + // 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 > 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) { //Action_6: Assign label of block S imgLabels_row[c] = imgLabels_row[c - 2]; @@ -478,134 +925,92 @@ 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]); + //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_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + //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_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]); + 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) { + 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 { - //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; + 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_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + //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]); + //Action_6: Assign label of block S + imgLabels_row[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_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]; @@ -618,20 +1023,13 @@ 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]); + //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_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) { @@ -646,298 +1044,826 @@ 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]); + //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_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + //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_12: Merge labels of block R and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]); + //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_i) { - if (condition_g) { - if (condition_b) { + 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]); + 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]); + 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; + 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 { - //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) { + if (condition_h) { + if (condition_m) { //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]); + // 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 { - //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; + 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 { - //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) { + if (condition_h) { + if (condition_m) { //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]); + // 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 { - //Action_11: Merge labels of block Q and S - imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]); + 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 { - //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; + 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_j) { - if (condition_i) { - //Action_4: Assign label of block Q - imgLabels_row[c] = imgLabels_row_prev_prev[c]; - 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_h) { - if (condition_c) { + if (condition_p) { + if (condition_j) { //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) { + 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_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]); + // 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_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]); + //Action_5: Assign label of block R + imgLabels_row[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; + 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_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 { + 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; + 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; } } @@ -945,10 +1871,786 @@ namespace cv{ } } } + //write in the follower memory location + _chunksSizeAndLabels[startR + 1] = label - firstLabel; } - else { - if (condition_s) { - if (condition_p) { + }; + + class SecondScan{ + 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 tbb::blocked_range &range) const{ + + int r = range.begin(); + 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 = range.second; + + if (_imgLabels.rows & 1){ + if (_imgLabels.cols & 1){ + //Case 1: both rows and cols odd + for (; r < range.end(); 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 < range.end(); 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 < range.end(); 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 < range.end(); 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 use 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 < range.end(); 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 < range.end(); 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 < range.end(); 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 < range.end(); 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 = tbb::task_scheduler_init::default_num_threads(); + tbb::task_scheduler_init init(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) * (size_t(w) + 1)) / 4 + 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; + + tbb::blocked_range range(0, h, h / nThreads); + + //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 + tbb::parallel_for(range, FirstScan(img, imgLabels, P, chunksSizeAndLabels), tbb::static_partitioner()); + + //merge labels of different chunks + mergeLabels(img, imgLabels, P, chunksSizeAndLabels); + + LabelT nLabels = 1; + for (int i = 0; i < h; i = chunksSizeAndLabels[i]){ + flattenL(P, i * w / 4 + 1, chunksSizeAndLabels[i + 1], nLabels); + } + + //Array for statistics data + StatsOp *SopArray = new StatsOp[h]; + sop.init(nLabels); + + //Second scan + tbb::parallel_for(range, SecondScan(img, imgLabels, P, sop, SopArray, nLabels), tbb::static_partitioner()); + + StatsOp::mergeStats(imgLabels, SopArray, sop, nLabels); + sop.finish(); + + delete[] SopArray; + cv::fastFree(chunksSizeAndLabels); + cv::fastFree(P); + return nLabels; + } +#endif +#else + 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) * (size_t(w) + 1)) / 4 + 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_prev[c+2]>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) { @@ -991,26 +2693,33 @@ namespace cv{ } } 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; + 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 @@ -1018,23 +2727,23 @@ 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 { + //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]); + //Action_6: Assign label of block S + imgLabels_row[c] = imgLabels_row[c - 2]; continue; } } @@ -1103,54 +2812,89 @@ 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]); - continue; + 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_k) { - if (condition_d) { + if (condition_p) { + if (condition_k) { 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) { + if (condition_d) { + if (condition_i) { //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) { + if (condition_c) { //Action_6: Assign label of block S imgLabels_row[c] = imgLabels_row[c - 2]; continue; } else { - 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_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 { - //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_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 @@ -1159,32 +2903,18 @@ 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 { - //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; + 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 @@ -1193,28 +2923,279 @@ namespace cv{ } } 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]); + //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 { - //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]); + 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 { - //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]); + 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) { - if (condition_m) { + //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) { + 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]; @@ -1246,6 +3227,55 @@ namespace cv{ 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]; @@ -1253,8 +3283,283 @@ namespace cv{ } } } + 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_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] = 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]; @@ -1296,409 +3601,361 @@ namespace cv{ } } } - } - } - 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] = 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]; - continue; - } - 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; } + else { + // Action_1: No action (the block has no foreground pixels) + imgLabels_row[c] = 0; + continue; + } } } } - 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; - } - 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); - } - else{ - imgLabels_row[c] = 0; - sop(r, c, 0); - } - if (c + 1 0){ - imgLabels_row[c + 1] = iLabel; - sop(r, c + 1, 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{ - 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[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 (img_row_fol[c + 1]>0){ + } + } + } + }//END Case 1 + else{ + //Case 2: only rows 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; 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 (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; 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 (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; 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{ + } + 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]); - - 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){ - 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(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){ - imgLabels_row[c + 1] = iLabel; - sop(r, c + 1, iLabel); - }else{ + else { + imgLabels_row[c] = 0; 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] = 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); } } } - 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{ - 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); - } - } + sop.finish(); + fastFree(P); + + return nLabels; + + } //End function LabelingGrana operator() +#endif + }; //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); } - }//END case 4 + 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; } - 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; -} - } // Simple wrapper to ensure binary and source compatibility (ABI) @@ -1725,13 +3982,13 @@ 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) + OutputArray centroids, int connectivity, int ltype) { 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) + OutputArray centroids, int connectivity, int ltype, int ccltype) { const cv::Mat img = _img.getMat(); _labels.create(img.size(), CV_MAT_DEPTH(ltype));