// Copyright (C) 2007 by Cristóbal Carnero Liñán
// grendel.ccl@gmail.com
//
// This file is part of cvBlob.
//
// cvBlob is free software: you can redistribute it and/or modify
// it under the terms of the Lesser GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// cvBlob is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// Lesser GNU General Public License for more details.
//
// You should have received a copy of the Lesser GNU General Public License
// along with cvBlob. If not, see .
//
#include
#include
using namespace std;
#if (defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || (defined(__APPLE__) & defined(__MACH__)))
#include
#include
#else
#include
#include
#endif
#include "cvblob.h"
namespace cvb
{
CvLabel cvGreaterBlob(const CvBlobs &blobs)
{
CvLabel label=0;
unsigned int maxArea=0;
for (CvBlobs::const_iterator it=blobs.begin();it!=blobs.end();++it)
{
CvBlob *blob=(*it).second;
//if ((!blob->_parent)&&(blob->area>maxArea))
if (blob->area>maxArea)
{
label=blob->label;
maxArea=blob->area;
}
}
return label;
}
void cvFilterByArea(CvBlobs &blobs, unsigned int minArea, unsigned int maxArea)
{
CvBlobs::iterator it=blobs.begin();
while(it!=blobs.end())
{
CvBlob *blob=(*it).second;
if ((blob->areaarea>maxArea))
{
cvReleaseBlob(blob);
CvBlobs::iterator tmp=it;
++it;
blobs.erase(tmp);
}
else
++it;
}
}
void cvFilterByLabel(CvBlobs &blobs, CvLabel label)
{
CvBlobs::iterator it=blobs.begin();
while(it!=blobs.end())
{
CvBlob *blob=(*it).second;
if (blob->label!=label)
{
delete blob;
CvBlobs::iterator tmp=it;
++it;
blobs.erase(tmp);
}
else
++it;
}
}
/*void cvCentralMoments(CvBlob *blob, const IplImage *img)
{
CV_FUNCNAME("cvCentralMoments");
__CV_BEGIN__;
if (!blob->centralMoments)
{
CV_ASSERT(img&&(img->depth==IPL_DEPTH_LABEL)&&(img->nChannels==1));
//cvCentroid(blob); // Here?
blob->u11=blob->u20=blob->u02=0.;
// Only in the bounding box
int stepIn = img->widthStep / (img->depth / 8);
int img_width = img->width;
int img_height = img->height;
int img_offset = 0;
if(0 != img->roi)
{
img_width = img->roi->width;
img_height = img->roi->height;
img_offset = img->roi->xOffset + (img->roi->yOffset * stepIn);
}
CvLabel *imgData=(CvLabel *)img->imageData + (blob->miny * stepIn) + img_offset;
for (unsigned int r=blob->miny;
rmaxy;
r++,imgData+=stepIn)
for (unsigned int c=blob->minx;cmaxx;c++)
if (imgData[c]==blob->label)
{
double tx=(c-blob->centroid.x);
double ty=(r-blob->centroid.y);
blob->u11+=tx*ty;
blob->u20+=tx*tx;
blob->u02+=ty*ty;
}
blob->centralMoments = true;
}
__CV_END__;
}*/
void cvRenderBlob(const IplImage *imgLabel, CvBlob *blob, IplImage *imgSource, IplImage *imgDest, unsigned short mode, CvScalar const &color, double alpha)
{
CV_FUNCNAME("cvRenderBlob");
__CV_BEGIN__;
CV_ASSERT(imgLabel&&(imgLabel->depth==IPL_DEPTH_LABEL)&&(imgLabel->nChannels==1));
CV_ASSERT(imgDest&&(imgDest->depth==IPL_DEPTH_8U)&&(imgDest->nChannels==3));
if (mode&CV_BLOB_RENDER_COLOR)
{
int stepLbl = imgLabel->widthStep/(imgLabel->depth/8);
int stepSrc = imgSource->widthStep/(imgSource->depth/8);
int stepDst = imgDest->widthStep/(imgDest->depth/8);
int imgLabel_width = imgLabel->width;
int imgLabel_height = imgLabel->height;
int imgLabel_offset = 0;
int imgSource_width = imgSource->width;
int imgSource_height = imgSource->height;
int imgSource_offset = 0;
int imgDest_width = imgDest->width;
int imgDest_height = imgDest->height;
int imgDest_offset = 0;
if(imgLabel->roi)
{
imgLabel_width = imgLabel->roi->width;
imgLabel_height = imgLabel->roi->height;
imgLabel_offset = (imgLabel->nChannels * imgLabel->roi->xOffset) + (imgLabel->roi->yOffset * stepLbl);
}
if(imgSource->roi)
{
imgSource_width = imgSource->roi->width;
imgSource_height = imgSource->roi->height;
imgSource_offset = (imgSource->nChannels * imgSource->roi->xOffset) + (imgSource->roi->yOffset * stepSrc);
}
if(imgDest->roi)
{
imgDest_width = imgDest->roi->width;
imgDest_height = imgDest->roi->height;
imgDest_offset = (imgDest->nChannels * imgDest->roi->xOffset) + (imgDest->roi->yOffset * stepDst);
}
CvLabel *labels = (CvLabel *)imgLabel->imageData + imgLabel_offset + (blob->miny * stepLbl);
unsigned char *source = (unsigned char *)imgSource->imageData + imgSource_offset + (blob->miny * stepSrc);
unsigned char *imgData = (unsigned char *)imgDest->imageData + imgDest_offset + (blob->miny * stepDst);
for (unsigned int r=blob->miny; rmaxy; r++, labels+=stepLbl, source+=stepSrc, imgData+=stepDst)
for (unsigned int c=blob->minx; cmaxx; c++)
{
if (labels[c]==blob->label)
{
imgData[imgDest->nChannels*c+0] = (unsigned char)((1.-alpha)*source[imgSource->nChannels*c+0]+alpha*color.val[0]);
imgData[imgDest->nChannels*c+1] = (unsigned char)((1.-alpha)*source[imgSource->nChannels*c+1]+alpha*color.val[1]);
imgData[imgDest->nChannels*c+2] = (unsigned char)((1.-alpha)*source[imgSource->nChannels*c+2]+alpha*color.val[2]);
}
}
}
if (mode)
{
if (mode&CV_BLOB_RENDER_TO_LOG)
{
std::clog << "Blob " << blob->label << std::endl;
std::clog << " - Bounding box: (" << blob->minx << ", " << blob->miny << ") - (" << blob->maxx << ", " << blob->maxy << ")" << std::endl;
std::clog << " - Bounding box area: " << (1 + blob->maxx - blob->minx) * (1 + blob->maxy - blob->miny) << std::endl;
std::clog << " - Area: " << blob->area << std::endl;
std::clog << " - Centroid: (" << blob->centroid.x << ", " << blob->centroid.y << ")" << std::endl;
std::clog << std::endl;
}
if (mode&CV_BLOB_RENDER_TO_STD)
{
std::cout << "Blob " << blob->label << std::endl;
std::cout << " - Bounding box: (" << blob->minx << ", " << blob->miny << ") - (" << blob->maxx << ", " << blob->maxy << ")" << std::endl;
std::cout << " - Bounding box area: " << (1 + blob->maxx - blob->minx) * (1 + blob->maxy - blob->miny) << std::endl;
std::cout << " - Area: " << blob->area << std::endl;
std::cout << " - Centroid: (" << blob->centroid.x << ", " << blob->centroid.y << ")" << std::endl;
std::cout << std::endl;
}
if (mode&CV_BLOB_RENDER_BOUNDING_BOX)
cvRectangle(imgDest, cvPoint(blob->minx, blob->miny), cvPoint(blob->maxx-1, blob->maxy-1), CV_RGB(255., 0., 0.));
if (mode&CV_BLOB_RENDER_ANGLE)
{
double angle = cvAngle(blob);
double x1,y1,x2,y2;
double lengthLine = MAX(blob->maxx-blob->minx, blob->maxy-blob->miny)/2.;
x1=blob->centroid.x-lengthLine*cos(angle);
y1=blob->centroid.y-lengthLine*sin(angle);
x2=blob->centroid.x+lengthLine*cos(angle);
y2=blob->centroid.y+lengthLine*sin(angle);
cvLine(imgDest,cvPoint(int(x1),int(y1)),cvPoint(int(x2),int(y2)),CV_RGB(0.,255.,0.));
}
if (mode&CV_BLOB_RENDER_CENTROID)
{
cvLine(imgDest,cvPoint(int(blob->centroid.x)-3,int(blob->centroid.y)),cvPoint(int(blob->centroid.x)+3,int(blob->centroid.y)),CV_RGB(0.,0.,255.));
cvLine(imgDest,cvPoint(int(blob->centroid.x),int(blob->centroid.y)-3),cvPoint(int(blob->centroid.x),int(blob->centroid.y)+3),CV_RGB(0.,0.,255.));
}
}
__CV_END__;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Based on http://en.wikipedia.org/wiki/HSL_and_HSV
/// \def _HSV2RGB_(H, S, V, R, G, B)
/// \brief Color translation between HSV and RGB.
#define _HSV2RGB_(H, S, V, R, G, B) \
{ \
double _h = H/60.; \
int _hf = (int)floor(_h); \
int _hi = ((int)_h)%6; \
double _f = _h - _hf; \
\
double _p = V * (1. - S); \
double _q = V * (1. - _f * S); \
double _t = V * (1. - (1. - _f) * S); \
\
switch (_hi) \
{ \
case 0: \
R = 255.*V; G = 255.*_t; B = 255.*_p; \
break; \
case 1: \
R = 255.*_q; G = 255.*V; B = 255.*_p; \
break; \
case 2: \
R = 255.*_p; G = 255.*V; B = 255.*_t; \
break; \
case 3: \
R = 255.*_p; G = 255.*_q; B = 255.*V; \
break; \
case 4: \
R = 255.*_t; G = 255.*_p; B = 255.*V; \
break; \
case 5: \
R = 255.*V; G = 255.*_p; B = 255.*_q; \
break; \
} \
}
///////////////////////////////////////////////////////////////////////////////////////////////////
typedef std::map Palete;
void cvRenderBlobs(const IplImage *imgLabel, CvBlobs &blobs, IplImage *imgSource, IplImage *imgDest, unsigned short mode, double alpha)
{
CV_FUNCNAME("cvRenderBlobs");
__CV_BEGIN__;
{
CV_ASSERT(imgLabel&&(imgLabel->depth==IPL_DEPTH_LABEL)&&(imgLabel->nChannels==1));
CV_ASSERT(imgDest&&(imgDest->depth==IPL_DEPTH_8U)&&(imgDest->nChannels==3));
Palete pal;
if (mode&CV_BLOB_RENDER_COLOR)
{
unsigned int colorCount = 0;
for (CvBlobs::const_iterator it=blobs.begin(); it!=blobs.end(); ++it)
{
CvLabel label = (*it).second->label;
double r, g, b;
_HSV2RGB_((double)((colorCount*77)%360), .5, 1., r, g, b);
colorCount++;
pal[label] = CV_RGB(r, g, b);
}
}
for (CvBlobs::iterator it=blobs.begin(); it!=blobs.end(); ++it)
cvRenderBlob(imgLabel, (*it).second, imgSource, imgDest, mode, pal[(*it).second->label], alpha);
}
__CV_END__;
}
// Returns radians
double cvAngle(CvBlob *blob)
{
CV_FUNCNAME("cvAngle");
__CV_BEGIN__;
return .5*atan2(2.*blob->u11,(blob->u20-blob->u02));
__CV_END__;
}
void cvSaveImageBlob(const char *filename, IplImage *img, CvBlob const *blob)
{
CvRect roi = cvGetImageROI(img);
cvSetImageROItoBlob(img, blob);
cvSaveImage(filename, img);
cvSetImageROI(img, roi);
}
}
ostream& operator<< (ostream& output, const cvb::CvBlob& b)
{
output << b.label << ": " << b.area << ", (" << b.centroid.x << ", " << b.centroid.y << "), [(" << b.minx << ", " << b.miny << ") - (" << b.maxx << ", " << b.maxy << ")]";
return output;
}