}
}
}
+#undef condition_p
+#undef condition_q
+#undef condition_r
+#undef condition_x
}
- //analysis
- LabelT nLabels = flattenL(P, lunique);
- sop.init(nLabels);
-
- for(int r_i = 0; r_i < rows; ++r_i){
- LabelT *Lrow_start = L.ptr<LabelT>(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);
+ 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<LabelT>(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
}
- sop.finish();
- fastFree(P);
+ 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);
- return nLabels;
- }//End function LabelingWu operator()
- };//End struct LabelingWu
+ const int nThreads = cv::getNumberOfCPUs();
+ cv::setNumThreads(nThreads);
- // Based on "Optimized Block-based Connected Components Labeling with Decision Trees", Costantino Grana et al
- // Only for 8-connectivity
+ const int h = img.rows;
+ const int w = img.cols;
+
+ //A quick and dirty upper bound for the maximimum number of labels.
+ //Following formula comes from the fact that a 2x2 block in 4-way connectivity
+ //labeling can never have more than 2 new labels and 1 label for background.
+ //Worst case image example pattern:
+ //1 0 1 0 1...
+ //0 1 0 1 0...
+ //1 0 1 0 1...
+ //............
+ //Obviously, 4-way connectivity upper bound is also good for 8-way connectivity labeling
+ const size_t Plength = (size_t(h) * size_t(w) + 1) / 2 + 1;
+
+ //Array used to store info and labeled pixel by each thread.
+ //Different threads affect different memory location of chunksSizeAndLabels
+ int *chunksSizeAndLabels = (int *)cv::fastMalloc(h * sizeof(int));
+
+ //Tree of labels
+ LabelT *P = (LabelT *)cv::fastMalloc(Plength * sizeof(LabelT));
+ //First label is for background
+ P[0] = 0;
+
+ cv::Range range(0, h);
+ LabelT nLabels = 1;
+
+ if (connectivity == 8){
+ //First scan, each thread works with chunk of img.rows/nThreads rows
+ //e.g. 300 rows, 4 threads -> each chunks is composed of 75 rows
+ cv::parallel_for_(range, FirstScan8Connectivity(img, imgLabels, P, chunksSizeAndLabels), nThreads);
+
+ //merge labels of different chunks
+ mergeLabels8Connectivity(imgLabels, P, chunksSizeAndLabels);
+ }
+ else{
+ //First scan, each thread works with chunk of img.rows/nThreads rows
+ //e.g. 300 rows, 4 threads -> each chunks is composed of 75 rows
+ cv::parallel_for_(range, FirstScan4Connectivity(img, imgLabels, P, chunksSizeAndLabels), nThreads);
+
+ //merge labels of different chunks
+ mergeLabels4Connectivity(imgLabels, P, chunksSizeAndLabels);
+ }
+
+ for (int i = 0; i < h; i = chunksSizeAndLabels[i]){
+ flattenL(P, (i * w + 1) / 2 + 1, chunksSizeAndLabels[i + 1], nLabels);
+ }
+
+ //Array for statistics dataof threads
+ StatsOp *sopArray = new StatsOp[h];
+
+ sop.init(nLabels);
+ //Second scan
+ cv::parallel_for_(range, SecondScan(imgLabels, P, sop, sopArray, nLabels), nThreads);
+ StatsOp::mergeStats(imgLabels, sopArray, sop, nLabels);
+ sop.finish();
+
+ delete[] sopArray;
+ cv::fastFree(chunksSizeAndLabels);
+ cv::fastFree(P);
+ return nLabels;
+ }
+ };//End struct LabelingWuParallel
+
++
+ //Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant
+ //using decision trees
+ //Kesheng Wu, et al
template<typename LabelT, typename PixelT, typename StatsOp = NoOp >
- struct LabelingGrana{
- LabelT operator()(const cv::Mat &img, cv::Mat &imgLabels, int connectivity, StatsOp &sop){
- CV_Assert(img.rows == imgLabels.rows);
- CV_Assert(img.cols == imgLabels.cols);
- CV_Assert(connectivity == 8 || connectivity == 4);
+ struct LabelingWu{
+ 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.
- const size_t Plength = img.rows*img.cols / 4;
- 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<PixelT>(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<LabelT>(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|
- // +-+-+
+ const int h = img.rows;
+ const int w = img.cols;
- // 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<w && r-2>=0 && img_row_prev_prev[c+1]>0
- #define condition_e c+2<w && r-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<w && r-1>=0 && img_row_prev[c+1]>0
- #define condition_k c+2<w && r-1>=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+1<w && img_row[c+1]>0
-
- #define condition_r c-1>=0 && r+1<h && img_row_fol[c-1]>0
- #define condition_s r+1<h && img_row_fol[c]>0
- #define condition_t c+1<w && r+1<h && img_row_fol[c+1]>0
-
- // 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;
+ //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<PixelT>(r);
+ PixelT const * const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]);
+ LabelT * const imgLabels_row = imgLabels.ptr<LabelT>(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 {
- if (condition_c) {
- if (condition_h) {
- //Action_6: Assign label of block S
- imgLabels_row[c] = imgLabels_row[c - 2];
- continue;
+ 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 {
- 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{
+ // 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 {
- //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{
+ // p = q = s = 0
+ // x <- 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 {
- 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{
+ // 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 {
- 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{
+ //new label
+ imgLabels_row[c] = lunique;
+ P[lunique] = lunique;
+ lunique = lunique + 1;
}
}
- 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) {
+ 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<PixelT>(r);
+ PixelT const * const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]);
+ LabelT * const imgLabels_row = imgLabels.ptr<LabelT>(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<LabelT>(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()
+ };//End struct LabelingWu
+
+
- // Based on \93Optimized Block-based Connected Components Labeling with Decision Trees\94, Costantino Grana et al
++ // Based on "Optimized Block-based Connected Components Labeling with Decision Trees", Costantino Grana et al
+ // Only for 8-connectivity
+ template<typename LabelT, typename PixelT, typename StatsOp = NoOp >
+ struct LabelingGranaParallel{
+
+ class FirstScan : public cv::ParallelLoopBody{
+ 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 cv::Range& range) const{
+
+ int r = range.start;
+ r += (r % 2);
+
+ chunksSizeAndLabels_[r] = range.end + (range.end % 2);
+
+ LabelT label = LabelT((r + 1) * (imgLabels_.cols + 1) / 4);
+
+ 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<uchar>(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<LabelT>(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<w && r > limitLine && img_row_prev_prev[c+1]>0
+#define condition_e c+2<w && r > 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<w && r > limitLine - 1 && img_row_prev[c+1]>0
+#define condition_k c+2<w && r > 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+1<w && img_row[c+1]>0
+
+#define condition_r c-1>=0 && r+1<h && img_row_fol[c-1]>0
+#define condition_s r+1<h && img_row_fol[c]>0
+#define condition_t c+1<w && r+1<h && img_row_fol[c+1]>0
+
+ // 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];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ if (condition_h) {
//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_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 {
+ 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 {
+ if (condition_h) {
+ if (condition_d) {
+ if (condition_c) {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ else {
+ //Action_15: Merge labels of block P_, R and S
+ imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_15: Merge labels of block P_, R and S
+ imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ if (condition_h) {
+ if (condition_m) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ // ACTION_9 Merge labels of block P_ and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ if (condition_i) {
+ if (condition_m) {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ if (condition_h) {
+ if (condition_m) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ // ACTION_9 Merge labels of block P_ and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ if (condition_i) {
+ if (condition_m) {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (condition_j) {
+ if (condition_i) {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ else {
+ if (condition_h) {
+ if (condition_c) {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ else {
+ //Action_7: Merge labels of block P_ and Q
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]);
+ continue;
+ }
+ }
+ else {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_p) {
+ if (condition_k) {
+ if (condition_i) {
+ if (condition_d) {
+ //Action_5: Assign label of block R
+ imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
+ continue;
+ }
+ else {
+ // ACTION_10 Merge labels of block Q and R
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]);
+ continue;
+ }
+ }
+ else {
+ if (condition_h) {
+ if (condition_d) {
+ if (condition_c) {
+ //Action_5: Assign label of block R
+ imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
+ continue;
+ }
+ else {
+ //Action_8: Merge labels of block P_ and R
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_8: Merge labels of block P_ and R
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_5: Assign label of block R
+ imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_i) {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ else {
+ if (condition_h) {
+ //Action_3: Assign label of block P_
+ imgLabels_row[c] = imgLabels_row_prev_prev[c - 2];
+ continue;
+ }
+ else {
+ //Action_2: New label (the block has foreground pixels and is not connected to anything else)
+ imgLabels_row[c] = label;
+ P_[label] = label;
+ label = label + 1;
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ if (condition_i) {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ else {
+ if (condition_h) {
+ //Action_3: Assign label of block P_
+ imgLabels_row[c] = imgLabels_row_prev_prev[c - 2];
+ continue;
+ }
+ else {
+ //Action_2: New label (the block has foreground pixels and is not connected to anything else)
+ imgLabels_row[c] = label;
+ P_[label] = label;
+ label = label + 1;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (condition_s) {
+ if (condition_p) {
+ if (condition_n) {
+ if (condition_j) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ if (condition_h) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_k) {
+ if (condition_d) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ if (condition_h) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_r) {
+ if (condition_j) {
+ if (condition_m) {
+ if (condition_h) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ if (condition_k) {
+ if (condition_d) {
+ if (condition_m) {
+ if (condition_h) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ if (condition_i) {
+ if (condition_m) {
+ if (condition_h) {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ else {
+ //Action_16: labels of block Q, R and S
+ imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_16: labels of block Q, R and S
+ imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_16: labels of block Q, R and S
+ imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_i) {
+ if (condition_m) {
+ if (condition_h) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ if (condition_j) {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ else {
+ if (condition_k) {
+ if (condition_i) {
+ if (condition_d) {
+ //Action_5: Assign label of block R
+ imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
+ continue;
+ }
+ else {
+ // ACTION_10 Merge labels of block Q and R
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_5: Assign label of block R
+ imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
+ continue;
+ }
+ }
+ else {
+ if (condition_i) {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ else {
+ //Action_2: New label (the block has foreground pixels and is not connected to anything else)
+ imgLabels_row[c] = label;
+ P_[label] = label;
+ label = label + 1;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (condition_r) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_n) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_2: New label (the block has foreground pixels and is not connected to anything else)
+ imgLabels_row[c] = label;
+ P_[label] = label;
+ label = label + 1;
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ if (condition_p) {
+ if (condition_j) {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ else {
+ if (condition_k) {
+ if (condition_i) {
+ if (condition_d) {
+ //Action_5: Assign label of block R
+ imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
+ continue;
+ }
+ else {
+ // ACTION_10 Merge labels of block Q and R
+ imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_5: Assign label of block R
+ imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
+ continue;
+ }
+ }
+ else {
+ if (condition_i) {
+ //Action_4: Assign label of block Q
+ imgLabels_row[c] = imgLabels_row_prev_prev[c];
+ continue;
+ }
+ else {
+ //Action_2: New label (the block has foreground pixels and is not connected to anything else)
+ imgLabels_row[c] = label;
+ P_[label] = label;
+ label = label + 1;
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ if (condition_t) {
+ //Action_2: New label (the block has foreground pixels and is not connected to anything else)
+ imgLabels_row[c] = label;
+ P_[label] = label;
+ label = label + 1;
+ continue;
+ }
+ else {
+ // Action_1: No action (the block has no foreground pixels)
+ imgLabels_row[c] = 0;
+ continue;
+ }
+ }
+ }
+ }
+ }
+ }
+ //write in the follower memory location
+ chunksSizeAndLabels_[startR + 1] = label - firstLabel;
+ }
+#undef condition_k
+#undef condition_j
+#undef condition_i
+#undef condition_h
+#undef condition_g
+#undef condition_e
+#undef condition_d
+#undef condition_c
+#undef condition_b
+ };
+
+ class SecondScan : public cv::ParallelLoopBody{
+ private:
+ const cv::Mat& img_;
+ cv::Mat& imgLabels_;
+ LabelT *P_;
+ StatsOp& sop_;
+ StatsOp *sopArray_;
+ LabelT& nLabels_;
+
+ public:
+ SecondScan(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, StatsOp& sop, StatsOp *sopArray, LabelT& nLabels)
+ : img_(img), imgLabels_(imgLabels), P_(P), sop_(sop), sopArray_(sopArray), nLabels_(nLabels){}
+
+ SecondScan& operator=(const SecondScan& ) { return *this; }
+
+ void operator()(const cv::Range& range) const{
+
+ int r = range.start;
+ r += (r % 2);
+ const int rowBegin = r;
+ const int rowEnd = range.end + range.end % 2;
+
+ if (rowBegin > 0){
+ sopArray_[rowBegin].initElement(nLabels_);
+ sopArray_[rowBegin].setNextLoc(rowEnd); //_nextLoc = rowEnd;
+
+ if (imgLabels_.rows& 1){
+ if (imgLabels_.cols& 1){
+ //Case 1: both rows and cols odd
+ for (; r < rowEnd; r += 2){
+ // Get rows pointer
+ const PixelT * const img_row = img_.ptr<PixelT>(r);
+ const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]);
+
+ LabelT * const imgLabels_row = imgLabels_.ptr<LabelT>(r);
+ LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]);
+ // Get rows pointer
+ for (int c = 0; c < imgLabels_.cols; c += 2) {
+ LabelT iLabel = imgLabels_row[c];
+ if (iLabel > 0) {
+ iLabel = P_[iLabel];
+ if (img_row[c] > 0){
+ imgLabels_row[c] = iLabel;
+ sopArray_[rowBegin](r, c, iLabel);
+ }
+ else{
+ imgLabels_row[c] = 0;
+ sopArray_[rowBegin](r, c, 0);
+ }
+ if (c + 1 < imgLabels_.cols) {
+ if (img_row[c + 1] > 0){
+ imgLabels_row[c + 1] = iLabel;
+ sopArray_[rowBegin](r, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row[c + 1] = 0;
+ sopArray_[rowBegin](r, c + 1, 0);
+ }
+ if (r + 1 < imgLabels_.rows) {
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sopArray_[rowBegin](r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sopArray_[rowBegin](r + 1, c, 0);
+ }
+ if (img_row_fol[c + 1] > 0){
+ imgLabels_row_fol[c + 1] = iLabel;
+ sopArray_[rowBegin](r + 1, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c + 1] = 0;
+ sopArray_[rowBegin](r + 1, c + 1, 0);
+ }
+ }
+ }
+ else if (r + 1 < imgLabels_.rows) {
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sopArray_[rowBegin](r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sopArray_[rowBegin](r + 1, c, 0);
+ }
+ }
+ }
+ else {
+ imgLabels_row[c] = 0;
+ sopArray_[rowBegin](r, c, 0);
+ if (c + 1 < imgLabels_.cols) {
+ imgLabels_row[c + 1] = 0;
+ sopArray_[rowBegin](r, c + 1, 0);
+ if (r + 1 < imgLabels_.rows) {
+ imgLabels_row_fol[c] = 0;
+ imgLabels_row_fol[c + 1] = 0;
+ sopArray_[rowBegin](r + 1, c, 0);
+ sopArray_[rowBegin](r + 1, c + 1, 0);
+ }
+ }
+ else if (r + 1 < imgLabels_.rows) {
+ imgLabels_row_fol[c] = 0;
+ sopArray_[rowBegin](r + 1, c, 0);
+ }
+ }
+ }
+ }
+ }//END Case 1
+ else{
+ //Case 2: only rows odd
+ for (; r < rowEnd; r += 2){
+ // Get rows pointer
+ const PixelT * const img_row = img_.ptr<PixelT>(r);
+ const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]);
+ LabelT * const imgLabels_row = imgLabels_.ptr<LabelT>(r);
+ LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]);
+ // Get rows pointer
+ for (int c = 0; c < imgLabels_.cols; c += 2) {
+ LabelT iLabel = imgLabels_row[c];
+ if (iLabel > 0) {
+ iLabel = P_[iLabel];
+ if (img_row[c] > 0){
+ imgLabels_row[c] = iLabel;
+ sopArray_[rowBegin](r, c, iLabel);
+ }
+ else{
+ imgLabels_row[c] = 0;
+ sopArray_[rowBegin](r, c, 0);
+ }
+ if (img_row[c + 1] > 0){
+ imgLabels_row[c + 1] = iLabel;
+ sopArray_[rowBegin](r, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row[c + 1] = 0;
+ sopArray_[rowBegin](r, c + 1, 0);
+ }
+ if (r + 1 < imgLabels_.rows) {
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sopArray_[rowBegin](r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sopArray_[rowBegin](r + 1, c, 0);
+ }
+ if (img_row_fol[c + 1] > 0){
+ imgLabels_row_fol[c + 1] = iLabel;
+ sopArray_[rowBegin](r + 1, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c + 1] = 0;
+ sopArray_[rowBegin](r + 1, c + 1, 0);
+ }
+ }
+ }
+ else {
+ imgLabels_row[c] = 0;
+ imgLabels_row[c + 1] = 0;
+ sopArray_[rowBegin](r, c, 0);
+ sopArray_[rowBegin](r, c + 1, 0);
+ if (r + 1 < imgLabels_.rows) {
+ imgLabels_row_fol[c] = 0;
+ imgLabels_row_fol[c + 1] = 0;
+ sopArray_[rowBegin](r + 1, c, 0);
+ sopArray_[rowBegin](r + 1, c + 1, 0);
+ }
+ }
+ }
+ }
+ }// END Case 2
+ }
+ else{
+ if (imgLabels_.cols& 1){
+ //Case 3: only cols odd
+ for (; r < rowEnd; r += 2){
+ // Get rows pointer
+ const PixelT * const img_row = img_.ptr<PixelT>(r);
+ const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]);
+ LabelT * const imgLabels_row = imgLabels_.ptr<LabelT>(r);
+ LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]);
+ // Get rows pointer
+ for (int c = 0; c < imgLabels_.cols; c += 2) {
+ LabelT iLabel = imgLabels_row[c];
+ if (iLabel > 0) {
+ iLabel = P_[iLabel];
+ if (img_row[c] > 0){
+ imgLabels_row[c] = iLabel;
+ sopArray_[rowBegin](r, c, iLabel);
+ }
+ else{
+ imgLabels_row[c] = 0;
+ sopArray_[rowBegin](r, c, 0);
+ }
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sopArray_[rowBegin](r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sopArray_[rowBegin](r + 1, c, 0);
+ }
+ if (c + 1 < imgLabels_.cols) {
+ if (img_row[c + 1] > 0){
+ imgLabels_row[c + 1] = iLabel;
+ sopArray_[rowBegin](r, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row[c + 1] = 0;
+ sopArray_[rowBegin](r, c + 1, 0);
+ }
+ if (img_row_fol[c + 1] > 0){
+ imgLabels_row_fol[c + 1] = iLabel;
+ sopArray_[rowBegin](r + 1, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c + 1] = 0;
+ sopArray_[rowBegin](r + 1, c + 1, 0);
+ }
+ }
+ }
+ else{
+ imgLabels_row[c] = 0;
+ imgLabels_row_fol[c] = 0;
+ sopArray_[rowBegin](r, c, 0);
+ sopArray_[rowBegin](r + 1, c, 0);
+ if (c + 1 < imgLabels_.cols) {
+ imgLabels_row[c + 1] = 0;
+ imgLabels_row_fol[c + 1] = 0;
+ sopArray_[rowBegin](r, c + 1, 0);
+ sopArray_[rowBegin](r + 1, c + 1, 0);
+ }
+ }
+ }
+ }
+ }// END case 3
+ else{
+ //Case 4: nothing odd
+ for (; r < rowEnd; r += 2){
+ // Get rows pointer
+ const PixelT * const img_row = img_.ptr<PixelT>(r);
+ const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]);
+ LabelT * const imgLabels_row = imgLabels_.ptr<LabelT>(r);
+ LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]);
+ // Get rows pointer
+ for (int c = 0; c < imgLabels_.cols; c += 2) {
+ LabelT iLabel = imgLabels_row[c];
+ if (iLabel > 0) {
+ iLabel = P_[iLabel];
+ if (img_row[c] > 0){
+ imgLabels_row[c] = iLabel;
+ sopArray_[rowBegin](r, c, iLabel);
+ }
+ else{
+ imgLabels_row[c] = 0;
+ sopArray_[rowBegin](r, c, 0);
+ }
+ if (img_row[c + 1] > 0){
+ imgLabels_row[c + 1] = iLabel;
+ sopArray_[rowBegin](r, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row[c + 1] = 0;
+ sopArray_[rowBegin](r, c + 1, 0);
+ }
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sopArray_[rowBegin](r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sopArray_[rowBegin](r + 1, c, 0);
+ }
+ if (img_row_fol[c + 1] > 0){
+ imgLabels_row_fol[c + 1] = iLabel;
+ sopArray_[rowBegin](r + 1, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c + 1] = 0;
+ sopArray_[rowBegin](r + 1, c + 1, 0);
+ }
+ }
+ else {
+ imgLabels_row[c] = 0;
+ imgLabels_row[c + 1] = 0;
+ imgLabels_row_fol[c] = 0;
+ imgLabels_row_fol[c + 1] = 0;
+ sopArray_[rowBegin](r, c, 0);
+ sopArray_[rowBegin](r, c + 1, 0);
+ sopArray_[rowBegin](r + 1, c, 0);
+ sopArray_[rowBegin](r + 1, c + 1, 0);
+ }
+ }
+ }//END case 4
+ }
+ }
+ }
+ else{
+ //the first thread uses sop in order to make less merges
+ sop_.setNextLoc(rowEnd);
+ if (imgLabels_.rows& 1){
+ if (imgLabels_.cols& 1){
+ //Case 1: both rows and cols odd
+ for (; r < rowEnd; r += 2){
+ // Get rows pointer
+ const PixelT * const img_row = img_.ptr<PixelT>(r);
+ const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]);
+
+ LabelT * const imgLabels_row = imgLabels_.ptr<LabelT>(r);
+ LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]);
+ // Get rows pointer
+ for (int c = 0; c < imgLabels_.cols; c += 2) {
+ LabelT iLabel = imgLabels_row[c];
+ if (iLabel > 0) {
+ iLabel = P_[iLabel];
+ if (img_row[c] > 0){
+ imgLabels_row[c] = iLabel;
+ sop_(r, c, iLabel);
+ }
+ else{
+ imgLabels_row[c] = 0;
+ sop_(r, c, 0);
+ }
+ if (c + 1 < imgLabels_.cols) {
+ if (img_row[c + 1] > 0){
+ imgLabels_row[c + 1] = iLabel;
+ sop_(r, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row[c + 1] = 0;
+ sop_(r, c + 1, 0);
+ }
+ if (r + 1 < imgLabels_.rows) {
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sop_(r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sop_(r + 1, c, 0);
+ }
+ if (img_row_fol[c + 1] > 0){
+ imgLabels_row_fol[c + 1] = iLabel;
+ sop_(r + 1, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c + 1] = 0;
+ sop_(r + 1, c + 1, 0);
+ }
+ }
+ }
+ else if (r + 1 < imgLabels_.rows) {
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sop_(r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sop_(r + 1, c, 0);
+ }
+ }
+ }
+ else {
+ imgLabels_row[c] = 0;
+ sop_(r, c, 0);
+ if (c + 1 < imgLabels_.cols) {
+ imgLabels_row[c + 1] = 0;
+ sop_(r, c + 1, 0);
+ if (r + 1 < imgLabels_.rows) {
+ imgLabels_row_fol[c] = 0;
+ imgLabels_row_fol[c + 1] = 0;
+ sop_(r + 1, c, 0);
+ sop_(r + 1, c + 1, 0);
+ }
+ }
+ else if (r + 1 < imgLabels_.rows) {
+ imgLabels_row_fol[c] = 0;
+ sop_(r + 1, c, 0);
+ }
+ }
+ }
+ }
+ }//END Case 1
+ else{
+ //Case 2: only rows odd
+ for (; r < rowEnd; r += 2){
+ // Get rows pointer
+ const PixelT * const img_row = img_.ptr<PixelT>(r);
+ const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]);
+ LabelT * const imgLabels_row = imgLabels_.ptr<LabelT>(r);
+ LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]);
+ // Get rows pointer
+ for (int c = 0; c < imgLabels_.cols; c += 2) {
+ LabelT iLabel = imgLabels_row[c];
+ if (iLabel > 0) {
+ iLabel = P_[iLabel];
+ if (img_row[c] > 0){
+ imgLabels_row[c] = iLabel;
+ sop_(r, c, iLabel);
+ }
+ else{
+ imgLabels_row[c] = 0;
+ sop_(r, c, 0);
+ }
+ if (img_row[c + 1] > 0){
+ imgLabels_row[c + 1] = iLabel;
+ sop_(r, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row[c + 1] = 0;
+ sop_(r, c + 1, 0);
+ }
+ if (r + 1 < imgLabels_.rows) {
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sop_(r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sop_(r + 1, c, 0);
+ }
+ if (img_row_fol[c + 1] > 0){
+ imgLabels_row_fol[c + 1] = iLabel;
+ sop_(r + 1, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c + 1] = 0;
+ sop_(r + 1, c + 1, 0);
+ }
+ }
+ }
+ else {
+ imgLabels_row[c] = 0;
+ imgLabels_row[c + 1] = 0;
+ sop_(r, c, 0);
+ sop_(r, c + 1, 0);
+ if (r + 1 < imgLabels_.rows) {
+ imgLabels_row_fol[c] = 0;
+ imgLabels_row_fol[c + 1] = 0;
+ sop_(r + 1, c, 0);
+ sop_(r + 1, c + 1, 0);
+ }
+ }
+ }
+ }
+ }// END Case 2
+ }
+ else{
+ if (imgLabels_.cols& 1){
+ //Case 3: only cols odd
+ for (; r < rowEnd; r += 2){
+ // Get rows pointer
+ const PixelT * const img_row = img_.ptr<PixelT>(r);
+ const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]);
+ LabelT * const imgLabels_row = imgLabels_.ptr<LabelT>(r);
+ LabelT * const imgLabels_row_fol = (LabelT *)(((char *)imgLabels_row) + imgLabels_.step.p[0]);
+ // Get rows pointer
+ for (int c = 0; c < imgLabels_.cols; c += 2) {
+ LabelT iLabel = imgLabels_row[c];
+ if (iLabel > 0) {
+ iLabel = P_[iLabel];
+ if (img_row[c] > 0){
+ imgLabels_row[c] = iLabel;
+ sop_(r, c, iLabel);
+ }
+ else{
+ imgLabels_row[c] = 0;
+ sop_(r, c, 0);
+ }
+ if (img_row_fol[c] > 0){
+ imgLabels_row_fol[c] = iLabel;
+ sop_(r + 1, c, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c] = 0;
+ sop_(r + 1, c, 0);
+ }
+ if (c + 1 < imgLabels_.cols) {
+ if (img_row[c + 1] > 0){
+ imgLabels_row[c + 1] = iLabel;
+ sop_(r, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row[c + 1] = 0;
+ sop_(r, c + 1, 0);
+ }
+ if (img_row_fol[c + 1] > 0){
+ imgLabels_row_fol[c + 1] = iLabel;
+ sop_(r + 1, c + 1, iLabel);
+ }
+ else{
+ imgLabels_row_fol[c + 1] = 0;
+ sop_(r + 1, c + 1, 0);
+ }
+ }
+ }
+ else{
+ imgLabels_row[c] = 0;
+ imgLabels_row_fol[c] = 0;
+ sop_(r, c, 0);
+ sop_(r + 1, c, 0);
+ if (c + 1 < imgLabels_.cols) {
+ imgLabels_row[c + 1] = 0;
+ imgLabels_row_fol[c + 1] = 0;
+ sop_(r, c + 1, 0);
+ sop_(r + 1, c + 1, 0);
+ }
+ }
+ }
+ }
+ }// END case 3
+ else{
+ //Case 4: nothing odd
+ for (; r < rowEnd; r += 2){
+ // Get rows pointer
+ const PixelT * const img_row = img_.ptr<PixelT>(r);
+ const PixelT * const img_row_fol = (PixelT *)(((char *)img_row) + img_.step.p[0]);
+ LabelT * const imgLabels_row = imgLabels_.ptr<LabelT>(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<LabelT>(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<PixelT>(r);
+ const PixelT * const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]);
+
+ for (int c = 0; c < w; c += 2){
+
+#define condition_x imgLabels_row[c] > 0
+#define condition_pppr c > 1 && imgLabels_row_prev_prev[c - 2] > 0
+#define condition_qppr imgLabels_row_prev_prev[c] > 0
+#define condition_qppr1 c < w - 1
+#define condition_qppr2 c < w
+#define condition_rppr c < w - 2 && imgLabels_row_prev_prev[c + 2] > 0
+
+ if (condition_x){
+ if (condition_pppr){
+ //check in img
+ if (img_row[c] > 0 && img_row_prev[c - 1] > 0)
+ //assign the same label
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row[c]);
+ }
+ if (condition_qppr){
+ if (condition_qppr1){
+ if ((img_row[c] > 0 && img_row_prev[c] > 0) || (img_row[c + 1] > 0 && img_row_prev[c] > 0) ||
+ (img_row[c] > 0 && img_row_prev[c + 1] > 0) || (img_row[c + 1] > 0 && img_row_prev[c + 1] > 0)){
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c]);
+ }
+ }
+ else /*if (condition_qppr2)*/{
+ if (img_row[c] > 0 && img_row_prev[c] > 0)
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c]);
+ }
+ }
+ if (condition_rppr){
+ if (img_row[c + 1] > 0 && img_row_prev[c + 2] > 0)
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c]);
+ }
+ }
+ }
+ }
+ }
+
+ LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){
+ CV_Assert(img.rows == imgLabels.rows);
+ CV_Assert(img.cols == imgLabels.cols);
+ CV_Assert(connectivity == 8);
+
+ const int nThreads = cv::getNumberOfCPUs();
+ cv::setNumThreads(nThreads);
+
+ const int h = img.rows;
+ const int w = img.cols;
+
+ //A quick and dirty upper bound for the maximimum number of labels.
+ //Following formula comes from the fact that a 2x2 block in 8-connectivity case
+ //can never have more than 1 new label and 1 label for background.
+ //Worst case image example pattern:
+ //1 0 1 0 1...
+ //0 0 0 0 0...
+ //1 0 1 0 1...
+ //............
+ const size_t Plength = ((size_t(h) + 1) * (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;
+
+ cv::Range range(0, h);
+
+ //First scan, each thread works with chunk of img.rows/nThreads rows
+ //e.g. 300 rows, 4 threads -> each chunks is composed of 75 rows
+ cv::parallel_for_(range, FirstScan(img, imgLabels, P, chunksSizeAndLabels), nThreads);
+
+ //merge labels of different chunks
+ mergeLabels(img, imgLabels, P, chunksSizeAndLabels);
+
+ LabelT nLabels = 1;
+ for (int i = 0; i < h; i = chunksSizeAndLabels[i]){
+ flattenL(P, (i + 1) * (w + 1) / 4, chunksSizeAndLabels[i + 1], nLabels);
+ }
+
+ //Array for statistics data
+ StatsOp *sopArray = new StatsOp[h];
+ sop.init(nLabels);
+
+ //Second scan
+ cv::parallel_for_(range, SecondScan(img, imgLabels, P, sop, sopArray, nLabels), nThreads);
+
+ StatsOp::mergeStats(imgLabels, sopArray, sop, nLabels);
+ sop.finish();
+
+ delete[] sopArray;
+ cv::fastFree(chunksSizeAndLabels);
+ cv::fastFree(P);
+ return nLabels;
+ }
+ };//End struct LabelingGranaParallel
+
- // Based on \93Optimized Block-based Connected Components Labeling with Decision Trees\94, Costantino Grana et al
++ // Based on �Optimized Block-based Connected Components Labeling with Decision Trees�, Costantino Grana et al
+ // Only for 8-connectivity
+ template<typename LabelT, typename PixelT, typename StatsOp = NoOp >
+ struct LabelingGrana{
+ LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){
+ CV_Assert(img.rows == imgLabels.rows);
+ CV_Assert(img.cols == imgLabels.cols);
+ CV_Assert(connectivity == 8);
+
+ const int h = img.rows;
+ const int w = img.cols;
+
+ //A quick and dirty upper bound for the maximimum number of labels.
+ //Following formula comes from the fact that a 2x2 block in 8-connectivity case
+ //can never have more than 1 new label and 1 label for background.
+ //Worst case image example pattern:
+ //1 0 1 0 1...
+ //0 0 0 0 0...
+ //1 0 1 0 1...
+ //............
+ const size_t Plength = ((size_t(h) + 1) * (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<PixelT>(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<LabelT>(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<w&& r-2>=0 && img_row_prev_prev[c+1]>0
+#define condition_e c+2<w && r-1>=0 && img_row_prev[c-1]>0
+
+#define condition_g c-2>=0 && r-1>=0 && img_row_prev[c-2]>0
+#define condition_h c-1>=0 && r-1>=0 && img_row_prev[c-1]>0
+#define condition_i r-1>=0 && img_row_prev[c]>0
+#define condition_j c+1<w && r-1>=0 && img_row_prev[c+1]>0
+#define condition_k c+2<w && r-1>=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+1<w && img_row[c+1]>0
+
+#define condition_r c-1>=0 && r+1<h && img_row_fol[c-1]>0
+#define condition_s r+1<h && img_row_fol[c]>0
+#define condition_t c+1<w && r+1<h && img_row_fol[c+1]>0
+
+ // This is a decision tree which allows to choose which action to
+ // perform, checking as few conditions as possible.
+ // Actions: the blocks label are provisionally stored in the top left
+ // pixel of the block in the labels image
+
+ if (condition_o) {
+ if (condition_n) {
+ if (condition_j) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ if (condition_h) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_p) {
+ if (condition_k) {
+ if (condition_d) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ if (condition_h) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ }
+ else {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_r) {
+ if (condition_j) {
+ if (condition_m) {
+ if (condition_h) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_g) {
+ if (condition_b) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ if (condition_i) {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ else {
+ if (condition_h) {
+ if (condition_c) {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ else {
+ //Action_14: Merge labels of block P, Q and S
+ imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_11: Merge labels of block Q and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ if (condition_p) {
+ if (condition_k) {
+ if (condition_m) {
+ if (condition_h) {
+ if (condition_d) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ if (condition_d) {
+ if (condition_g) {
+ if (condition_b) {
+ if (condition_i) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ if (condition_c) {
+ //Action_6: Assign label of block S
+ imgLabels_row[c] = imgLabels_row[c - 2];
+ continue;
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ if (condition_i) {
+ if (condition_g) {
+ if (condition_b) {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ else {
+ //Action_16: labels of block Q, R and S
+ imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_16: labels of block Q, R and S
+ imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ }
+ }
+ else {
+ if (condition_i) {
+ if (condition_d) {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ else {
+ //Action_16: labels of block Q, R and S
+ imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ if (condition_h) {
+ if (condition_d) {
+ if (condition_c) {
+ //Action_12: Merge labels of block R and S
+ imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
+ continue;
+ }
+ else {
+ //Action_15: Merge labels of block P, R and S
+ imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
+ continue;
+ }
+ }
+ else {
+ //Action_15: Merge labels of block P, R and S
+ imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}