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
pull/7179/merge
Vadim Pisarevsky 8 years ago committed by GitHub
parent 4f0f5a24ef
commit 5ddd25313f
  1. 51
      modules/imgproc/include/opencv2/imgproc.hpp
  2. 1401
      modules/imgproc/src/connectedcomponents.cpp
  3. 2
      modules/imgproc/src/distransform.cpp
  4. 85
      modules/imgproc/test/test_connectedcomponents.cpp

@ -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

File diff suppressed because it is too large Load Diff

@ -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);
}

Loading…
Cancel
Save