Merge pull request #19631 from prittt:sota-ccl

* Add Spaghetti algorithm for CCL

* Add stat tests for new and old algorithms

* Switch license header to short version
This commit is contained in:
Federico Bolelli 2021-02-27 18:27:24 +01:00 committed by GitHub
parent 1a855fed8f
commit 9695165877
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 3435 additions and 117 deletions

View File

@ -110,6 +110,29 @@
year = {2010},
url = {http://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf}
}
@inproceedings{Bolelli2017,
title = {{Two More Strategies to Speed Up Connected Components Labeling Algorithms}},
author = {Bolelli, Federico and Cancilla, Michele and Grana, Costantino},
year = 2017,
booktitle = {Image Analysis and Processing - ICIAP 2017},
publisher = {Springer},
volume = 10485,
pages = {48--58},
doi = {10.1007/978-3-319-68548-9_5},
isbn = {978-3-319-68547-2}
}
@article{Bolelli2019,
title = {{Spaghetti Labeling: Directed Acyclic Graphs for Block-Based Connected Components Labeling}},
author = {Bolelli, Federico and Allegretti, Stefano and Baraldi, Lorenzo and Grana, Costantino},
year = 2019,
journal = {IEEE Transactions on Image Processing},
publisher = {IEEE},
volume = 29,
number = 1,
pages = {1999--2012},
doi = {10.1109/TIP.2019.2946979},
issn = {1057-7149}
}
@article{Borgefors86,
author = {Borgefors, Gunilla},
title = {Distance transformations in digital images},
@ -420,6 +443,16 @@
volume = {51},
pages = {378-384}
}
@article{Grana2010,
title = {{Optimized Block-Based Connected Components Labeling With Decision Trees}},
author = {Grana, Costantino and Borghesani, Daniele and Cucchiara, Rita},
year = 2010,
journal = {IEEE Transactions on Image Processing},
volume = 19,
number = 6,
pages = {1596--1609},
doi = {10.1109/TIP.2010.2044963}
}
@article{taubin1991,
abstract = {The author addresses the problem of parametric representation and estimation of complex planar curves in 2-D surfaces in 3-D, and nonplanar space curves in 3-D. Curves and surfaces can be defined either parametrically or implicitly, with the latter representation used here. A planar curve is the set of zeros of a smooth function of two variables <e1>x</e1>-<e1>y</e1>, a surface is the set of zeros of a smooth function of three variables <e1>x</e1>-<e1>y</e1>-<e1>z</e1>, and a space curve is the intersection of two surfaces, which are the set of zeros of two linearly independent smooth functions of three variables <e1>x</e1>-<e1>y</e1>-<e1>z</e1> For example, the surface of a complex object in 3-D can be represented as a subset of a single implicit surface, with similar results for planar and space curves. It is shown how this unified representation can be used for object recognition, object position estimation, and segmentation of objects into meaningful subobjects, that is, the detection of `interest regions' that are more complex than high curvature regions and, hence, more useful as features for object recognition},
author = {Taubin, Gabriel},

View File

@ -406,9 +406,13 @@ enum ConnectedComponentsTypes {
//! connected components algorithm
enum ConnectedComponentsAlgorithmsTypes {
CCL_WU = 0, //!< SAUF @cite Wu2009 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity
CCL_DEFAULT = -1, //!< BBDT algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity
CCL_GRANA = 1 //!< BBDT algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity
CCL_DEFAULT = -1, //!< BBDT @cite Grana2010 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for both BBDT and SAUF.
CCL_WU = 0, //!< SAUF @cite Wu2009 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for SAUF.
CCL_GRANA = 1, //!< BBDT @cite Grana2010 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity. The parallel implementation described in @cite Bolelli2017 is available for both BBDT and SAUF.
CCL_BOLELLI = 2, //!< Spaghetti @cite Bolelli2019 algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity.
CCL_SAUF = 3, //!< Same as CCL_WU. It is preferable to use the flag with the name of the algorithm (CCL_SAUF) rather than the one with the name of the first author (CCL_WU).
CCL_BBDT = 4, //!< Same as CCL_GRANA. It is preferable to use the flag with the name of the algorithm (CCL_BBDT) rather than the one with the name of the first author (CCL_GRANA).
CCL_SPAGHETTI = 5, //!< Same as CCL_BOLELLI. It is preferable to use the flag with the name of the algorithm (CCL_SPAGHETTI) rather than the one with the name of the first author (CCL_BOLELLI).
};
//! mode of the contour retrieval algorithm

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,218 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// 2021 Federico Bolelli <federico.bolelli@unimore.it>
// 2021 Stefano Allegretti <stefano.allegretti@unimore.it>
// 2021 Costantino Grana <costantino.grana@unimore.it>
//
// This file has been automatically generated using GRAPHGEN (https://github.com/prittt/GRAPHGEN)
// and taken from the YACCLAB repository (https://github.com/prittt/YACCLAB).
fl_tree_0: if ((c+=2) >= w - 2) { if (c > w - 2) { goto fl_break_0_0; } else { goto fl_break_1_0; } }
if (CONDITION_O) {
NODE_253:
if (CONDITION_P) {
ACTION_2
goto fl_tree_1;
}
else {
ACTION_2
goto fl_tree_2;
}
}
else {
if (CONDITION_S){
goto NODE_253;
}
else {
NODE_255:
if (CONDITION_P) {
ACTION_2
goto fl_tree_1;
}
else {
if (CONDITION_T) {
ACTION_2
goto fl_tree_1;
}
else {
ACTION_1
goto fl_tree_0;
}
}
}
}
fl_tree_1: if ((c+=2) >= w - 2) { if (c > w - 2) { goto fl_break_0_1; } else { goto fl_break_1_1; } }
if (CONDITION_O) {
NODE_257:
if (CONDITION_P) {
ACTION_6
goto fl_tree_1;
}
else {
ACTION_6
goto fl_tree_2;
}
}
else {
if (CONDITION_S){
goto NODE_257;
}
else{
goto NODE_255;
}
}
fl_tree_2: if ((c+=2) >= w - 2) { if (c > w - 2) { goto fl_break_0_2; } else { goto fl_break_1_2; } }
if (CONDITION_O) {
if (CONDITION_R){
goto NODE_257;
}
else{
goto NODE_253;
}
}
else {
if (CONDITION_S) {
if (CONDITION_P) {
if (CONDITION_R) {
ACTION_6
goto fl_tree_1;
}
else {
ACTION_2
goto fl_tree_1;
}
}
else {
if (CONDITION_R) {
ACTION_6
goto fl_tree_2;
}
else {
ACTION_2
goto fl_tree_2;
}
}
}
else{
goto NODE_255;
}
}
fl_break_0_0:
if (CONDITION_O) {
ACTION_2
}
else {
if (CONDITION_S) {
ACTION_2
}
else {
ACTION_1
}
}
goto end_fl;
fl_break_0_1:
if (CONDITION_O) {
ACTION_6
}
else {
if (CONDITION_S) {
ACTION_6
}
else {
ACTION_1
}
}
goto end_fl;
fl_break_0_2:
if (CONDITION_O) {
NODE_266:
if (CONDITION_R) {
ACTION_6
}
else {
ACTION_2
}
}
else {
if (CONDITION_S){
goto NODE_266;
}
else {
ACTION_1
}
}
goto end_fl;
fl_break_1_0:
if (CONDITION_O) {
NODE_268:
if (CONDITION_P) {
ACTION_2
}
else {
ACTION_2
}
}
else {
if (CONDITION_S){
goto NODE_268;
}
else {
NODE_270:
if (CONDITION_P) {
ACTION_2
}
else {
if (CONDITION_T) {
ACTION_2
}
else {
ACTION_1
}
}
}
}
goto end_fl;
fl_break_1_1:
if (CONDITION_O) {
NODE_272:
if (CONDITION_P) {
ACTION_6
}
else {
ACTION_6
}
}
else {
if (CONDITION_S){
goto NODE_272;
}
else{
goto NODE_270;
}
}
goto end_fl;
fl_break_1_2:
if (CONDITION_O) {
if (CONDITION_R){
goto NODE_272;
}
else{
goto NODE_268;
}
}
else {
if (CONDITION_S) {
if (CONDITION_P){
goto NODE_266;
}
else{
goto NODE_266;
}
}
else{
goto NODE_270;
}
}
goto end_fl;
end_fl:;

View File

@ -0,0 +1,731 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// 2021 Federico Bolelli <federico.bolelli@unimore.it>
// 2021 Stefano Allegretti <stefano.allegretti@unimore.it>
// 2021 Costantino Grana <costantino.grana@unimore.it>
//
// This file has been automatically generated using GRAPHGEN (https://github.com/prittt/GRAPHGEN)
// and taken from the YACCLAB repository (https://github.com/prittt/YACCLAB).
ll_tree_0: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_0; } else { goto ll_break_1_0; } }
if (CONDITION_O) {
if (CONDITION_J) {
ACTION_4
goto ll_tree_6;
}
else {
if (CONDITION_P) {
NODE_277:
if (CONDITION_K) {
if (CONDITION_I) {
NODE_279:
if (CONDITION_D) {
ACTION_5
goto ll_tree_4;
}
else {
ACTION_10
goto ll_tree_4;
}
}
else {
ACTION_5
goto ll_tree_4;
}
}
else {
if (CONDITION_I) {
ACTION_4
goto ll_tree_3;
}
else {
ACTION_2
goto ll_tree_2;
}
}
}
else {
if (CONDITION_I) {
ACTION_4
goto ll_tree_0;
}
else {
ACTION_2
goto ll_tree_0;
}
}
}
}
else {
NODE_282:
if (CONDITION_P) {
if (CONDITION_J) {
ACTION_4
goto ll_tree_5;
}
else{
goto NODE_277;
}
}
else {
ACTION_1
goto ll_tree_1;
}
}
ll_tree_1: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_1; } else { goto ll_break_1_1; } }
if (CONDITION_O) {
if (CONDITION_J) {
if (CONDITION_I) {
ACTION_4
goto ll_tree_6;
}
else {
if (CONDITION_H) {
NODE_287:
if (CONDITION_C) {
ACTION_4
goto ll_tree_6;
}
else {
ACTION_7
goto ll_tree_6;
}
}
else {
ACTION_4
goto ll_tree_6;
}
}
}
else {
if (CONDITION_P) {
if (CONDITION_K) {
if (CONDITION_I){
goto NODE_279;
}
else {
if (CONDITION_H) {
NODE_292:
if (CONDITION_D) {
if (CONDITION_C) {
ACTION_5
goto ll_tree_4;
}
else {
ACTION_8
goto ll_tree_4;
}
}
else {
ACTION_8
goto ll_tree_4;
}
}
else {
ACTION_5
goto ll_tree_4;
}
}
}
else {
if (CONDITION_I) {
ACTION_4
goto ll_tree_3;
}
else {
if (CONDITION_H) {
ACTION_3
goto ll_tree_2;
}
else {
ACTION_2
goto ll_tree_2;
}
}
}
}
else {
if (CONDITION_I) {
ACTION_4
goto ll_tree_0;
}
else {
if (CONDITION_H) {
ACTION_3
goto ll_tree_0;
}
else {
ACTION_2
goto ll_tree_0;
}
}
}
}
}
else{
goto NODE_282;
}
ll_tree_2: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_2; } }
if (CONDITION_O) {
if (CONDITION_J) {
ACTION_11
goto ll_tree_6;
}
else {
if (CONDITION_P) {
if (CONDITION_K) {
ACTION_12
goto ll_tree_4;
}
else {
ACTION_6
goto ll_tree_7;
}
}
else {
ACTION_6
goto ll_tree_0;
}
}
}
else {
NODE_301:
if (CONDITION_P) {
if (CONDITION_J) {
ACTION_4
goto ll_tree_5;
}
else {
if (CONDITION_K) {
ACTION_5
goto ll_tree_4;
}
else {
ACTION_2
goto ll_tree_2;
}
}
}
else {
ACTION_1
goto ll_tree_1;
}
}
ll_tree_3: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_3; } }
if (CONDITION_O) {
if (CONDITION_J) {
if (CONDITION_C) {
NODE_306:
if (CONDITION_B) {
ACTION_4
goto ll_tree_6;
}
else {
ACTION_7
goto ll_tree_6;
}
}
else {
ACTION_11
goto ll_tree_6;
}
}
else {
if (CONDITION_P) {
if (CONDITION_K) {
if (CONDITION_D) {
if (CONDITION_C) {
NODE_311:
if (CONDITION_B) {
ACTION_5
goto ll_tree_4;
}
else {
ACTION_12
goto ll_tree_4;
}
}
else {
ACTION_12
goto ll_tree_4;
}
}
else {
ACTION_12
goto ll_tree_4;
}
}
else {
ACTION_6
goto ll_tree_7;
}
}
else {
ACTION_6
goto ll_tree_0;
}
}
}
else{
goto NODE_301;
}
ll_tree_4: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_4; } }
if (CONDITION_O) {
if (CONDITION_J) {
ACTION_4
goto ll_tree_6;
}
else {
if (CONDITION_P) {
if (CONDITION_K) {
if (CONDITION_D) {
ACTION_5
goto ll_tree_4;
}
else {
ACTION_12
goto ll_tree_4;
}
}
else {
ACTION_6
goto ll_tree_7;
}
}
else {
ACTION_6
goto ll_tree_0;
}
}
}
else {
if (CONDITION_P) {
if (CONDITION_J) {
ACTION_4
goto ll_tree_5;
}
else {
if (CONDITION_K){
goto NODE_279;
}
else {
ACTION_4
goto ll_tree_3;
}
}
}
else {
ACTION_1
goto ll_tree_1;
}
}
ll_tree_5: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_5; } }
if (CONDITION_O) {
NODE_319:
if (CONDITION_J) {
if (CONDITION_I) {
ACTION_4
goto ll_tree_6;
}
else {
if (CONDITION_C) {
ACTION_4
goto ll_tree_6;
}
else {
ACTION_11
goto ll_tree_6;
}
}
}
else {
if (CONDITION_P) {
if (CONDITION_K) {
if (CONDITION_D) {
if (CONDITION_I) {
ACTION_5
goto ll_tree_4;
}
else {
if (CONDITION_C) {
ACTION_5
goto ll_tree_4;
}
else {
ACTION_12
goto ll_tree_4;
}
}
}
else {
ACTION_12
goto ll_tree_4;
}
}
else {
ACTION_6
goto ll_tree_7;
}
}
else {
ACTION_6
goto ll_tree_0;
}
}
}
else{
goto NODE_282;
}
ll_tree_6: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_3; } else { goto ll_break_1_6; } }
if (CONDITION_O) {
if (CONDITION_N){
goto NODE_319;
}
else {
if (CONDITION_J) {
if (CONDITION_I) {
ACTION_4
goto ll_tree_6;
}
else{
goto NODE_287;
}
}
else {
if (CONDITION_P) {
if (CONDITION_K) {
if (CONDITION_I){
goto NODE_279;
}
else{
goto NODE_292;
}
}
else {
if (CONDITION_I) {
ACTION_4
goto ll_tree_3;
}
else {
ACTION_3
goto ll_tree_2;
}
}
}
else {
if (CONDITION_I) {
ACTION_4
goto ll_tree_0;
}
else {
ACTION_3
goto ll_tree_0;
}
}
}
}
}
else{
goto NODE_282;
}
ll_tree_7: if ((c+=2) >= w - 2) { if (c > w - 2) { goto ll_break_0_2; } else { goto ll_break_1_7; } }
if (CONDITION_O) {
if (CONDITION_J) {
if (CONDITION_C) {
if (CONDITION_G){
goto NODE_306;
}
else {
ACTION_11
goto ll_tree_6;
}
}
else {
ACTION_11
goto ll_tree_6;
}
}
else {
if (CONDITION_P) {
if (CONDITION_K) {
if (CONDITION_D) {
if (CONDITION_C) {
if (CONDITION_G){
goto NODE_311;
}
else {
ACTION_12
goto ll_tree_4;
}
}
else {
ACTION_12
goto ll_tree_4;
}
}
else {
ACTION_12
goto ll_tree_4;
}
}
else {
ACTION_6
goto ll_tree_7;
}
}
else {
ACTION_6
goto ll_tree_0;
}
}
}
else{
goto NODE_301;
}
ll_break_0_0:
if (CONDITION_O) {
NODE_343:
if (CONDITION_I) {
ACTION_4
}
else {
ACTION_2
}
}
else {
ACTION_1
}
goto ll_end;
ll_break_0_1:
if (CONDITION_O) {
NODE_344:
if (CONDITION_I) {
ACTION_4
}
else {
if (CONDITION_H) {
ACTION_3
}
else {
ACTION_2
}
}
}
else {
ACTION_1
}
goto ll_end;
ll_break_0_2:
if (CONDITION_O) {
ACTION_6
}
else {
ACTION_1
}
goto ll_end;
ll_break_0_3:
if (CONDITION_O) {
if (CONDITION_N) {
ACTION_6
}
else {
NODE_347:
if (CONDITION_I) {
ACTION_4
}
else {
ACTION_3
}
}
}
else {
ACTION_1
}
goto ll_end;
ll_break_1_0:
if (CONDITION_O) {
NODE_348:
if (CONDITION_J) {
ACTION_4
}
else{
goto NODE_343;
}
}
else {
NODE_349:
if (CONDITION_P){
goto NODE_348;
}
else {
ACTION_1
}
}
goto ll_end;
ll_break_1_1:
if (CONDITION_O) {
if (CONDITION_J) {
if (CONDITION_I) {
ACTION_4
}
else {
if (CONDITION_H) {
NODE_353:
if (CONDITION_C) {
ACTION_4
}
else {
ACTION_7
}
}
else {
ACTION_4
}
}
}
else{
goto NODE_344;
}
}
else{
goto NODE_349;
}
goto ll_end;
ll_break_1_2:
if (CONDITION_O) {
if (CONDITION_J) {
ACTION_11
}
else {
ACTION_6
}
}
else {
NODE_355:
if (CONDITION_P) {
if (CONDITION_J) {
ACTION_4
}
else {
ACTION_2
}
}
else {
ACTION_1
}
}
goto ll_end;
ll_break_1_3:
if (CONDITION_O) {
if (CONDITION_J) {
if (CONDITION_C) {
NODE_359:
if (CONDITION_B) {
ACTION_4
}
else {
ACTION_7
}
}
else {
ACTION_11
}
}
else {
ACTION_6
}
}
else{
goto NODE_355;
}
goto ll_end;
ll_break_1_4:
if (CONDITION_O) {
if (CONDITION_J) {
ACTION_4
}
else {
ACTION_6
}
}
else {
if (CONDITION_P) {
ACTION_4
}
else {
ACTION_1
}
}
goto ll_end;
ll_break_1_5:
if (CONDITION_O) {
NODE_362:
if (CONDITION_J) {
if (CONDITION_I) {
ACTION_4
}
else {
if (CONDITION_C) {
ACTION_4
}
else {
ACTION_11
}
}
}
else {
ACTION_6
}
}
else{
goto NODE_349;
}
goto ll_end;
ll_break_1_6:
if (CONDITION_O) {
if (CONDITION_N){
goto NODE_362;
}
else {
if (CONDITION_J) {
if (CONDITION_I) {
ACTION_4
}
else{
goto NODE_353;
}
}
else{
goto NODE_347;
}
}
}
else{
goto NODE_349;
}
goto ll_end;
ll_break_1_7:
if (CONDITION_O) {
if (CONDITION_J) {
if (CONDITION_C) {
if (CONDITION_G){
goto NODE_359;
}
else {
ACTION_11
}
}
else {
ACTION_11
}
}
else {
ACTION_6
}
}
else{
goto NODE_355;
}
goto ll_end;
ll_end:;

View File

@ -0,0 +1,95 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// 2021 Federico Bolelli <federico.bolelli@unimore.it>
// 2021 Stefano Allegretti <stefano.allegretti@unimore.it>
// 2021 Costantino Grana <costantino.grana@unimore.it>
//
// This file has been automatically generated using GRAPHGEN (https://github.com/prittt/GRAPHGEN)
// and taken from the YACCLAB repository (https://github.com/prittt/YACCLAB).
sl_tree_0: if ((c+=2) >= w - 2) { if (c > w - 2) { goto sl_break_0_0; } else { goto sl_break_1_0; } }
if (CONDITION_O) {
if (CONDITION_P) {
ACTION_2
goto sl_tree_1;
}
else {
ACTION_2
goto sl_tree_0;
}
}
else {
NODE_372:
if (CONDITION_P) {
ACTION_2
goto sl_tree_1;
}
else {
ACTION_1
goto sl_tree_0;
}
}
sl_tree_1: if ((c+=2) >= w - 2) { if (c > w - 2) { goto sl_break_0_1; } else { goto sl_break_1_1; } }
if (CONDITION_O) {
if (CONDITION_P) {
ACTION_6
goto sl_tree_1;
}
else {
ACTION_6
goto sl_tree_0;
}
}
else{
goto NODE_372;
}
sl_break_0_0:
if (CONDITION_O) {
ACTION_2
}
else {
ACTION_1
}
goto end_sl;
sl_break_0_1:
if (CONDITION_O) {
ACTION_6
}
else {
ACTION_1
}
goto end_sl;
sl_break_1_0:
if (CONDITION_O) {
if (CONDITION_P) {
ACTION_2
}
else {
ACTION_2
}
}
else {
NODE_375:
if (CONDITION_P) {
ACTION_2
}
else {
ACTION_1
}
}
goto end_sl;
sl_break_1_1:
if (CONDITION_O) {
if (CONDITION_P) {
ACTION_6
}
else {
ACTION_6
}
}
else{
goto NODE_375;
}
goto end_sl;
end_sl:;

View File

@ -38,11 +38,12 @@
// the use of this software, even if advised of the possibility of such damage.
//
// 2011 Jason Newton <nevion@gmail.com>
// 2016 Costantino Grama <costantino.grana@unimore.it>
// 2016 Federico Bolelli <federico.bolelli@hotmail.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"
@ -286,10 +287,366 @@ namespace cv{
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);
//Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant
//using decision trees
//Kesheng Wu, et al
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{
@ -332,11 +689,11 @@ namespace cv{
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
#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){
@ -390,11 +747,11 @@ namespace cv{
//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
#undef condition_p
#undef condition_q
#undef condition_r
#undef condition_s
#undef condition_x
};
class FirstScan4Connectivity : public cv::ParallelLoopBody{
@ -435,9 +792,9 @@ namespace cv{
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
#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){
@ -471,9 +828,9 @@ namespace cv{
//write in the following memory location
chunksSizeAndLabels_[startR + 1] = label - firstLabel;
}
#undef condition_q
#undef condition_s
#undef condition_x
#undef condition_q
#undef condition_s
#undef condition_x
};
class SecondScan : public cv::ParallelLoopBody{
@ -541,10 +898,10 @@ namespace cv{
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
#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){
@ -562,10 +919,10 @@ namespace cv{
}
}
}
#undef condition_p
#undef condition_q
#undef condition_r
#undef condition_x
#undef condition_p
#undef condition_q
#undef condition_r
#undef condition_x
}
inline static
@ -586,8 +943,8 @@ namespace cv{
for (int c = 0; c < w; ++c){
#define condition_q imgLabels_row_prev[c] > 0
#define condition_x imgLabels_row[c] > 0
#define condition_q imgLabels_row_prev[c] > 0
#define condition_x imgLabels_row[c] > 0
if (condition_x){
if (condition_q){
@ -597,8 +954,8 @@ namespace cv{
}
}
}
#undef condition_q
#undef condition_x
#undef condition_q
#undef condition_x
}
LabelT operator()(const cv::Mat& img, cv::Mat& imgLabels, int connectivity, StatsOp& sop){
@ -671,10 +1028,9 @@ namespace cv{
}
};//End struct LabelingWuParallel
//Based on "Two Strategies to Speed up Connected Components Algorithms", the SAUF (Scan array union find) variant
//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
//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){
@ -712,11 +1068,11 @@ namespace cv{
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
#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){
@ -770,11 +1126,11 @@ namespace cv{
}
}
}
#undef condition_p
#undef condition_q
#undef condition_r
#undef condition_s
#undef condition_x
#undef condition_p
#undef condition_q
#undef condition_r
#undef condition_s
#undef condition_x
}
else{
for (int r = 0; r < h; ++r){
@ -784,9 +1140,9 @@ namespace cv{
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
#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){
@ -818,9 +1174,9 @@ namespace cv{
}
}
}
#undef condition_q
#undef condition_s
#undef condition_x
#undef condition_q
#undef condition_s
#undef condition_x
}
//analysis
@ -842,9 +1198,9 @@ namespace cv{
}//End function LabelingWu operator()
};//End struct LabelingWu
// Based on "Optimized Block-based Connected Components Labeling with Decision Trees", Costantino Grana et al
// Only for 8-connectivity
//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{
@ -901,31 +1257,31 @@ namespace cv{
// +---+---+
// Pixels a, f, l, q are not needed, since we need to understand the
// the connectivity between these blocks and those pixels only metter
// 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_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_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_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
#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.
@ -1903,15 +2259,15 @@ namespace cv{
//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
#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{
@ -2511,12 +2867,12 @@ namespace cv{
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
#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){
@ -2603,8 +2959,9 @@ namespace cv{
}
};//End struct LabelingGranaParallel
// Based on "Optimized Block-based Connected Components Labeling with Decision Trees", Costantino Grana et al
// Only for 8-connectivity
//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){
@ -2658,30 +3015,30 @@ namespace cv{
// +---+---+
// Pixels a, f, l, q are not needed, since we need to understand the
// the connectivity between these blocks and those pixels only metter
// 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_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_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_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
#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.
@ -3948,7 +4305,7 @@ namespace cv{
int connectedComponents_sub1(const cv::Mat& I, cv::Mat& L, int connectivity, int ccltype, StatsOp& sop){
CV_Assert(L.channels() == 1 && I.channels() == 1);
CV_Assert(connectivity == 8 || connectivity == 4);
CV_Assert(ccltype == CCL_GRANA || ccltype == CCL_WU || ccltype == CCL_DEFAULT);
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();
@ -3960,8 +4317,8 @@ namespace cv{
//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_WU || connectivity == 4){
// Wu algorithm is used
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?
@ -3980,8 +4337,8 @@ namespace cv{
return (int)LabelingWuParallel<int, uchar, StatsOp>()(I, L, connectivity, sop);
}
}
else if ((ccltype == CCL_GRANA || ccltype == CCL_DEFAULT) && connectivity == 8){
// Grana algorithm is used
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?
@ -4000,6 +4357,23 @@ namespace cv{
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");
}

View File

@ -74,11 +74,10 @@ void normalizeLabels(Mat1i& imgLabels, int iNumLabels) {
}
}
void CV_ConnectedComponentsTest::run( int /* start_from */)
{
int ccltype[] = { cv::CCL_WU, cv::CCL_DEFAULT, cv::CCL_GRANA };
int ccltype[] = { cv::CCL_DEFAULT, cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI };
string exp_path = string(ts->get_data_path()) + "connectedcomponents/ccomp_exp.png";
Mat exp = imread(exp_path, 0);
@ -150,7 +149,6 @@ TEST(Imgproc_ConnectedComponents, grana_buffer_overflow)
EXPECT_EQ(1, nbComponents);
}
static cv::Mat createCrashMat(int numThreads) {
const int h = numThreads * 4 * 2 + 8;
const double nParallelStripes = std::max(1, std::min(h / 2, numThreads * 4));
@ -239,5 +237,124 @@ TEST(Imgproc_ConnectedComponents, missing_background_pixels)
EXPECT_TRUE(std::isnan(centroids.at<double>(0, 1)));
}
TEST(Imgproc_ConnectedComponents, spaghetti_bbdt_sauf_stats)
{
cv::Mat1b img(16, 16);
img << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1;
cv::Mat1i labels;
cv::Mat1i stats;
cv::Mat1d centroids;
int ccltype[] = { cv::CCL_WU, cv::CCL_GRANA, cv::CCL_BOLELLI, cv::CCL_SAUF, cv::CCL_BBDT, cv::CCL_SPAGHETTI };
for (uint cclt = 0; cclt < sizeof(ccltype) / sizeof(int); ++cclt) {
EXPECT_NO_THROW(cv::connectedComponentsWithStats(img, labels, stats, centroids, 8, CV_32S, ccltype[cclt]));
EXPECT_EQ(stats(0, cv::CC_STAT_LEFT), 0);
EXPECT_EQ(stats(0, cv::CC_STAT_TOP), 0);
EXPECT_EQ(stats(0, cv::CC_STAT_WIDTH), 16);
EXPECT_EQ(stats(0, cv::CC_STAT_HEIGHT), 15);
EXPECT_EQ(stats(0, cv::CC_STAT_AREA), 144);
EXPECT_EQ(stats(1, cv::CC_STAT_LEFT), 1);
EXPECT_EQ(stats(1, cv::CC_STAT_TOP), 1);
EXPECT_EQ(stats(1, cv::CC_STAT_WIDTH), 3);
EXPECT_EQ(stats(1, cv::CC_STAT_HEIGHT), 3);
EXPECT_EQ(stats(1, cv::CC_STAT_AREA), 9);
EXPECT_EQ(stats(2, cv::CC_STAT_LEFT), 1);
EXPECT_EQ(stats(2, cv::CC_STAT_TOP), 1);
EXPECT_EQ(stats(2, cv::CC_STAT_WIDTH), 8);
EXPECT_EQ(stats(2, cv::CC_STAT_HEIGHT), 7);
EXPECT_EQ(stats(2, cv::CC_STAT_AREA), 40);
EXPECT_EQ(stats(3, cv::CC_STAT_LEFT), 10);
EXPECT_EQ(stats(3, cv::CC_STAT_TOP), 2);
EXPECT_EQ(stats(3, cv::CC_STAT_WIDTH), 5);
EXPECT_EQ(stats(3, cv::CC_STAT_HEIGHT), 2);
EXPECT_EQ(stats(3, cv::CC_STAT_AREA), 8);
EXPECT_EQ(stats(4, cv::CC_STAT_LEFT), 11);
EXPECT_EQ(stats(4, cv::CC_STAT_TOP), 5);
EXPECT_EQ(stats(4, cv::CC_STAT_WIDTH), 3);
EXPECT_EQ(stats(4, cv::CC_STAT_HEIGHT), 3);
EXPECT_EQ(stats(4, cv::CC_STAT_AREA), 9);
EXPECT_EQ(stats(5, cv::CC_STAT_LEFT), 2);
EXPECT_EQ(stats(5, cv::CC_STAT_TOP), 9);
EXPECT_EQ(stats(5, cv::CC_STAT_WIDTH), 1);
EXPECT_EQ(stats(5, cv::CC_STAT_HEIGHT), 1);
EXPECT_EQ(stats(5, cv::CC_STAT_AREA), 1);
EXPECT_EQ(stats(6, cv::CC_STAT_LEFT), 12);
EXPECT_EQ(stats(6, cv::CC_STAT_TOP), 9);
EXPECT_EQ(stats(6, cv::CC_STAT_WIDTH), 1);
EXPECT_EQ(stats(6, cv::CC_STAT_HEIGHT), 1);
EXPECT_EQ(stats(6, cv::CC_STAT_AREA), 1);
// Labels' order could be different!
if (cclt == cv::CCL_WU || cclt == cv::CCL_SAUF) {
// CCL_SAUF, CCL_WU
EXPECT_EQ(stats(9, cv::CC_STAT_LEFT), 1);
EXPECT_EQ(stats(9, cv::CC_STAT_TOP), 11);
EXPECT_EQ(stats(9, cv::CC_STAT_WIDTH), 4);
EXPECT_EQ(stats(9, cv::CC_STAT_HEIGHT), 2);
EXPECT_EQ(stats(9, cv::CC_STAT_AREA), 8);
EXPECT_EQ(stats(7, cv::CC_STAT_LEFT), 6);
EXPECT_EQ(stats(7, cv::CC_STAT_TOP), 10);
EXPECT_EQ(stats(7, cv::CC_STAT_WIDTH), 4);
EXPECT_EQ(stats(7, cv::CC_STAT_HEIGHT), 2);
EXPECT_EQ(stats(7, cv::CC_STAT_AREA), 8);
EXPECT_EQ(stats(8, cv::CC_STAT_LEFT), 0);
EXPECT_EQ(stats(8, cv::CC_STAT_TOP), 10);
EXPECT_EQ(stats(8, cv::CC_STAT_WIDTH), 16);
EXPECT_EQ(stats(8, cv::CC_STAT_HEIGHT), 6);
EXPECT_EQ(stats(8, cv::CC_STAT_AREA), 21);
}
else {
// CCL_BBDT, CCL_GRANA, CCL_SPAGHETTI, CCL_BOLELLI
EXPECT_EQ(stats(7, cv::CC_STAT_LEFT), 1);
EXPECT_EQ(stats(7, cv::CC_STAT_TOP), 11);
EXPECT_EQ(stats(7, cv::CC_STAT_WIDTH), 4);
EXPECT_EQ(stats(7, cv::CC_STAT_HEIGHT), 2);
EXPECT_EQ(stats(7, cv::CC_STAT_AREA), 8);
EXPECT_EQ(stats(8, cv::CC_STAT_LEFT), 6);
EXPECT_EQ(stats(8, cv::CC_STAT_TOP), 10);
EXPECT_EQ(stats(8, cv::CC_STAT_WIDTH), 4);
EXPECT_EQ(stats(8, cv::CC_STAT_HEIGHT), 2);
EXPECT_EQ(stats(8, cv::CC_STAT_AREA), 8);
EXPECT_EQ(stats(9, cv::CC_STAT_LEFT), 0);
EXPECT_EQ(stats(9, cv::CC_STAT_TOP), 10);
EXPECT_EQ(stats(9, cv::CC_STAT_WIDTH), 16);
EXPECT_EQ(stats(9, cv::CC_STAT_HEIGHT), 6);
EXPECT_EQ(stats(9, cv::CC_STAT_AREA), 21);
}
EXPECT_EQ(stats(10, cv::CC_STAT_LEFT), 9);
EXPECT_EQ(stats(10, cv::CC_STAT_TOP), 12);
EXPECT_EQ(stats(10, cv::CC_STAT_WIDTH), 5);
EXPECT_EQ(stats(10, cv::CC_STAT_HEIGHT), 2);
EXPECT_EQ(stats(10, cv::CC_STAT_AREA), 7);
}
}
}} // namespace