[Jderobot-admin] jderobot-r973 - in trunk/src/stable/libs: . cvBlob
rocapal en jderobot.org
rocapal en jderobot.org
Mie Ago 14 11:12:02 CEST 2013
Author: rocapal
Date: 2013-08-14 11:11:01 +0200 (Wed, 14 Aug 2013)
New Revision: 973
Added:
trunk/src/stable/libs/cvBlob/
trunk/src/stable/libs/cvBlob/CMakeLists.txt
trunk/src/stable/libs/cvBlob/cvaux.cpp
trunk/src/stable/libs/cvBlob/cvblob.cpp
trunk/src/stable/libs/cvBlob/cvblob.h
trunk/src/stable/libs/cvBlob/cvcolor.cpp
trunk/src/stable/libs/cvBlob/cvcontour.cpp
trunk/src/stable/libs/cvBlob/cvlabel.cpp
trunk/src/stable/libs/cvBlob/cvtrack.cpp
Log:
#28 added cvblob library
Added: trunk/src/stable/libs/cvBlob/CMakeLists.txt
===================================================================
--- trunk/src/stable/libs/cvBlob/CMakeLists.txt (rev 0)
+++ trunk/src/stable/libs/cvBlob/CMakeLists.txt 2013-08-14 09:11:01 UTC (rev 973)
@@ -0,0 +1,44 @@
+# Copyright (C) 2007 by Cristóbal Carnero Liñán
+# grendel.ccl en 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 <http://www.gnu.org/licenses/>.
+#
+
+
+set(cvBlob_CVBLOB cvblob.cpp
+ cvlabel.cpp
+ cvaux.cpp
+ cvcontour.cpp
+ cvtrack.cpp
+ cvcolor.cpp
+)
+
+set_source_files_properties(${cvBlob_SRC}
+ PROPERTIES
+ COMPILE_FLAGS "-O3"
+)
+
+add_library(cvblob SHARED ${cvBlob_CVBLOB})
+
+target_link_libraries(cvblob ${OPENGL_LIBRARIES})
+
+#install(FILES cvblob.h DESTINATION include)
+
+#install(TARGETS cvblob
+# RUNTIME DESTINATION bin
+# LIBRARY DESTINATION lib
+# ARCHIVE DESTINATION lib
+#)
Added: trunk/src/stable/libs/cvBlob/cvaux.cpp
===================================================================
--- trunk/src/stable/libs/cvBlob/cvaux.cpp (rev 0)
+++ trunk/src/stable/libs/cvBlob/cvaux.cpp 2013-08-14 09:11:01 UTC (rev 973)
@@ -0,0 +1,79 @@
+// Copyright (C) 2007 by Cristóbal Carnero Liñán
+// grendel.ccl en 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 <http://www.gnu.org/licenses/>.
+//
+
+#include <cmath>
+
+#if (defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || (defined(__APPLE__) & defined(__MACH__)))
+#include <cv.h>
+#else
+#include <opencv/cv.h>
+#endif
+
+#include "cvblob.h"
+
+namespace cvb
+{
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry1
+
+ double cvDotProductPoints(CvPoint const &a, CvPoint const &b, CvPoint const &c)
+ {
+ double abx = b.x-a.x;
+ double aby = b.y-a.y;
+ double bcx = c.x-b.x;
+ double bcy = c.y-b.y;
+
+ return abx*bcx + aby*bcy;
+ }
+
+ double cvCrossProductPoints(CvPoint const &a, CvPoint const &b, CvPoint const &c)
+ {
+ double abx = b.x-a.x;
+ double aby = b.y-a.y;
+ double acx = c.x-a.x;
+ double acy = c.y-a.y;
+
+ return abx*acy - aby*acx;
+ }
+
+ double cvDistancePointPoint(CvPoint const &a, CvPoint const &b)
+ {
+ double abx = a.x-b.x;
+ double aby = a.y-b.y;
+
+ return sqrt(abx*abx + aby*aby);
+ }
+
+ double cvDistanceLinePoint(CvPoint const &a, CvPoint const &b, CvPoint const &c, bool isSegment)
+ {
+ if (isSegment)
+ {
+ double dot1 = cvDotProductPoints(a, b, c);
+ if (dot1>0) return cvDistancePointPoint(b, c);
+
+ double dot2 = cvDotProductPoints(b, a, c);
+ if(dot2>0) return cvDistancePointPoint(a, c);
+ }
+
+ return fabs(cvCrossProductPoints(a,b,c)/cvDistancePointPoint(a,b));
+ }
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+}
Added: trunk/src/stable/libs/cvBlob/cvblob.cpp
===================================================================
--- trunk/src/stable/libs/cvBlob/cvblob.cpp (rev 0)
+++ trunk/src/stable/libs/cvBlob/cvblob.cpp 2013-08-14 09:11:01 UTC (rev 973)
@@ -0,0 +1,344 @@
+// Copyright (C) 2007 by Cristóbal Carnero Liñán
+// grendel.ccl en 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 <http://www.gnu.org/licenses/>.
+//
+
+#include <cmath>
+#include <iostream>
+using namespace std;
+
+#if (defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || (defined(__APPLE__) & defined(__MACH__)))
+#include <cv.h>
+#include <highgui.h>
+#else
+#include <opencv/cv.h>
+#include <opencv/highgui.h>
+#endif
+
+#include "cvblob.h"
+
+namespace cvb
+{
+
+ CvLabel cvLargestBlob(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->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->area<minArea)||(blob->area>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;
+ r<blob->maxy;
+ r++,imgData+=stepIn)
+ for (unsigned int c=blob->minx;c<blob->maxx;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; r<blob->maxy; r++, labels+=stepLbl, source+=stepSrc, imgData+=stepDst)
+ for (unsigned int c=blob->minx; c<blob->maxx; 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<CvLabel, CvScalar> 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;
+}
Added: trunk/src/stable/libs/cvBlob/cvblob.h
===================================================================
--- trunk/src/stable/libs/cvBlob/cvblob.h (rev 0)
+++ trunk/src/stable/libs/cvBlob/cvblob.h 2013-08-14 09:11:01 UTC (rev 973)
@@ -0,0 +1,588 @@
+// Copyright (C) 2007 by Cristóbal Carnero Liñán
+// grendel.ccl en 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 <http://www.gnu.org/licenses/>.
+//
+
+/// \file cvblob.h
+/// \brief OpenCV Blob header file.
+
+#ifdef SWIG
+%module cvblob
+%{
+#include "cvblob.h"
+%}
+#endif
+
+#ifndef CVBLOB_H
+#define CVBLOB_H
+
+#include <iostream>
+#include <map>
+#include <list>
+#include <vector>
+#include <limits>
+
+#if (defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || (defined(__APPLE__) & defined(__MACH__)))
+#include <cv.h>
+#else
+#include <opencv/cv.h>
+#endif
+
+#ifndef __CV_BEGIN__
+#define __CV_BEGIN__ __BEGIN__
+#endif
+#ifndef __CV_END__
+#define __CV_END__ __END__
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ namespace cvb
+ {
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Contours
+
+ // Chain code:
+ // 7 0 1
+ // 6 2
+ // 5 4 3
+#define CV_CHAINCODE_UP 0 ///< Up.
+#define CV_CHAINCODE_UP_RIGHT 1 ///< Up and right.
+#define CV_CHAINCODE_RIGHT 2 ///< Right.
+#define CV_CHAINCODE_DOWN_RIGHT 3 ///< Down and right.
+#define CV_CHAINCODE_DOWN 4 ///< Down.
+#define CV_CHAINCODE_DOWN_LEFT 5 ///< Down and left.
+#define CV_CHAINCODE_LEFT 6 ///< Left.
+#define CV_CHAINCODE_UP_LEFT 7 ///< Up and left.
+
+ /// \brief Move vectors of chain codes.
+ /// \see CV_CHAINCODE_UP
+ /// \see CV_CHAINCODE_UP_LEFT
+ /// \see CV_CHAINCODE_LEFT
+ /// \see CV_CHAINCODE_DOWN_LEFT
+ /// \see CV_CHAINCODE_DOWN
+ /// \see CV_CHAINCODE_DOWN_RIGHT
+ /// \see CV_CHAINCODE_RIGHT
+ /// \see CV_CHAINCODE_UP_RIGHT
+ const char cvChainCodeMoves[8][2] = { { 0, -1},
+ { 1, -1},
+ { 1, 0},
+ { 1, 1},
+ { 0, 1},
+ {-1, 1},
+ {-1, 0},
+ {-1, -1}
+ };
+
+ /// \brief Direction.
+ /// \see CV_CHAINCODE_UP
+ /// \see CV_CHAINCODE_UP_LEFT
+ /// \see CV_CHAINCODE_LEFT
+ /// \see CV_CHAINCODE_DOWN_LEFT
+ /// \see CV_CHAINCODE_DOWN
+ /// \see CV_CHAINCODE_DOWN_RIGHT
+ /// \see CV_CHAINCODE_RIGHT
+ /// \see CV_CHAINCODE_UP_RIGHT
+ typedef unsigned char CvChainCode;
+
+ /// \brief Chain code.
+ /// \see CvChainCode
+ typedef std::list<CvChainCode> CvChainCodes;
+
+ /// \brief Chain code contour.
+ /// \see CvChainCodes
+ struct CvContourChainCode
+ {
+ CvPoint startingPoint; ///< Point where contour begin.
+ CvChainCodes chainCode; ///< Polygon description based on chain codes.
+ };
+
+ typedef std::list<CvContourChainCode *> CvContoursChainCode; ///< List of contours (chain codes type).
+
+ /// \brief Polygon based contour.
+ typedef std::vector<CvPoint> CvContourPolygon;
+
+ /// \fn void cvRenderContourChainCode(CvContourChainCode const *contour, IplImage const *img, CvScalar const &color=CV_RGB(255, 255, 255))
+ /// \brief Draw a contour.
+ /// \param contour Chain code contour.
+ /// \param img Image to draw on.
+ /// \param color Color to draw (default, white).
+ /// \see CvContourChainCode
+ void cvRenderContourChainCode(CvContourChainCode const *contour, IplImage const *img, CvScalar const &color=CV_RGB(255, 255, 255));
+
+ /// \fn CvContourPolygon *cvConvertChainCodesToPolygon(CvContourChainCode const *cc)
+ /// \brief Convert a chain code contour to a polygon.
+ /// \param cc Chain code contour.
+ /// \return A polygon.
+ /// \see CvContourChainCode
+ /// \see CvContourPolygon
+ CvContourPolygon *cvConvertChainCodesToPolygon(CvContourChainCode const *cc);
+
+ /// \fn void cvRenderContourPolygon(CvContourPolygon const *contour, IplImage *img, CvScalar const &color=CV_RGB(255, 255, 255))
+ /// \brief Draw a polygon.
+ /// \param contour Polygon contour.
+ /// \param img Image to draw on.
+ /// \param color Color to draw (default, white).
+ /// \see CvContourPolygon
+ void cvRenderContourPolygon(CvContourPolygon const *contour, IplImage *img, CvScalar const &color=CV_RGB(255, 255, 255));
+
+ /// \fn double cvContourPolygonArea(CvContourPolygon const *p)
+ /// \brief Calculates area of a polygonal contour.
+ /// \param p Contour (polygon type).
+ /// \return Area of the contour.
+ double cvContourPolygonArea(CvContourPolygon const *p);
+
+ /// \fn double cvContourChainCodePerimeter(CvContourChainCode const *c)
+ /// \brief Calculates perimeter of a chain code contour.
+ /// \param c Contour (chain code type).
+ /// \return Perimeter of the contour.
+ double cvContourChainCodePerimeter(CvContourChainCode const *c);
+
+ /// \fn double cvContourPolygonPerimeter(CvContourPolygon const *p)
+ /// \brief Calculates perimeter of a polygonal contour.
+ /// \param p Contour (polygon type).
+ /// \return Perimeter of the contour.
+ double cvContourPolygonPerimeter(CvContourPolygon const *p);
+
+ /// \fn double cvContourPolygonCircularity(const CvContourPolygon *p)
+ /// \brief Calculates the circularity of a polygon (compactness measure).
+ /// \param p Contour (polygon type).
+ /// \return Circularity: a non-negative value, where 0 correspond with a circumference.
+ double cvContourPolygonCircularity(const CvContourPolygon *p);
+
+ /// \fn CvContourPolygon *cvSimplifyPolygon(CvContourPolygon const *p, double const delta=1.)
+ /// \brief Simplify a polygon reducing the number of vertex according the distance "delta".
+ /// Uses a version of the Ramer-Douglas-Peucker algorithm (http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm).
+ /// \param p Contour (polygon type).
+ /// \param delta Minimun distance.
+ /// \return A simplify version of the original polygon.
+ CvContourPolygon *cvSimplifyPolygon(CvContourPolygon const *p, double const delta=1.);
+
+ /// \fn CvContourPolygon *cvPolygonContourConvexHull(CvContourPolygon const *p)
+ /// \brief Calculates convex hull of a contour.
+ /// Uses the Melkman Algorithm. Code based on the version in http://w3.impa.br/~rdcastan/Cgeometry/.
+ /// \param p Contour (polygon type).
+ /// \return Convex hull.
+ CvContourPolygon *cvPolygonContourConvexHull(CvContourPolygon const *p);
+
+ /// \fn void cvWriteContourPolygonCSV(const CvContourPolygon& p, const std::string& filename)
+ /// \brief Write a contour to a CSV (Comma-separated values) file.
+ /// \param p Polygon contour.
+ /// \param filename File name.
+ void cvWriteContourPolygonCSV(const CvContourPolygon& p, const std::string& filename);
+
+ /// \fn void cvWriteContourPolygonSVG(const CvContourPolygon& p, const std::string& filename, const CvScalar& stroke=cvScalar(0,0,0), const CvScalar& fill=cvScalar(255,255,255))
+ /// \brief Write a contour to a SVG file (http://en.wikipedia.org/wiki/Scalable_Vector_Graphics).
+ /// \param p Polygon contour.
+ /// \param filename File name.
+ /// \param stroke Stroke color (black by default).
+ /// \param fill Fill color (white by default).
+ void cvWriteContourPolygonSVG(const CvContourPolygon& p, const std::string& filename, const CvScalar& stroke=cvScalar(0,0,0), const CvScalar& fill=cvScalar(255,255,255));
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Blobs
+
+ /// \brief Type of label.
+ /// \see IPL_DEPTH_LABEL
+ typedef unsigned int CvLabel;
+ //typedef unsigned char CvLabel;
+
+ /// \def IPL_DEPTH_LABEL
+ /// \brief Size of a label in bits.
+ /// \see CvLabel
+#define IPL_DEPTH_LABEL (sizeof(cvb::CvLabel)*8)
+
+ /// \def CV_BLOB_MAX_LABEL
+ /// \brief Max label number.
+ /// \see CvLabel.
+#define CV_BLOB_MAX_LABEL std::numeric_limits<CvLabel>::max()
+
+ /// \brief Type of identification numbers.
+ typedef unsigned int CvID;
+
+ /// \brief Struct that contain information about one blob.
+ struct CvBlob
+ {
+ CvLabel label; ///< Label assigned to the blob.
+
+ union
+ {
+ unsigned int area; ///< Area (moment 00).
+ unsigned int m00; ///< Moment 00 (area).
+ };
+
+ unsigned int minx; ///< X min.
+ unsigned int maxx; ///< X max.
+ unsigned int miny; ///< Y min.
+ unsigned int maxy; ///< y max.
+
+ CvPoint2D64f centroid; ///< Centroid.
+
+ double m10; ///< Moment 10.
+ double m01; ///< Moment 01.
+ double m11; ///< Moment 11.
+ double m20; ///< Moment 20.
+ double m02; ///< Moment 02.
+
+ double u11; ///< Central moment 11.
+ double u20; ///< Central moment 20.
+ double u02; ///< Central moment 02.
+
+ double n11; ///< Normalized central moment 11.
+ double n20; ///< Normalized central moment 20.
+ double n02; ///< Normalized central moment 02.
+
+ double p1; ///< Hu moment 1.
+ double p2; ///< Hu moment 2.
+
+ CvContourChainCode contour; ///< Contour.
+ CvContoursChainCode internalContours; ///< Internal contours.
+ };
+
+ /// \var typedef std::map<CvLabel,CvBlob *> CvBlobs
+ /// \brief List of blobs.
+ /// A map is used to access each blob from its label number.
+ /// \see CvLabel
+ /// \see CvBlob
+ typedef std::map<CvLabel,CvBlob *> CvBlobs;
+
+ /// \var typedef std::pair<CvLabel,CvBlob *> CvLabelBlob
+ /// \brief Pair (label, blob).
+ /// \see CvLabel
+ /// \see CvBlob
+ typedef std::pair<CvLabel,CvBlob *> CvLabelBlob;
+
+ /// \fn unsigned int cvLabel (IplImage const *img, IplImage *imgOut, CvBlobs &blobs);
+ /// \brief Label the connected parts of a binary image.
+ /// Algorithm based on paper "A linear-time component-labeling algorithm using contour tracing technique" of Fu Chang, Chun-Jen Chen and Chi-Jen Lu.
+ /// \param img Input binary image (depth=IPL_DEPTH_8U and num. channels=1).
+ /// \param imgOut Output image (depth=IPL_DEPTH_LABEL and num. channels=1).
+ /// \param blobs List of blobs.
+ /// \return Number of pixels that has been labeled.
+ unsigned int cvLabel (IplImage const *img, IplImage *imgOut, CvBlobs &blobs);
+
+ //IplImage *cvFilterLabel(IplImage *imgIn, CvLabel label);
+
+ /// \fn void cvFilterLabels(IplImage *imgIn, IplImage *imgOut, const CvBlobs &blobs)
+ /// \brief Draw a binary image with the blobs that have been given.
+ /// \param imgIn Input image (depth=IPL_DEPTH_LABEL and num. channels=1).
+ /// \param imgOut Output binary image (depth=IPL_DEPTH_8U and num. channels=1).
+ /// \param blobs List of blobs to be drawn.
+ /// \see cvLabel
+ void cvFilterLabels(IplImage *imgIn, IplImage *imgOut, const CvBlobs &blobs);
+
+ /// \fn CvLabel cvGetLabel(IplImage const *img, unsigned int x, unsigned int y)
+ /// \brief Get the label value from a labeled image.
+ /// \param img Label image.
+ /// \param x X coordenate.
+ /// \param y Y coordenate.
+ /// \return Label value.
+ /// \see CvLabel
+ CvLabel cvGetLabel(IplImage const *img, unsigned int x, unsigned int y);
+
+ /// \fn inline void cvReleaseBlob(CvBlob *blob)
+ /// \brief Clear a blob structure.
+ /// \param blob Blob.
+ /// \see CvBlob
+ inline void cvReleaseBlob(CvBlob *blob)
+ {
+ if (blob)
+ {
+ for (CvContoursChainCode::iterator jt=blob->internalContours.begin(); jt!=blob->internalContours.end(); ++jt)
+ {
+ CvContourChainCode *contour = *jt;
+ if (contour)
+ delete contour;
+ }
+ blob->internalContours.clear();
+
+ delete blob;
+ }
+ }
+
+ /// \fn inline void cvReleaseBlobs(CvBlobs &blobs)
+ /// \brief Clear blobs structure.
+ /// \param blobs List of blobs.
+ /// \see CvBlobs
+ inline void cvReleaseBlobs(CvBlobs &blobs)
+ {
+ for (CvBlobs::iterator it=blobs.begin(); it!=blobs.end(); ++it)
+ {
+ cvReleaseBlob((*it).second);
+ }
+ blobs.clear();
+ }
+
+ /// \fn CvLabel cvLargestBlob(const CvBlobs &blobs)
+ /// \brief Find largest blob (biggest area).
+ /// \param blobs List of blobs.
+ /// \return Label of the largest blob or 0 if there are no blobs.
+ /// \see cvLabel
+ CvLabel cvLargestBlob(const CvBlobs &blobs);
+
+ inline CvLabel cvGreaterBlob(const CvBlobs &blobs)
+ {
+ return cvLargestBlob(blobs);
+ }
+
+ /// \fn void cvFilterByArea(CvBlobs &blobs, unsigned int minArea, unsigned int maxArea)
+ /// \brief Filter blobs by area.
+ /// Those blobs whose areas are not in range will be erased from the input list of blobs.
+ /// \param blobs List of blobs.
+ /// \param minArea Minimun area.
+ /// \param maxArea Maximun area.
+ void cvFilterByArea(CvBlobs &blobs, unsigned int minArea, unsigned int maxArea);
+
+ /// \fn void cvFilterByLabel(CvBlobs &blobs, CvLabel label)
+ /// \brief Filter blobs by label.
+ /// Delete all blobs except those with label l.
+ /// \param blobs List of blobs.
+ /// \param label Label to leave.
+ void cvFilterByLabel(CvBlobs &blobs, CvLabel label);
+
+ /// \fn inline CvPoint2D64f cvCentroid(CvBlob *blob)
+ /// \brief Calculates centroid.
+ /// Centroid will be returned and stored in the blob structure.
+ /// \param blob Blob whose centroid will be calculated.
+ /// \return Centroid.
+ /// \see CvBlob
+ inline CvPoint2D64f cvCentroid(CvBlob *blob)
+ {
+ return blob->centroid=cvPoint2D64f(blob->m10/blob->area, blob->m01/blob->area);
+ }
+
+ /// \fn double cvAngle(CvBlob *blob)
+ /// \brief Calculates angle orientation of a blob.
+ /// \param blob Blob.
+ /// \return Angle orientation in radians.
+ /// \see CvBlob
+ double cvAngle(CvBlob *blob);
+
+ /// \fn cvSaveImageBlob(const char *filename, IplImage *img, CvBlob const *blob)
+ /// \brief Save the image of a blob to a file.
+ /// The function uses an image (that can be the original pre-processed image or a processed one, or even the result of cvRenderBlobs, for example) and a blob structure.
+ /// Then the function saves a copy of the part of the image where the blob is.
+ /// \param filename Name of the file.
+ /// \param img Image.
+ /// \param blob Blob.
+ /// \see CvBlob
+ /// \see cvRenderBlob
+ void cvSaveImageBlob(const char *filename, IplImage *img, CvBlob const *blob);
+
+#define CV_BLOB_RENDER_COLOR 0x0001 ///< Render each blog with a different color. \see cvRenderBlobs
+#define CV_BLOB_RENDER_CENTROID 0x0002 ///< Render centroid. \see cvRenderBlobs
+#define CV_BLOB_RENDER_BOUNDING_BOX 0x0004 ///< Render bounding box. \see cvRenderBlobs
+#define CV_BLOB_RENDER_ANGLE 0x0008 ///< Render angle. \see cvRenderBlobs
+#define CV_BLOB_RENDER_TO_LOG 0x0010 ///< Print blob data to log out. \see cvRenderBlobs
+#define CV_BLOB_RENDER_TO_STD 0x0020 ///< Print blob data to std out. \see cvRenderBlobs
+
+ /// \fn void cvRenderBlob(const IplImage *imgLabel, CvBlob *blob, IplImage *imgSource, IplImage *imgDest, unsigned short mode=0x000f, CvScalar const &color=CV_RGB(255, 255, 255), double alpha=1.)
+ /// \brief Draws or prints information about a blob.
+ /// \param imgLabel Label image (depth=IPL_DEPTH_LABEL and num. channels=1).
+ /// \param blob Blob.
+ /// \param imgSource Input image (depth=IPL_DEPTH_8U and num. channels=3).
+ /// \param imgDest Output image (depth=IPL_DEPTH_8U and num. channels=3).
+ /// \param mode Render mode. By default is CV_BLOB_RENDER_COLOR|CV_BLOB_RENDER_CENTROID|CV_BLOB_RENDER_BOUNDING_BOX|CV_BLOB_RENDER_ANGLE.
+ /// \param color Color to render (if CV_BLOB_RENDER_COLOR is used).
+ /// \param alpha If mode CV_BLOB_RENDER_COLOR is used. 1.0 indicates opaque and 0.0 translucent (1.0 by default).
+ /// \see CV_BLOB_RENDER_COLOR
+ /// \see CV_BLOB_RENDER_CENTROID
+ /// \see CV_BLOB_RENDER_BOUNDING_BOX
+ /// \see CV_BLOB_RENDER_ANGLE
+ /// \see CV_BLOB_RENDER_TO_LOG
+ /// \see CV_BLOB_RENDER_TO_STD
+ void cvRenderBlob(const IplImage *imgLabel, CvBlob *blob, IplImage *imgSource, IplImage *imgDest, unsigned short mode=0x000f, CvScalar const &color=CV_RGB(255, 255, 255), double alpha=1.);
+
+ /// \fn void cvRenderBlobs(const IplImage *imgLabel, CvBlobs &blobs, IplImage *imgSource, IplImage *imgDest, unsigned short mode=0x000f, double alpha=1.)
+ /// \brief Draws or prints information about blobs.
+ /// \param imgLabel Label image (depth=IPL_DEPTH_LABEL and num. channels=1).
+ /// \param blobs List of blobs.
+ /// \param imgSource Input image (depth=IPL_DEPTH_8U and num. channels=3).
+ /// \param imgDest Output image (depth=IPL_DEPTH_8U and num. channels=3).
+ /// \param mode Render mode. By default is CV_BLOB_RENDER_COLOR|CV_BLOB_RENDER_CENTROID|CV_BLOB_RENDER_BOUNDING_BOX|CV_BLOB_RENDER_ANGLE.
+ /// \param alpha If mode CV_BLOB_RENDER_COLOR is used. 1.0 indicates opaque and 0.0 translucent (1.0 by default).
+ /// \see CV_BLOB_RENDER_COLOR
+ /// \see CV_BLOB_RENDER_CENTROID
+ /// \see CV_BLOB_RENDER_BOUNDING_BOX
+ /// \see CV_BLOB_RENDER_ANGLE
+ /// \see CV_BLOB_RENDER_TO_LOG
+ /// \see CV_BLOB_RENDER_TO_STD
+ void cvRenderBlobs(const IplImage *imgLabel, CvBlobs &blobs, IplImage *imgSource, IplImage *imgDest, unsigned short mode=0x000f, double alpha=1.);
+
+ /// \fn void cvSetImageROItoBlob(IplImage *img, CvBlob const *blob)
+ /// \brief Set the ROI of an image to the bounding box of a blob.
+ /// \param img Image.
+ /// \param blob Blob.
+ /// \see CvBlob
+ inline void cvSetImageROItoBlob(IplImage *img, CvBlob const *blob)
+ {
+ cvSetImageROI(img, cvRect(blob->minx, blob->miny, blob->maxx-blob->minx, blob->maxy-blob->miny));
+ };
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Color
+
+ /// \fn CvScalar cvBlobMeanColor(CvBlob const *blob, IplImage const *imgLabel, IplImage const *img)
+ /// \brief Calculates mean color of a blob in an image.
+ /// \param blob Blob.
+ /// \param imgLabel Image of labels.
+ /// \param img Original image.
+ /// \return Average color.
+ CvScalar cvBlobMeanColor(CvBlob const *blob, IplImage const *imgLabel, IplImage const *img);
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Aux
+
+ /// \fn double cvDotProductPoints(CvPoint const &a, CvPoint const &b, CvPoint const &c)
+ /// \brief Dot product of the vectors ab and bc.
+ /// \param a First point.
+ /// \param b Middle point.
+ /// \param c Last point.
+ /// \return Dot product of ab and bc.
+ double cvDotProductPoints(CvPoint const &a, CvPoint const &b, CvPoint const &c);
+
+ /// \fn double cvCrossProductPoints(CvPoint const &a, CvPoint const &b, CvPoint const &c)
+ /// \brief Cross product of the vectors ab and bc.
+ /// \param a Point.
+ /// \param b Point.
+ /// \param c Point.
+ /// \return Cross product of ab and bc.
+ double cvCrossProductPoints(CvPoint const &a, CvPoint const &b, CvPoint const &c);
+
+ /// \fn double cvDistancePointPoint(CvPoint const &a, CvPoint const &b)
+ /// \brief Distance between two points.
+ /// \param a Point.
+ /// \param b Point.
+ /// \return Distance.
+ double cvDistancePointPoint(CvPoint const &a, CvPoint const &b);
+
+ /// \fn double cvDistanceLinePoint(CvPoint const &a, CvPoint const &b, CvPoint const &c, bool isSegment=true)
+ /// \brief Distance between line ab and point c.
+ /// \param a First point of the segment.
+ /// \param b Second point of the segment.
+ /// \param c Point.
+ /// \param isSegment If false then the distance will be calculated from the line defined by the points a and b, to the point c.
+ /// \return Distance between ab and c.
+ double cvDistanceLinePoint(CvPoint const &a, CvPoint const &b, CvPoint const &c, bool isSegment=true);
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Tracking
+
+ /// \brief Struct that contain information about one track.
+ /// \see CvID
+ /// \see CvLabel
+ struct CvTrack
+ {
+ CvID id; ///< Track identification number.
+
+ CvLabel label; ///< Label assigned to the blob related to this track.
+
+ unsigned int minx; ///< X min.
+ unsigned int maxx; ///< X max.
+ unsigned int miny; ///< Y min.
+ unsigned int maxy; ///< y max.
+
+ CvPoint2D64f centroid; ///< Centroid.
+
+ unsigned int lifetime; ///< Indicates how much frames the object has been in scene.
+ unsigned int active; ///< Indicates number of frames that has been active from last inactive period.
+ unsigned int inactive; ///< Indicates number of frames that has been missing.
+ };
+
+ /// \var typedef std::map<CvID, CvTrack *> CvTracks
+ /// \brief List of tracks.
+ /// \see CvID
+ /// \see CvTrack
+ typedef std::map<CvID, CvTrack *> CvTracks;
+
+ /// \var typedef std::pair<CvID, CvTrack *> CvIDTrack
+ /// \brief Pair (identification number, track).
+ /// \see CvID
+ /// \see CvTrack
+ typedef std::pair<CvID, CvTrack *> CvIDTrack;
+
+ /// \fn inline void cvReleaseTracks(CvTracks &tracks)
+ /// \brief Clear tracks structure.
+ /// \param tracks List of tracks.
+ /// \see CvTracks
+ inline void cvReleaseTracks(CvTracks &tracks)
+ {
+ for (CvTracks::iterator it=tracks.begin(); it!=tracks.end(); it++)
+ {
+ CvTrack *track = (*it).second;
+ if (track) delete track;
+ }
+
+ tracks.clear();
+ }
+
+ /// \fn cvUpdateTracks(CvBlobs const &b, CvTracks &t, const double thDistance, const unsigned int thInactive, const unsigned int thActive=0)
+ /// \brief Updates list of tracks based on current blobs.
+ /// Tracking based on:
+ /// A. Senior, A. Hampapur, Y-L Tian, L. Brown, S. Pankanti, R. Bolle. Appearance Models for
+ /// Occlusion Handling. Second International workshop on Performance Evaluation of Tracking and
+ /// Surveillance Systems & CVPR'01. December, 2001.
+ /// (http://www.research.ibm.com/peoplevision/PETS2001.pdf)
+ /// \param b List of blobs.
+ /// \param t List of tracks.
+ /// \param thDistance Max distance to determine when a track and a blob match.
+ /// \param thInactive Max number of frames a track can be inactive.
+ /// \param thActive If a track becomes inactive but it has been active less than thActive frames, the track will be deleted.
+ /// \see CvBlobs
+ /// \see Tracks
+ void cvUpdateTracks(CvBlobs const &b, CvTracks &t, const double thDistance, const unsigned int thInactive, const unsigned int thActive=0);
+
+#define CV_TRACK_RENDER_ID 0x0001 ///< Print the ID of each track in the image. \see cvRenderTracks
+#define CV_TRACK_RENDER_BOUNDING_BOX 0x0002 ///< Draw bounding box of each track in the image. \see cvRenderTracks
+#define CV_TRACK_RENDER_TO_LOG 0x0010 ///< Print track info to log out. \see cvRenderTracks
+#define CV_TRACK_RENDER_TO_STD 0x0020 ///< Print track info to log out. \see cvRenderTracks
+
+ /// \fn void cvRenderTracks(CvTracks const tracks, IplImage *imgSource, IplImage *imgDest, unsigned short mode=0x00ff, CvFont *font=NULL)
+ /// \brief Prints tracks information.
+ /// \param tracks List of tracks.
+ /// \param imgSource Input image (depth=IPL_DEPTH_8U and num. channels=3).
+ /// \param imgDest Output image (depth=IPL_DEPTH_8U and num. channels=3).
+ /// \param mode Render mode. By default is CV_TRACK_RENDER_ID|CV_TRACK_RENDER_BOUNDING_BOX.
+ /// \param font OpenCV font for print on the image.
+ /// \see CV_TRACK_RENDER_ID
+ /// \see CV_TRACK_RENDER_BOUNDING_BOX
+ /// \see CV_TRACK_RENDER_TO_LOG
+ /// \see CV_TRACK_RENDER_TO_STD
+ void cvRenderTracks(CvTracks const tracks, IplImage *imgSource, IplImage *imgDest, unsigned short mode=0x000f, CvFont *font=NULL);
+ }
+#ifdef __cplusplus
+}
+#endif
+
+/// \fn std::ostream& operator<< (std::ostream& output, const cvb::CvBlob& b)
+/// \brief Overload operator "<<" for printing blob structure.
+/// \return Stream.
+std::ostream& operator<< (std::ostream& output, const cvb::CvBlob& b);
+
+/// \fn std::ostream& operator<< (std::ostream& output, const cvb::CvContourPolygon& p)
+/// \brief Overload operator "<<" for printing polygons in CSV format.
+/// \return Stream.
+std::ostream& operator<< (std::ostream& output, const cvb::CvContourPolygon& p);
+
+/// \fn std::ostream& operator<< (std::ostream& output, const cvb::CvTrack& t)
+/// \brief Overload operator "<<" for printing track structure.
+/// \return Stream.
+std::ostream& operator<< (std::ostream& output, const cvb::CvTrack& t);
+#endif
Added: trunk/src/stable/libs/cvBlob/cvcolor.cpp
===================================================================
--- trunk/src/stable/libs/cvBlob/cvcolor.cpp (rev 0)
+++ trunk/src/stable/libs/cvBlob/cvcolor.cpp 2013-08-14 09:11:01 UTC (rev 973)
@@ -0,0 +1,104 @@
+// Copyright (C) 2007 by Cristóbal Carnero Liñán
+// grendel.ccl en 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 <http://www.gnu.org/licenses/>.
+//
+
+#include <cmath>
+#include <iostream>
+using namespace std;
+
+#if (defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || (defined(__APPLE__) & defined(__MACH__)))
+#include <cv.h>
+#else
+#include <opencv/cv.h>
+#endif
+
+#include "cvblob.h"
+
+namespace cvb
+{
+
+ CvScalar cvBlobMeanColor(CvBlob const *blob, IplImage const *imgLabel, IplImage const *img)
+ {
+ CV_FUNCNAME("cvBlobMeanColor");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(imgLabel&&(imgLabel->depth==IPL_DEPTH_LABEL)&&(imgLabel->nChannels==1));
+ CV_ASSERT(img&&(img->depth==IPL_DEPTH_8U)&&(img->nChannels==3));
+
+ int stepLbl = imgLabel->widthStep/(imgLabel->depth/8);
+ int stepImg = img->widthStep/(img->depth/8);
+ int imgLabel_width = imgLabel->width;
+ int imgLabel_height = imgLabel->height;
+ int imgLabel_offset = 0;
+ int img_width = img->width;
+ int img_height = img->height;
+ int img_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(img->roi)
+ {
+ img_width = img->roi->width;
+ img_height = img->roi->height;
+ img_offset = (img->nChannels * img->roi->xOffset) + (img->roi->yOffset * stepImg);
+ }
+
+ CvLabel *labels = (CvLabel *)imgLabel->imageData + imgLabel_offset;
+ unsigned char *imgData = (unsigned char *)img->imageData + img_offset;
+
+ double mb = 0;
+ double mg = 0;
+ double mr = 0;
+ double pixels = (double)blob->area;
+
+ for (unsigned int r=0; r<(unsigned int)imgLabel_height; r++, labels+=stepLbl, imgData+=stepImg)
+ for (unsigned int c=0; c<(unsigned int)imgLabel_width; c++)
+ {
+ if (labels[c]==blob->label)
+ {
+ mb += ((double)imgData[img->nChannels*c+0])/pixels; // B
+ mg += ((double)imgData[img->nChannels*c+1])/pixels; // G
+ mr += ((double)imgData[img->nChannels*c+2])/pixels; // R
+ }
+ }
+
+ /*double mb = 0;
+ double mg = 0;
+ double mr = 0;
+ double pixels = (double)blob->area;
+ for (unsigned int y=0; y<imgLabel->height; y++)
+ for (unsigned int x=0; x<imgLabel->width; x++)
+ {
+ if (cvGetLabel(imgLabel, x, y)==blob->label)
+ {
+ CvScalar color = cvGet2D(img, y, x);
+ mb += color.val[0]/pixels;
+ mg += color.val[1]/pixels;
+ mr += color.val[2]/pixels;
+ }
+ }*/
+
+ return cvScalar(mr, mg, mb);
+ }
+ __CV_END__;
+ }
+
+}
Added: trunk/src/stable/libs/cvBlob/cvcontour.cpp
===================================================================
--- trunk/src/stable/libs/cvBlob/cvcontour.cpp (rev 0)
+++ trunk/src/stable/libs/cvBlob/cvcontour.cpp 2013-08-14 09:11:01 UTC (rev 973)
@@ -0,0 +1,436 @@
+// Copyright (C) 2007 by Cristóbal Carnero Liñán
+// grendel.ccl en 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 <http://www.gnu.org/licenses/>.
+//
+
+#include <climits>
+
+#define _USE_MATH_DEFINES
+#include <cmath>
+
+#include <deque>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+using namespace std;
+
+#if (defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || (defined(__APPLE__) & defined(__MACH__)))
+#include <cv.h>
+#else
+#include <opencv/cv.h>
+#endif
+
+#include "cvblob.h"
+
+#ifdef M_PI
+const double pi = M_PI;
+#else
+const double pi = std::atan(1.)*4.;
+#endif // M_PI
+
+namespace cvb
+{
+
+ void cvRenderContourChainCode(CvContourChainCode const *contour, IplImage const *img, CvScalar const &color)
+ {
+ CV_FUNCNAME("cvRenderContourChainCode");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(img&&(img->depth==IPL_DEPTH_8U)&&(img->nChannels==3));
+
+ int stepDst = img->widthStep/(img->depth/8);
+ int img_width = img->width;
+ int img_height = img->height;
+ int img_offset = 0;
+
+ if(img->roi)
+ {
+ img_width = img->roi->width;
+ img_height = img->roi->height;
+ img_offset = (img->nChannels * img->roi->xOffset) + (img->roi->yOffset * stepDst);
+ }
+
+ unsigned char *imgData = (unsigned char *)img->imageData + img_offset;
+
+ unsigned int x = contour->startingPoint.x;
+ unsigned int y = contour->startingPoint.y;
+
+ for (CvChainCodes::const_iterator it=contour->chainCode.begin(); it!=contour->chainCode.end(); ++it)
+ {
+ imgData[img->nChannels*x+img->widthStep*y+0] = (unsigned char)(color.val[0]); // Blue
+ imgData[img->nChannels*x+img->widthStep*y+1] = (unsigned char)(color.val[1]); // Green
+ imgData[img->nChannels*x+img->widthStep*y+2] = (unsigned char)(color.val[2]); // Red
+
+ x += cvChainCodeMoves[*it][0];
+ y += cvChainCodeMoves[*it][1];
+ }
+ }
+ __CV_END__;
+ }
+
+ CvContourPolygon *cvConvertChainCodesToPolygon(CvContourChainCode const *cc)
+ {
+ CV_FUNCNAME("cvConvertChainCodesToPolygon");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(cc!=NULL);
+
+ CvContourPolygon *contour = new CvContourPolygon;
+
+ unsigned int x = cc->startingPoint.x;
+ unsigned int y = cc->startingPoint.y;
+ contour->push_back(cvPoint(x, y));
+
+ if (cc->chainCode.size())
+ {
+ CvChainCodes::const_iterator it=cc->chainCode.begin();
+ CvChainCode lastCode = *it;
+
+ x += cvChainCodeMoves[*it][0];
+ y += cvChainCodeMoves[*it][1];
+
+ ++it;
+
+ for (; it!=cc->chainCode.end(); ++it)
+ {
+ if (lastCode!=*it)
+ {
+ contour->push_back(cvPoint(x, y));
+ lastCode=*it;
+ }
+
+ x += cvChainCodeMoves[*it][0];
+ y += cvChainCodeMoves[*it][1];
+ }
+ }
+
+ return contour;
+ }
+ __CV_END__;
+ }
+
+ void cvRenderContourPolygon(CvContourPolygon const *contour, IplImage *img, CvScalar const &color)
+ {
+ CV_FUNCNAME("cvRenderContourPolygon");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(img&&(img->depth==IPL_DEPTH_8U)&&(img->nChannels==3));
+
+ CvContourPolygon::const_iterator it=contour->begin();
+
+ if (it!=contour->end())
+ {
+ unsigned int fx, x, fy, y;
+ fx = x = it->x;
+ fy = y = it->y;
+
+ for (; it!=contour->end(); ++it)
+ {
+ cvLine(img, cvPoint(x, y), cvPoint(it->x, it->y), color, 1);
+ x = it->x;
+ y = it->y;
+ }
+
+ cvLine(img, cvPoint(x, y), cvPoint(fx, fy), color, 1);
+ }
+ }
+ __CV_END__;
+ }
+
+ double cvContourPolygonArea(CvContourPolygon const *p)
+ {
+ CV_FUNCNAME("cvContourPolygonArea");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(p!=NULL);
+
+ if (p->size()<=2)
+ return 1.;
+
+ CvContourPolygon::const_iterator it=p->begin();
+ CvPoint lastPoint = p->back();
+
+ double a = 0.;
+
+ for (; it!=p->end(); ++it)
+ {
+ a += lastPoint.x*it->y - lastPoint.y*it->x;
+ lastPoint = *it;
+ }
+
+ return a*0.5;
+ }
+ __CV_END__;
+ }
+
+ double cvContourChainCodePerimeter(CvContourChainCode const *c)
+ {
+ CV_FUNCNAME("cvContourChainCodePerimeter");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(c!=NULL);
+
+ double perimeter = 0.;
+
+ for(CvChainCodes::const_iterator it=c->chainCode.begin(); it!=c->chainCode.end(); ++it)
+ {
+ if ((*it)%2)
+ perimeter+=sqrt(1.+1.);
+ else
+ perimeter+=1.;
+ }
+
+ return perimeter;
+ }
+ __CV_END__;
+ }
+
+ double cvContourPolygonPerimeter(CvContourPolygon const *p)
+ {
+ CV_FUNCNAME("cvContourPolygonPerimeter");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(p!=NULL);
+
+ double perimeter = cvDistancePointPoint((*p)[p->size()-1], (*p)[0]);
+
+ for (unsigned int i=0; i<p->size()-1; i++)
+ perimeter+=cvDistancePointPoint((*p)[i], (*p)[i+1]);
+
+ return perimeter;
+ }
+ __CV_END__;
+ }
+
+ double cvContourPolygonCircularity(const CvContourPolygon *p)
+ {
+ CV_FUNCNAME("cvContourPolygonCircularity");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(p!=NULL);
+
+ double l = cvContourPolygonPerimeter(p);
+ double c = (l*l/cvContourPolygonArea(p)) - 4.*pi;
+
+ if (c>=0.)
+ return c;
+ else // This could happen if the blob it's only a pixel: the perimeter will be 0. Another solution would be to force "cvContourPolygonPerimeter" to be 1 or greater.
+ return 0.;
+ }
+ __CV_END__;
+ }
+
+ void simplifyPolygonRecursive(CvContourPolygon const *p, int const i1, int const i2, bool *pnUseFlag, double const delta)
+ {
+ CV_FUNCNAME("cvSimplifyPolygonRecursive");
+ __CV_BEGIN__;
+ {
+ int endIndex = (i2<0)?p->size():i2;
+
+ if (abs(i1-endIndex)<=1)
+ return;
+
+ CvPoint firstPoint = (*p)[i1];
+ CvPoint lastPoint = (i2<0)?p->front():(*p)[i2];
+
+ double furtherDistance=0.;
+ int furtherIndex=0;
+
+ for (int i=i1+1; i<endIndex; i++)
+ {
+ double d = cvDistanceLinePoint(firstPoint, lastPoint, (*p)[i]);
+
+ if ((d>=delta)&&(d>furtherDistance))
+ {
+ furtherDistance=d;
+ furtherIndex=i;
+ }
+ }
+
+ if (furtherIndex)
+ {
+ pnUseFlag[furtherIndex]=true;
+
+ simplifyPolygonRecursive(p, i1, furtherIndex, pnUseFlag, delta);
+ simplifyPolygonRecursive(p, furtherIndex, i2, pnUseFlag, delta);
+ }
+ }
+ __CV_END__;
+ }
+
+ CvContourPolygon *cvSimplifyPolygon(CvContourPolygon const *p, double const delta)
+ {
+ CV_FUNCNAME("cvSimplifyPolygon");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(p!=NULL);
+
+ double furtherDistance=0.;
+ unsigned int furtherIndex=0;
+
+ CvContourPolygon::const_iterator it=p->begin();
+ ++it;
+ for (unsigned int i=1; it!=p->end(); ++it, i++)
+ {
+ double d = cvDistancePointPoint(*it, p->front());
+
+ if (d>furtherDistance)
+ {
+ furtherDistance = d;
+ furtherIndex = i;
+ }
+ }
+
+ if (furtherDistance<delta)
+ {
+ CvContourPolygon *result = new CvContourPolygon;
+ result->push_back(p->front());
+ return result;
+ }
+
+ bool *pnUseFlag = new bool[p->size()];
+ for (unsigned int i=1; i<p->size(); i++) pnUseFlag[i] = false;
+
+ pnUseFlag[0] = pnUseFlag[furtherIndex] = true;
+
+ simplifyPolygonRecursive(p, 0, furtherIndex, pnUseFlag, delta);
+ simplifyPolygonRecursive(p, furtherIndex, -1, pnUseFlag, delta);
+
+ CvContourPolygon *result = new CvContourPolygon;
+
+ for (unsigned int i=0; i<p->size(); i++)
+ if (pnUseFlag[i])
+ result->push_back((*p)[i]);
+
+ delete[] pnUseFlag;
+
+ return result;
+ }
+ __CV_END__;
+ }
+
+ CvContourPolygon *cvPolygonContourConvexHull(CvContourPolygon const *p)
+ {
+ CV_FUNCNAME("cvPolygonContourConvexHull");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(p!=NULL);
+
+ if (p->size()<=3)
+ {
+ return new CvContourPolygon(p->begin(), p->end());
+ }
+
+ deque<CvPoint> dq;
+
+ if (cvCrossProductPoints((*p)[0], (*p)[1], (*p)[2])>0)
+ {
+ dq.push_back((*p)[0]);
+ dq.push_back((*p)[1]);
+ }
+ else
+ {
+ dq.push_back((*p)[1]);
+ dq.push_back((*p)[0]);
+ }
+
+ dq.push_back((*p)[2]);
+ dq.push_front((*p)[2]);
+
+ for (unsigned int i=3; i<p->size(); i++)
+ {
+ int s = dq.size();
+
+ if ((cvCrossProductPoints((*p)[i], dq.at(0), dq.at(1))>=0) && (cvCrossProductPoints(dq.at(s-2), dq.at(s-1), (*p)[i])>=0))
+ continue; // TODO Optimize.
+
+ while (cvCrossProductPoints(dq.at(s-2), dq.at(s-1), (*p)[i])<0)
+ {
+ dq.pop_back();
+ s = dq.size();
+ }
+
+ dq.push_back((*p)[i]);
+
+ while (cvCrossProductPoints((*p)[i], dq.at(0), dq.at(1))<0)
+ dq.pop_front();
+
+ dq.push_front((*p)[i]);
+ }
+
+ return new CvContourPolygon(dq.begin(), dq.end());
+ }
+ __CV_END__;
+ }
+
+ void cvWriteContourPolygonCSV(const CvContourPolygon& p, const string& filename)
+ {
+ ofstream f;
+ f.open(filename.c_str());
+
+ f << p << endl;
+
+ f.close();
+ }
+
+ void cvWriteContourPolygonSVG(const CvContourPolygon& p, const string& filename, const CvScalar& stroke, const CvScalar& fill)
+ {
+ int minx=INT_MAX;
+ int miny=INT_MAX;
+ int maxx=INT_MIN;
+ int maxy=INT_MIN;
+
+ stringstream buffer("");
+
+ for (CvContourPolygon::const_iterator it=p.begin(); it!=p.end(); ++it)
+ {
+ if (it->x>maxx)
+ maxx = it->x;
+ if (it->x<minx)
+ minx = it->x;
+
+ if (it->y>maxy)
+ maxy = it->y;
+ if (it->y<miny)
+ miny = it->y;
+
+ buffer << it->x << "," << it->y << " ";
+ }
+
+ ofstream f;
+ f.open(filename.c_str());
+
+ f << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" standalone=\"no\"?>" << endl;
+ f << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">" << endl;
+ f << "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\" width=\"" << maxx-minx << "px\" height=\"" << maxy-miny << "px\" viewBox=\"" << minx << " " << miny << " " << maxx << " " << maxy << "\" zoomAndPan=\"disable\" >" << endl;
+
+ f << "<polygon fill=\"rgb(" << fill.val[0] << "," << fill.val[1] << "," << fill.val[2] << ")\" stroke=\"rgb(" << stroke.val[0] << "," << stroke.val[1] << "," << stroke.val[2] << ")\" stroke-width=\"1\" points=\"" << buffer.str() << "\"/>" << endl;
+
+ f << "</svg>" << endl;
+
+ f.close();
+ }
+
+}
+
+ostream& operator<< (ostream& output, const cvb::CvContourPolygon& p)
+{
+ for (cvb::CvContourPolygon::const_iterator it=p.begin(); it!=p.end(); ++it)
+ output << it->x << ", " << it->y << endl;
+
+ return output;
+}
Added: trunk/src/stable/libs/cvBlob/cvlabel.cpp
===================================================================
--- trunk/src/stable/libs/cvBlob/cvlabel.cpp (rev 0)
+++ trunk/src/stable/libs/cvBlob/cvlabel.cpp 2013-08-14 09:11:01 UTC (rev 973)
@@ -0,0 +1,444 @@
+// Copyright (C) 2007 by Cristóbal Carnero Liñán
+// grendel.ccl en 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 <http://www.gnu.org/licenses/>.
+//
+
+#include <stdexcept>
+#include <iostream>
+using namespace std;
+
+#if (defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || (defined(__APPLE__) & defined(__MACH__)))
+#include <cv.h>
+#else
+#include <opencv/cv.h>
+#endif
+
+#include "cvblob.h"
+
+namespace cvb
+{
+ const char movesE[4][3][4] = { { {-1, -1, 3, CV_CHAINCODE_UP_LEFT }, { 0, -1, 0, CV_CHAINCODE_UP }, { 1, -1, 0, CV_CHAINCODE_UP_RIGHT } },
+ { { 1, -1, 0, CV_CHAINCODE_UP_RIGHT }, { 1, 0, 1, CV_CHAINCODE_RIGHT}, { 1, 1, 1, CV_CHAINCODE_DOWN_RIGHT } },
+ { { 1, 1, 1, CV_CHAINCODE_DOWN_RIGHT}, { 0, 1, 2, CV_CHAINCODE_DOWN }, {-1, 1, 2, CV_CHAINCODE_DOWN_LEFT } },
+ { {-1, 1, 2, CV_CHAINCODE_DOWN_LEFT }, {-1, 0, 3, CV_CHAINCODE_LEFT }, {-1, -1, 3, CV_CHAINCODE_UP_LEFT } }
+ };
+
+ const char movesI[4][3][4] = { { { 1, -1, 3, CV_CHAINCODE_UP_RIGHT }, { 0, -1, 0, CV_CHAINCODE_UP }, {-1, -1, 0, CV_CHAINCODE_UP_LEFT } },
+ { {-1, -1, 0, CV_CHAINCODE_UP_LEFT }, {-1, 0, 1, CV_CHAINCODE_LEFT }, {-1, 1, 1, CV_CHAINCODE_DOWN_LEFT } },
+ { {-1, 1, 1, CV_CHAINCODE_DOWN_LEFT }, { 0, 1, 2, CV_CHAINCODE_DOWN }, { 1, 1, 2, CV_CHAINCODE_DOWN_RIGHT } },
+ { { 1, 1, 2, CV_CHAINCODE_DOWN_RIGHT }, { 1, 0, 3, CV_CHAINCODE_RIGHT}, { 1, -1, 3, CV_CHAINCODE_UP_RIGHT } }
+ };
+
+
+ unsigned int cvLabel (IplImage const *img, IplImage *imgOut, CvBlobs &blobs)
+ {
+ CV_FUNCNAME("cvLabel");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(img&&(img->depth==IPL_DEPTH_8U)&&(img->nChannels==1));
+ CV_ASSERT(imgOut&&(imgOut->depth==IPL_DEPTH_LABEL)&&(imgOut->nChannels==1));
+
+ unsigned int numPixels=0;
+
+ cvSetZero(imgOut);
+
+ CvLabel label=0;
+ cvReleaseBlobs(blobs);
+
+ unsigned int stepIn = img->widthStep / (img->depth / 8);
+ unsigned int stepOut = imgOut->widthStep / (imgOut->depth / 8);
+ unsigned int imgIn_width = img->width;
+ unsigned int imgIn_height = img->height;
+ unsigned int imgIn_offset = 0;
+ unsigned int imgOut_width = imgOut->width;
+ unsigned int imgOut_height = imgOut->height;
+ unsigned int imgOut_offset = 0;
+ if(img->roi)
+ {
+ imgIn_width = img->roi->width;
+ imgIn_height = img->roi->height;
+ imgIn_offset = img->roi->xOffset + (img->roi->yOffset * stepIn);
+ }
+ if(imgOut->roi)
+ {
+ imgOut_width = imgOut->roi->width;
+ imgOut_height = imgOut->roi->height;
+ imgOut_offset = imgOut->roi->xOffset + (imgOut->roi->yOffset * stepOut);
+ }
+
+ unsigned char *imgDataIn = (unsigned char *)img->imageData + imgIn_offset;
+ CvLabel *imgDataOut = (CvLabel *)imgOut->imageData + imgOut_offset;
+
+#define imageIn(X, Y) imgDataIn[(X) + (Y)*stepIn]
+#define imageOut(X, Y) imgDataOut[(X) + (Y)*stepOut]
+
+ CvLabel lastLabel = 0;
+ CvBlob *lastBlob = NULL;
+
+ for (unsigned int y=0; y<imgIn_height; y++)
+ {
+ for (unsigned int x=0; x<imgIn_width; x++)
+ {
+ if (imageIn(x, y))
+ {
+ bool labeled = imageOut(x, y);
+
+ if ((!imageOut(x, y))&&((y==0)||(!imageIn(x, y-1))))
+ {
+ labeled = true;
+
+ // Label contour.
+ label++;
+ CV_ASSERT(label!=CV_BLOB_MAX_LABEL);
+
+ imageOut(x, y) = label;
+ numPixels++;
+
+ // XXX This is not necessary at all. I only do this for consistency.
+ if (y>0)
+ imageOut(x, y-1) = CV_BLOB_MAX_LABEL;
+
+ CvBlob *blob = new CvBlob;
+ blob->label = label;
+ blob->area = 1;
+ blob->minx = x; blob->maxx = x;
+ blob->miny = y; blob->maxy = y;
+ blob->m10=x; blob->m01=y;
+ blob->m11=x*y;
+ blob->m20=x*x; blob->m02=y*y;
+ blob->internalContours.clear();
+ blobs.insert(CvLabelBlob(label,blob));
+
+ lastLabel = label;
+ lastBlob = blob;
+
+ blob->contour.startingPoint = cvPoint(x, y);
+
+ unsigned char direction=1;
+ unsigned int xx = x;
+ unsigned int yy = y;
+
+
+ bool contourEnd = false;
+
+ do
+ {
+ for (unsigned int numAttempts=0; numAttempts<3; numAttempts++)
+ {
+ bool found = false;
+
+ for (unsigned char i=0; i<3; i++)
+ {
+ int nx = xx+movesE[direction][i][0];
+ int ny = yy+movesE[direction][i][1];
+ if ((nx<imgIn_width)&&(nx>=0)&&(ny<imgIn_height)&&(ny>=0))
+ {
+ if (imageIn(nx, ny))
+ {
+ found = true;
+
+ blob->contour.chainCode.push_back(movesE[direction][i][3]);
+
+ xx=nx;
+ yy=ny;
+
+ direction=movesE[direction][i][2];
+ break;
+ }
+ else
+ {
+ imageOut(nx, ny) = CV_BLOB_MAX_LABEL;
+ }
+ }
+ }
+
+ if (!found)
+ direction=(direction+1)%4;
+ else
+ {
+ if (imageOut(xx, yy) != label)
+ {
+ imageOut(xx, yy) = label;
+ numPixels++;
+
+ if (xx<blob->minx) blob->minx = xx;
+ else if (xx>blob->maxx) blob->maxx = xx;
+ if (yy<blob->miny) blob->miny = yy;
+ else if (yy>blob->maxy) blob->maxy = yy;
+
+ blob->area++;
+ blob->m10+=xx; blob->m01+=yy;
+ blob->m11+=xx*yy;
+ blob->m20+=xx*xx; blob->m02+=yy*yy;
+ }
+
+ break;
+ }
+
+ if (contourEnd = ((xx==x) && (yy==y) && (direction==1)))
+ break;
+ }
+ }
+ while (!contourEnd);
+
+ }
+
+ if ((y+1<imgIn_height)&&(!imageIn(x, y+1))&&(!imageOut(x, y+1)))
+ {
+ labeled = true;
+
+ // Label internal contour
+ CvLabel l;
+ CvBlob *blob = NULL;
+
+ if (!imageOut(x, y))
+ {
+ /*if (!imageOut(x-1, y))
+ {
+ cerr << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
+ continue;
+ }*/
+
+ l = imageOut(x-1, y);
+
+ imageOut(x, y) = l;
+ numPixels++;
+
+ if (l==lastLabel)
+ blob = lastBlob;
+ else
+ {
+ blob = blobs.find(l)->second;
+ lastLabel = l;
+ lastBlob = blob;
+ }
+ blob->area++;
+ blob->m10+=x; blob->m01+=y;
+ blob->m11+=x*y;
+ blob->m20+=x*x; blob->m02+=y*y;
+ }
+ else
+ {
+ l = imageOut(x, y);
+
+ if (l==lastLabel)
+ blob = lastBlob;
+ else
+ {
+ blob = blobs.find(l)->second;
+ lastLabel = l;
+ lastBlob = blob;
+ }
+ }
+
+ // XXX This is not necessary (I believe). I only do this for consistency.
+ imageOut(x, y+1) = CV_BLOB_MAX_LABEL;
+
+ CvContourChainCode *contour = new CvContourChainCode;
+ contour->startingPoint = cvPoint(x, y);
+
+ unsigned char direction = 3;
+ unsigned int xx = x;
+ unsigned int yy = y;
+
+ do
+ {
+ for (unsigned int numAttempts=0; numAttempts<3; numAttempts++)
+ {
+ bool found = false;
+
+ for (unsigned char i=0; i<3; i++)
+ {
+ int nx = xx+movesI[direction][i][0];
+ int ny = yy+movesI[direction][i][1];
+ if (imageIn(nx, ny))
+ {
+ found = true;
+
+ contour->chainCode.push_back(movesI[direction][i][3]);
+
+ xx=nx;
+ yy=ny;
+
+ direction=movesI[direction][i][2];
+ break;
+ }
+ else
+ {
+ imageOut(nx, ny) = CV_BLOB_MAX_LABEL;
+ }
+ }
+
+ if (!found)
+ direction=(direction+1)%4;
+ else
+ {
+ if (!imageOut(xx, yy))
+ {
+ imageOut(xx, yy) = l;
+ numPixels++;
+
+ blob->area++;
+ blob->m10+=xx; blob->m01+=yy;
+ blob->m11+=xx*yy;
+ blob->m20+=xx*xx; blob->m02+=yy*yy;
+ }
+
+ break;
+ }
+ }
+ }
+ while (!(xx==x && yy==y));
+
+ blob->internalContours.push_back(contour);
+ }
+
+ //else if (!imageOut(x, y))
+ if (!labeled)
+ {
+ // Internal pixel
+ CvLabel l = imageOut(x-1, y);
+
+ imageOut(x, y) = l;
+ numPixels++;
+
+ CvBlob *blob = NULL;
+ if (l==lastLabel)
+ blob = lastBlob;
+ else
+ {
+ blob = blobs.find(l)->second;
+ lastLabel = l;
+ lastBlob = blob;
+ }
+ blob->area++;
+ blob->m10+=x; blob->m01+=y;
+ blob->m11+=x*y;
+ blob->m20+=x*x; blob->m02+=y*y;
+ }
+ }
+ }
+ }
+
+ for (CvBlobs::iterator it=blobs.begin(); it!=blobs.end(); ++it)
+ {
+ cvCentroid((*it).second);
+
+ (*it).second->u11 = (*it).second->m11 - ((*it).second->m10*(*it).second->m01)/(*it).second->m00;
+ (*it).second->u20 = (*it).second->m20 - ((*it).second->m10*(*it).second->m10)/(*it).second->m00;
+ (*it).second->u02 = (*it).second->m02 - ((*it).second->m01*(*it).second->m01)/(*it).second->m00;
+
+ double m00_2 = (*it).second->m00 * (*it).second->m00;
+
+ (*it).second->n11 = (*it).second->u11 / m00_2;
+ (*it).second->n20 = (*it).second->u20 / m00_2;
+ (*it).second->n02 = (*it).second->u02 / m00_2;
+
+ (*it).second->p1 = (*it).second->n20 + (*it).second->n02;
+
+ double nn = (*it).second->n20 - (*it).second->n02;
+ (*it).second->p2 = nn*nn + 4.*((*it).second->n11*(*it).second->n11);
+ }
+
+ return numPixels;
+
+ }
+ __CV_END__;
+ }
+
+ void cvFilterLabels(IplImage *imgIn, IplImage *imgOut, const CvBlobs &blobs)
+ {
+ CV_FUNCNAME("cvFilterLabels");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(imgIn&&(imgIn->depth==IPL_DEPTH_LABEL)&&(imgIn->nChannels==1));
+ CV_ASSERT(imgOut&&(imgOut->depth==IPL_DEPTH_8U)&&(imgOut->nChannels==1));
+
+ int stepIn = imgIn->widthStep / (imgIn->depth / 8);
+ int stepOut = imgOut->widthStep / (imgOut->depth / 8);
+ int imgIn_width = imgIn->width;
+ int imgIn_height = imgIn->height;
+ int imgIn_offset = 0;
+ int imgOut_width = imgOut->width;
+ int imgOut_height = imgOut->height;
+ int imgOut_offset = 0;
+ if(imgIn->roi)
+ {
+ imgIn_width = imgIn->roi->width;
+ imgIn_height = imgIn->roi->height;
+ imgIn_offset = imgIn->roi->xOffset + (imgIn->roi->yOffset * stepIn);
+ }
+ if(imgOut->roi)
+ {
+ imgOut_width = imgOut->roi->width;
+ imgOut_height = imgOut->roi->height;
+ imgOut_offset = imgOut->roi->xOffset + (imgOut->roi->yOffset * stepOut);
+ }
+
+ char *imgDataOut=imgOut->imageData + imgOut_offset;
+ CvLabel *imgDataIn=(CvLabel *)imgIn->imageData + imgIn_offset;
+
+ for (unsigned int r=0;r<(unsigned int)imgIn_height;r++,
+ imgDataIn+=stepIn,imgDataOut+=stepOut)
+ {
+ for (unsigned int c=0;c<(unsigned int)imgIn_width;c++)
+ {
+ if (imgDataIn[c])
+ {
+ if (blobs.find(imgDataIn[c])==blobs.end()) imgDataOut[c]=0x00;
+ else imgDataOut[c]=(char)0xff;
+ }
+ else
+ imgDataOut[c]=0x00;
+ }
+ }
+ }
+ __CV_END__;
+ }
+
+
+ CvLabel cvGetLabel(IplImage const *img, unsigned int x, unsigned int y)
+ {
+ CV_FUNCNAME("cvGetLabel");
+ __CV_BEGIN__;
+ {
+ CV_ASSERT(img&&(img->depth==IPL_DEPTH_LABEL)&&(img->nChannels==1));
+
+ int step = img->widthStep / (img->depth / 8);
+ int img_width = 0;
+ int img_height= 0;
+ int img_offset = 0;
+ if(img->roi)
+ {
+ img_width = img->roi->width;
+ img_height = img->roi->height;
+ img_offset = img->roi->xOffset + (img->roi->yOffset * step);
+ }
+ else
+ {
+ img_width = img->width;
+ img_height= img->height;
+ }
+
+ CV_ASSERT((x>=0)&&(x<img_width)&&(y>=0)&&(y<img_height));
+
+ return ((CvLabel *)(img->imageData + img_offset))[x + y*step];
+ }
+ __CV_END__;
+ }
+
+}
Added: trunk/src/stable/libs/cvBlob/cvtrack.cpp
===================================================================
--- trunk/src/stable/libs/cvBlob/cvtrack.cpp (rev 0)
+++ trunk/src/stable/libs/cvBlob/cvtrack.cpp 2013-08-14 09:11:01 UTC (rev 973)
@@ -0,0 +1,419 @@
+// Copyright (C) 2007 by Cristóbal Carnero Liñán
+// grendel.ccl en 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 <http://www.gnu.org/licenses/>.
+//
+
+#include <cmath>
+#include <iostream>
+#include <sstream>
+using namespace std;
+
+#if (defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) || (defined(__APPLE__) & defined(__MACH__)))
+#include <cv.h>
+#else
+#include <opencv/cv.h>
+#endif
+
+#include "cvblob.h"
+
+namespace cvb
+{
+
+ double distantBlobTrack(CvBlob const *b, CvTrack const *t)
+ {
+ double d1;
+ if (b->centroid.x<t->minx)
+ {
+ if (b->centroid.y<t->miny)
+ d1 = MAX(t->minx - b->centroid.x, t->miny - b->centroid.y);
+ else if (b->centroid.y>t->maxy)
+ d1 = MAX(t->minx - b->centroid.x, b->centroid.y - t->maxy);
+ else // if (t->miny < b->centroid.y)&&(b->centroid.y < t->maxy)
+ d1 = t->minx - b->centroid.x;
+ }
+ else if (b->centroid.x>t->maxx)
+ {
+ if (b->centroid.y<t->miny)
+ d1 = MAX(b->centroid.x - t->maxx, t->miny - b->centroid.y);
+ else if (b->centroid.y>t->maxy)
+ d1 = MAX(b->centroid.x - t->maxx, b->centroid.y - t->maxy);
+ else
+ d1 = b->centroid.x - t->maxx;
+ }
+ else // if (t->minx =< b->centroid.x) && (b->centroid.x =< t->maxx)
+ {
+ if (b->centroid.y<t->miny)
+ d1 = t->miny - b->centroid.y;
+ else if (b->centroid.y>t->maxy)
+ d1 = b->centroid.y - t->maxy;
+ else
+ return 0.;
+ }
+
+ double d2;
+ if (t->centroid.x<b->minx)
+ {
+ if (t->centroid.y<b->miny)
+ d2 = MAX(b->minx - t->centroid.x, b->miny - t->centroid.y);
+ else if (t->centroid.y>b->maxy)
+ d2 = MAX(b->minx - t->centroid.x, t->centroid.y - b->maxy);
+ else // if (b->miny < t->centroid.y)&&(t->centroid.y < b->maxy)
+ d2 = b->minx - t->centroid.x;
+ }
+ else if (t->centroid.x>b->maxx)
+ {
+ if (t->centroid.y<b->miny)
+ d2 = MAX(t->centroid.x - b->maxx, b->miny - t->centroid.y);
+ else if (t->centroid.y>b->maxy)
+ d2 = MAX(t->centroid.x - b->maxx, t->centroid.y - b->maxy);
+ else
+ d2 = t->centroid.x - b->maxx;
+ }
+ else // if (b->minx =< t->centroid.x) && (t->centroid.x =< b->maxx)
+ {
+ if (t->centroid.y<b->miny)
+ d2 = b->miny - t->centroid.y;
+ else if (t->centroid.y>b->maxy)
+ d2 = t->centroid.y - b->maxy;
+ else
+ return 0.;
+ }
+
+ return MIN(d1, d2);
+ }
+
+ // Access to matrix
+#define C(blob, track) close[((blob) + (track)*(nBlobs+2))]
+ // Access to accumulators
+#define AB(label) C((label), (nTracks))
+#define AT(id) C((nBlobs), (id))
+ // Access to identifications
+#define IB(label) C((label), (nTracks)+1)
+#define IT(id) C((nBlobs)+1, (id))
+ // Access to registers
+#define B(label) blobs.find(IB(label))->second
+#define T(id) tracks.find(IT(id))->second
+
+ void getClusterForTrack(unsigned int trackPos, CvID *close, unsigned int nBlobs, unsigned int nTracks, CvBlobs const &blobs, CvTracks const &tracks, list<CvBlob*> &bb, list<CvTrack*> &tt);
+
+ void getClusterForBlob(unsigned int blobPos, CvID *close, unsigned int nBlobs, unsigned int nTracks, CvBlobs const &blobs, CvTracks const &tracks, list<CvBlob*> &bb, list<CvTrack*> &tt)
+ {
+ for (unsigned int j=0; j<nTracks; j++)
+ {
+ if (C(blobPos, j))
+ {
+ tt.push_back(T(j));
+
+ unsigned int c = AT(j);
+
+ C(blobPos, j) = 0;
+ AB(blobPos)--;
+ AT(j)--;
+
+ if (c>1)
+ {
+ getClusterForTrack(j, close, nBlobs, nTracks, blobs, tracks, bb, tt);
+ }
+ }
+ }
+ }
+
+ void getClusterForTrack(unsigned int trackPos, CvID *close, unsigned int nBlobs, unsigned int nTracks, CvBlobs const &blobs, CvTracks const &tracks, list<CvBlob*> &bb, list<CvTrack*> &tt)
+ {
+ for (unsigned int i=0; i<nBlobs; i++)
+ {
+ if (C(i, trackPos))
+ {
+ bb.push_back(B(i));
+
+ unsigned int c = AB(i);
+
+ C(i, trackPos) = 0;
+ AB(i)--;
+ AT(trackPos)--;
+
+ if (c>1)
+ {
+ getClusterForBlob(i, close, nBlobs, nTracks, blobs, tracks, bb, tt);
+ }
+ }
+ }
+ }
+
+ void cvUpdateTracks(CvBlobs const &blobs, CvTracks &tracks, const double thDistance, const unsigned int thInactive, const unsigned int thActive)
+ {
+ CV_FUNCNAME("cvUpdateTracks");
+ __CV_BEGIN__;
+
+ unsigned int nBlobs = blobs.size();
+ unsigned int nTracks = tracks.size();
+
+ // Proximity matrix:
+ // Last row/column is for ID/label.
+ // Last-1 "/" is for accumulation.
+ CvID *close = new unsigned int[(nBlobs+2)*(nTracks+2)]; // XXX Must be same type than CvLabel.
+
+ try
+ {
+ // Inicialization:
+ unsigned int i=0;
+ for (CvBlobs::const_iterator it = blobs.begin(); it!=blobs.end(); ++it, i++)
+ {
+ AB(i) = 0;
+ IB(i) = it->second->label;
+ }
+
+ CvID maxTrackID = 0;
+
+ unsigned int j=0;
+ for (CvTracks::const_iterator jt = tracks.begin(); jt!=tracks.end(); ++jt, j++)
+ {
+ AT(j) = 0;
+ IT(j) = jt->second->id;
+ if (jt->second->id > maxTrackID)
+ maxTrackID = jt->second->id;
+ }
+
+ // Proximity matrix calculation and "used blob" list inicialization:
+ for (i=0; i<nBlobs; i++)
+ for (j=0; j<nTracks; j++)
+ if (C(i, j) = (distantBlobTrack(B(i), T(j)) < thDistance))
+ {
+ AB(i)++;
+ AT(j)++;
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Detect inactive tracks
+ for (j=0; j<nTracks; j++)
+ {
+ unsigned int c = AT(j);
+
+ if (c==0)
+ {
+ //cout << "Inactive track: " << j << endl;
+
+ // Inactive track.
+ CvTrack *track = T(j);
+ track->inactive++;
+ track->label = 0;
+ }
+ }
+
+ // Detect new tracks
+ for (i=0; i<nBlobs; i++)
+ {
+ unsigned int c = AB(i);
+
+ if (c==0)
+ {
+ //cout << "Blob (new track): " << maxTrackID+1 << endl;
+ //cout << *B(i) << endl;
+
+ // New track.
+ maxTrackID++;
+ CvBlob *blob = B(i);
+ CvTrack *track = new CvTrack;
+ track->id = maxTrackID;
+ track->label = blob->label;
+ track->minx = blob->minx;
+ track->miny = blob->miny;
+ track->maxx = blob->maxx;
+ track->maxy = blob->maxy;
+ track->centroid = blob->centroid;
+ track->lifetime = 0;
+ track->active = 0;
+ track->inactive = 0;
+ tracks.insert(CvIDTrack(maxTrackID, track));
+ }
+ }
+
+ // Clustering
+ for (j=0; j<nTracks; j++)
+ {
+ unsigned int c = AT(j);
+
+ if (c)
+ {
+ list<CvTrack*> tt; tt.push_back(T(j));
+ list<CvBlob*> bb;
+
+ getClusterForTrack(j, close, nBlobs, nTracks, blobs, tracks, bb, tt);
+
+ // Select track
+ CvTrack *track;
+ unsigned int area = 0;
+ for (list<CvTrack*>::const_iterator it=tt.begin(); it!=tt.end(); ++it)
+ {
+ CvTrack *t = *it;
+
+ unsigned int a = (t->maxx-t->minx)*(t->maxy-t->miny);
+ if (a>area)
+ {
+ area = a;
+ track = t;
+ }
+ }
+
+ // Select blob
+ CvBlob *blob;
+ area = 0;
+ //cout << "Matching blobs: ";
+ for (list<CvBlob*>::const_iterator it=bb.begin(); it!=bb.end(); ++it)
+ {
+ CvBlob *b = *it;
+
+ //cout << b->label << " ";
+
+ if (b->area>area)
+ {
+ area = b->area;
+ blob = b;
+ }
+ }
+ //cout << endl;
+
+ // Update track
+ //cout << "Matching: track=" << track->id << ", blob=" << blob->label << endl;
+ track->label = blob->label;
+ track->centroid = blob->centroid;
+ track->minx = blob->minx;
+ track->miny = blob->miny;
+ track->maxx = blob->maxx;
+ track->maxy = blob->maxy;
+ if (track->inactive)
+ track->active = 0;
+ track->inactive = 0;
+
+ // Others to inactive
+ for (list<CvTrack*>::const_iterator it=tt.begin(); it!=tt.end(); ++it)
+ {
+ CvTrack *t = *it;
+
+ if (t!=track)
+ {
+ //cout << "Inactive: track=" << t->id << endl;
+ t->inactive++;
+ t->label = 0;
+ }
+ }
+ }
+ }
+ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ for (CvTracks::iterator jt=tracks.begin(); jt!=tracks.end();)
+ if ((jt->second->inactive>=thInactive)||((jt->second->inactive)&&(thActive)&&(jt->second->active<thActive)))
+ {
+ delete jt->second;
+ tracks.erase(jt++);
+ }
+ else
+ {
+ jt->second->lifetime++;
+ if (!jt->second->inactive)
+ jt->second->active++;
+ ++jt;
+ }
+ }
+ catch (...)
+ {
+ delete[] close;
+ throw; // TODO: OpenCV style.
+ }
+
+ delete[] close;
+
+ __CV_END__;
+ }
+
+ CvFont *defaultFont = NULL;
+
+ void cvRenderTracks(CvTracks const tracks, IplImage *imgSource, IplImage *imgDest, unsigned short mode, CvFont *font)
+ {
+ CV_FUNCNAME("cvRenderTracks");
+ __CV_BEGIN__;
+
+ CV_ASSERT(imgDest&&(imgDest->depth==IPL_DEPTH_8U)&&(imgDest->nChannels==3));
+
+ if ((mode&CV_TRACK_RENDER_ID)&&(!font))
+ {
+ if (!defaultFont)
+ {
+ font = defaultFont = new CvFont;
+ cvInitFont(font, CV_FONT_HERSHEY_DUPLEX, 0.5, 0.5, 0, 1);
+ // Other fonts:
+ // CV_FONT_HERSHEY_SIMPLEX, CV_FONT_HERSHEY_PLAIN,
+ // CV_FONT_HERSHEY_DUPLEX, CV_FONT_HERSHEY_COMPLEX,
+ // CV_FONT_HERSHEY_TRIPLEX, CV_FONT_HERSHEY_COMPLEX_SMALL,
+ // CV_FONT_HERSHEY_SCRIPT_SIMPLEX, CV_FONT_HERSHEY_SCRIPT_COMPLEX
+ }
+ else
+ font = defaultFont;
+ }
+
+ if (mode)
+ {
+ for (CvTracks::const_iterator it=tracks.begin(); it!=tracks.end(); ++it)
+ {
+ if (mode&CV_TRACK_RENDER_ID)
+ if (!it->second->inactive)
+ {
+ stringstream buffer;
+ buffer << it->first;
+ cvPutText(imgDest, buffer.str().c_str(), cvPoint((int)it->second->centroid.x, (int)it->second->centroid.y), font, CV_RGB(0.,255.,0.));
+ }
+
+ if (mode&CV_TRACK_RENDER_BOUNDING_BOX)
+ if (it->second->inactive)
+ cvRectangle(imgDest, cvPoint(it->second->minx, it->second->miny), cvPoint(it->second->maxx-1, it->second->maxy-1), CV_RGB(0., 0., 50.));
+ else
+ cvRectangle(imgDest, cvPoint(it->second->minx, it->second->miny), cvPoint(it->second->maxx-1, it->second->maxy-1), CV_RGB(0., 0., 255.));
+
+ if (mode&CV_TRACK_RENDER_TO_LOG)
+ {
+ clog << "Track " << it->second->id << endl;
+ if (it->second->inactive)
+ clog << " - Inactive for " << it->second->inactive << " frames" << endl;
+ else
+ clog << " - Associated with blob " << it->second->label << endl;
+ clog << " - Lifetime " << it->second->lifetime << endl;
+ clog << " - Active " << it->second->active << endl;
+ clog << " - Bounding box: (" << it->second->minx << ", " << it->second->miny << ") - (" << it->second->maxx << ", " << it->second->maxy << ")" << endl;
+ clog << " - Centroid: (" << it->second->centroid.x << ", " << it->second->centroid.y << ")" << endl;
+ clog << endl;
+ }
+
+ if (mode&CV_TRACK_RENDER_TO_STD)
+ {
+ cout << "Track " << it->second->id << endl;
+ if (it->second->inactive)
+ cout << " - Inactive for " << it->second->inactive << " frames" << endl;
+ else
+ cout << " - Associated with blobs " << it->second->label << endl;
+ cout << " - Lifetime " << it->second->lifetime << endl;
+ cout << " - Active " << it->second->active << endl;
+ cout << " - Bounding box: (" << it->second->minx << ", " << it->second->miny << ") - (" << it->second->maxx << ", " << it->second->maxy << ")" << endl;
+ cout << " - Centroid: (" << it->second->centroid.x << ", " << it->second->centroid.y << ")" << endl;
+ cout << endl;
+ }
+ }
+ }
+
+ __CV_END__;
+ }
+
+}
More information about the Jderobot-admin
mailing list