[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