active_contour.h

Go to the documentation of this file.
00001 //
00002 // Active contour 
00003 //
00004 // Bala Amavasai (bala@amavasai.org)
00005 // Sat Apr  28 10:21:18 BST 2001 
00006 //
00007 // Original C code by bpa & fbc
00008 
00009 #ifndef ACTIVE_CONTOUR_H
00010 #define ACTIVE_CONTOUR_H
00011 
00012 /* #pragma interface  */
00013 
00014 #include <iostream>
00015 #include <fstream>
00016 #include <list>
00017 #include <cstring>
00018 #include <cmath>
00019 #include <values.h>
00020 #include "cartesian.h"
00021 #include "image.h"
00022 #include "image_fileoutput.h"
00023 #include "image_funcs.h"
00024 
00025 namespace mimas {
00026 /* correlation tracking method extends image */
00027 template<typename T> 
00028 class activeContour : public image <T>
00029 {
00030   protected:
00031     int numPolyPoints; /* number of points on polygon */
00032 
00033   private:
00034     // void findRectTemplate(Cartesian<int> &topLeft, Cartesian<int> &bottomRight);
00035     int pointInPolygon(int x, int y);
00036     image<double> imageEnergy;
00037     double eucDist(double x1, double y1, double x2, double y2);
00038     void computeImageEnergy(const const_image_ref<T> &inputImage);
00039     
00040   public:
00041     void pushPolyCoord(int xcoord, int ycoord);
00042     void showPolyCoords(void);
00043     void clearPolyCoords(void);
00044     void sortPolyCoords(void);
00045     void showPolygonOnImage( image_ref<T> &someImage);
00046     
00047     void setPolyCircle(int xcenter, int ycenter, int circleRadius, int numPoints);
00048     int adaptContour( const const_image_ref<T> &inputImage );
00049       std::list< Cartesian<int> > adaptContourList( const const_image_ref<T> &trackingImage );
00050       std::list< Cartesian<int> > polyCoords;
00051     
00052     // variables that can be changed by the user
00053     int maxTotalPointsMoved;
00054     int maxNumIterations;
00055     double alpha, beta, gamma;
00056 #ifndef NDEBUG
00057     bool debug;
00058 #endif
00059       bool smooth;
00060     int showPolygonOnImage_graylevel, showPolygonOnImage_thickness;
00061 
00062     // constructor
00063     activeContour()
00064     {
00065       numPolyPoints=0;
00066       maxTotalPointsMoved=4000;
00067       maxNumIterations=1000;
00068       alpha=1.0;
00069       beta=1.0;
00070       gamma=5.0;
00071 #ifndef NDEBUG
00072       debug=false;
00073 #endif
00074       smooth=false; // smoothing is off by default
00075       showPolygonOnImage_graylevel=127;
00076       showPolygonOnImage_thickness=1;
00077     }
00078     
00079     // destructor
00080     ~activeContour()
00081     {
00082       //polyCoords.clear();
00083     }
00084 };
00085 
00086 
00087 template<typename T> 
00088 double activeContour<T>::eucDist(double x1, double y1, double x2, double y2)
00089 {
00090   return sqrt(((x1-x2)*(x1-x2))+((y1-y2)*(y1-y2)));
00091 }
00092 
00093 
00094 template<typename T> 
00095 int activeContour<T>::adaptContour( const const_image_ref<T> &inputImage )
00096 {
00097   int j, k, jmin, kmin;
00098   int pointcount, ptsmoved, totptsmoved=0, itercount=0;
00099   double Esnake, Emin, E, Eimage, aveDist;
00100   double Econt[9], Econtmax, Econtmin;
00101   double Ecurv[9], Ecurvmax, Ecurvmin;
00102   
00103   std::list< Cartesian<int> >::iterator curpoint, prevpoint, nextpoint;
00104 
00105   computeImageEnergy(inputImage);
00106 
00107   do
00108   { 
00109     /* compute average distance between points */
00110     aveDist=0.0;
00111     for(curpoint=polyCoords.begin(), prevpoint=--polyCoords.end() ; curpoint != polyCoords.end(); prevpoint=curpoint,++curpoint)
00112     {
00113       aveDist+=eucDist((*curpoint).x,(*curpoint).y,(*prevpoint).x,(*prevpoint).y);
00114     } 
00115     aveDist=aveDist/(double)numPolyPoints;
00116 
00117     /* cycle through the snake point list */
00118     ptsmoved=0;
00119     Esnake=0.0;
00120 
00121 //    for(nextpoint=++polyCoords.begin(), curpoint=polyCoords.begin(), prevpoint=--polyCoords.end() ; 
00122 //      curpoint != polyCoords.end(); prevpoint=curpoint, curpoint=nextpoint, nextpoint++)
00123     
00124     prevpoint=--polyCoords.end();
00125     nextpoint=++polyCoords.begin();
00126     for (curpoint=polyCoords.begin(); curpoint != polyCoords.end(); prevpoint++, curpoint++, nextpoint++)
00127     {
00128         
00129 
00130       if (prevpoint==polyCoords.end()) prevpoint=polyCoords.begin();
00131       if (nextpoint==polyCoords.end()) nextpoint=polyCoords.begin();
00132       
00133       /* calculate Econt and Ecurv */
00134       for (j=-1, pointcount=0, Econtmax=Ecurvmax=-FLT_MAX, Econtmin=Ecurvmin=FLT_MAX; j<=1; j++)
00135         for (k=-1; k<=1; k++, pointcount++)
00136         { 
00137           /* continuity energy */   
00138           Econt[pointcount]=fabs(aveDist-
00139                 eucDist( (*prevpoint).x, (*prevpoint).y,
00140                   (*curpoint).x + k, (*curpoint).y + j ) );
00141 
00142           if (Econt[pointcount]>Econtmax) Econtmax=Econt[pointcount];
00143           if (Econt[pointcount]<Econtmin) Econtmin=Econt[pointcount];
00144           
00145           /* curvature energy */  
00146           Ecurv[pointcount]=
00147             ((*prevpoint).x - 2.0 * ((*curpoint).x + k) + (*nextpoint).x) *
00148             ((*prevpoint).x - 2.0 * ((*curpoint).x + k) + (*nextpoint).x) +
00149             ((*prevpoint).y - 2.0 * ((*curpoint).y + j) + (*nextpoint).y) *
00150             ((*prevpoint).y - 2.0 * ((*curpoint).y + j) + (*nextpoint).y);
00151 
00152           if (Ecurv[pointcount]>Ecurvmax) Ecurvmax=Ecurv[pointcount];
00153           if (Ecurv[pointcount]<Ecurvmin) Ecurvmin=Ecurv[pointcount];
00154         }
00155         
00156         
00157       /* calculate Emin */
00158       for (j=-1, pointcount=0, jmin=kmin=0, Emin=FLT_MAX; j<=1; j++)
00159         for (k=-1; k<=1; k++, pointcount++)
00160         {
00161           if (Econtmax != Econtmin) Econt[pointcount]=(Econt[pointcount]-Econtmin)/(Econtmax-Econtmin);
00162           if (Ecurvmax != Ecurvmin) Ecurv[pointcount]=(Ecurv[pointcount]-Ecurvmin)/(Ecurvmax-Ecurvmin);
00163           Eimage = imageEnergy.getPixel((*curpoint).x + k, (*curpoint).y + j);
00164             
00165           E = alpha * Econt[pointcount] + beta * Ecurv[pointcount] + gamma * Eimage;
00166               
00167           if (E < Emin)
00168           {  Emin = E; jmin = j; kmin = k; }
00169         }
00170         
00171       /* update total snake energy */
00172       Esnake += Emin;
00173 
00174       /* update snake list point position */
00175       if ((jmin!=0) || (kmin!=0))
00176       {
00177         (*curpoint).y = (*curpoint).y + jmin;
00178         (*curpoint).x = (*curpoint).x + kmin;
00179         ptsmoved++; totptsmoved++;
00180       }     
00181     }
00182 #ifndef NDEBUG
00183     if (debug) std::cerr << itercount << "  " << totptsmoved   << std::endl;
00184 #endif
00185 
00186 
00187     // If debugging then dump one frame per iteration
00188 #ifndef NDEBUG
00189     if (debug)
00190     {
00191         image<int> debugimage( inputImage );
00192     
00193       showPolygonOnImage(debugimage);
00194                   char framefilename[80];
00195         sprintf(framefilename,"frame%.3d.pgm", itercount);
00196       std::ofstream file( framefilename, std::ios::binary );
00197       file << debugimage;
00198       debugimage.clear();
00199     }
00200 #endif
00201 
00202   } while ((++itercount < maxNumIterations) && (totptsmoved < maxTotalPointsMoved) && (ptsmoved > 0));
00203     
00204   // ensure that snake is within image boundary
00205   std::list< Cartesian<int> > ::iterator m;
00206   for(m=polyCoords.begin(); m != polyCoords.end(); m++)
00207   {
00208     if ((*m).x<0) (*m).x=0;
00209     if ((*m).x>=inputImage.getWidth()) (*m).x=inputImage.getWidth()-1;
00210     if ((*m).y<0) (*m).y=0;
00211     if ((*m).y>=inputImage.getHeight()) (*m).y=inputImage.getHeight()-1;
00212   }
00213       
00214 
00215 
00216   return (itercount);
00217 }
00218 
00219 
00220 template<typename T> 
00221 void activeContour<T>::setPolyCircle(int xcenter, int ycenter, int circleRadius, int numPoints)
00222 {
00223   double angle;
00224   int count;
00225     
00226 
00227   for (angle=0.0, count=0; angle<360.0; angle+=(360.0/numPoints))
00228   {
00229     /* random circle
00230     snakelist[count][YCOORD]=(int)(ycenter-circleRadius*sin(angle*M_PI/180.0))+(5.0*drand48());
00231     snakelist[count][XCOORD]=(int)(xcenter+circleRadius*cos(angle*M_PI/180.0))+(5.0*drand48());
00232     */
00233         
00234     pushPolyCoord((int)(xcenter+circleRadius*cos(angle*M_PI/180.0)),
00235       (int)(ycenter-circleRadius*sin(angle*M_PI/180.0)));
00236     
00237     if (++count==numPoints) break;
00238   }
00239 }
00240 
00241 
00242 template<typename T>
00243 void activeContour<T>::computeImageEnergy(const const_image_ref<T> &inputImage)
00244 {
00245    if (smooth)
00246      imageEnergy = edgeSobelSqr( softenHeavy( inputImage ) );
00247    else
00248      imageEnergy = edgeSobelSqr( inputImage );
00249 
00250    double
00251      minval = min_val( imageEnergy ),
00252      maxval = max_val( imageEnergy );
00253 
00254   /* this is a hack. see Williams and Shah paper */
00255   if ((maxval-minval)<5.0) maxval=minval+5.0;
00256   
00257   /* normalise image pixels between 0 and 1 */
00258    imageEnergy = ( imageEnergy - minval ) * ( 1.0 / ( maxval - minval ) );
00259 }
00260 
00261 
00262 
00263 
00264 template<typename T>
00265 void activeContour<T>::pushPolyCoord(int xcoord, int ycoord)
00266 {
00267   //polyCoords.push_front(xcoord,ycoord);
00268   
00269   Cartesian<int> tempcoord;
00270   tempcoord.x=xcoord;
00271   tempcoord.y=ycoord;
00272 
00273   polyCoords.push_back(tempcoord);
00274 
00275   numPolyPoints++;
00276 }
00277 
00278 
00279 template<typename T>
00280 void activeContour<T>::showPolyCoords(void)
00281 {
00282   std::list< Cartesian<int> > ::iterator i;
00283 
00284   for(i=polyCoords.begin(); i != polyCoords.end(); ++i) 
00285     std::cout << *i << std::endl; 
00286 }
00287 
00288 template<typename T>
00289 void activeContour<T>::clearPolyCoords(void)
00290 {
00291   polyCoords.clear();
00292 }
00293 
00294 
00295 template<typename T>
00296 void activeContour<T>::sortPolyCoords(void)
00297 {
00298   polyCoords.sort();
00299 }
00300 
00301 /*
00302 template<typename T>
00303 void activeContour<T>::findRectTemplate(Cartesian<int> &topLeft, Cartesian<int> &bottomRight)
00304 {
00305   std::list< Cartesian<int> > ::iterator i;
00306 
00307   int xmax=-1, ymax=-1, xmin=INT_MAX, ymin=INT_MAX;
00308 
00309   for(i=polyCoords.begin(); i != polyCoords.end(); ++i)
00310   {
00311     xmax=( (xmax<(*i).x) ? (*i).x:xmax );
00312     ymax=( (ymax<(*i).y) ? (*i).y:ymax );
00313     xmin=( (xmin>(*i).x) ? (*i).x:xmin );
00314     ymin=( (ymin>(*i).y) ? (*i).y:ymin );   
00315   }
00316   templateRows=xmax-xmin;
00317   templateCols=ymax-ymin;
00318 
00319   topLeft.x=xmin; topLeft.y=ymin;
00320   bottomRight.x=xmax; bottomRight.y=ymax;
00321 
00322   offsetForFirstCoord.x=(*polyCoords.begin()).x-xmin;
00323   offsetForFirstCoord.y=(*polyCoords.begin()).y-ymin;
00324 } */
00325 
00326 
00327 template<typename T>
00328 int activeContour<T>::pointInPolygon(int x, int y)
00329 {
00330   std::list< Cartesian<int> > ::iterator i,j;
00331   int c = 0;
00332 
00333   // ugly logic in the following line but I can't see how to achieve it otherwise!
00334   for(i=polyCoords.begin(), j=--polyCoords.end() ; i != polyCoords.end(); j=i,++i)
00335   {
00336     if (((((*i).y<=y) && (y<(*j).y)) ||
00337       (((*j).y<=y) && (y<(*i).y))) &&
00338       (x < ((*j).x - (*i).x) * (y - (*i).y) / ((*j).y - (*i).y) + (*i).x))
00339       c = !c;
00340   }
00341 
00342   return c;
00343 }
00344 
00345 
00346 template<typename T>
00347 void activeContour<T>::showPolygonOnImage( image_ref<T> &someImage)
00348 {
00349   std::list< Cartesian<int> > ::iterator i,j;
00350   for(i=polyCoords.begin(), j=--polyCoords.end() ; i != polyCoords.end(); j=i,++i)
00351   if (showPolygonOnImage_thickness == 1)
00352     drawLine(someImage, (*i).x, (*i).y, (*j).x, (*j).y, showPolygonOnImage_graylevel);
00353   else
00354     drawThickLine(someImage, (*i).x, (*i).y, (*j).x, (*j).y, showPolygonOnImage_graylevel, 5);
00355 }
00356 
00357 
00358 // Run adaptContour but return list of points
00359 template<typename T>
00360 std::list< Cartesian<int> > activeContour<T>::adaptContourList( const const_image_ref<T> &trackingImage )
00361 {
00362   std::list< Cartesian<int> > ::iterator i;
00363   std::list< Cartesian<int> >  outCoordList;
00364   Cartesian<int> tempcoord;
00365 
00366   // call the snake algorithm
00367   adaptContour(trackingImage);
00368 
00369   for(i=polyCoords.begin(); i != polyCoords.end(); i++)
00370   {
00371     tempcoord.x=(*i).x;
00372     tempcoord.y=(*i).y;
00373     outCoordList.push_back(tempcoord);      
00374   }
00375 
00376 /*
00377   for(k=polyCoords.begin(); k != polyCoords.end(); ++k) 
00378   {
00379     Cartesian<int> tempcoord;
00380 
00381     tempcoord.x=(*k).x-(*polyCoords.begin()).x+outCoord.x+offsetForFirstCoord.x;
00382     tempcoord.y=(*k).y-(*polyCoords.begin()).y+outCoord.y+offsetForFirstCoord.y;
00383 
00384     outCoordList.push_back(tempcoord);      
00385   }
00386 */
00387   
00388   return outCoordList;
00389 }   
00390 
00391 }
00392 
00393 #endif

[GNU/Linux] [Qt] [Mesa] [STL] [Lapack] [Boost] [Magick++] [Xalan-C and Xerces-C] [doxygen] [graphviz] [FFTW] [popt] [xine] [Gnuplot] [gnu-arch] [gcc] [gstreamer] [autoconf/automake/make] [freshmeat.net] [opensource.org] [sourceforge.net] [MMVL]
mimas 2.1 - Copyright Mon Oct 30 11:31:16 2006, Bala Amavasai, Stuart Meikle, Arul Selvan, Fabio Caparrelli, Jan Wedekind, Manuel Boissenin, ...