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 /*  Retinal_Object                                                            */
00030 /******************************************************************************/
00031 
00032 //#include <typeinfo> /* Uncomment if typeid is enabled below */
00033 
00034 string Retinal_Object::stringrep() const
00035 {
00036   /* Should be modified to use an ostringstream when sstream becomes available;
00037      also use the "fixed" iomanip instead of the setf below. */
00038   const char bufsize=100;
00039   static char buf[bufsize];
00040   ostrstream os(buf, bufsize);
00041   os.setf(std::ios::fixed,std::ios::floatfield);
00042   //os << typeid(*this).name() << " "; /* For debugging */
00043   if (name()!="") os << name() << " ";
00044   if (get_active()) {
00045     os << std::setprecision(1) 
00046        <<  "cx:"    << std::setw(4) << std::setfill('0') << get_var("cx")
00047        << " cy:"    << std::setw(4) << std::setfill('0') << get_var("cy")
00048        << " theta:" << std::setw(5) << std::setfill('0')
00049        << RADIANS_TO_DEGREES(CONSTRAIN_ANGLE(     get_var("theta")));
00050   }
00051   else
00052     os << "inactive";
00053       
00054   /* For debugging or other reasons, print all variables */
00055   //for(VarMap::const_iterator i=vars.begin(); i!= vars.end(); i++) {
00056   //  const string& nam= i->first;
00057   //  if (name != "cx" && nam != "cy" && nam != "theta")
00058   //      os << " " << nam << ":" << i->second->value();
00059   //}
00060 
00061   os << std::ends;
00062   
00063   return buf;
00064 }
00065 
00066 
00067 #ifndef NO_VALGEN_STRINGS
00068 Retinal_Object::VarMap RetinalObjectStringArgs::vars(const ParamList& params)
00069 {
00070   Retinal_Object::VarMap vars;
00071   
00072   /* Parse all positional arguments possible, substituting default if arg is missing */
00073   for (ParamList::const_iterator paramptr=params.begin();
00074        paramptr!=params.end(); paramptr++) {
00075     const string& name = *paramptr;
00076     const string  nextarg = args.top(string(""));
00077     const unsigned pos = nextarg.find("=");
00078     const bool    nextarghaseq = (pos<nextarg.length());
00079     const bool    positionalargsremain = !args.empty() && !nextarghaseq;
00080     const string  value = (positionalargsremain ? args.next(string("")) : get_default(name));
00081     set_linked(name,value,vars);
00082   }
00083   
00084   /* Parse any named arguments present, overriding defaults and positional arguments */
00085   while (!args.empty()) {
00086     const string   arg   = args.next(string(""));
00087     const unsigned pivot = arg.find("=");
00088     if (pivot>=arg.length()) {
00089       error("Syntax error in retinal object parameter specification");
00090       return vars;
00091     }
00092     const string name  = string(arg,0,pivot);
00093     const string value = String::strip_quotes(string(arg,pivot+1,arg.length()-pivot-1));
00094     set_linked(name,value,vars);
00095   }
00096 
00097   return vars;
00098 }
00099 #endif /* NO_VALGEN_STRINGS */
00100 
00101 
00102 
00103 /******************************************************************************/
00104 /*  Retinal_Composite objects                                                 */
00105 /******************************************************************************/
00106 
00107 
00108 bool Retinal_Composite::next()
00109 {
00110   return
00111     !std::count_if(ISEQ(children),std::not1(std::mem_fun(&ValueGen::next)))
00112     && Retinal_Object::next();
00113 }
00114 
00115 
00116 
00117 void Retinal_Composite::reset() {
00118   dominant_child = (accum_type==OneHot && children.size() ? children[0] : 0);
00119   std::for_each(ISEQ(children),std::mem_fun(&Retinal_Object::reset));
00120   Retinal_Object::reset();
00121 }
00122 
00123 
00124 
00125 #ifndef NO_VALGEN_STRINGS
00126 RetinalObjectStringArgs::ParamList Retinal_Composite::paramlist() {
00127   RetinalObjectStringArgs::ParamList p;
00128   p.push_back("theta");
00129   p.push_back("cx");
00130   p.push_back("cy");
00131   p.push_back("size_scale");
00132   p.push_back("scale");
00133   p.push_back("offset");
00134   p.push_back("accum_type");
00135   p.push_back("hot");
00136   return p;
00137 }
00138 #endif
00139 
00140 
00141 
00142 bool Retinal_Composite::update() const
00143 {
00144   std::for_each(ISEQ(children),std::mem_fun(&Retinal_Object::update));
00145 
00146   /* Select single dominant child, if any */
00147   if (accum_type==OneHot && children.size()) {
00148     const Variable hot = get_var("hot");
00149     const Variable relative_index = hot-int(hot); /* Value between 0 and 1 */
00150     const int      child_index    = (int)( children.size()*relative_index );
00151     assert(child_index<(int)children.size());
00152     dominant_child = children[child_index];
00153   }
00154   else
00155     dominant_child=0;
00156   
00157   /* Cache the result of sub-computations */
00158   cx                        = get_var("cx");
00159   cy                        = get_var("cy");
00160   const Variable mt         = -get_var("theta");
00161   cosmt                     = cos(mt);
00162   sinmt                     = sin(mt);
00163   const Variable size_scale = get_var("size_scale");
00164   div_size                  = 1/size_scale;
00165 
00166 
00167   /* Construct bounding box enclosing all children in local coordinates  */
00168   bounding_box.set(0,0,0,0); /* Initially empty */
00169   for(const_iterator i=children.begin(); i!=children.end(); i++)
00170     bounding_box += (*i)->bounding_box;
00171   
00172   /* Convert the bounding box into world coordinates */
00173   bounding_box.scale(size_scale,size_scale).rotate(mt).translate(cx,cy);
00174 
00175   return Retinal_Object::update();
00176 }
00177 
00178 
00180 class ActivityAccumulator : public std::binary_function<Retinal_Object*,Retinal_Object*,bool> {
00181 public:
00182   explicit ActivityAccumulator(const Retinal_Obj::Coordinate xi, const Retinal_Obj::Coordinate yi,
00183                                Retinal_Composite::AccumulationType accum_type_i)
00184     : x(xi), y(yi), accum_type(accum_type_i) { }
00185 
00187   Retinal_Obj::Activity operator() (Retinal_Obj::Activity prev, const Retinal_Object* r) {
00188     const Retinal_Obj::Activity now=r->activation(x,y);
00189     switch (accum_type) {
00190     case Retinal_Composite::Max: return std::max(prev,now);
00191     case Retinal_Composite::Min: return std::min(prev,now);
00192     case Retinal_Composite::Add: return prev+now;
00193     case Retinal_Composite::Replace:
00194       return (r->inside(x,y)? now : prev);
00195     default: return prev+now;
00196     }
00197   }
00198   
00199 private:
00200   Retinal_Obj::Coordinate x,y;
00201   const Retinal_Composite::AccumulationType accum_type;
00202 };
00203 
00204 
00205 
00212 class DefaultActivityAccumulator : public std::binary_function<Retinal_Object*,Retinal_Object*,bool> {
00213 public:
00214   explicit DefaultActivityAccumulator(Retinal_Composite::AccumulationType accum_type_i)
00215     : accum_type(accum_type_i) { }
00216 
00218   Retinal_Obj::Activity operator() (Retinal_Obj::Activity prev, const Retinal_Object* r) {
00219     const Retinal_Obj::Activity now=r->default_activation();
00220     switch (accum_type) {
00221     case Retinal_Composite::Max: return std::max(prev,now);
00222     case Retinal_Composite::Min: return std::min(prev,now);
00223     case Retinal_Composite::Add: return prev+now;
00224     case Retinal_Composite::Replace: return prev;
00225     default: return prev+now;
00226     }
00227   }
00228   
00229 private:
00230   const Retinal_Composite::AccumulationType accum_type;
00231 };
00232 
00233 
00234 
00236 Retinal_Obj::Activity Retinal_Composite::accum_base() const {
00237   switch (accum_type) {
00238     /* Returns activation value assumed to be higher than all others */
00239   case Min: return  10000000;
00240     
00241     /* Returns activation value assumed to be lower  than all others */
00242   case Max: return -10000000; 
00243 
00244     /* Returns the default activation value of the first child (if any),
00245        assumed to be a background pattern. */
00246   case Replace: return children.size()? children[0]->default_activation() : 0;
00247 
00248   default:  return 0;
00249   }
00250 }
00251 
00252 
00253 
00254 Retinal_Object::Activity Retinal_Composite::default_activ() const
00255 {
00256   //return =0.5; /* To debug Composite bounding boxes */
00257   return 
00258     (children.empty() ? 0 
00259      : (dominant_child? dominant_child->default_activation()
00260         : std::accumulate(ISEQ(children),accum_base(),DefaultActivityAccumulator(accum_type))));
00261 }
00262 
00263 
00264   
00265 Retinal_Object::Activity Retinal_Composite::activ(Coordinate x, Coordinate y) const
00266 {
00267   const Coordinate dx      = (x-cx)*div_size;
00268   const Coordinate dy      = (y-cy)*div_size;
00269   const Coordinate xp      = dx*cosmt-dy*sinmt;
00270   const Coordinate yp      = dx*sinmt+dy*cosmt;
00271   
00272   return (dominant_child ? dominant_child->activation(xp,yp)
00273           : std::accumulate(ISEQ(children),accum_base(),ActivityAccumulator(xp,yp,accum_type)));
00274 }
00275 
00276 
00277 
00280 const Retinal_Object& Retinal_Composite::mostactive(Coordinate x, Coordinate y) const
00281 {
00282   if (children.begin()==children.end())
00283     return *this;
00284   else {
00285     Retinal_Object* champ = *children.begin();
00286     Activity high_act = champ->activation(x,y);
00287     Activity act;
00288     for (Retinal_Composite::const_iterator i = children.begin()+1; i!= children.end(); i++) {
00289       act = (*i)->activation(x,y);
00290       if (act>high_act) {
00291         champ = *i;
00292         high_act=act;
00293       }
00294     }
00295     return *champ;
00296   }
00297 }
00298 
00299 
00300 
00303 string accumulate_stringreps(string val, Retinal_Object* r)
00304 {  return val + " [" + r->stringrep() + "]";  }
00305 
00306 
00307 
00308 string Retinal_Composite::stringrep() const
00309 {
00310   return Retinal_Object::stringrep() +
00311     (dominant_child ? " " + dominant_child->stringrep()
00312      : std::accumulate(ISEQ(children),string(""),accumulate_stringreps));
00313 }
00314 
00315 
00316 
00317 bool Retinal_Composite::inside(Coordinate x, Coordinate y) const
00318 {
00319   /* Normal case */
00320   if (!(accum_type==OneHot && dominant_child))
00321     return bounding_box.inside(x,y);
00322 
00323   /* When there is one dominant child, ask it instead */
00324   const Coordinate dx      = (x-cx)*div_size;
00325   const Coordinate dy      = (y-cy)*div_size;
00326   const Coordinate xp      = dx*cosmt-dy*sinmt;
00327   const Coordinate yp      = dx*sinmt+dy*cosmt;
00328   
00329   return dominant_child->inside(xp,yp);
00330 }
00331 
00332 
00333 /******************************************************************************/
00334 /*  Retinal_ManagedComposite objects                                          */
00335 /******************************************************************************/
00336 
00337 
00338 bool Retinal_ManagedComposite::distance_valid (const Retinal_Object& obj1, const Retinal_Object& obj2)
00339 {
00340   if (!*min_dist_enforce && !*max_dist_enforce)
00341     return true;
00342 
00343   const Coordinate dist = Generic::hypot(obj1.get_var("cx") - obj2.get_var("cx"),
00344                                          obj1.get_var("cy") - obj2.get_var("cy"));
00345 
00346   return ((!*min_dist_enforce || dist >= *min_dist) &&
00347           (!*max_dist_enforce || dist <= *max_dist) );
00348 }
00349 
00350 
00351 
00352 bool Retinal_ManagedComposite::accumulate_managed_next(bool val, Retinal_Object* r)
00353 {
00354   /*
00355     Arbitrary "timeout" value to prevent endless loop; with
00356     some parameter values it may be impossible to find a valid
00357     distance.
00358   */
00359   const int max_trials=100; 
00360 
00361   /* Generate and test possible positions until a valid one is found */
00362   for (int trial=0; trial<max_trials; trial++) {
00363     r->next();
00364     
00365     iterator it = children.begin();
00366     while (*it != r && distance_valid(*r,**it))
00367       it++;
00368 
00369     if (*it == r) {
00370       r->set_active(true);
00371       return val;
00372     }
00373   }
00374   
00375   r->set_active(false);
00376   return false;
00377 }
00378 
00379 
00380 
00381 bool Retinal_ManagedComposite::next()
00382 {
00383   bool val=true;
00384   for (iterator i = Retinal_Composite::children.begin();
00385        i != children.end();  i++) {
00386     val = accumulate_managed_next(val,*i);
00387   }
00388 
00389   return val && Retinal_Object::next();
00390 }
00391 
00392 
00393 
00394 /******************************************************************************/
00395 /*  Retinal_AnchoredManagedComposite objects                                  */
00396 /******************************************************************************/
00397 
00399 Retinal_Object::Activity Retinal_AnchoredManagedComposite::activ(Coordinate x, Coordinate y) const
00400 {
00401   return (dominant_child ? dominant_child->activation(x,y)
00402           : std::accumulate(ISEQ(children),accum_base(),ActivityAccumulator(x,y,accum_type)));
00403 }
00404 
00405 
00406 
00408 string Retinal_AnchoredManagedComposite::stringrep() const
00409 {
00410   return name() + 
00411     (dominant_child ? " " + dominant_child->stringrep()
00412      : std::accumulate(ISEQ(children),string(""),accumulate_stringreps));
00413 }
00414 
00415 
00416 
00417 /******************************************************************************/
00418 /*                                                                            */
00419 /*  Miscellaneous leaf objects                                                */
00420 /*  (See corresponding constants for documentation)                           */
00421 /*                                                                            */
00422 /*  Each usually caches parameter values that it needs (once) in update() and */
00423 /*  then uses them (many times) in activ().  If it uses a bounding box, it    */
00424 /*  usually computes the bounding box in update().                            */
00425 /*                                                                            */
00426 /******************************************************************************/
00427 
00428 #ifndef NO_VALGEN_STRINGS
00429 RetinalObjectStringArgs::ParamList Retinal_CircularGaussian::paramlist() {
00430   RetinalObjectStringArgs::ParamList p;
00431   p.push_back("cx");
00432   p.push_back("cy");
00433   p.push_back("xsigma");
00434   p.push_back("scale");
00435   p.push_back("offset");
00436   return p;
00437 }
00438 #endif
00439 
00440 
00441 bool   Retinal_CircularGaussian::update() const
00442 {
00443   /* Cache the result of sub-computations */
00444   cx                        = get_var("cx");
00445   cy                        = get_var("cy");
00446   const Variable xsigma     = get_var("xsigma");
00447   div_sigmasq               = 1/(xsigma*xsigma);
00448 
00449   /* Compute bounding box */
00450   const Coordinate boundrad=bound_mult*xsigma;
00451   bounding_box.set(cx-boundrad,cy-boundrad,
00452                    cx+boundrad,cy+boundrad);
00453 
00454   return Retinal_Object::update();
00455 }
00456 
00457 
00458 Retinal_Object::Activity Retinal_CircularGaussian::activ(Coordinate x, Coordinate y) const
00459 {
00460   const Coordinate dx  = x-cx;
00461   const Coordinate dy  = y-cy;
00462 
00463   return exp( -(dx*dx+dy*dy)*div_sigmasq);
00464 }
00465 
00466 
00467 
00468 #ifndef NO_VALGEN_STRINGS
00469 RetinalObjectStringArgs::ParamList Retinal_Gaussian::paramlist() {
00470   RetinalObjectStringArgs::ParamList p;
00471   p.push_back("theta");
00472   p.push_back("cx");
00473   p.push_back("cy");
00474   p.push_back("xsigma");
00475   p.push_back("ysigma");
00476   p.push_back("scale");
00477   p.push_back("offset");
00478   return p;
00479 }
00480 #endif
00481 
00482 
00483 bool Retinal_Gaussian::update() const
00484 {
00485   /* Cache the result of sub-computations */
00486   cx                        = get_var("cx");
00487   cy                        = get_var("cy");
00488   const Variable t          = get_var("theta");
00489   cost                      = cos(t);
00490   sint                      = sin(t);
00491   const Variable xsigma     = get_var("xsigma");
00492   const Variable ysigma     = get_var("ysigma");
00493   div_xsigma                = 1/xsigma;
00494   div_ysigma                = 1/ysigma;
00495   
00496   /* Compute bounding box */
00497   const Variable xrad=bound_mult*xsigma;
00498   const Variable yrad=bound_mult*ysigma;
00499   bounding_box.set(-xrad,-yrad,xrad,yrad).rotate(t).translate(cx,cy);
00500 
00501   return Retinal_Object::update();
00502 }
00503 
00504 
00505 Retinal_Object::Activity Retinal_Gaussian::activ(Coordinate x, Coordinate y) const
00506 {
00507   const Coordinate dx    = x-cx;
00508   const Coordinate dy    = y-cy;
00509   const Coordinate xp    = ( dx * cost + dy * sint)*div_xsigma;
00510   const Coordinate yp    = (-dx * sint + dy * cost)*div_ysigma;
00511 
00512   return exp(-(xp*xp + yp*yp));
00513 }
00514 
00515 
00516 
00517 #ifndef NO_VALGEN_STRINGS
00518 RetinalObjectStringArgs::ParamList Retinal_Rectangle::paramlist() {
00519   RetinalObjectStringArgs::ParamList p;
00520   p.push_back("cx");
00521   p.push_back("cy");
00522   p.push_back("xsigma");
00523   p.push_back("ysigma");
00524   p.push_back("scale");
00525   p.push_back("offset");
00526   return p;
00527 }
00528 #endif
00529 
00530 
00531 bool Retinal_Rectangle::update() const
00532 {
00533   /* Compute bounding box. */
00534   const Coordinate xrad   = get_var("xsigma");
00535   const Coordinate yrad   = get_var("ysigma");
00536   const Coordinate cx_    = get_var("cx");
00537   const Coordinate cy_    = get_var("cy");
00538   bounding_box.set(-xrad,-yrad,xrad,yrad).translate(cx_,cy_);
00539 
00540   return Retinal_Object::update();
00541 }
00542 
00543 
00544 
00545 #ifndef NO_VALGEN_STRINGS
00546 RetinalObjectStringArgs::ParamList Retinal_SineGrating::paramlist() {
00547   RetinalObjectStringArgs::ParamList p;
00548   p.push_back("theta");
00549   p.push_back("freq");
00550   p.push_back("phase");
00551   p.push_back("scale");
00552   p.push_back("offset");
00553   p.push_back("cx");
00554   p.push_back("cy");
00555   return p;
00556 }
00557 #endif
00558 
00559 
00560 bool Retinal_SineGrating::update() const
00561 {
00562   /* Cache the result of sub-computations */
00563   const Variable t          = get_var("theta");
00564   cost                      = cos(t);
00565   sint                      = sin(t);
00566   phase                     = get_var("phase");
00567   freq                      = get_var("freq");
00568   cx                        = get_var("cx");
00569   cy                        = get_var("cy");
00570 
00571   return Retinal_Object::update();
00572 }
00573 
00574 
00575 Retinal_Object::Activity Retinal_SineGrating::activ(Coordinate x, Coordinate y) const
00576 {  return sin( freq*((x-cx)*sint-(y-cy)*cost) + phase);  }
00577 
00578 
00579   
00580 #ifndef NO_VALGEN_STRINGS  
00581 RetinalObjectStringArgs::ParamList Retinal_Gabor::paramlist() {
00582   RetinalObjectStringArgs::ParamList p;
00583   p.push_back("theta");
00584   p.push_back("cx");
00585   p.push_back("cy");
00586   p.push_back("xsigma");
00587   p.push_back("ysigma");
00588   p.push_back("freq");
00589   p.push_back("phase");
00590   p.push_back("scale");
00591   p.push_back("offset");
00592   return p;
00593 }
00594 #endif
00595 
00596 
00597 bool Retinal_Gabor::update() const
00598 {
00599   /* Cache the result of sub-computations */
00600   cx                        = get_var("cx");
00601   cy                        = get_var("cy");
00602   const Variable t          = get_var("theta");
00603   cost                      = cos(t);
00604   sint                      = sin(t);
00605   phase                     = get_var("phase");
00606   freq                      = get_var("freq");
00607   const Variable xsigma     = get_var("xsigma");
00608   const Variable ysigma     = get_var("ysigma");
00609   div_xsigmasq              = 1/(xsigma*xsigma);
00610   div_ysigmasq              = 1/(ysigma*ysigma);
00611 
00612   /* Compute bounding box */
00613   const Variable xrad=bound_mult*xsigma;
00614   const Variable yrad=bound_mult*ysigma;
00615   bounding_box.set(-xrad,-yrad,xrad,yrad).rotate(t).translate(cx,cy);
00616   
00617   return Retinal_Object::update();
00618 }
00619 
00620 
00621 Retinal_Object::Activity Retinal_Gabor::activ(Coordinate x, Coordinate y) const
00622 {
00623   const Coordinate dx       = x-cx;
00624   const Coordinate dy       = y-cy;
00625   const Coordinate xp       = ( dx * cost + dy * sint);
00626   const Coordinate yp       = (-dx * sint + dy * cost);
00627 
00628   const Activity   gaussian = exp(-(xp*xp*div_xsigmasq + yp*yp*div_ysigmasq));
00629   const Activity   grating  = 0.5+0.5*cos( freq*yp + phase );
00630   
00631   return grating * gaussian;
00632 }
00633 
00634 
00635 
00636 #ifndef NO_VALGEN_STRINGS  
00637 RetinalObjectStringArgs::ParamList Retinal_FuzzyLine::paramlist() {
00638   RetinalObjectStringArgs::ParamList p;
00639   p.push_back("theta");
00640   p.push_back("cx");
00641   p.push_back("cy");
00642   p.push_back("ysigma");
00643   p.push_back("center_width");
00644   p.push_back("scale");
00645   p.push_back("offset");
00646   return p;
00647 }
00648 #endif
00649 
00650 
00651 bool Retinal_FuzzyLine::update() const
00652 {
00653   /* Cache the result of sub-computations */
00654   cx                        = get_var("cx");
00655   cy                        = get_var("cy");
00656   centerw                   = get_var("center_width");
00657   const Variable t          = get_var("theta");
00658   cost                      = cos(t);
00659   sint                      = sin(t);
00660   const Variable ysigma     = get_var("ysigma");
00661   div_ysigmasq              = 1/(ysigma*ysigma);
00662 
00663   return Retinal_Object::update();
00664 }
00665 
00666 
00667 Retinal_Object::Activity Retinal_FuzzyLine::activ(Coordinate x, Coordinate y) const
00668 {
00669   const Coordinate dx                 = x-cx;
00670   const Coordinate dy                 = y-cy;
00671   const Coordinate distance_from_line = fabs(dy*cost - dx*sint);
00672   const Coordinate gaussian_x_coord   = (distance_from_line - centerw/2);
00673   return (gaussian_x_coord<=0 ? 1.0
00674           : exp(-gaussian_x_coord*gaussian_x_coord*div_ysigmasq));
00675 }
00676 
00677 
00678 
00679 #ifndef NO_VALGEN_STRINGS  
00680 RetinalObjectStringArgs::ParamList Retinal_FuzzyDisc::paramlist() {
00681   RetinalObjectStringArgs::ParamList p;
00682   p.push_back("cx");
00683   p.push_back("cy");
00684   p.push_back("ysigma");
00685   p.push_back("center_width");
00686   p.push_back("scale");
00687   p.push_back("offset");
00688   return p;
00689 }
00690 #endif
00691 
00692 
00693 bool Retinal_FuzzyDisc::update() const
00694 {
00695   /* Cache the result of sub-computations */
00696   cx                        = get_var("cx");
00697   cy                        = get_var("cy");
00698   centerw                   = get_var("center_width");
00699   const Variable ysigma     = get_var("ysigma");
00700   div_ysigmasq              = 1/(ysigma*ysigma);
00701 
00702   return Retinal_Object::update();
00703 }
00704 
00705 
00706 Retinal_Object::Activity Retinal_FuzzyDisc::activ(Coordinate x, Coordinate y) const
00707 {
00708   const Coordinate dx                 = x-cx;
00709   const Coordinate dy                 = y-cy;
00710   const Coordinate distance_from_line = sqrt(dx*dx+dy*dy);
00711   const Coordinate gaussian_x_coord   = (distance_from_line - centerw/2);
00712   return (gaussian_x_coord<=0 ? 1.0
00713           : exp(-gaussian_x_coord*gaussian_x_coord*div_ysigmasq));
00714 }
00715 
00716 
00717 
00718 #ifndef NO_VALGEN_STRINGS  
00719 RetinalObjectStringArgs::ParamList Retinal_FuzzyRing::paramlist() {
00720   RetinalObjectStringArgs::ParamList p;
00721   p.push_back("cx");
00722   p.push_back("cy");
00723   p.push_back("ysigma");
00724   p.push_back("center_width");
00725   p.push_back("radius");
00726   p.push_back("scale");
00727   p.push_back("offset");
00728   return p;
00729 }
00730 #endif
00731 
00732 
00733 bool Retinal_FuzzyRing::update() const
00734 {
00735   /* Cache the result of sub-computations */
00736   cx                        = get_var("cx");
00737   cy                        = get_var("cy");
00738   centerw                   = get_var("center_width");
00739   radius                    = get_var("radius");
00740   const Variable ysigma     = get_var("ysigma");
00741   div_ysigmasq              = 1/(ysigma*ysigma);
00742 
00743   return Retinal_Object::update();
00744 }
00745 
00746 
00747 Retinal_Object::Activity Retinal_FuzzyRing::activ(Coordinate x, Coordinate y) const
00748 {
00749   const Coordinate dx                 = x-cx;
00750   const Coordinate dy                 = y-cy;
00751   const Coordinate distance_from_line = fabs(radius - sqrt(dx*dx+dy*dy));
00752   const Coordinate gaussian_x_coord   = (distance_from_line - centerw/2);
00753   return (gaussian_x_coord<=0 ? 1.0
00754           : exp(-gaussian_x_coord*gaussian_x_coord*div_ysigmasq));
00755 }
00756 
00757 
00758 
00759 /******************************************************************************/
00760 /*  Retinal_PGM objects                                                       */
00761 /******************************************************************************/
00762 
00763 #ifndef NO_VALGEN_STRINGS
00764 
00766 string pgm_full_pathname(const string& filename, const string& paths="")
00767 {
00768   StringParser::arglist words;
00769   const StringParser p;
00770   p.parse(paths,words);
00771   
00772   StringParser::arglist::iterator i=words.begin();
00773   FILE *file=NULL;
00774   string name=filename;
00775   
00776   /* First try the current directory, then try all the possible paths */
00777   for (;;) {
00778     file=fopen(name.c_str(),"r");
00779     if (file) {
00780       fclose(file);
00781       return name;
00782     }
00783     if (i==words.end())
00784       return filename; /* Couldn't find file */
00785     name = (*i++)+filename;
00786   }
00787 #ifdef __GNUC__
00788   return ""; /* Never reached; just silences warning */
00789 #endif
00790 }
00791 
00792 
00793 RetinalObjectStringArgs::ParamList Retinal_PGM::paramlist() {
00794   RetinalObjectStringArgs::ParamList p;
00795   p.push_back("theta");
00796   p.push_back("cx");
00797   p.push_back("cy");
00798   p.push_back("size_scale");
00799   p.push_back("autoscale");
00800   p.push_back("transparent");
00801   p.push_back("scale");
00802   p.push_back("offset");
00803   return p;
00804 }
00805 
00806 
00807 Retinal_PGM::Retinal_PGM( const string& name_val, RetinalObjectStringArgs& sa,
00808                           Retinal_Composite* parent)
00809   : Retinal_Object(name_val), ascale(1.0), aoffset(0.0), transparent_level(-1)
00810 {
00811   const string filename = sa.parsed_next("image_filename");
00812   const string paths    = sa.parsed_get_default("image_paths");
00813   basename=pgm_full_pathname(filename, paths);
00814   const std::pair<int,int> size = pnm_size(basename);
00815   const int width  = size.first;
00816   const int height = size.second;
00817   
00818   /* Read the image itself into memory if valid */
00819   if (width<=0 || height<=0) {
00820     sa.error("Could not read image file `"+basename+"'");
00821     return;
00822   }
00823   set_active(pgm_read(basename,image));
00824   if (!get_active())
00825     sa.error("Problem reading image file `"+basename+"'; not in .pgm or .pbm format?");
00826   border=mat::edge_average(image);
00827 
00828   /*
00829     Compute special default parameter values
00830     
00831     Images smaller than the retina will always have at least one
00832     border showing, so we assume that all borders are ok.  If that's
00833     true, the picture can be placed and rotated just like any other
00834     object.  Larger images are assumed to represent the entire retina,
00835     so their position range is cropped to fully cover the retina at
00836     all times, never showing a border.  All such images default to
00837     being upright (with the width and height assuming that
00838     orientation), because rotating them would show a border unless the
00839     positions were cropped to the axis-aligned rectangle inscribed
00840     within the largest circle fitting in the ellipse inscribed within
00841     the image border.  (Even if that wouldn't be as complicated to
00842     calculate as to describe, it would certainly limit the usable area
00843     of the picture.)
00844 
00845     Note: The defaults will not work if the user specifies a size_scale.
00846   */
00847   if (parent) {
00848     double dummy;
00849     const string size_scale_str = sa.parsed_get_default("size_scale");
00850     const double size_scale     = sa.parse(size_scale_str,dummy);
00851     const double parentwidth    = parent->nominal_width();
00852     const double parentheight   = parent->nominal_height();
00853     const double excesswidth    =  width*size_scale - parentwidth;
00854     const double excessheight   = height*size_scale - parentheight;
00855 
00856     if (excesswidth>=0 && excessheight>=0) {
00857       /* The formulas for the Uncorrelation bounds are not yet correct. */
00858       const string cx_default     = "Uncorrelate &uncorrelation " + String::stringrep(parentwidth/2-excesswidth/2) + " " + String::stringrep(parentwidth/2+excesswidth/2) + " Random " + String::stringrep(parentwidth/2)
00859         + " " + String::stringrep(excesswidth/2);
00860       const string cy_default     = "Uncorrelate &uncorrelation " + String::stringrep(parentheight/2-excessheight/2) + " " + String::stringrep(parentheight/2+excessheight/2) + " Random " + String::stringrep(parentheight/2)
00861         + " " + String::stringrep(excessheight/2);
00862       
00863       sa.set_default("cx",    cx_default);
00864       sa.set_default("cy",    cy_default);
00865       sa.set_default("theta", "PI/2");
00866     }
00867   }
00868 
00869   /* Process the user-supplied arguments using the constructed set of defaults */
00870   merge_vars(sa.vars(paramlist()));
00871   
00872   /* Set up transparency, if any */
00873   const Variable transparent = get_var("transparent");
00874   if (transparent>0)
00875     transparent_level=transparent;
00876   else if (pgm_white_is_transparent(basename))
00877     transparent_level=0.999; /* Slightly larger than 254/255 (0.996) */
00878 
00879   //cout << "[" << "Transparent: " << transparent_level << "(" << transparent << ")]" << endl;
00880 
00881   /* To debug the computed default values */
00882   //cout << "Retinal_PGM -- file " << filename << " will use cx '"
00883   //         << vargen_stringrep("cx") << "' and cy '"
00884   //         << vargen_stringrep("cy") << "'" << endl;
00885   
00886   /* Automatic scaling */
00887   const Variable autoscale  = get_var("autoscale");
00888   if (autoscale>0) {
00889     const double maxval = *(std::max_element(MSEQ(image)));
00890     const double minval = *(std::min_element(MSEQ(image)));
00891     const double range  = maxval-minval;
00892     if (range>0) {
00893       ascale  = 1/range;
00894       aoffset = minval;
00895       border  = border*ascale+aoffset;
00896       //cout << "Autoscaling to " << range << " (range " << minval << ".." << maxval << ")" << endl;
00897     }
00898   }
00899 }
00900 #endif /* NO_VALGEN_STRINGS */
00901 
00902 
00903 bool Retinal_PGM::update() const
00904 {
00905   /* Cache the result of sub-computations */
00906   const Variable mt         = -get_var("theta");
00907   cx                        =  get_var("cx");
00908   cy                        =  get_var("cy");
00909   cosmt                     = cos(mt);
00910   sinmt                     = sin(mt);
00911   const Variable size_scale = get_var("size_scale");
00912   div_size                  = 1/size_scale;
00913   /* Image is stored normally but rotated 90 degrees for drawing;
00914      see Retinal_PGM::activ.  Thus the bounds are switched here. */
00915   xoff                      = image.nrows()/2;
00916   yoff                      = image.ncols()/2;
00917     
00918   /* Compute bounding box */
00919   bounding_box.set(-xoff-1,-yoff-1,+xoff+1,+yoff+1); /* Generous to avoid truncation */
00920   bounding_box.scale(size_scale,size_scale).rotate(mt).translate(cx,cy);
00921 
00922   return Retinal_Object::update();
00923 }
00924 
00925 
00926 
00933 Retinal_Object::Activity Retinal_PGM::pixel_value(Coordinate x, Coordinate y, bool& inbounds) const
00934 {
00935   const Coordinate dx = (x-cx)*div_size;
00936   const Coordinate dy = (y-cy)*div_size;
00937   const Coordinate xp = dx*cosmt-dy*sinmt+xoff;
00938   const Coordinate yp = dx*sinmt+dy*cosmt+yoff;
00939       
00940   /*
00941     Even though the image is stored in the usual orientation, it is
00942     rotated for drawing so that the top points right for theta=0; that
00943     way, it will be upright given the usual PI/2 orientation default.
00944   */
00945   const int r = image.nrows()-int(xp+0.5)-1;
00946   const int c = image.ncols()-int(yp+0.5)-1;
00947  
00948   inbounds = ( r >= 0 && c >= 0 && r < int(image.nrows()) && c < int(image.ncols()) );
00949   return inbounds? image[r][c] : 0;
00950 }
00951 
00952 
00953 
00955 Retinal_Object::Activity Retinal_PGM::activ(Coordinate x, Coordinate y) const
00956 {
00957   bool inbounds;
00958   Retinal_Object::Activity pixval=pixel_value(x,y,inbounds);
00959   return inbounds? (ascale * (pixval-aoffset)) : border;
00960   //: 0.75; /* To debug image boundaries */ 
00961 }
00962 
00963 
00964 bool Retinal_PGM::inside(Coordinate x, Coordinate y) const
00965 {
00966   /* Quick check */
00967   if (!bounding_box.inside(x,y)) return false;
00968 
00969   /* W/o transparency, bbox determines inside() */
00970   if (transparent_level<0) return true;
00971   
00972   /* W/ transparency, a slower, pixel-by-pixel check against
00973      transparency threshold is needed */
00974   bool inbounds;
00975   Retinal_Object::Activity pixval=pixel_value(x,y,inbounds);
00976   return (inbounds && !(pixval>transparent_level));
00977 }
00978 

Generated on Mon Jan 20 02:35:45 2003 for RF-LISSOM by doxygen1.3-rc2