nim_duilib/examples/contour/blob/ComponentLabeling.cpp
2025-03-16 16:42:44 +08:00

539 lines
17 KiB
C++

#include "ComponentLabeling.h"
CompLabeler::CompLabeler(cv::Mat& binImage, BlobContour** lab, cv::Point start, cv::Point end)
: m_binaryImage(binImage)
, m_startPoint(start)
, m_endPoint(end)
{
m_parent = NULL;
m_labels = lab;
m_r = 0;
m_c = 0;
m_dir = 0;
}
CompLabeler::~CompLabeler()
{
}
void CompLabeler::reset()
{
m_blobs.clear();
}
void CompLabeler::label()
{
m_ptrDataBinary = m_binaryImage.data;
m_ptrDataLabels = m_labels;
m_currentBlob = NULL;
m_h = m_binaryImage.size().height;
m_w = m_binaryImage.size().width;
BlobContour* label = NULL;
for (m_r = m_startPoint.y; m_r < m_endPoint.y; ++m_r) {
//First col
m_pos = m_r * m_w;
m_c = m_startPoint.x;
if (m_ptrDataBinary[m_pos]) {
label = m_ptrDataLabels[m_pos];
if (label) {
m_currentBlob = label->m_parent;
}
//Else if so to not check for label==NULL
else if (m_ptrDataBinary[m_pos] /*&& ptrDataLabels[pos]==NULL*/) {
m_currentBlob = new Blob(m_currentLabel, cv::Point(m_c, m_r), cv::Size(m_w, m_h));
m_blobs.push_back(m_currentBlob);
tracerExt();
}
if (!m_ptrDataBinary[m_pos + 1] && !m_ptrDataLabels[m_pos + 1]) {
tracerInt();
}
}
//Other cols
for (m_c = m_startPoint.x+1; m_c < m_endPoint.x-1; ++m_c) {
m_pos = m_r * m_w + m_c;
if (m_ptrDataBinary[m_pos]) {
label = m_ptrDataLabels[m_pos];
if (label != 0) {
m_currentBlob = label->m_parent;
}
else if (!m_ptrDataBinary[m_pos - 1]) {
m_currentBlob = new Blob(m_currentLabel, cv::Point(m_c, m_r), cv::Size(m_w, m_h));
m_blobs.push_back(m_currentBlob);
tracerExt();
}
if (!m_ptrDataBinary[m_pos+1] && m_ptrDataLabels[m_pos+1] == 0) {
tracerInt();
}
}
}
m_pos = m_r * m_w + m_c;
//Last column
if (m_ptrDataBinary[m_pos]) {
label = m_ptrDataLabels[m_pos];
if (label != 0) {
m_currentBlob = label->m_parent;
}
else if (!m_ptrDataBinary[m_pos - 1]) {
m_currentBlob = new Blob(m_currentLabel, cv::Point(m_c, m_r), cv::Size(m_w, m_h));
m_blobs.push_back(m_currentBlob);
tracerExt();
}
}
}
}
void* CompLabeler::threadLabeling(void* o)
{
CompLabeler* obj = (CompLabeler*)o;
obj->label();
return 0;
}
void CompLabeler::tracerExt()
{
//Dir:
//321
//4-0
//567
//cout << "tracerExt\t" << c<<","<<r<<endl;
m_currentContour = &m_currentBlob->m_externalContour;
#ifdef DEBUG_COMPONENT_LABELLING
std::cout << "tracerExt\t" << m_c << ", "<< m_r << std::endl;
bool debugDraw = true;
#endif
int sR = m_r, sC = m_c;
int startPos = sR * m_w + sC;
m_dir = 6;
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos] = 150; //Debug
#endif
getNextPointCCW();
if (m_singlePixBlob) {
m_r = sR;
m_c = sC;
m_pos = m_r * m_w + m_c;
m_ptrDataLabels[m_pos] = m_currentContour;
return;
}
ChainCodeList* cont = &m_currentBlob->m_externalContour.m_contour[0];
while (m_pos != startPos) {
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos] = 150;
#endif
cont->push_back(m_dir);
m_ptrDataLabels[m_pos] = m_currentContour;
getNextPointCCW();
#ifdef DEBUG_COMPONENT_LABELLING
if (debugDraw) {
cv::namedWindow("im", CV_WINDOW_NORMAL + CV_GUI_EXPANDED + CV_WINDOW_KEEPRATIO);
cv::imshow("im", m_binaryImage);
std::stringstream ss;
ss << m_c << ", " << m_r;
cv::displayOverlay("im", ss.str());
int k = cv::waitKey();
if (k == ' ') {
debugDraw = false;
}
}
#endif
}
cont->push_back(m_dir);
m_ptrDataLabels[m_pos] = m_currentContour;
//For blobs in which the starting point must be crossed many times
for (int i = 0; i < 3; ++i) {
getNextPointCCW();
if (m_ptrDataLabels[m_pos] == 0) {
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos] = 150;
#endif
while (m_pos != startPos) {
//cout << r << "," << c << endl;
m_ptrDataLabels[m_pos] = m_currentContour;
cont->push_back(m_dir);
getNextPointCCW();
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos]= 150;
if (debugDraw) {
cv::namedWindow("im", CV_WINDOW_NORMAL + CV_GUI_EXPANDED + CV_WINDOW_KEEPRATIO);
cv::imshow("im", m_binaryImage);
int k = cv::waitKey();
if (k == ' ') {
debugDraw = false;
}
}
#endif
}
cont->push_back(m_dir);
m_ptrDataLabels[m_pos] = m_currentContour;
}
else {
m_r = sR;
m_c = sC;
m_pos = m_r * m_w + m_c;
break;
}
}
}
void CompLabeler::tracerInt(int startDir /*= 5*/)
{
//Dir:
//321
//4-0
//567
//cout << "tracerInt\t" << c<<","<<r<<endl;
#ifdef DEBUG_COMPONENT_LABELLING
std::cout << "tracerInt\t" << m_c << ", " << m_r << std::endl;
bool debugDraw=true;
#endif
int sR = m_r, sC = m_c;
int startPos = sR * m_w + sC;
m_currentContour = new BlobContour(cv::Point(m_c, m_r), cv::Size(m_w, m_h));
m_currentContour->m_parent = m_currentBlob;
ChainCodeList* cont = &m_currentContour->m_contour[0];
m_dir = startDir;
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos] = 50;
#endif
getNextPointCW();
//uchar firstDir = m_dir;
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos] = 100;
#endif
cont->push_back(m_dir);
m_ptrDataLabels[m_pos] = m_currentContour;
while (m_pos != startPos) {
// cout << r << "," << c << endl;
getNextPointCW();
m_ptrDataLabels[m_pos] = m_currentContour;
cont->push_back(m_dir);
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos] = 100;
if (debugDraw) {
cv::namedWindow("im", CV_WINDOW_NORMAL + CV_GUI_EXPANDED + CV_WINDOW_KEEPRATIO);
cv::imshow("im", m_binaryImage);
std::stringstream ss;
ss << m_c << ", " << m_r;
cv::displayOverlay("im", ss.str());
int k = cv::waitKey();
if (k == ' ') {
debugDraw = false;
}
}
#endif
}
//For internal contours in which the starting point must be crossed many times, like:
// ooooooooooooooo
// ooooos o
// ooooo o o
// ooooo o o
// ooooo o
// ooooooooooooooo
for (int i = 0; i < 3; ++i) {
getNextPointCW();
//In tracerExt i check for label==0, beacuse there can not be situations in which a pixel belongs to 2 contours.
//This can happen with internal contorus, so I have to modify the condition.
if (m_ptrDataLabels[m_pos] != m_currentContour) {
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos] = 100;
#endif
while (m_pos != startPos) {
m_ptrDataLabels[m_pos] = m_currentContour;
cont->push_back(m_dir);
getNextPointCW();
#ifdef DEBUG_COMPONENT_LABELLING
m_ptrDataBinary[m_pos] = 100;
if (debugDraw) {
cv::namedWindow("im",CV_WINDOW_NORMAL + CV_GUI_EXPANDED + CV_WINDOW_KEEPRATIO);
cv::imshow("im", m_binaryImage);
int k = cv::waitKey();
if (k == ' ') {
debugDraw = false;
}
}
#endif
}
cont->push_back(m_dir);
m_ptrDataLabels[m_pos] = m_currentContour;
}
else {
m_r = sR;
m_c = sC;
m_pos = m_r * m_w + m_c;
break;
}
}
//If labeler has m_parent it means that I'm using more than 1 thread.
//Then I must check for collisions when adding internal contours to the same blob.
if (m_parent) {
std::lock_guard<std::mutex> lock(m_parent->m_mutexBlob);
//m_parent->acquireMutex();
m_currentBlob->m_internalContours.push_back(m_currentContour);
//m_parent->releaseMutex();
}
else {
m_currentBlob->m_internalContours.push_back(m_currentContour);
}
}
void CompLabeler::getNextPointCCW()
{
//Dir:
//321
//4-0
//567
m_dir = (m_dir + 6) % 8;
int i = 0;
for (; i < 8; i++, m_dir = (m_dir + 1) % 8) {
m_tempR = m_r + m_freemanR[m_dir];
m_tempC = m_c + m_freemanC[m_dir];
if (!(m_tempR < 0 || m_tempR >= m_h || m_tempC < 0 || m_tempC >= m_w)) {
m_pos = m_tempR * m_w + m_tempC;
if (m_ptrDataBinary[m_pos]) {
m_r = m_tempR;
m_c = m_tempC;
break;
}
m_ptrDataLabels[m_pos] = m_currentContour;
}
}
m_singlePixBlob = i == 8;
m_pos = m_r * m_w + m_c;
/*
// I tried to pre-create all the various direction vectors in order to avoid checking too many times for the boundary of the image.
// However, there was no gain in performance.
//It's useless (in terms of performance) to create the direction vectors according to all cases.
8 directions
cases:
0-r=0
1-r=0,c=0
2-r=0,c=w
3-r=h
4-r=h,c=0
5-r=h,c=w
6-c=0
7-c=w
8-otherwise
total:9
*/
//static uchar directions[8][9][8] = {{{0,4,5,6,7,0,0,0},{0,6,7,0,0,0,0,0},{5,6,7,0,0,0,0,0},{0,1,2,3,4,0,0,0},{0,1,2,0,0,0,0,0},{2,3,4,0,0,0,0,0},{0,1,2,6,7,0,0,0},{2,3,4,5,6,0,0,0},{0,1,2,3,4,5,6,7}},{{4,5,6,7,0,0,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{1,2,3,4,0,0,0,0},{1,2,0,0,0,0,0,0},{2,3,4,0,0,0,0,0},{1,2,6,7,0,0,0,0},{2,3,4,5,6,0,0,0},{1,2,3,4,5,6,7,0}},{{4,5,6,7,0,0,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{2,3,4,0,1,0,0,0},{2,0,1,0,0,0,0,0},{2,3,4,0,0,0,0,0},{2,6,7,0,1,0,0,0},{2,3,4,5,6,0,0,0},{2,3,4,5,6,7,0,1}},{{4,5,6,7,0,0,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{3,4,0,1,2,0,0,0},{0,1,2,0,0,0,0,0},{3,4,2,0,0,0,0,0},{6,7,0,1,2,0,0,0},{3,4,5,6,2,0,0,0},{3,4,5,6,7,0,1,2}},{{4,5,6,7,0,4,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{4,0,1,2,3,0,0,0},{0,1,2,0,0,0,0,0},{4,2,3,0,0,0,0,0},{6,7,0,1,2,0,0,0},{4,5,6,2,3,0,0,0},{4,5,6,7,0,1,2,3}},{{5,6,7,0,4,5,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{0,1,2,3,4,0,0,0},{0,1,2,0,0,0,0,0},{2,3,4,0,0,0,0,0},{6,7,0,1,2,0,0,0},{5,6,2,3,4,0,0,0},{5,6,7,0,1,2,3,4}},{{6,7,0,4,5,6,0,0},{6,7,0,0,0,0,0,0},{6,7,5,0,0,0,0,0},{0,1,2,3,4,0,0,0},{0,1,2,0,0,0,0,0},{2,3,4,0,0,0,0,0},{6,7,0,1,2,0,0,0},{6,2,3,4,5,0,0,0},{6,7,0,1,2,3,4,5}},{{7,0,4,5,6,7,0,0},{7,0,6,0,0,0,0,0},{7,5,6,0,0,0,0,0},{0,1,2,3,4,0,0,0},{0,1,2,0,0,0,0,0},{2,3,4,0,0,0,0,0},{7,0,1,2,6,0,0,0},{2,3,4,5,6,0,0,0},{7,0,1,2,3,4,5,6}}};
//static uchar nDirs[8][9] = {{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8}};
//static uchar* dirVec;
//int p =-1;
//if (r== 0) {
// if (c= =0)
// p = 1;
// else if (c == (w-1))
// p = 2;
// else
// p = 0;
//}
//else if (r== (h-1)) {
// if (c == 0)
// p = 4;
// else if (c == (w-1))
// p = 5;
// else
// p = 3;
//}
//else if (c == 0)
// p = 6;
//else if (c == (w-1))
// p = 7;
//else
// p = 8;
//
//dirVec = directions[dir][p];
//int i = 0;
//int d = dir;
//for (i; i < nDirs[d][p]; ++i) {
// tempR = r + freemanR[dirVec[i]];
// tempC = c + freemanC[dirVec[i]];
// pos = tempR*w +tempC;
// if (ptrDataBinary[pos]) {
// r = tempR;
// c = tempC;
// dir = dirVec[i];
// break;
// }
// ptrDataLabels[pos] = (int)currentBlob;
//}
//singlePixBlob = i == nDirs[d][p];
//pos = r*w+c;
}
void CompLabeler::getNextPointCW()
{
//Dir:
//321
//4-0
//567
m_dir = (m_dir + 2) % 8;
for (int i = 0; i < 8; ++i) {
m_tempR = m_r + m_freemanR[m_dir];
m_tempC = m_c + m_freemanC[m_dir];
if (!(m_tempR < 0 || m_tempR >= m_h || m_tempC < 0 || m_tempC >= m_w)) {
m_pos = m_tempR * m_w + m_tempC;
if (m_ptrDataBinary[m_pos]) {
m_r = m_tempR;
m_c = m_tempC;
break;
}
m_ptrDataLabels[m_pos] = m_currentContour;
}
m_dir--;
m_dir = m_dir % 8; //To cycle through values 7->0
}
m_pos = m_r * m_w + m_c;
}
int CompLabeler::m_freemanC[8] = {1, 1, 0, -1, -1, -1, 0, 1};
int CompLabeler::m_freemanR[8] = {0, -1, -1, -1, 0, 1, 1, 1};
void CompLabelerGroup::doLabeling(BlobVector& blobs)
{
LabelID label = 0;
if (m_numThreads > 1) {
//Preliminary step in order to pre-compute all the blobs crossing the border
for (int i = 1; i < m_numThreads; i++) {
cv::Point offset(m_img.size().width, 1);
CompLabeler lbl(m_img, m_labels, m_labelers[i]->m_startPoint, m_labelers[i]->m_startPoint + offset);
lbl.m_parent = this;
lbl.label();
//cout << "Single pass\t" << lbl.blobs.size()<<endl;
for (unsigned int i = 0; i < lbl.m_blobs.size(); i++) {
lbl.m_blobs[i]->setID(label);
label++;
blobs.push_back(lbl.m_blobs[i]);
}
}
std::vector<std::thread> ths;
for (int i = 0; i < m_numThreads; i++) {
//pthread_create(&tIds[i], NULL, CompLabeler::thread_Labeling, labelers[i]);
ths.emplace_back(std::thread([label = m_labelers[i]]() {
CompLabeler::threadLabeling(label);
}));
}
for (int i = 0; i < m_numThreads; i++) {
//pthread_join(tIds[i], 0);
if (ths[i].joinable()) {
ths[i].join();
}
}
}
else {
m_labelers[0]->label();
}
for (int i = 0; i < m_numThreads; ++i) {
//cout << "MT pass " <<i<<"\t" << labelers[i]->blobs.size()<<endl;
for (unsigned int j = 0; j < m_labelers[i]->m_blobs.size(); ++j) {
m_labelers[i]->m_blobs[j]->setID(label);
label++;
blobs.push_back(m_labelers[i]->m_blobs[j]);
}
}
}
CompLabelerGroup::CompLabelerGroup()
{
//m_mutexBlob = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
m_labels = NULL;
m_labelers = NULL;
//m_tIds = NULL;
}
CompLabelerGroup::~CompLabelerGroup()
{
if (m_labelers) {
for (int i = 0; i < m_numThreads; ++i) {
delete m_labelers[i];
}
delete []m_labelers;
}
//if (m_tIds) {
// delete [] m_tIds;
//}
if (m_labels) {
delete [] m_labels;
}
}
void CompLabelerGroup::set(int nThreads, const cv::Mat& binImg)
{
this->m_numThreads = nThreads;
if (binImg.isContinuous()) {
this->m_img = binImg;
}
else {
this->m_img = binImg.clone();
}
//this->labels = Mat_<int>::zeros(img.size());
if (m_labels) {
delete [] m_labels;
}
int nPts = binImg.size().width * binImg.size().height;
m_labels = new BlobContour*[nPts];
memset(m_labels, 0, nPts*sizeof(Blob*));
//if (m_tIds) {
// delete [] m_tIds;
//}
if (m_labelers) {
for (int i = 0; i < nThreads; ++i) {
delete m_labelers[i];
}
delete [] m_labelers;
}
//m_tIds = new pthread_t[nThreads];
m_labelers = new CompLabeler*[nThreads];
cv::Size sz = binImg.size();
//int numPx = sz.width*sz.width;
int i = 0;
for (; i < nThreads-1; ++i) {
int yStart = (int)((float)i / nThreads * sz.height);
int yEnd = (int)((float)(i + 1) / nThreads * sz.height);
cv::Point st(0, yStart);
cv::Point en(sz.width, yEnd);
m_labelers[i] = new CompLabeler(m_img, m_labels, st, en);
m_labelers[i]->m_parent = this;
}
cv::Point st(0, (int)((float)i / nThreads * sz.height));
cv::Point en(sz.width, sz.height);
//st = Point(0,187);
m_labelers[i] = new CompLabeler(m_img, m_labels, st, en);
m_labelers[i]->m_parent = this;
}
void CompLabelerGroup::reset()
{
if (m_labels) {
delete [] m_labels;
m_labels = NULL;
}
for (int i = 0; i < m_numThreads; ++i) {
m_labelers[i]->reset();
}
}
void CompLabelerGroup::acquireMutex()
{
//pthread_mutex_lock(&m_mutexBlob);
}
void CompLabelerGroup::releaseMutex()
{
//pthread_mutex_unlock(&m_mutexBlob);
}