Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

retinalobjs.c

Go to the documentation of this file.
00001 
00007 #include <cmath> 
00008 #include <iomanip>
00009 #include <strstream.h>
00010 #include <numeric>
00011 #include <fstream>
00012 #include <utility>
00013 
00014 #include "retinalobjs.h"
00015 
00016 #ifndef NO_VALGEN_STRINGS
00017 #include <stdio.h>
00018 #include "stringutils.h"
00019 #endif
00020 
00021 #ifdef ANSI_COMPATIBLE
00022 /* Optional: provides definition of hypot on systems lacking it */
00023 #include "ind_types.h"
00024 #endif
00025 
00026 
00027 
00028 /******************************************************************************/
00029 /*  Global defines                                                            */
00030 /******************************************************************************/
00031 
00032 
00033 #ifndef M_PI
00034 #define M_PI            3.14159265358979323846  /* pi */
00035 #endif
00036 
00037 #ifndef RADIANS_TO_DEGREES
00038 #define RADIANS_TO_DEGREES(theta) (180.0*(theta)/M_PI)
00039 #endif
00040 
00041 #ifndef CONSTRAIN_ANGLE
00042 #define CONSTRAIN_ANGLE(angle)                                    \
00043   (((angle)>=0) ?                                                  \
00044    fmod( (angle),                                          M_PI ) : \
00045    fmod( (angle) + 2*M_PI*(-1*floor((angle)/(M_PI*2))+1),  M_PI)     )
00046 #endif
00047 
00048 
00049 
00050 /******************************************************************************/
00051 /*  Retinal_Object                                                            */
00052 /******************************************************************************/
00053 
00054 //#include <typeinfo> /* Uncomment if typeid is enabled below */
00055 
00056 string Retinal_Object::stringrep() const
00057 {
00058   /* Should be modified to use an ostringstream when sstream becomes available;
00059      also use the "fixed" iomanip instead of the setf below. */
00060   const char bufsize=100;
00061   static char buf[bufsize];
00062   ostrstream os(buf, bufsize);
00063   os.setf(ios::fixed,ios::floatfield);
00064   //os << typeid(*this).name() << " "; /* For debugging */
00065   if (name!="") os << name << " ";
00066   os << setprecision(1) 
00067      <<  "cx:"    << setw(4) << setfill('0') << get_var("cx")
00068      << " cy:"    << setw(4) << setfill('0') << get_var("cy")
00069      << " theta:" << setw(5) << setfill('0')
00070      << RADIANS_TO_DEGREES(CONSTRAIN_ANGLE(     get_var("theta")));
00071 
00072   /* For debugging or other reasons, print all variables */
00073   //for(VarMap::const_iterator i=vars.begin(); i!= vars.end(); i++) {
00074   //  const string& name= i->first;
00075   //  if (name != "cx" && name != "cy" && name != "theta")
00076   //      os << " " << name << ":" << i->second->value();
00077   //}
00078 
00079   os << ends;
00080   
00081   return buf;
00082 }
00083 
00084 
00085 
00086 #ifndef NO_VALGEN_STRINGS
00087 Retinal_Object::VarMap RetinalObjectStringArgs::vars(const ParamList& params)
00088 {
00089   Retinal_Object::VarMap vars;
00090   
00091   /* Parse all positional arguments possible, substituting default if arg is missing */
00092   for (ParamList::const_iterator paramptr=params.begin();
00093        paramptr!=params.end(); paramptr++) {
00094     const string& name = *paramptr;
00095     const bool    positionalargsremain = !args.empty() && !args.lookaheadone(string("="));
00096     const string  value = (positionalargsremain ? args.next(string("")) : get_default(name));
00097     set_linked(name,value,vars);
00098   }
00099   
00100   /* Parse any named arguments present, overriding defaults and positional arguments */
00101   while (!args.empty()) {
00102     const string name = args.next(string(""));
00103     if (args.empty() || !args.nextis(string("=")) || args.empty()) {
00104       error("Syntax error in retinal object parameter specification");
00105       return vars;
00106     }
00107     const string value = args.next(string(""));
00108     set_linked(name,value,vars);
00109   }
00110 
00111   return vars;
00112 }
00113 #endif /* NO_VALGEN_STRINGS */
00114 
00115 
00116 
00117 /******************************************************************************/
00118 /*  Retinal_Composite objects                                                 */
00119 /******************************************************************************/
00120 
00121 
00122 bool Retinal_Composite::next()
00123 {
00124   return
00125     !std::count_if(ISEQ(children),not1(mem_fun(&ValueGen::next)))
00126     && Retinal_Object::next();
00127 }
00128 
00129 
00130 
00131 void Retinal_Composite::reset() {
00132   dominant_child = (accum_type==OneHot && children.size() ? children[0] : 0);
00133   std::for_each(ISEQ(children),mem_fun(&Retinal_Object::reset));
00134   Retinal_Object::reset();
00135 }
00136 
00137 
00138 
00139 #ifndef NO_VALGEN_STRINGS
00140 RetinalObjectStringArgs::ParamList Retinal_Composite::paramlist() {
00141   RetinalObjectStringArgs::ParamList p;
00142   p.push_back("theta");
00143   p.push_back("cx");
00144   p.push_back("cy");
00145   p.push_back("size_scale");
00146   p.push_back("scale");
00147   p.push_back("offset");
00148   p.push_back("hot");
00149   p.push_back("accum_type");
00150   return p;
00151 }
00152 #endif
00153 
00154 
00155 
00156 bool Retinal_Composite::update() const
00157 {
00158   std::for_each(ISEQ(children),mem_fun(&Retinal_Object::update));
00159 
00160   /* Select single dominant child, if any */
00161   if (accum_type==OneHot && children.size()) {
00162     const Variable hot = get_var("hot");
00163     const Variable relative_index = hot-int(hot); /* Value between 0 and 1 */
00164     const int      child_index    = (int)( children.size()*relative_index );
00165     assert(child_index<(int)children.size());
00166     dominant_child = children[child_index];
00167   }
00168   else
00169     dominant_child=0;
00170   
00171   /* Cache the result of sub-computations */
00172   cx                        = get_var("cx");
00173   cy                        = get_var("cy");
00174   const Variable mt         = -get_var("theta");
00175   cosmt                     = cos(mt);
00176   sinmt                     = sin(mt);
00177   const Variable size_scale = get_var("size_scale");
00178   div_size                  = 1/size_scale;
00179 
00180 
00181   /* Construct bounding box enclosing all children in local coordinates  */
00182   bounding_box.set(0,0,0,0); /* Initially empty */
00183   for(const_iterator i=children.begin(); i!=children.end(); i++)
00184     bounding_box += (*i)->bounding_box;
00185   
00186   /* Convert the bounding box into world coordinates */
00187   bounding_box.scale(size_scale,size_scale).rotate(mt).translate(cx,cy);
00188 
00189   return Retinal_Object::update();
00190 }
00191 
00192 
00194 class ActivityAccumulator : public binary_function<Retinal_Object*,Retinal_Object*,bool> {
00195 public:
00196   explicit ActivityAccumulator(const Retinal_Obj::Coordinate xi, const Retinal_Obj::Coordinate yi,
00197                                Retinal_Composite::AccumulationType accum_type_i)
00198     : x(xi), y(yi), accum_type(accum_type_i) { }
00199 
00201   Retinal_Obj::Activity operator() (Retinal_Obj::Activity prev, const Retinal_Object* r) {
00202     const Retinal_Obj::Activity now=r->activation(x,y);
00203     switch (accum_type) {
00204     case Retinal_Composite::Max: return std::max(prev,now);
00205     case Retinal_Composite::Min: return std::min(prev,now);
00206     case Retinal_Composite::Add: return prev+now;
00207     default:                     return prev+now;
00208     }
00209   }
00210   
00211 private:
00212   Retinal_Obj::Coordinate x,y;
00213   const Retinal_Composite::AccumulationType accum_type;
00214 };
00215 
00216 
00217 
00224 class DefaultActivityAccumulator : public binary_function<Retinal_Object*,Retinal_Object*,bool> {
00225 public:
00226   explicit DefaultActivityAccumulator(Retinal_Composite::AccumulationType accum_type_i)
00227     : accum_type(accum_type_i) { }
00228 
00230   Retinal_Obj::Activity operator() (Retinal_Obj::Activity prev, const Retinal_Object* r) {
00231     const Retinal_Obj::Activity now=r->default_activation();
00232     switch (accum_type) {
00233     case Retinal_Composite::Max: return std::max(prev,now);
00234     case Retinal_Composite::Min: return std::min(prev,now);
00235     case Retinal_Composite::Add: return prev+now;
00236     default:                     return prev+now;
00237     }
00238   }
00239   
00240 private:
00241   const Retinal_Composite::AccumulationType accum_type;
00242 };
00243 
00244 
00245 
00247 Retinal_Obj::Activity Retinal_Composite::accum_base() const {
00248   switch (accum_type) {
00249   case Min: return  10000000; /* Activation value higher than all others */
00250   case Max: return -10000000; /* Activation value lower  than all others */
00251   default:  return 0;
00252   }
00253 }
00254 
00255 
00256 
00257 Retinal_Object::Activity Retinal_Composite::default_activ() const
00258 {
00259   //return =0.5; /* To debug Composite bounding boxes */
00260   return 
00261     (children.empty() ? 0 
00262      : (dominant_child? dominant_child->default_activation()
00263         : std::accumulate(ISEQ(children),accum_base(),DefaultActivityAccumulator(accum_type))));
00264 }
00265 
00266 
00267   
00268 Retinal_Object::Activity Retinal_Composite::activ(Coordinate x, Coordinate y) const
00269 {
00270   const Coordinate dx      = (x-cx)*div_size;
00271   const Coordinate dy      = (y-cy)*div_size;
00272   const Coordinate xp      = dx*cosmt-dy*sinmt;
00273   const Coordinate yp      = dx*sinmt+dy*cosmt;
00274   
00275   return (dominant_child ? dominant_child->activation(xp,yp)
00276           : std::accumulate(ISEQ(children),accum_base(),ActivityAccumulator(xp,yp,accum_type)));
00277 }
00278 
00279 
00280 
00283 const Retinal_Object& Retinal_Composite::mostactive(Coordinate x, Coordinate y) const
00284 {
00285   if (children.begin()==children.end())
00286     return *this;
00287   else {
00288     Retinal_Object* champ = *children.begin();
00289     Activity high_act = champ->activation(x,y);
00290     Activity act;
00291     for (Retinal_Composite::const_iterator i = children.begin()+1; i!= children.end(); i++) {
00292       act = (*i)->activation(x,y);
00293       if (act>high_act) {
00294         champ = *i;
00295         high_act=act;
00296       }
00297     }
00298     return *champ;
00299   }
00300 }
00301 
00302 
00303 
00306 string accumulate_stringreps(string val, Retinal_Object* r)
00307 {  return val + " [" + r->stringrep() + "]";  }
00308 
00309 
00310 
00311 string Retinal_Composite::stringrep() const
00312 {
00313   return Retinal_Object::stringrep() +
00314     (dominant_child ? " " + dominant_child->stringrep()
00315      : std::accumulate(ISEQ(children),string(""),accumulate_stringreps));
00316 }
00317 
00318 
00319 
00320 /******************************************************************************/
00321 /*  Retinal_ManagedComposite objects                                          */
00322 /******************************************************************************/
00323 
00324 
00325 bool Retinal_ManagedComposite::distance_valid (const Retinal_Object& obj1, const Retinal_Object& obj2)
00326 {
00327   if (!*min_dist_enforce && !*max_dist_enforce)
00328     return true;
00329 
00330   const Coordinate dist = hypot(obj1.get_var("cx") - obj2.get_var("cx"),
00331                                 obj1.get_var("cy") - obj2.get_var("cy"));
00332 
00333   return ((!*min_dist_enforce || dist >= *min_dist) &&
00334           (!*max_dist_enforce || dist <= *max_dist) );
00335 }
00336 
00337 
00338 
00339 bool Retinal_ManagedComposite::accumulate_managed_next(bool val, Retinal_Object* r)
00340 {
00341   /*
00342     Arbitrary "timeout" value to prevent endless loop; with
00343     some parameter values it may be impossible to find a valid
00344     distance.
00345   */
00346   const int max_trials=100; 
00347 
00348   /* Generate and test possible positions until a valid one is found */
00349   for (int trial=0; trial<max_trials; trial++) {
00350     r->next();
00351     
00352     iterator it = children.begin();
00353     while (*it != r && distance_valid(*r,**it))
00354       it++;
00355 
00356     if (*it == r) {
00357       r->set_active(true);
00358       return val;
00359     }
00360   }
00361   
00362   r->set_active(false);
00363   return false;
00364 }
00365 
00366 
00367 
00368 bool Retinal_ManagedComposite::next()
00369 {
00370   bool val=true;
00371   for (iterator i = Retinal_Composite::children.begin();
00372        i != children.end();  i++) {
00373     val = accumulate_managed_next(val,*i);
00374   }
00375 
00376   return val && Retinal_Object::next();
00377 }
00378 
00379 
00380 
00381 /******************************************************************************/
00382 /*  Retinal_AnchoredManagedComposite objects                                  */
00383 /******************************************************************************/
00384 
00386 Retinal_Object::Activity Retinal_AnchoredManagedComposite::activ(Coordinate x, Coordinate y) const
00387 {
00388   return (dominant_child ? dominant_child->activation(x,y)
00389           : std::accumulate(ISEQ(children),accum_base(),ActivityAccumulator(x,y,accum_type)));
00390 }
00391 
00392 
00393 
00395 string Retinal_AnchoredManagedComposite::stringrep() const
00396 {
00397   return get_name() + 
00398     (dominant_child ? " " + dominant_child->stringrep()
00399      : std::accumulate(ISEQ(children),string(""),accumulate_stringreps));
00400 }
00401 
00402 
00403 
00404 /******************************************************************************/
00405 /*                                                                            */
00406 /*  Miscellaneous leaf objects                                                */
00407 /*  (See corresponding constants for documentation)                           */
00408 /*                                                                            */
00409 /*  Each usually caches parameter values that it needs (once) in update() and */
00410 /*  then uses them (many times) in activ().  If it uses a bounding box, it    */
00411 /*  usually computes the bounding box in update().                            */
00412 /*                                                                            */
00413 /******************************************************************************/
00414 
00415 #ifndef NO_VALGEN_STRINGS
00416 RetinalObjectStringArgs::ParamList Retinal_CircularGaussian::paramlist() {
00417   RetinalObjectStringArgs::ParamList p;
00418   p.push_back("cx");
00419   p.push_back("cy");
00420   p.push_back("xsigma");
00421   p.push_back("scale");
00422   p.push_back("offset");
00423   return p;
00424 }
00425 #endif
00426 
00427 
00428 bool   Retinal_CircularGaussian::update() const
00429 {
00430   /* Cache the result of sub-computations */
00431   cx                        = get_var("cx");
00432   cy                        = get_var("cy");
00433   const Variable xsigma     = get_var("xsigma");
00434   div_sigmasq               = 1/(xsigma*xsigma);
00435 
00436   /* Compute bounding box */
00437   const Coordinate boundrad=bound_mult*xsigma;
00438   bounding_box.set(cx-boundrad,cy-boundrad,
00439                    cx+boundrad,cy+boundrad);
00440 
00441   return Retinal_Object::update();
00442 }
00443 
00444 
00445 Retinal_Object::Activity Retinal_CircularGaussian::activ(Coordinate x, Coordinate y) const
00446 {
00447   const Coordinate dx  = x-cx;
00448   const Coordinate dy  = y-cy;
00449 
00450   return exp( -(dx*dx+dy*dy)*div_sigmasq);
00451 }
00452 
00453 
00454 
00455 #ifndef NO_VALGEN_STRINGS
00456 RetinalObjectStringArgs::ParamList Retinal_Gaussian::paramlist() {
00457   RetinalObjectStringArgs::ParamList p;
00458   p.push_back("theta");
00459   p.push_back("cx");
00460   p.push_back("cy");
00461   p.push_back("xsigma");
00462   p.push_back("ysigma");
00463   p.push_back("scale");
00464   p.push_back("offset");
00465   return p;
00466 }
00467 #endif
00468 
00469 
00470 bool Retinal_Gaussian::update() const
00471 {
00472   /* Cache the result of sub-computations */
00473   cx                        = get_var("cx");
00474   cy                        = get_var("cy");
00475   const Variable t          = get_var("theta");
00476   cost                      = cos(t);
00477   sint                      = sin(t);
00478   const Variable xsigma     = get_var("xsigma");
00479   const Variable ysigma     = get_var("ysigma");
00480   div_xsigma                = 1/xsigma;
00481   div_ysigma                = 1/ysigma;
00482   
00483   /* Compute bounding box */
00484   const Variable xrad=bound_mult*xsigma;
00485   const Variable yrad=bound_mult*ysigma;
00486   bounding_box.set(-xrad,-yrad,xrad,yrad).rotate(t).translate(cx,cy);
00487 
00488   return Retinal_Object::update();
00489 }
00490 
00491 
00492 Retinal_Object::Activity Retinal_Gaussian::activ(Coordinate x, Coordinate y) const
00493 {
00494   const Coordinate dx    = x-cx;
00495   const Coordinate dy    = y-cy;
00496   const Coordinate xp    = ( dx * cost + dy * sint)*div_xsigma;
00497   const Coordinate yp    = (-dx * sint + dy * cost)*div_ysigma;
00498 
00499   return exp(-(xp*xp + yp*yp));
00500 }
00501 
00502 
00503 
00504 #ifndef NO_VALGEN_STRINGS
00505 RetinalObjectStringArgs::ParamList Retinal_Rectangle::paramlist() {
00506   RetinalObjectStringArgs::ParamList p;
00507   p.push_back("cx");
00508   p.push_back("cy");
00509   p.push_back("xsigma");
00510   p.push_back("ysigma");
00511   p.push_back("scale");
00512   p.push_back("offset");
00513   return p;
00514 }
00515 #endif
00516 
00517 
00518 bool Retinal_Rectangle::update() const
00519 {
00520   /* Compute bounding box. */
00521   const Coordinate xrad   = get_var("xsigma");
00522   const Coordinate yrad   = get_var("ysigma");
00523   const Coordinate cx     = get_var("cx");
00524   const Coordinate cy     = get_var("cy");
00525   bounding_box.set(-xrad,-yrad,xrad,yrad).translate(cx,cy);
00526 
00527   return Retinal_Object::update();
00528 }
00529 
00530 
00531 
00532 #ifndef NO_VALGEN_STRINGS
00533 RetinalObjectStringArgs::ParamList Retinal_SineGrating::paramlist() {
00534   RetinalObjectStringArgs::ParamList p;
00535   p.push_back("theta");
00536   p.push_back("freq");
00537   p.push_back("phase");
00538   p.push_back("scale");
00539   p.push_back("offset");
00540   return p;
00541 }
00542 #endif
00543 
00544 
00545 bool Retinal_SineGrating::update() const
00546 {
00547   /* Cache the result of sub-computations */
00548   const Variable t          = get_var("theta");
00549   cost                      = cos(t);
00550   sint                      = sin(t);
00551   phase                     = get_var("phase");
00552   freq                      = get_var("freq");
00553 
00554   return Retinal_Object::update();
00555 }
00556 
00557 
00558 Retinal_Object::Activity Retinal_SineGrating::activ(Coordinate x, Coordinate y) const
00559 {  return sin( freq*(x*sint-y*cost+1.0) + phase);  }
00560 
00561 
00562   
00563 #ifndef NO_VALGEN_STRINGS  
00564 RetinalObjectStringArgs::ParamList Retinal_Gabor::paramlist() {
00565   RetinalObjectStringArgs::ParamList p;
00566   p.push_back("theta");
00567   p.push_back("cx");
00568   p.push_back("cy");
00569   p.push_back("xsigma");
00570   p.push_back("ysigma");
00571   p.push_back("freq");
00572   p.push_back("phase");
00573   p.push_back("scale");
00574   p.push_back("offset");
00575   return p;
00576 }
00577 #endif
00578 
00579 
00580 bool Retinal_Gabor::update() const
00581 {
00582   /* Cache the result of sub-computations */
00583   cx                        = get_var("cx");
00584   cy                        = get_var("cy");
00585   const Variable t          = get_var("theta");
00586   cost                      = cos(t);
00587   sint                      = sin(t);
00588   phase                     = get_var("phase");
00589   freq                      = get_var("freq");
00590   const Variable xsigma     = get_var("xsigma");
00591   const Variable ysigma     = get_var("ysigma");
00592   div_xsigmasq              = 1/(xsigma*xsigma);
00593   div_ysigmasq              = 1/(ysigma*ysigma);
00594 
00595   /* Compute bounding box */
00596   const Variable xrad=bound_mult*xsigma;
00597   const Variable yrad=bound_mult*ysigma;
00598   bounding_box.set(-xrad,-yrad,xrad,yrad).rotate(t).translate(cx,cy);
00599   
00600   return Retinal_Object::update();
00601 }
00602 
00603 
00604 Retinal_Object::Activity Retinal_Gabor::activ(Coordinate x, Coordinate y) const
00605 {
00606   const Coordinate dx       = x-cx;
00607   const Coordinate dy       = y-cy;
00608   const Coordinate xp       = ( dx * cost + dy * sint);
00609   const Coordinate yp       = (-dx * sint + dy * cost);
00610 
00611   const Activity   gaussian = exp(-(xp*xp*div_xsigmasq + yp*yp*div_ysigmasq));
00612   const Activity   grating  = 0.5+0.5*cos( freq*yp + phase );
00613   
00614   return grating * gaussian;
00615 }
00616 
00617 
00618 
00619 #ifndef NO_VALGEN_STRINGS  
00620 RetinalObjectStringArgs::ParamList Retinal_FuzzyLine::paramlist() {
00621   RetinalObjectStringArgs::ParamList p;
00622   p.push_back("theta");
00623   p.push_back("cx");
00624   p.push_back("cy");
00625   p.push_back("ysigma");
00626   p.push_back("center_width");
00627   p.push_back("scale");
00628   p.push_back("offset");
00629   return p;
00630 }
00631 #endif
00632 
00633 
00634 bool Retinal_FuzzyLine::update() const
00635 {
00636   /* Cache the result of sub-computations */
00637   cx                        = get_var("cx");
00638   cy                        = get_var("cy");
00639   centerw                   = get_var("center_width");
00640   const Variable t          = get_var("theta");
00641   cost                      = cos(t);
00642   sint                      = sin(t);
00643   const Variable ysigma     = get_var("ysigma");
00644   div_ysigmasq              = 1/(ysigma*ysigma);
00645 
00646   return Retinal_Object::update();
00647 }
00648 
00649 
00650 Retinal_Object::Activity Retinal_FuzzyLine::activ(Coordinate x, Coordinate y) const
00651 {
00652   const Coordinate dx                 = x-cx;
00653   const Coordinate dy                 = y-cy;
00654   const Coordinate distance_from_line = fabs(dy*cost - dx*sint);
00655   const Coordinate gaussian_x_coord   = (distance_from_line - centerw/2);
00656   return (gaussian_x_coord<=0 ? 1.0
00657           : exp(-gaussian_x_coord*gaussian_x_coord*div_ysigmasq));
00658 }
00659 
00660 
00661 
00662 #ifndef NO_VALGEN_STRINGS  
00663 RetinalObjectStringArgs::ParamList Retinal_FuzzyRing::paramlist() {
00664   RetinalObjectStringArgs::ParamList p;
00665   p.push_back("cx");
00666   p.push_back("cy");
00667   p.push_back("ysigma");
00668   p.push_back("center_width");
00669   p.push_back("radius");
00670   p.push_back("scale");
00671   p.push_back("offset");
00672   return p;
00673 }
00674 #endif
00675 
00676 
00677 bool Retinal_FuzzyRing::update() const
00678 {
00679   /* Cache the result of sub-computations */
00680   cx                        = get_var("cx");
00681   cy                        = get_var("cy");
00682   centerw                   = get_var("center_width");
00683   radius                    = get_var("radius");
00684   const Variable ysigma     = get_var("ysigma");
00685   div_ysigmasq              = 1/(ysigma*ysigma);
00686 
00687   return Retinal_Object::update();
00688 }
00689 
00690 
00691 Retinal_Object::Activity Retinal_FuzzyRing::activ(Coordinate x, Coordinate y) const
00692 {
00693   const Coordinate dx                 = x-cx;
00694   const Coordinate dy                 = y-cy;
00695   const Coordinate distance_from_line = fabs(radius - sqrt(dx*dx+dy*dy));
00696   const Coordinate gaussian_x_coord   = (distance_from_line - centerw/2);
00697   return (gaussian_x_coord<=0 ? 1.0
00698           : exp(-gaussian_x_coord*gaussian_x_coord*div_ysigmasq));
00699 }
00700 
00701 
00702 
00703 /******************************************************************************/
00704 /*  Retinal_PGM objects                                                       */
00705 /******************************************************************************/
00706 
00707 #ifndef NO_VALGEN_STRINGS
00708 
00710 string pgm_full_pathname(const string& filename, const string& paths="")
00711 {
00712   StringParser::arglist words;
00713   const StringParser p;
00714   p.parse(paths,words);
00715   
00716   StringParser::arglist::iterator i=words.begin();
00717   FILE *file=NULL;
00718   string name=filename;
00719   
00720   /* First try the current directory, then try all the possible paths */
00721   for (;;) {
00722     file=fopen(name.c_str(),"r");
00723     if (file) {
00724       fclose(file);
00725       return name;
00726     }
00727     if (i==words.end())
00728       return filename; /* Couldn't find file */
00729     name = (*i++)+filename;
00730   }
00731 #ifdef __GNUC__
00732   return ""; /* Never reached; just silences warning */
00733 #endif
00734 }
00735 
00736 
00737 RetinalObjectStringArgs::ParamList Retinal_PGM::paramlist() {
00738   RetinalObjectStringArgs::ParamList p;
00739   p.push_back("theta");
00740   p.push_back("cx");
00741   p.push_back("cy");
00742   p.push_back("size_scale");
00743   p.push_back("scale");
00744   p.push_back("offset");
00745   return p;
00746 }
00747 
00748 
00749 Retinal_PGM::Retinal_PGM( const string& name_val, RetinalObjectStringArgs& sa,
00750                           Coordinate visible_width, Coordinate visible_height )
00751   : Retinal_Object(name_val)
00752 {
00753   const string filename = sa.parsed_next("image_filename");
00754   const string paths    = sa.parsed_get_default("image_paths");
00755   basename=pgm_full_pathname(filename, paths);
00756   const pair<int,int> size = pnm_size(basename);
00757   const int width  = size.first;
00758   const int height = size.second;
00759   
00760   /* Read the image itself into memory if valid */
00761   if (width<=0 || height<=0) {
00762     sa.error("Problem reading image file `"+basename+"'");
00763     return;
00764   }
00765   set_active(pgm_read(basename,image));
00766   border=mat::edge_average(image);
00767   
00768   /*
00769     Compute special default parameter values
00770     
00771     Images smaller than the retina will always have at least one
00772     border showing, so we assume that all borders are ok.  If that's
00773     true, the picture can be placed and rotated just like any other
00774     object.  Larger images are assumed to represent the entire retina,
00775     so their position range is cropped to fully cover the retina at
00776     all times, never showing a border.  All such images default to
00777     being upright (with the width and height assuming that
00778     orientation), since rotating them would show a border unless the
00779     positions were cropped to the axis-aligned rectangle inscribed
00780     within the largest circle fitting in the ellipse inscribed within
00781     the image border.  (Even if that wouldn't be as complicated to
00782     calculate as to describe, it would certainly limit the usable area
00783     of the picture.)
00784   */
00785   if (width>=visible_width && height>=visible_height) {
00786     double dummy;
00787     const string size_scale_str = sa.parsed_get_default("size_scale");
00788     const double size_scale     = sa.parse(size_scale_str,dummy);
00789 
00790     sa.set_default("cx",    "Random "+
00791                    String::stringrep(visible_width /2)+" "+
00792                    String::stringrep(( width*size_scale-visible_width )/2));
00793     sa.set_default("cy",    "Random "+
00794                    String::stringrep(visible_height/2)+" "+
00795                    String::stringrep((height*size_scale-visible_height)/2));
00796     sa.set_default("theta", "PI/2");
00797   }
00798 
00799   /* Process the user-supplied arguments using the constructed set of defaults */
00800   merge_vars(sa.vars(paramlist()));
00801 }
00802 #endif /* NO_VALGEN_STRINGS */
00803 
00804 
00805 bool Retinal_PGM::update() const
00806 {
00807   /* Cache the result of sub-computations */
00808   const Variable mt         = -get_var("theta");
00809   cx                        =  get_var("cx");
00810   cy                        =  get_var("cy");
00811   cosmt                     = cos(mt);
00812   sinmt                     = sin(mt);
00813   const Variable size_scale = get_var("size_scale");
00814   div_size                  = 1/size_scale;
00815   /* Image is stored normally but rotated 90 degrees for drawing;
00816      see Retinal_PGM::activ.  Thus the bounds are switched here. */
00817   xoff                      = image.nrows()/2;
00818   yoff                      = image.ncols()/2;
00819     
00820   /* Compute bounding box */
00821   bounding_box.set(-xoff,-yoff,+xoff,+yoff);
00822   bounding_box.scale(size_scale,size_scale).rotate(mt).translate(cx,cy);
00823 
00824   return Retinal_Object::update();
00825 }
00826 
00827 
00828 
00830 Retinal_Object::Activity Retinal_PGM::activ(Coordinate x, Coordinate y) const
00831 {
00832   const Coordinate dx = (x-cx)*div_size;
00833   const Coordinate dy = (y-cy)*div_size;
00834   const image_type::size_type xp = static_cast<image_type::size_type>(dx*cosmt-dy*sinmt+xoff);
00835   const image_type::size_type yp = static_cast<image_type::size_type>(dx*sinmt+dy*cosmt+yoff);
00836 
00837   /*
00838     Even though the image is stored in the usual orientation, it is
00839     rotated for drawing so that the top points right for theta=0; that
00840     way, it will be upright given the usual PI/2 orientation default.
00841   */
00842   return
00843     ( int(xp) > 0 && int(yp) > 0 && xp < image.nrows() && yp < image.ncols() ) ?
00844     image[image.nrows()-xp-1][image.ncols()-yp-1]
00845     : border;
00846     //: 0.75; /* To debug image boundaries */ 
00847 }
00848 
00849 
00850 

Generated at Mon Aug 21 00:30:55 2000 for RF-LISSOM by doxygen1.2.1 written by Dimitri van Heesch, © 1997-2000