Open Source Computer Vision Library https://opencv.org/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4428 lines
259 KiB

/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
// 2011 Jason Newton <nevion@gmail.com>
// 2016, 2021 Costantino Grana <costantino.grana@unimore.it>
// 2016, 2021 Federico Bolelli <federico.bolelli@unimore.it>
// 2016 Lorenzo Baraldi <lorenzo.baraldi@unimore.it>
// 2016 Roberto Vezzani <roberto.vezzani@unimore.it>
// 2016 Michele Cancilla <cancilla.michele@gmail.com>
// 2021 Stefano Allegretti <stefano.allegretti@unimore.it>
//M*/
//
#include "precomp.hpp"
#include <vector>
namespace cv{
namespace connectedcomponents{
struct NoOp{
NoOp(){
}
inline
void init(int /*labels*/){
}
inline
void initElement(const int /*nlabels*/){
}
inline
void operator()(int r, int c, int l){
CV_UNUSED(r);
CV_UNUSED(c);
CV_UNUSED(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<Point2ui64> integrals;
int _nextLoc;
CCStatsOp() : _mstatsv(0), _mcentroidsv(0), _nextLoc(0) {}
CCStatsOp(OutputArray _statsv, OutputArray _centroidsv) : _mstatsv(&_statsv), _mcentroidsv(&_centroidsv), _nextLoc(0){}
inline
void init(int nlabels){
_mstatsv->create(cv::Size(CC_STAT_MAX, nlabels), cv::DataType<int>::type);
statsv = _mstatsv->getMat();
_mcentroidsv->create(cv::Size(2, nlabels), cv::DataType<double>::type);
centroidsv = _mcentroidsv->getMat();
for (int l = 0; l < (int)nlabels; ++l){
int *row = (int *)&statsv.at<int>(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<int>::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<int>(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){
int *row =& statsv.at<int>(l, 0);
double area = ((unsigned*)row)[CC_STAT_AREA];
double *centroid = &centroidsv.at<double>(l, 0);
if (area > 0){
row[CC_STAT_WIDTH] = row[CC_STAT_WIDTH] - row[CC_STAT_LEFT] + 1;
row[CC_STAT_HEIGHT] = row[CC_STAT_HEIGHT] - row[CC_STAT_TOP] + 1;
Point2ui64& integral = integrals[l];
centroid[0] = double(integral.x) / area;
centroid[1] = double(integral.y) / area;
} else {
row[CC_STAT_WIDTH] = 0;
row[CC_STAT_HEIGHT] = 0;
row[CC_STAT_LEFT] = -1;
centroid[0] = std::numeric_limits<double>::quiet_NaN();
centroid[1] = std::numeric_limits<double>::quiet_NaN();
}
}
}
inline
void setNextLoc(const int nextLoc){
_nextLoc = nextLoc;
}
inline static
void mergeStats(const cv::Mat& imgLabels, CCStatsOp *sopArray, CCStatsOp& sop, const int& nLabels){
const int h = imgLabels.rows;
if (sop._nextLoc != h){
for (int nextLoc = sop._nextLoc; nextLoc < h; nextLoc = sopArray[nextLoc]._nextLoc){
//merge between sopNext and sop
for (int l = 0; l < nLabels; ++l){
int *rowNext = (int*)sopArray[nextLoc].statsv.ptr(l);
if (rowNext[CC_STAT_AREA] > 0){ //if changed merge all the stats
int *rowMerged = (int*)sop.statsv.ptr(l);
rowMerged[CC_STAT_LEFT] = MIN(rowMerged[CC_STAT_LEFT], rowNext[CC_STAT_LEFT]);
rowMerged[CC_STAT_WIDTH] = MAX(rowMerged[CC_STAT_WIDTH], rowNext[CC_STAT_WIDTH]);
rowMerged[CC_STAT_TOP] = MIN(rowMerged[CC_STAT_TOP], rowNext[CC_STAT_TOP]);
rowMerged[CC_STAT_HEIGHT] = MAX(rowMerged[CC_STAT_HEIGHT], rowNext[CC_STAT_HEIGHT]);
rowMerged[CC_STAT_AREA] += rowNext[CC_STAT_AREA];
sop.integrals[l].x += sopArray[nextLoc].integrals[l].x;
sop.integrals[l].y += sopArray[nextLoc].integrals[l].y;
}
}
}
}
}
};
//Find the root of the tree of node i
template<typename LabelT>
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<typename LabelT>
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<typename LabelT>
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<typename LabelT>
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;
}
setRoot(P, j, root);
}
setRoot(P, i, root);
return root;
}
//Flatten the Union Find tree and relabel the components
template<typename LabelT>
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<typename LabelT>
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;
}
}
}
template <typename LT> static inline
LT stripeFirstLabel4Connectivity(int y, int w)
{
CV_DbgAssert((y & 1) == 0);
return (LT(y) * LT(w) /*+ 1*/) / 2 + 1;
}
template <typename LT> static inline
LT stripeFirstLabel8Connectivity(int y, int w)
{
CV_DbgAssert((y & 1) == 0);
return LT((y /*+ 1*/) / 2) * LT((w + 1) / 2) + 1;
}
//Implementation of Spaghetti algorithm, as described in "Spaghetti Labeling: Directed Acyclic Graphs for Block-Based
//Connected Components Labeling" (only for 8-connectivity)
//Federico Bolelli et. al.
template<typename LabelT, typename PixelT, typename StatsOp = NoOp >
struct LabelingBolelli
{
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;
const int e_rows = h & -2;
const bool o_rows = h % 2 == 1;
const int e_cols = w & -2;
const bool o_cols = w % 2 == 1;
// A quick and dirty upper bound for the maximum number of labels.
// Following formula comes from the fact that a 2x2 block in 8-connectivity case
// can never have more than 1 new label and 1 label for background.
// Worst case image example pattern:
// 1 0 1 0 1...
// 0 0 0 0 0...
// 1 0 1 0 1...
// ............
const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1;
std::vector<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//P[0] = 0;
LabelT lunique = 1;
// First scan
// 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 matter
// when considering the outer connectivities
// A bunch of defines is used to check if the pixels are foreground
// and to define actions to be performed on blocks
{
#define CONDITION_B img_row_prev_prev[c-1]>0
#define CONDITION_C img_row_prev_prev[c]>0
#define CONDITION_D img_row_prev_prev[c+1]>0
#define CONDITION_E img_row_prev_prev[c+2]>0
#define CONDITION_G img_row_prev[c-2]>0
#define CONDITION_H img_row_prev[c-1]>0
#define CONDITION_I img_row_prev[c]>0
#define CONDITION_J img_row_prev[c+1]>0
#define CONDITION_K img_row_prev[c+2]>0
#define CONDITION_M img_row[c-2]>0
#define CONDITION_N img_row[c-1]>0
#define CONDITION_O img_row[c]>0
#define CONDITION_P img_row[c+1]>0
#define CONDITION_R img_row_fol[c-1]>0
#define CONDITION_S img_row_fol[c]>0
#define CONDITION_T img_row_fol[c+1]>0
// Action 1: No action
#define ACTION_1 img_labels_row[c] = 0;
// Action 2: New label (the block has foreground pixels and is not connected to anything else)
#define ACTION_2 img_labels_row[c] = lunique; \
P[lunique] = lunique; \
lunique = lunique + 1;
//Action 3: Assign label of block P
#define ACTION_3 img_labels_row[c] = img_labels_row_prev_prev[c - 2];
// Action 4: Assign label of block Q
#define ACTION_4 img_labels_row[c] = img_labels_row_prev_prev[c];
// Action 5: Assign label of block R
#define ACTION_5 img_labels_row[c] = img_labels_row_prev_prev[c + 2];
// Action 6: Assign label of block S
#define ACTION_6 img_labels_row[c] = img_labels_row[c - 2];
// Action 7: Merge labels of block P and Q
#define ACTION_7 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c]);
//Action 8: Merge labels of block P and R
#define ACTION_8 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c + 2]);
// Action 9 Merge labels of block P and S
#define ACTION_9 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row[c - 2]);
// Action 10 Merge labels of block Q and R
#define ACTION_10 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c], img_labels_row_prev_prev[c + 2]);
// Action 11: Merge labels of block Q and S
#define ACTION_11 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c], img_labels_row[c - 2]);
// Action 12: Merge labels of block R and S
#define ACTION_12 img_labels_row[c] = set_union(P, img_labels_row_prev_prev[c + 2], img_labels_row[c - 2]);
// Action 13: Merge labels of block P, Q and R
#define ACTION_13 img_labels_row[c] = set_union(P, set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c]), img_labels_row_prev_prev[c + 2]);
// Action 14: Merge labels of block P, Q and S
#define ACTION_14 img_labels_row[c] = set_union(P, set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c]), img_labels_row[c - 2]);
//Action 15: Merge labels of block P, R and S
#define ACTION_15 img_labels_row[c] = set_union(P, set_union(P, img_labels_row_prev_prev[c - 2], img_labels_row_prev_prev[c + 2]), img_labels_row[c - 2]);
//Action 16: labels of block Q, R and S
#define ACTION_16 img_labels_row[c] = set_union(P, set_union(P, img_labels_row_prev_prev[c], img_labels_row_prev_prev[c + 2]), img_labels_row[c - 2]);
}
// The following Directed Rooted Acyclic Graphs (DAGs) allow to choose which action to
// perform, checking as few conditions as possible. Special DAGs are used for the first/last
// line of the image and for single line images. Actions: the blocks label are provisionally
// stored in the top left pixel of the block in the labels image.
if (h == 1) {
// Single line
const PixelT * const img_row = img.ptr<PixelT>(0);
LabelT * const img_labels_row = imgLabels.ptr<LabelT>(0);
int c = -2;
#include "ccl_bolelli_forest_singleline.inc.hpp"
}
else {
// More than one line
// First couple of lines
{
const PixelT * const img_row = img.ptr<PixelT>(0);
const PixelT * const img_row_fol = (PixelT *)(((char*)img_row) + img.step.p[0]);
LabelT * const img_labels_row = imgLabels.ptr<LabelT>(0);
int c = -2;
#include "ccl_bolelli_forest_firstline.inc.hpp"
}
// Every other line but the last one if image has an odd number of rows
for (int r = 2; r < e_rows; 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 img_labels_row = imgLabels.ptr<LabelT>(r);
LabelT * const img_labels_row_prev_prev = (LabelT *)(((char*)img_labels_row) - imgLabels.step.p[0] - imgLabels.step.p[0]);
int c = -2;
goto tree_0;
#include "ccl_bolelli_forest.inc.hpp"
}
// Last line (in case the rows are odd)
if (o_rows) {
int r = h - 1;
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]);
LabelT * const img_labels_row = imgLabels.ptr<LabelT>(r);
LabelT * const img_labels_row_prev_prev = (LabelT *)(((char*)img_labels_row) - imgLabels.step.p[0] - imgLabels.step.p[0]);
int c = -2;
#include "ccl_bolelli_forest_lastline.inc.hpp"
}
}
// undef conditions and actions
{
#undef ACTION_1
#undef ACTION_2
#undef ACTION_3
#undef ACTION_4
#undef ACTION_5
#undef ACTION_6
#undef ACTION_7
#undef ACTION_8
#undef ACTION_9
#undef ACTION_10
#undef ACTION_11
#undef ACTION_12
#undef ACTION_13
#undef ACTION_14
#undef ACTION_15
#undef ACTION_16
#undef CONDITION_B
#undef CONDITION_C
#undef CONDITION_D
#undef CONDITION_E
#undef CONDITION_G
#undef CONDITION_H
#undef CONDITION_I
#undef CONDITION_J
#undef CONDITION_K
#undef CONDITION_M
#undef CONDITION_N
#undef CONDITION_O
#undef CONDITION_P
#undef CONDITION_R
#undef CONDITION_S
#undef CONDITION_T
}
// Second scan + analysis
LabelT nLabels = flattenL(P, lunique);
sop.init(nLabels);
int r = 0;
for (; r < e_rows; 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 img_labels_row = imgLabels.ptr<LabelT>(r);
LabelT * const img_labels_row_fol = (LabelT *)(((char*)img_labels_row) + imgLabels.step.p[0]);
int c = 0;
for (; c < e_cols; c += 2) {
LabelT iLabel = img_labels_row[c];
if (iLabel > 0) {
iLabel = P[iLabel];
if (img_row[c] > 0) {
img_labels_row[c] = iLabel;
sop(r, c, iLabel);
}
else {
img_labels_row[c] = 0;
sop(r, c, 0);
}
if (img_row[c + 1] > 0) {
img_labels_row[c + 1] = iLabel;
sop(r, c + 1, iLabel);
}
else {
img_labels_row[c + 1] = 0;
sop(r, c + 1, 0);
}
if (img_row_fol[c] > 0) {
img_labels_row_fol[c] = iLabel;
sop(r + 1, c, iLabel);
}
else {
img_labels_row_fol[c] = 0;
sop(r + 1, c, 0);
}
if (img_row_fol[c + 1] > 0) {
img_labels_row_fol[c + 1] = iLabel;
sop(r + 1, c + 1, iLabel);
}
else {
img_labels_row_fol[c + 1] = 0;
sop(r + 1, c + 1, 0);
}
}
else {
img_labels_row[c] = 0;
sop(r, c, 0);
img_labels_row[c + 1] = 0;
sop(r, c + 1, 0);
img_labels_row_fol[c] = 0;
sop(r + 1, c, 0);
img_labels_row_fol[c + 1] = 0;
sop(r + 1, c + 1, 0);
}
}
// Last column if the number of columns is odd
if (o_cols) {
LabelT iLabel = img_labels_row[c];
if (iLabel > 0) {
iLabel = P[iLabel];
if (img_row[c] > 0) {
img_labels_row[c] = iLabel;
sop(r, c, iLabel);
}
else {
img_labels_row[c] = 0;
sop(r, c, 0);
}
if (img_row_fol[c] > 0) {
img_labels_row_fol[c] = iLabel;
sop(r + 1, c, iLabel);
}
else {
img_labels_row_fol[c] = 0;
sop(r + 1, c, 0);
}
}
else {
img_labels_row[c] = 0;
sop(r, c, 0);
img_labels_row_fol[c] = 0;
sop(r + 1, c, 0);
}
}
}
// Last row if the number of rows is odd
if (o_rows) {
// Get rows pointer
const PixelT * const img_row = img.ptr<PixelT>(r);
LabelT * const img_labels_row = imgLabels.ptr<LabelT>(r);
int c = 0;
for (; c < e_cols; c += 2) {
LabelT iLabel = img_labels_row[c];
if (iLabel > 0) {
iLabel = P[iLabel];
if (img_row[c] > 0) {
img_labels_row[c] = iLabel;
sop(r, c, iLabel);
}
else {
img_labels_row[c] = 0;
sop(r, c, 0);
}
if (img_row[c + 1] > 0) {
img_labels_row[c + 1] = iLabel;
sop(r, c + 1, iLabel);
}
else {
img_labels_row[c + 1] = 0;
sop(r, c + 1, 0);
}
}
else {
img_labels_row[c] = 0;
sop(r, c, 0);
img_labels_row[c + 1] = 0;
sop(r, c + 1, 0);
}
}
// Last column if the number of columns is odd
if (o_cols) {
LabelT iLabel = img_labels_row[c];
if (iLabel > 0) {
iLabel = P[iLabel];
if (img_row[c] > 0) {
img_labels_row[c] = iLabel;
sop(r, c, iLabel);
}
else {
img_labels_row[c] = 0;
sop(r, c, 0);
}
}
else {
img_labels_row[c] = 0;
sop(r, c, iLabel);
}
}
}
sop.finish();
return nLabels;
}//End function LabelingBolelli operator()
};//End struct LabelingBolelli
//Parallel implementation of Scan Array-based Union Find (SAUF) algorithm, as described in "Two More Strategies to Speed
//Up Connected Components Labeling Algorithms"
//Federico Bolelli et. al.
template<typename LabelT, typename PixelT, typename StatsOp = NoOp >
struct LabelingWuParallel{
class FirstScan8Connectivity : public cv::ParallelLoopBody{
const cv::Mat& img_;
cv::Mat& imgLabels_;
LabelT *P_;
int *chunksSizeAndLabels_;
public:
FirstScan8Connectivity(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, int *chunksSizeAndLabels)
: img_(img), imgLabels_(imgLabels), P_(P), chunksSizeAndLabels_(chunksSizeAndLabels){}
FirstScan8Connectivity& operator=(const FirstScan8Connectivity& ) { return *this; }
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, img_.rows));
int r = range.start;
chunksSizeAndLabels_[r] = range.end;
LabelT label = stripeFirstLabel8Connectivity<LabelT>(r, imgLabels_.cols);
const LabelT firstLabel = label;
const int w = img_.cols;
const int limitLine = r, startR = r;
// Rosenfeld Mask
// +-+-+-+
// |p|q|r|
// +-+-+-+
// |s|x|
// +-+-+
for (; r != range.end; ++r)
{
PixelT const * const img_row = img_.ptr<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 > limitLine && img_row_prev[c - 1] > 0
#define condition_q r > limitLine && img_row_prev[c] > 0
#define condition_r c < w - 1 && r > limitLine && img_row_prev[c + 1] > 0
#define condition_s c > 0 && img_row[c - 1] > 0
#define condition_x img_row[c] > 0
if (condition_x){
if (condition_q){
//copy q
imgLabels_row[c] = imgLabels_row_prev[c];
}
else{
//not q
if (condition_r){
if (condition_p){
//concavity p->x->r. Merge
imgLabels_row[c] = set_union(P_, imgLabels_row_prev[c - 1], imgLabels_row_prev[c + 1]);
}
else{ //not p and q
if (condition_s){
//step s->x->r. Merge
imgLabels_row[c] = set_union(P_, imgLabels_row[c - 1], imgLabels_row_prev[c + 1]);
}
else{ //not p, q and s
//copy r
imgLabels_row[c] = imgLabels_row_prev[c + 1];
}
}
}
else{
//not r and q
if (condition_p){
//copy p
imgLabels_row[c] = imgLabels_row_prev[c - 1];
}
else{//not r,q and p
if (condition_s){
imgLabels_row[c] = imgLabels_row[c - 1];
}
else{
//new label
imgLabels_row[c] = label;
P_[label] = label;
label = label + 1;
}
}
}
}
}
else{
//x is a background pixel
imgLabels_row[c] = 0;
}
}
}
//write in the follower memory location
chunksSizeAndLabels_[startR + 1] = label - firstLabel;
}
#undef condition_p
#undef condition_q
#undef condition_r
#undef condition_s
#undef condition_x
};
class FirstScan4Connectivity : public cv::ParallelLoopBody{
const cv::Mat& img_;
cv::Mat& imgLabels_;
LabelT *P_;
int *chunksSizeAndLabels_;
public:
FirstScan4Connectivity(const cv::Mat& img, cv::Mat& imgLabels, LabelT *P, int *chunksSizeAndLabels)
: img_(img), imgLabels_(imgLabels), P_(P), chunksSizeAndLabels_(chunksSizeAndLabels){}
FirstScan4Connectivity& operator=(const FirstScan4Connectivity& ) { return *this; }
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, img_.rows));
int r = range.start;
chunksSizeAndLabels_[r] = range.end;
LabelT label = stripeFirstLabel4Connectivity<LabelT>(r, imgLabels_.cols);
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<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 > limitLine && img_row_prev[c] > 0
#define condition_s c > 0 && img_row[c - 1] > 0
#define condition_x img_row[c] > 0
if (condition_x){
if (condition_q){
if (condition_s){
//step s->x->q. Merge
imgLabels_row[c] = set_union(P_, imgLabels_row[c - 1], imgLabels_row_prev[c]);
}
else{
//copy q
imgLabels_row[c] = imgLabels_row_prev[c];
}
}
else{
if (condition_s){ // copy s
imgLabels_row[c] = imgLabels_row[c - 1];
}
else{
//new label
imgLabels_row[c] = label;
P_[label] = label;
label = label + 1;
}
}
}
else{
//x is a background pixel
imgLabels_row[c] = 0;
}
}
}
//write in the following memory location
chunksSizeAndLabels_[startR + 1] = label - firstLabel;
}
#undef condition_q
#undef condition_s
#undef condition_x
};
class SecondScan : public cv::ParallelLoopBody{
cv::Mat& imgLabels_;
const LabelT *P_;
StatsOp& sop_;
StatsOp *sopArray_;
LabelT& nLabels_;
public:
SecondScan(cv::Mat& imgLabels, const LabelT *P, StatsOp& sop, StatsOp *sopArray, LabelT& nLabels)
: imgLabels_(imgLabels), P_(P), sop_(sop), sopArray_(sopArray), nLabels_(nLabels){}
SecondScan& operator=(const SecondScan& ) { return *this; }
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, imgLabels_.rows));
int r = range.start;
const int rowBegin = r;
const int rowEnd = range.end;
if (rowBegin > 0){
sopArray_[rowBegin].initElement(nLabels_);
sopArray_[rowBegin].setNextLoc(rowEnd); //_nextLoc = rowEnd;
for (; r < rowEnd; ++r) {
LabelT * img_row_start = imgLabels_.ptr<LabelT>(r);
LabelT * const img_row_end = img_row_start + imgLabels_.cols;
for (int c = 0; img_row_start != img_row_end; ++img_row_start, ++c){
*img_row_start = P_[*img_row_start];
sopArray_[rowBegin](r, c, *img_row_start);
}
}
}
else{
//the first thread uses sop in order to make less merges
sop_.setNextLoc(rowEnd);
for (; r < rowEnd; ++r) {
LabelT * img_row_start = imgLabels_.ptr<LabelT>(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<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 && imgLabels_row_prev[c - 1] > 0
#define condition_q imgLabels_row_prev[c] > 0
#define condition_r c < w - 1 && imgLabels_row_prev[c + 1] > 0
#define condition_x imgLabels_row[c] > 0
if (condition_x){
if (condition_p){
//merge of two label
imgLabels_row[c] = set_union(P, imgLabels_row_prev[c - 1], imgLabels_row[c]);
}
if (condition_r){
//merge of two label
imgLabels_row[c] = set_union(P, imgLabels_row_prev[c + 1], imgLabels_row[c]);
}
if (condition_q){
//merge of two label
imgLabels_row[c] = set_union(P, imgLabels_row_prev[c], imgLabels_row[c]);
}
}
}
}
#undef condition_p
#undef condition_q
#undef condition_r
#undef condition_x
}
inline static
void mergeLabels4Connectivity(cv::Mat& imgLabels, LabelT *P, const int *chunksSizeAndLabels){
// Merge Mask
// +-+-+-+
// |-|q|-|
// +-+-+-+
// |x|
// +-+
const int w = imgLabels.cols, h = imgLabels.rows;
for (int r = chunksSizeAndLabels[0]; r < h; r = chunksSizeAndLabels[r]){
LabelT * const imgLabels_row = imgLabels.ptr<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
}
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 h = img.rows;
const int w = img.cols;
//A quick and dirty upper bound for the maximum 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
std::vector<int> chunksSizeAndLabels(roundUp(h, 2));
//Tree of labels
std::vector<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//First label is for background
//P[0] = 0;
cv::Range range2(0, divUp(h, 2));
const double nParallelStripes = std::max(1, std::min(h / 2, getNumThreads()*4));
LabelT nLabels = 1;
if (connectivity == 8){
//First scan
cv::parallel_for_(range2, FirstScan8Connectivity(img, imgLabels, P, chunksSizeAndLabels.data()), nParallelStripes);
//merge labels of different chunks
mergeLabels8Connectivity(imgLabels, P, chunksSizeAndLabels.data());
for (int i = 0; i < h; i = chunksSizeAndLabels[i]){
flattenL(P, stripeFirstLabel8Connectivity<int>(i, w), chunksSizeAndLabels[i + 1], nLabels);
}
}
else{
//First scan
cv::parallel_for_(range2, FirstScan4Connectivity(img, imgLabels, P, chunksSizeAndLabels.data()), nParallelStripes);
//merge labels of different chunks
mergeLabels4Connectivity(imgLabels, P, chunksSizeAndLabels.data());
for (int i = 0; i < h; i = chunksSizeAndLabels[i]){
flattenL(P, stripeFirstLabel4Connectivity<int>(i, w), chunksSizeAndLabels[i + 1], nLabels);
}
}
//Array for statistics dataof threads
std::vector<StatsOp> sopArray(h);
sop.init(nLabels);
//Second scan
cv::parallel_for_(range2, SecondScan(imgLabels, P, sop, sopArray.data(), nLabels), nParallelStripes);
StatsOp::mergeStats(imgLabels, sopArray.data(), sop, nLabels);
sop.finish();
return nLabels;
}
};//End struct LabelingWuParallel
//Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan Array-based Union Find) variant
//using decision trees
//Kesheng Wu et. al.
template<typename LabelT, typename PixelT, typename StatsOp = NoOp >
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 maximum 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
std::vector<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//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{
// 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<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();
return nLabels;
}//End function LabelingWu operator()
};//End struct LabelingWu
//Parallel implementation of BBDT (Block-Based with Decision Tree) algorithm, as described in "Two More Strategies to Speed
//Up Connected Components Labeling Algorithms"
//Federico Bolelli et. al.
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& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, img_.rows));
int r = range.start;
chunksSizeAndLabels_[r] = range.end;
LabelT label = stripeFirstLabel8Connectivity<LabelT>(r, imgLabels_.cols);
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 matter
// 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 {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
if (condition_p) {
if (condition_k) {
if (condition_d) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
if (condition_h) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
}
else {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
}
}
else {
if (condition_r) {
if (condition_j) {
if (condition_m) {
if (condition_h) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
if (condition_g) {
if (condition_b) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
if (condition_i) {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
else {
if (condition_h) {
if (condition_c) {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
else {
//Action_14: Merge labels of block P_, Q and S
imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]), imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
}
else {
if (condition_p) {
if (condition_k) {
if (condition_m) {
if (condition_h) {
if (condition_d) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_d) {
if (condition_g) {
if (condition_b) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_i) {
if (condition_g) {
if (condition_b) {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
else {
//Action_16: labels of block Q, R and S
imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_16: labels of block Q, R and S
imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
}
else {
if (condition_i) {
if (condition_d) {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
else {
//Action_16: labels of block Q, R and S
imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_h) {
if (condition_d) {
if (condition_c) {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
else {
//Action_15: Merge labels of block P_, R and S
imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_15: Merge labels of block P_, R and S
imgLabels_row[c] = set_union(P_, set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
}
else {
if (condition_h) {
if (condition_m) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
// ACTION_9 Merge labels of block P_ and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_i) {
if (condition_m) {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
}
}
}
else {
if (condition_h) {
if (condition_m) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
// ACTION_9 Merge labels of block P_ and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_i) {
if (condition_m) {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
}
}
}
}
else {
if (condition_j) {
if (condition_i) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
if (condition_h) {
if (condition_c) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
//Action_7: Merge labels of block P_ and Q
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]);
continue;
}
}
else {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
}
}
else {
if (condition_p) {
if (condition_k) {
if (condition_i) {
if (condition_d) {
//Action_5: Assign label of block R
imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
continue;
}
else {
// ACTION_10 Merge labels of block Q and R
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]);
continue;
}
}
else {
if (condition_h) {
if (condition_d) {
if (condition_c) {
//Action_5: Assign label of block R
imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
continue;
}
else {
//Action_8: Merge labels of block P_ and R
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]);
continue;
}
}
else {
//Action_8: Merge labels of block P_ and R
imgLabels_row[c] = set_union(P_, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]);
continue;
}
}
else {
//Action_5: Assign label of block R
imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
continue;
}
}
}
else {
if (condition_i) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
if (condition_h) {
//Action_3: Assign label of block P_
imgLabels_row[c] = imgLabels_row_prev_prev[c - 2];
continue;
}
else {
//Action_2: New label (the block has foreground pixels and is not connected to anything else)
imgLabels_row[c] = 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){}
void operator()(const cv::Range& range2) const CV_OVERRIDE
{
const Range range(range2.start * 2, std::min(range2.end * 2, img_.rows));
int r = range.start;
const int rowBegin = r;
const int rowEnd = range.end;
if (rowBegin > 0){
sopArray_[rowBegin].initElement(nLabels_);
sopArray_[rowBegin].setNextLoc(rowEnd); //_nextLoc = rowEnd;
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 h = img.rows;
const int w = img.cols;
//A quick and dirty upper bound for the maximum number of labels.
//Following formula comes from the fact that a 2x2 block in 8-connectivity case
//can never have more than 1 new label and 1 label for background.
//Worst case image example pattern:
//1 0 1 0 1...
//0 0 0 0 0...
//1 0 1 0 1...
//............
const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1;
//Array used to store info and labeled pixel by each thread.
//Different threads affect different memory location of chunksSizeAndLabels
const int chunksSizeAndLabelsSize = roundUp(h, 2);
std::vector<int> chunksSizeAndLabels(chunksSizeAndLabelsSize);
//Tree of labels
std::vector<LabelT> P(Plength, 0);
//First label is for background
//P[0] = 0;
cv::Range range2(0, divUp(h, 2));
const double nParallelStripes = std::max(1, std::min(h / 2, getNumThreads()*4));
//First scan
cv::parallel_for_(range2, FirstScan(img, imgLabels, P.data(), chunksSizeAndLabels.data()), nParallelStripes);
//merge labels of different chunks
mergeLabels(img, imgLabels, P.data(), chunksSizeAndLabels.data());
LabelT nLabels = 1;
for (int i = 0; i < h; i = chunksSizeAndLabels[i]){
CV_DbgAssert(i + 1 < chunksSizeAndLabelsSize);
flattenL(P.data(), stripeFirstLabel8Connectivity<LabelT>(i, w), chunksSizeAndLabels[i + 1], nLabels);
}
//Array for statistics data
std::vector<StatsOp> sopArray(h);
sop.init(nLabels);
//Second scan
cv::parallel_for_(range2, SecondScan(img, imgLabels, P.data(), sop, sopArray.data(), nLabels), nParallelStripes);
StatsOp::mergeStats(imgLabels, sopArray.data(), sop, nLabels);
sop.finish();
return nLabels;
}
};//End struct LabelingGranaParallel
//Implementation of BBDT (Block-Based with Decision Tree) algorithm, as described in "Optimized Block-based Connected
//Components Labeling with Decision Trees" (only for 8-connectivity)
//Costantino Grana 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);
const int h = img.rows;
const int w = img.cols;
//A quick and dirty upper bound for the maximum number of labels.
//Following formula comes from the fact that a 2x2 block in 8-connectivity case
//can never have more than 1 new label and 1 label for background.
//Worst case image example pattern:
//1 0 1 0 1...
//0 0 0 0 0...
//1 0 1 0 1...
//............
const size_t Plength = size_t(((h + 1) / 2) * size_t((w + 1) / 2)) + 1;
std::vector<LabelT> P_(Plength, 0);
LabelT *P = P_.data();
//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 matter
// 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;
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
}
else {
if (condition_h) {
if (condition_m) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
// ACTION_9 Merge labels of block P and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_i) {
if (condition_m) {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
}
}
}
else {
if (condition_h) {
if (condition_m) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
// ACTION_9 Merge labels of block P and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_i) {
if (condition_m) {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
}
}
}
}
else {
if (condition_j) {
if (condition_i) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
if (condition_h) {
if (condition_c) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
//Action_7: Merge labels of block P and Q
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c]);
continue;
}
}
else {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
}
}
else {
if (condition_p) {
if (condition_k) {
if (condition_i) {
if (condition_d) {
//Action_5: Assign label of block R
imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
continue;
}
else {
// ACTION_10 Merge labels of block Q and R
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]);
continue;
}
}
else {
if (condition_h) {
if (condition_d) {
if (condition_c) {
//Action_5: Assign label of block R
imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
continue;
}
else {
//Action_8: Merge labels of block P and R
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]);
continue;
}
}
else {
//Action_8: Merge labels of block P and R
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c - 2], imgLabels_row_prev_prev[c + 2]);
continue;
}
}
else {
//Action_5: Assign label of block R
imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
continue;
}
}
}
else {
if (condition_i) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
if (condition_h) {
//Action_3: Assign label of block P
imgLabels_row[c] = imgLabels_row_prev_prev[c - 2];
continue;
}
else {
//Action_2: New label (the block has foreground pixels and is not connected to anything else)
imgLabels_row[c] = lunique;
P[lunique] = lunique;
lunique = lunique + 1;
continue;
}
}
}
}
else {
if (condition_i) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
if (condition_h) {
//Action_3: Assign label of block P
imgLabels_row[c] = imgLabels_row_prev_prev[c - 2];
continue;
}
else {
//Action_2: New label (the block has foreground pixels and is not connected to anything else)
imgLabels_row[c] = lunique;
P[lunique] = lunique;
lunique = lunique + 1;
continue;
}
}
}
}
}
}
}
else {
if (condition_s) {
if (condition_p) {
if (condition_n) {
if (condition_j) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
if (condition_h) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
if (condition_k) {
if (condition_d) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
if (condition_h) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
}
}
else {
if (condition_r) {
if (condition_j) {
if (condition_m) {
if (condition_h) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
if (condition_g) {
if (condition_b) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_k) {
if (condition_d) {
if (condition_m) {
if (condition_h) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
if (condition_g) {
if (condition_b) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_c) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
else {
if (condition_i) {
if (condition_m) {
if (condition_h) {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
else {
if (condition_g) {
if (condition_b) {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
else {
//Action_16: labels of block Q, R and S
imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_16: labels of block Q, R and S
imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_16: labels of block Q, R and S
imgLabels_row[c] = set_union(P, set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]), imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_12: Merge labels of block R and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c + 2], imgLabels_row[c - 2]);
continue;
}
}
}
else {
if (condition_i) {
if (condition_m) {
if (condition_h) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
if (condition_g) {
if (condition_b) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
}
else {
//Action_11: Merge labels of block Q and S
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row[c - 2]);
continue;
}
}
else {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
}
}
}
else {
if (condition_j) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
if (condition_k) {
if (condition_i) {
if (condition_d) {
//Action_5: Assign label of block R
imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
continue;
}
else {
// ACTION_10 Merge labels of block Q and R
imgLabels_row[c] = set_union(P, imgLabels_row_prev_prev[c], imgLabels_row_prev_prev[c + 2]);
continue;
}
}
else {
//Action_5: Assign label of block R
imgLabels_row[c] = imgLabels_row_prev_prev[c + 2];
continue;
}
}
else {
if (condition_i) {
//Action_4: Assign label of block Q
imgLabels_row[c] = imgLabels_row_prev_prev[c];
continue;
}
else {
//Action_2: New label (the block has foreground pixels and is not connected to anything else)
imgLabels_row[c] = lunique;
P[lunique] = lunique;
lunique = lunique + 1;
continue;
}
}
}
}
}
}
else {
if (condition_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 {
//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_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);
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<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]);
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 (int r = 0; r < imgLabels.rows; 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]);
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<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]);
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<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]);
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
}
sop.finish();
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<typename StatsOp>
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_SPAGHETTI || ccltype == CCL_BBDT || ccltype == CCL_SAUF || ccltype == CCL_BOLELLI || ccltype == CCL_GRANA || ccltype == CCL_WU || ccltype == CCL_DEFAULT);
int lDepth = L.depth();
int iDepth = I.depth();
const char *currentParallelFramework = cv::currentParallelFramework();
const int nThreads = cv::getNumThreads();
CV_Assert(iDepth == CV_8U || iDepth == CV_8S);
//Run parallel labeling only if the rows of the image are at least twice the number of available threads
const bool is_parallel = currentParallelFramework != NULL && nThreads > 1 && L.rows / nThreads >= 2;
if (ccltype == CCL_SAUF || ccltype == CCL_WU || connectivity == 4){
// SAUF algorithm is used
using connectedcomponents::LabelingWu;
using connectedcomponents::LabelingWuParallel;
//warn if L's depth is not sufficient?
if (lDepth == CV_8U){
//Not supported yet
}
else if (lDepth == CV_16U){
return (int)LabelingWu<ushort, uchar, StatsOp>()(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<T> typechecks in debug mode
if (!is_parallel)
return (int)LabelingWu<int, uchar, StatsOp>()(I, L, connectivity, sop);
else
return (int)LabelingWuParallel<int, uchar, StatsOp>()(I, L, connectivity, sop);
}
}
else if ((ccltype == CCL_BBDT || ccltype == CCL_GRANA || ccltype == CCL_DEFAULT) && connectivity == 8){
// BBDT algorithm is used
using connectedcomponents::LabelingGrana;
using connectedcomponents::LabelingGranaParallel;
//warn if L's depth is not sufficient?
if (lDepth == CV_8U){
//Not supported yet
}
else if (lDepth == CV_16U){
return (int)LabelingGrana<ushort, uchar, StatsOp>()(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<T> typechecks in debug mode
if (!is_parallel)
return (int)LabelingGrana<int, uchar, StatsOp>()(I, L, connectivity, sop);
else
return (int)LabelingGranaParallel<int, uchar, StatsOp>()(I, L, connectivity, sop);
}
}
else if ((ccltype == CCL_SPAGHETTI || ccltype == CCL_BOLELLI) && connectivity == 8) {
// Spaghetti algorithm is used
using connectedcomponents::LabelingBolelli;
//using connectedcomponents::LabelingBolelliParallel; // Not implemented
//warn if L's depth is not sufficient?
if (lDepth == CV_8U) {
//Not supported yet
}
else if (lDepth == CV_16U) {
return (int)LabelingBolelli<ushort, uchar, StatsOp>()(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<T> typechecks in debug mode
return (int)LabelingBolelli<int, uchar, StatsOp>()(I, L, connectivity, sop);
}
}
CV_Error(CV_StsUnsupportedFormat, "unsupported label/image type");
}
}
// Simple wrapper to ensure binary and source compatibility (ABI)
int cv::connectedComponents(InputArray img_, OutputArray _labels, int connectivity, int ltype){
return cv::connectedComponents(img_, _labels, connectivity, ltype, CCL_DEFAULT);
}
int cv::connectedComponents(InputArray img_, OutputArray _labels, int connectivity, int ltype, int ccltype){
const cv::Mat img = img_.getMat();
_labels.create(img.size(), CV_MAT_DEPTH(ltype));
cv::Mat labels = _labels.getMat();
connectedcomponents::NoOp sop;
if (ltype == CV_16U){
return connectedComponents_sub1(img, labels, connectivity, ccltype, sop);
}
else if (ltype == CV_32S){
return connectedComponents_sub1(img, labels, connectivity, ccltype, sop);
}
else{
CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s");
}
}
// Simple wrapper to ensure binary and source compatibility (ABI)
int cv::connectedComponentsWithStats(InputArray img_, OutputArray _labels, OutputArray statsv,
OutputArray centroids, int connectivity, int ltype)
{
return cv::connectedComponentsWithStats(img_, _labels, statsv, centroids, connectivity, ltype, CCL_DEFAULT);
}
int cv::connectedComponentsWithStats(InputArray img_, OutputArray _labels, OutputArray statsv,
OutputArray centroids, int connectivity, int ltype, int ccltype)
{
const cv::Mat img = img_.getMat();
_labels.create(img.size(), CV_MAT_DEPTH(ltype));
cv::Mat labels = _labels.getMat();
connectedcomponents::CCStatsOp sop(statsv, centroids);
if (ltype == CV_16U){
return connectedComponents_sub1(img, labels, connectivity, ccltype, sop);
}
else if (ltype == CV_32S){
return connectedComponents_sub1(img, labels, connectivity, ccltype, sop);
}
else{
CV_Error(CV_StsUnsupportedFormat, "the type of labels must be 16u or 32s");
return 0;
}
}