Commit 5ddd2531 authored by Vadim Pisarevsky's avatar Vadim Pisarevsky Committed by GitHub

Add Grana's connected components algorithm for 8-way connectivity. (#6823)

* Add Grana's connected components algorithm for 8-way connectivity. That algorithm is faster than Wu's one (currently implemented in opencv). For more details see https://github.com/prittt/YACCLAB.

* New functions signature and distance transform compatibility

* Add tests to imgproc/test/test_connectedcomponents.cpp

* Change of test_connectedcomponents.cpp for c++98 support
parent 4f0f5a24
......@@ -413,6 +413,13 @@ enum ConnectedComponentsTypes {
CC_STAT_MAX = 5
};
//! connected components algorithm
enum ConnectedComponentsAlgorithmsTypes {
CCL_WU = 0, //!< SAUF algorithm for 8-way connectivity, SAUF algorithm for 4-way connectivity
CCL_DEFAULT = -1, //!< BBDT algortihm 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
};
//! mode of the contour retrieval algorithm
enum RetrievalModes {
/** retrieves only the extreme outer contours. It sets `hierarchy[i][2]=hierarchy[i][3]=-1` for
......@@ -3648,16 +3655,56 @@ CV_EXPORTS_W void matchTemplate( InputArray image, InputArray templ,
image with 4 or 8 way connectivity - returns N, the total number of labels [0, N-1] where 0
represents the background label. ltype specifies the output label image type, an important
consideration based on the total number of labels or alternatively the total number of pixels in
the source image.
the source image. ccltype specifies the connected components labeling algorithm to use, currently
Grana's (BBDT) and Wu's (SAUF) algorithms are supported, see the cv::ConnectedComponentsAlgorithmsTypes
for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not.
@param image the 8-bit single-channel image to be labeled
@param labels destination labeled image
@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively
@param ltype output image label type. Currently CV_32S and CV_16U are supported.
*/
@param ccltype connected components algorithm type (see the cv::ConnectedComponentsAlgorithmsTypes).
*/
CV_EXPORTS_AS(connectedComponentsWithAlgorithm) int connectedComponents(InputArray image, OutputArray labels,
int connectivity, int ltype, int ccltype);
/** @overload
@param image the 8-bit single-channel image to be labeled
@param labels destination labeled image
@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively
@param ltype output image label type. Currently CV_32S and CV_16U are supported.
*/
CV_EXPORTS_W int connectedComponents(InputArray image, OutputArray labels,
int connectivity = 8, int ltype = CV_32S);
/** @brief computes the connected components labeled image of boolean image and also produces a statistics output for each label
image with 4 or 8 way connectivity - returns N, the total number of labels [0, N-1] where 0
represents the background label. ltype specifies the output label image type, an important
consideration based on the total number of labels or alternatively the total number of pixels in
the source image. ccltype specifies the connected components labeling algorithm to use, currently
Grana's (BBDT) and Wu's (SAUF) algorithms are supported, see the cv::ConnectedComponentsAlgorithmsTypes
for details. Note that SAUF algorithm forces a row major ordering of labels while BBDT does not.
@param image the 8-bit single-channel image to be labeled
@param labels destination labeled image
@param stats statistics output for each label, including the background label, see below for
available statistics. Statistics are accessed via stats(label, COLUMN) where COLUMN is one of
cv::ConnectedComponentsTypes. The data type is CV_32S.
@param centroids centroid output for each label, including the background label. Centroids are
accessed via centroids(label, 0) for x and centroids(label, 1) for y. The data type CV_64F.
@param connectivity 8 or 4 for 8-way or 4-way connectivity respectively
@param ltype output image label type. Currently CV_32S and CV_16U are supported.
@param ccltype connected components algorithm type (see the cv::ConnectedComponentsAlgorithmsTypes).
*/
CV_EXPORTS_AS(connectedComponentsWithStatsWithAlgorithm) int connectedComponentsWithStats(InputArray image, OutputArray labels,
OutputArray stats, OutputArray centroids,
int connectivity, int ltype, int ccltype);
/** @overload
@param image the 8-bit single-channel image to be labeled
@param labels destination labeled image
......
......@@ -38,6 +38,10 @@
// 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 Lorenzo Baraldi <lorenzo.baraldi@unimore.it>
// 2016 Roberto Vezzani <roberto.vezzani@unimore.it>
//M*/
//
#include "precomp.hpp"
......@@ -188,7 +192,7 @@ namespace cv{
//reference for 8-way: {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}};//a, b, c, d neighborhoods
const int G8[4][2] = {{1, -1}, {1, 0}, {1, 1}, {0, -1}};//a, b, c, d neighborhoods
template<typename LabelT, typename PixelT, typename StatsOp = NoOp >
struct LabelingImpl{
struct LabelingWu{
LabelT operator()(const cv::Mat &I, cv::Mat &L, int connectivity, StatsOp &sop){
CV_Assert(L.rows == I.rows);
CV_Assert(L.cols == I.cols);
......@@ -329,33 +333,1366 @@ namespace cv{
fastFree(P);
return nLabels;
}//End function LabelingImpl operator()
}//End function LabelingWu operator()
};//End struct LabelingWu
};//End struct LabelingImpl
// Based on “Optimized Block-based Connected Components Labeling with Decision Trees”, Costantino Grana et al
// Only for 8-connectivity
template<typename LabelT, typename PixelT, typename StatsOp = NoOp >
struct LabelingGrana{
LabelT operator()(const cv::Mat &img, cv::Mat &imgLabels, int connectivity, StatsOp &sop){
CV_Assert(img.rows == imgLabels.rows);
CV_Assert(img.cols == imgLabels.cols);
CV_Assert(connectivity == 8 || connectivity == 4);
const int h = img.rows;
const int w = img.cols;
//A quick and dirty upper bound for the maximimum number of labels.
const size_t Plength = img.rows*img.cols / 4;
LabelT *P = (LabelT *)fastMalloc(sizeof(LabelT)* Plength);
P[0] = 0;
LabelT lunique = 1;
// First scan
for (int r = 0; r<h; r += 2) {
// Get rows pointer
const PixelT* const img_row = img.ptr<PixelT>(r);
const PixelT* const img_row_prev = (PixelT *)(((char *)img_row) - img.step.p[0]);
const PixelT* const img_row_prev_prev = (PixelT *)(((char *)img_row_prev) - img.step.p[0]);
const PixelT* const img_row_fol = (PixelT *)(((char *)img_row) + img.step.p[0]);
LabelT* const imgLabels_row = imgLabels.ptr<LabelT>(r);
LabelT* const imgLabels_row_prev_prev = (LabelT *)(((char *)imgLabels_row) - imgLabels.step.p[0] - imgLabels.step.p[0]);
for (int c = 0; c < w; c += 2) {
// We work with 2x2 blocks
// +-+-+-+
// |P|Q|R|
// +-+-+-+
// |S|X|
// +-+-+
// The pixels are named as follows
// +---+---+---+
// |a b|c d|e f|
// |g h|i j|k l|
// +---+---+---+
// |m n|o p|
// |q r|s t|
// +---+---+
// Pixels a, f, l, q are not needed, since we need to understand the
// the connectivity between these blocks and those pixels only metter
// when considering the outer connectivities
// A bunch of defines used to check if the pixels are foreground,
// without going outside the image limits.
#define condition_b c-1>=0 && r-2>=0 && img_row_prev_prev[c-1]>0
#define condition_c r-2>=0 && img_row_prev_prev[c]>0
#define condition_d c+1<w && r-2>=0 && img_row_prev_prev[c+1]>0
#define condition_e c+2<w && r-2>=0 && img_row_prev_prev[c+2]>0
#define condition_g c-2>=0 && r-1>=0 && img_row_prev[c-2]>0
#define condition_h c-1>=0 && r-1>=0 && img_row_prev[c-1]>0
#define condition_i r-1>=0 && img_row_prev[c]>0
#define condition_j c+1<w && r-1>=0 && img_row_prev[c+1]>0
#define condition_k c+2<w && r-1>=0 && img_row_prev[c+2]>0
#define condition_m c-2>=0 && img_row[c-2]>0
#define condition_n c-1>=0 && img_row[c-1]>0
#define condition_o img_row[c]>0
#define condition_p c+1<w && img_row[c+1]>0
#define condition_r c-1>=0 && r+1<h && img_row_fol[c-1]>0
#define condition_s r+1<h && img_row_fol[c]>0
#define condition_t c+1<w && r+1<h && img_row_fol[c+1]>0
// This is a decision tree which allows to choose which action to
// perform, checking as few conditions as possible.
// Actions: the blocks label are provisionally stored in the top left
// pixel of the block in the labels image
if (condition_o) {
if (condition_n) {
if (condition_j) {
if (condition_i) {
//Action_6: Assign label of block S
imgLabels_row[c] = imgLabels_row[c - 2];
continue;
}
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();
fastFree(P);
return nLabels;
} //End function LabelingGrana operator()
}; //End struct LabelingGrana
}//end namespace connectedcomponents
//L's type must have an appropriate depth for the number of pixels in I
template<typename StatsOp>
static
int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, StatsOp &sop){
int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, int ccltype, StatsOp &sop){
CV_Assert(L.channels() == 1 && I.channels() == 1);
CV_Assert(connectivity == 8 || connectivity == 4);
CV_Assert(ccltype == CCL_GRANA || ccltype == CCL_WU || ccltype == CCL_DEFAULT);
int lDepth = L.depth();
int iDepth = I.depth();
using connectedcomponents::LabelingImpl;
//warn if L's depth is not sufficient?
CV_Assert(iDepth == CV_8U || iDepth == CV_8S);
if(lDepth == CV_8U){
return (int) LabelingImpl<uchar, uchar, StatsOp>()(I, L, connectivity, sop);
}else if(lDepth == CV_16U){
return (int) LabelingImpl<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) LabelingImpl<int, uchar, StatsOp>()(I, L, connectivity, sop);
if (ccltype == CCL_WU || connectivity == 4){
// Wu algorithm is used
using connectedcomponents::LabelingWu;
//warn if L's depth is not sufficient?
if (lDepth == CV_8U){
return (int)LabelingWu<uchar, uchar, StatsOp>()(I, L, connectivity, sop);
}
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
return (int)LabelingWu<int, uchar, StatsOp>()(I, L, connectivity, sop);
}
}else if ((ccltype == CCL_GRANA || ccltype == CCL_DEFAULT) && connectivity == 8){
// Grana algorithm is used
using connectedcomponents::LabelingGrana;
//warn if L's depth is not sufficient?
if (lDepth == CV_8U){
return (int)LabelingGrana<uchar, uchar, StatsOp>()(I, L, connectivity, sop);
}
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
return (int)LabelingGrana<int, uchar, StatsOp>()(I, L, connectivity, sop);
}
}
CV_Error(CV_StsUnsupportedFormat, "unsupported label/image type");
......@@ -364,33 +1701,49 @@ int connectedComponents_sub1(const cv::Mat &I, cv::Mat &L, int connectivity, Sta
}
// 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, sop);
}else if(ltype == CV_32S){
return connectedComponents_sub1(img, labels, connectivity, sop);
}else{
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;
}
}
// 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, sop);
}else if(ltype == CV_32S){
return connectedComponents_sub1(img, labels, connectivity, sop);
}else{
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;
}
......
......@@ -827,7 +827,7 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe
if( labelType == CV_DIST_LABEL_CCOMP )
{
Mat zpix = src == 0;
connectedComponents(zpix, labels, 8, CV_32S);
connectedComponents(zpix, labels, 8, CV_32S, CCL_WU);
}
else
{
......
......@@ -42,6 +42,7 @@
#include "test_precomp.hpp"
#include <string>
#include <vector>
using namespace cv;
using namespace std;
......@@ -58,49 +59,81 @@ protected:
CV_ConnectedComponentsTest::CV_ConnectedComponentsTest() {}
CV_ConnectedComponentsTest::~CV_ConnectedComponentsTest() {}
// This function force a row major order for the labels
void normalizeLabels(Mat1i& imgLabels, int iNumLabels) {
vector<int> vecNewLabels(iNumLabels + 1, 0);
int iMaxNewLabel = 0;
for (int r = 0; r<imgLabels.rows; ++r) {
for (int c = 0; c<imgLabels.cols; ++c) {
int iCurLabel = imgLabels(r, c);
if (iCurLabel>0) {
if (vecNewLabels[iCurLabel] == 0) {
vecNewLabels[iCurLabel] = ++iMaxNewLabel;
}
imgLabels(r, c) = vecNewLabels[iCurLabel];
}
}
}
}
void CV_ConnectedComponentsTest::run( int /* start_from */)
{
int ccltype[] = { cv::CCL_WU, cv::CCL_DEFAULT, cv::CCL_GRANA };
string exp_path = string(ts->get_data_path()) + "connectedcomponents/ccomp_exp.png";
Mat exp = imread(exp_path, 0);
Mat orig = imread(string(ts->get_data_path()) + "connectedcomponents/concentric_circles.png", 0);
if (orig.empty())
{
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
return;
}
Mat bw = orig > 128;
Mat labelImage;
int nLabels = connectedComponents(bw, labelImage, 8, CV_32S);
for(int r = 0; r < labelImage.rows; ++r){
for(int c = 0; c < labelImage.cols; ++c){
int l = labelImage.at<int>(r, c);
bool pass = l >= 0 && l <= nLabels;
if(!pass){
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
return;
for (uint cclt = 0; cclt < sizeof(ccltype)/sizeof(int); ++cclt)
{
Mat1i labelImage;
int nLabels = connectedComponents(bw, labelImage, 8, CV_32S, ccltype[cclt]);
normalizeLabels(labelImage, nLabels);
// Validate test results
for (int r = 0; r < labelImage.rows; ++r){
for (int c = 0; c < labelImage.cols; ++c){
int l = labelImage.at<int>(r, c);
bool pass = l >= 0 && l <= nLabels;
if (!pass){
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
return;
}
}
}
}
if( exp.empty() || orig.size() != exp.size() )
{
imwrite(exp_path, labelImage);
exp = labelImage;
}
if (exp.empty() || orig.size() != exp.size())
{
imwrite(exp_path, labelImage);
exp = labelImage;
}
if (0 != cvtest::norm(labelImage > 0, exp > 0, NORM_INF))
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return;
}
if (nLabels != cvtest::norm(labelImage, NORM_INF) + 1)
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return;
}
if (0 != cvtest::norm(labelImage > 0, exp > 0, NORM_INF))
{
ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
return;
}
if (nLabels != cvtest::norm(labelImage, NORM_INF)+1)
{
ts->set_failed_test_info( cvtest::TS::FAIL_MISMATCH );
return;
}
ts->set_failed_test_info(cvtest::TS::OK);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment