Jerome Piovano

Research in Medical Imaging and Computer Vision

/home/jpiovano/src/Odyssee++/trunk/Libs/LevelSet/src/FastMarching.h

Go to the documentation of this file.
00001 
00007 #ifndef FAST_MARCHING_H
00008 #define FAST_MARCHING_H
00009 
00010 #include <algorithm>
00011 #include <limits>
00012 
00013 #include <Image.H>
00014 #include <Globals.h>
00015 #include <PriorityQueue.h>
00016 
00017 using namespace Images;
00018 
00019 namespace levelset {
00020 
00025         template<unsigned DIM, typename Pixel = float, int sign = +1>
00026         class FastMarching : public BaseImage<DIM, unsigned>
00027         {
00028         public:
00029 
00030                 typedef          BaseImage<DIM, unsigned>        base;
00031                 typedef          BaseImage<DIM, Pixel>           ImageType;
00032                 typedef typename base::Index                     Index;
00033                 typedef          PriorityQueue< Index >          CoordQueue;
00034                 typedef typename CoordQueue::QueuePosition       QueuePosition;
00035                 typedef          BaseImage<DIM, QueuePosition>   ImageQueuePosition;
00036 
00038                 typedef enum { eAlive=0, eTrial=1, eFar=2, eForbidden=3 } eState;
00039 
00042         
00044                 FastMarching();
00046                 FastMarching(ImageType *_phi);
00048                 virtual ~FastMarching();
00049                 
00051                 void init(ImageType & _phi);
00052 
00054 
00056                 
00058                 unsigned nb_alive_points() const;
00060                 const Index& get_alive_point(unsigned i);
00062                 void add_alive_point(const Index& ind);
00064                 void add_trial_point(const Index& ind);
00066                 void add_forbidden_point(const Index& ind);
00068                 void init_trial_from_alive();
00070                 void clear();
00071 
00073 
00075 
00077                 void run();
00079                 void run(Pixel _limit);
00080 
00082 
00084 
00085         protected:
00086 
00088                 Pixel _get_value(const Index& ind) const;
00090                 virtual Pixel _update_value(const Index& ind) const = 0;
00092                 bool _solve_trinome(const Pixel a, const Pixel b, const Pixel c, Pixel &sol_max, Pixel &sol_min) const;
00093 
00094         private:
00096                 void __update_point(const Index& ind);
00097 
00099 
00101 
00102                 Index *              m_alive_tab;            
00103                 unsigned             m_nb_alive;             
00104                 CoordQueue           m_trial_queue;          
00105                 ImageQueuePosition   m_image_queue_position; 
00106         protected:
00107                 Pixel                m_big;                  
00108                 ImageType *          m_phi;                  
00109 
00110         };
00111 
00112 
00123         template <unsigned DIM, typename Pixel, int sign = +1>
00124         class Eikonal : public FastMarching<DIM, Pixel, sign> 
00125         {
00126         public:
00127 
00128                 typedef FastMarching<DIM, Pixel, sign> base;
00129                 typedef typename base::ImageType       ImageType;
00130                 typedef typename base::Index           Index;
00131 
00133                 Eikonal();
00135                 Eikonal(ImageType * _phi);
00137                 virtual ~Eikonal() {}
00138         
00140                 void set_metric(ImageType * _metric);
00141 
00142         protected:
00144                 virtual Pixel _update_value(const Index& ind) const;
00146                 Pixel _solve(const Index& ind, Pixel val[DIM]) const;
00147                 
00148         private:
00149 
00150                 ImageType * m_metric;  
00151         };
00152 
00153 
00159 
00160         // Constructors - Destructor - Init
00161 
00162 
00163         template<unsigned DIM, typename Pixel, int sign>
00164                 FastMarching<DIM, Pixel, sign>::FastMarching()
00165                 : base(), m_phi(NULL), m_alive_tab(NULL), m_nb_alive(0), m_trial_queue(), m_image_queue_position(), m_big(std::numeric_limits<Pixel>::max())
00166         { }
00167 
00168         template<unsigned DIM, typename Pixel, int sign>
00169                 FastMarching<DIM, Pixel, sign>::FastMarching(ImageType *_phi)
00170                 : base(_phi->shape()), m_phi(_phi), m_nb_alive(0), m_trial_queue(), m_image_queue_position(_phi->shape()), m_big(std::numeric_limits<Pixel>::max()) 
00171         {
00172                 m_alive_tab    = new Index[base::size()];
00173                 base::operator = (eFar);
00174                 m_image_queue_position = QueuePosition();
00175         }
00176 
00177         template<unsigned DIM, typename Pixel, int sign>
00178                 FastMarching<DIM, Pixel, sign>::~FastMarching() 
00179         {
00180                 if (m_alive_tab != NULL) delete[] m_alive_tab;
00181         }
00182 
00183         template<unsigned DIM, typename Pixel, int sign>
00184                 void FastMarching<DIM, Pixel, sign>::init(ImageType & _phi) 
00185         {
00186                 // if not the same shape, resize the datas
00187                 if (_phi.shape() != base::shape()){
00188 
00189                         base::resize(_phi.shape());
00190                         m_image_queue_position.resize(_phi.shape());
00191 
00192                         if (m_alive_tab != NULL) delete[] m_alive_tab;
00193                         m_alive_tab = new Index[base::size()];
00194                 }
00195                 clear();
00196                 m_phi = &_phi;
00197         }
00198 
00199 
00201         //  Access/Modif the Alive-Trial-Forbidden points
00202         
00203 
00204          template<unsigned DIM, typename Pixel, int sign>
00205                 unsigned FastMarching<DIM, Pixel, sign>::nb_alive_points() const
00206         {
00207                 return m_nb_alive;
00208         }
00209                 
00210         template<unsigned DIM, typename Pixel, int sign>
00211                 const typename FastMarching<DIM, Pixel, sign>::Index& FastMarching<DIM, Pixel, sign>::get_alive_point(unsigned i)
00212         {
00213                 return m_alive_tab[i];
00214         }
00215         
00216         template<unsigned DIM, typename Pixel, int sign>
00217                 void FastMarching<DIM, Pixel, sign>::add_alive_point(const Index& ind)
00218         {
00219                 if (base::operator()(ind) == eFar) {
00220                         base::operator()(ind) = eAlive;
00221                         m_alive_tab[m_nb_alive++] = ind;
00222                 }
00223         }
00224 
00225         template<unsigned DIM, typename Pixel, int sign>
00226                 void FastMarching<DIM, Pixel, sign>::add_trial_point(const Index& ind)
00227         {
00228                 if (base::operator()(ind) == eFar) {
00229                         base::operator()(ind) = eTrial;
00230                         m_image_queue_position(ind) = m_trial_queue.push(ind, sign*(*m_phi)(ind));
00231                 }
00232         }
00233 
00234         template<unsigned DIM, typename Pixel, int sign>
00235                 void FastMarching<DIM, Pixel, sign>::add_forbidden_point(const Index& ind) 
00236         {
00237                 if (base::operator()(ind) == eFar) {
00238                         base::operator()(ind) = eForbidden;
00239                 }
00240         }
00241 
00242         template<unsigned DIM, typename Pixel, int sign>
00243                 void FastMarching<DIM, Pixel, sign>::init_trial_from_alive()
00244         {
00245                 // we first insert the alive point with the lowest value in the trial point heap 
00246                 // ( to assure that the points inserted will be > to the lowest point in the heap )
00247                 if (nb_alive_points() > 0){
00248                         Index min_ind = get_alive_point(0);
00249                         Pixel min_val = sign * (*m_phi)(min_ind);
00250         
00251                         for (unsigned int i = 0 ; i < nb_alive_points() ; i++)
00252                         {
00253                                 const Index& ind = get_alive_point(i);
00254                                 Pixel        val = sign * (*m_phi)(ind);
00255                                 if (val < min_val){
00256                                         min_ind = ind;
00257                                         min_val = val;
00258                                 }       
00259                         }
00260                         //std::cout << " min_val = " << min_val << std::endl;
00261                         // TODO inserer direct 0 a la place ??? 
00262                         m_trial_queue.push(min_ind, min_val);
00263                 }
00264 
00265                 // we iterate on the list of alive points, and we add their neighborings point to the trial point heap.
00266                 for (unsigned int j = 0 ; j < nb_alive_points() ; j++)
00267                 {
00268                         const Index& ind = get_alive_point(j);
00269 
00270                         for (int i=1 ; i<=DIM ; i++){
00271                                 Index inf = ind;
00272                                 Index sup = ind;
00273                                 inf(i)--;
00274                                 sup(i)++;
00275 
00276                                 if (sup(i)<m_phi->extent(i-1)) __update_point(sup);
00277                                 if (inf(i)>=0)                 __update_point(inf);
00278                         }
00279                 }
00280                 
00281                 // we remove the first alive point inserted
00282                 if (nb_alive_points() > 0)
00283                         m_trial_queue.pop();
00284         }
00285 
00286         template<unsigned DIM, typename Pixel, int sign>
00287                 void FastMarching<DIM, Pixel, sign>::clear() 
00288         {
00289                 m_nb_alive = 0;
00290                 base::operator=(eFar);
00291                 m_trial_queue.clear();
00292                 m_image_queue_position = QueuePosition();
00293         }
00294 
00295 
00297         //  Launch the fast marching
00298 
00299 
00300         template<unsigned DIM, typename Pixel, int sign>
00301                 void FastMarching<DIM, Pixel, sign>::run() 
00302         {
00303                 run( (sign>0) ? m_big : -m_big );
00304         }
00305 
00306         template<unsigned DIM, typename Pixel, int sign>
00307                 void FastMarching<DIM, Pixel, sign>::run(Pixel _limit) 
00308         {
00309                 // while there are still trial points in the heap
00310                 while(!m_trial_queue.empty()) {
00311                 
00312                         // we get the trial point with the weakest value
00313                         Index ind = m_trial_queue.top();
00314 
00315                         // If we are at the limit
00316                         if (sign*(*m_phi)(ind)>=sign*_limit) {
00317                                 // we clear the heap, and mark all its point at 'Far'
00318                                 while (!m_trial_queue.empty()) {
00319                                         ind = m_trial_queue.top();
00320                                         m_trial_queue.pop();
00321                                         base::operator()(ind) = eFar;
00322                                         m_image_queue_position(ind)   = QueuePosition();
00323                                         (*m_phi)(ind)         = _limit;
00324                                 }
00325                                 return;
00326                         }
00327 
00328                         // we look for the neighborings point of this point, and we update their values
00329                         for (int i=1 ; i<=DIM ; i++){
00330                                 Index inf = ind;
00331                                 Index sup = ind;
00332                                 inf(i)--;
00333                                 sup(i)++;
00334 
00335                                 if (sup(i)<m_phi->extent(i-1)) __update_point(sup);
00336                                 if (inf(i)>=0)                 __update_point(inf);
00337                         }
00338 
00339                         // we remove this point from the heap, and add it to the alive points list
00340                         m_trial_queue.pop();    
00341                         m_image_queue_position(ind) = QueuePosition();
00342                         base::operator()(ind) = eAlive;
00343                         m_alive_tab[m_nb_alive++] = ind;
00344                 }
00345         }
00346 
00347 
00349         //  member functions
00350 
00351 
00352         template<unsigned DIM, typename Pixel, int sign>
00353                 Pixel FastMarching<DIM, Pixel, sign>::_get_value(const Index& ind) const 
00354         {
00355                 if (base::operator()(ind) <= eTrial)
00356                         return sign*(*m_phi)(ind);
00357                 return m_big;
00358         }
00359 
00360         template<unsigned DIM, typename Pixel, int sign>
00361                 bool FastMarching<DIM, Pixel, sign>::_solve_trinome(const Pixel a, const Pixel b, const Pixel c, Pixel &sol_max, Pixel &sol_min) const 
00362         {
00363                 const Pixel delta = b*b - 4*a*c;
00364                 if (delta < 0) return false;
00365                 const Pixel sqrtDelta = std::sqrt(delta);
00366                 sol_max = (- b + sqrtDelta) / a / 2;
00367                 sol_min = (- b - sqrtDelta) / a / 2;
00368                 return true;
00369         }
00370 
00371         template<unsigned DIM, typename Pixel, int sign>
00372                 void FastMarching<DIM, Pixel, sign>::__update_point(const Index& ind) 
00373         {
00374                 const unsigned st = base::operator()(ind);
00375                 if (st == eFar) {
00376                         // if it was a 'Far' point, we set it to 'Trial', we compute its value and we add it to the heap
00377                         base::operator()(ind) = eTrial;
00378                         const Pixel val = _update_value(ind);
00379                         (*m_phi)(ind) = sign*val;
00380                         m_image_queue_position(ind) = m_trial_queue.push(ind, val);
00381                 }
00382                 else if (st == eTrial) {
00383                         // if it was a 'Trial' point, we compute its new value
00384                         const Pixel val = _update_value(ind);
00385                         // if its value is smaller, we accept it
00386                         if (val<sign*(*m_phi)(ind)) {
00387                                 // specify the element in the queue, position in the priority queue, and new priority.
00388                                 m_image_queue_position(ind) = m_trial_queue.increase_priority(ind, m_image_queue_position(ind), val);
00389                                 (*m_phi)(ind) = sign*val;
00390                         }
00391                 }
00392         }
00393         
00394 
00396         // Eikonal Equation
00397 
00398 
00399         template<unsigned DIM, typename Pixel, int sign>
00400                 Eikonal<DIM, Pixel, sign>::Eikonal()
00401                 : base(), m_metric(NULL)
00402         { }
00403 
00404         template<unsigned DIM, typename Pixel, int sign>
00405                 Eikonal<DIM, Pixel, sign>::Eikonal(ImageType *_phi)
00406                 : base(_phi), m_metric(NULL)
00407         { }
00408         
00409         template<unsigned DIM, typename Pixel, int sign>
00410                 void Eikonal<DIM, Pixel, sign>::set_metric(ImageType * _metric)
00411         {
00412                 m_metric = _metric;
00413         }
00414 
00415         template<unsigned DIM, typename Pixel, int sign>
00416                 Pixel Eikonal<DIM, Pixel, sign>::_update_value(const Index& ind) const 
00417         {
00418                 Pixel val[DIM];
00419                 for (int i=1 ; i<=DIM ; i++){
00420                         Index inf = ind;
00421                         Index sup = ind;
00422                         inf(i)--;
00423                         sup(i)++;
00424                 
00425                         if (sup(i)>=base::extent(i-1))  val[i-1] = base::_get_value(inf);
00426                         else if (inf(i)<0)              val[i-1] = base::_get_value(sup);
00427                         else val[i-1] = std::min(base::_get_value(inf), base::_get_value(sup));
00428                 }
00429 
00430                 return _solve(ind,val);
00431         }
00432 
00433         template<unsigned DIM, typename Pixel, int sign>
00434                 Pixel Eikonal<DIM, Pixel, sign>::_solve(const Index& ind, Pixel val[DIM]) const
00435         {
00436                 // we reorder value to have val[DIM-1] max
00437                 std::sort(val, val+DIM);
00438                 
00439                 // we initialise the datas of the trinom to solve
00440                 Pixel a[DIM];
00441                 Pixel b[DIM];
00442                 Pixel c[DIM];
00443 
00444                 a[0] = 1;
00445                 b[0] = -2*val[0];
00446                 c[0] = val[0]*val[0] - ((m_metric == NULL)?1:Maths::sqr((*m_metric)(ind)));
00447                 
00448                 for (int i=1; i<DIM ; i++){
00449                         a[i] = a[i-1] + 1;
00450                         b[i] = b[i-1] - 2*val[i];
00451                         c[i] = c[i-1] + val[i]*val[i];
00452                 }
00453         
00454                 // we test the differents cases
00455                 Pixel sol_max, sol_min;
00456                 for (int i=DIM-1 ; i>0 ; i--)
00457                         if ((val[i]<base::m_big) && _solve_trinome(a[i], b[i], c[i], sol_max, sol_min) && sol_max+Globals::EPS>=val[i])
00458                                 return sol_max;
00459         
00460                 return val[0] + ((m_metric == NULL)? 1 : (*m_metric)(ind));
00461         }
00462 }
00463 
00464 
00465 #endif // FAST_MARCHING_H

For further information, please contact Jerome Piovano - Last update 2008-02-08