SearchArray.C

Go to the documentation of this file.
00001 /** @file Psycho/SearchArray.C jittered array of search elements */
00002 
00003 // //////////////////////////////////////////////////////////////////// //
00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00005 // by the University of Southern California (USC) and the iLab at USC.  //
00006 // See http://iLab.usc.edu for information about this project.          //
00007 // //////////////////////////////////////////////////////////////////// //
00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00010 // in Visual Environments, and Applications'' by Christof Koch and      //
00011 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00012 // pending; application number 09/912,225 filed July 23, 2001; see      //
00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00016 //                                                                      //
00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00018 // redistribute it and/or modify it under the terms of the GNU General  //
00019 // Public License as published by the Free Software Foundation; either  //
00020 // version 2 of the License, or (at your option) any later version.     //
00021 //                                                                      //
00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00025 // PURPOSE.  See the GNU General Public License for more details.       //
00026 //                                                                      //
00027 // You should have received a copy of the GNU General Public License    //
00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00030 // Boston, MA 02111-1307 USA.                                           //
00031 // //////////////////////////////////////////////////////////////////// //
00032 //
00033 // Primary maintainer for this file: Rob Peters <rjpeters at usc dot edu>
00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Psycho/SearchArray.C $
00035 // $Id: SearchArray.C 14200 2010-11-04 03:10:43Z jshen $
00036 //
00037 
00038 // Code herein is derived from GroovX, also licensed under the GPL
00039 // Copyright (c) 2002-2004 California Institute of Technology
00040 // Copyright (c) 2004-2007 University of Southern California
00041 // [http://ilab.usc.edu/rjpeters/groovx/]
00042 
00043 #ifndef PSYCHO_SEARCHARRAY_C_DEFINED
00044 #define PSYCHO_SEARCHARRAY_C_DEFINED
00045 
00046 #include "Psycho/SearchArray.H"
00047 
00048 #include "Image/CutPaste.H"
00049 #include "Image/Image.H"
00050 #include "Raster/Raster.H"
00051 #include "Image/geom.h"
00052 #include "Image/Rectangle.H"
00053 #include "rutz/rand.h"
00054 #include "Util/StringConversions.H"
00055 #include <cstdio>
00056 #include <algorithm> //for random_shuffle
00057 
00058 using geom::vec2d;
00059 
00060 namespace
00061 {
00062   void dumpFrame(const Image<byte>& bmap)
00063   {
00064     static int framecount = 0;
00065 
00066     Raster::WriteGray(bmap, sformat("frame_%06d.png", framecount++));
00067   }
00068 }
00069 
00070 SearchArray::SearchArray(const Dims& dims,
00071                          double gridSpacing,
00072                          double minSpacing,
00073                          double margin)
00074   :
00075   itsDims(dims),
00076   itsGridSpacing(gridSpacing),
00077   itsMinSpacing(minSpacing),
00078   itsFillResolution(15.0),
00079   itsMargin(margin),
00080 
00081   itsArray(),
00082 
00083   itsDumpingFrames(false),
00084   itsFrameDumpPeriod(20)
00085 {}
00086 
00087 SearchArray::~SearchArray() throw() {}
00088 
00089 void SearchArray::addElement(const rutz::shared_ptr<SearchItem>& e, const bool displace) const
00090 {
00091   if (tooClose(e->pos, -1))
00092     {
00093     if (!displace)
00094       LFATAL("element @ %g,%g is too close to an existing element",
00095            e->pos.x(), e->pos.y());
00096     else
00097       {
00098         std::vector<rutz::shared_ptr<SearchItem> > isTooClose;
00099         const double minSpacingSqr = itsMinSpacing*itsMinSpacing;
00100         for (size_t n = 0; n < itsArray.size(); ++n)
00101           {
00102             const vec2d v = e->pos; 
00103             const double dx = itsArray[n]->pos.x() - v.x();
00104             const double dy = itsArray[n]->pos.y() - v.y();
00105 
00106             if(dx*dx+dy*dy <= minSpacingSqr)
00107               isTooClose.push_back(itsArray[n]);
00108           }
00109         for (size_t i = 0; i < isTooClose.size(); ++i)
00110           doRemoveElement(isTooClose[i]);
00111       }
00112     }
00113   doAddElement(e);
00114 }
00115 
00116 void SearchArray::replaceElementAtSamePos(size_t i, const rutz::shared_ptr<SearchItem>& e)
00117 {
00118   if (i >= itsArray.size())
00119     LFATAL("expected idx < %"ZU", got idx = %"ZU, itsArray.size(), i);
00120 
00121   const rutz::shared_ptr<SearchItem> old = itsArray[i];
00122   itsArray[i] = e;
00123   itsArray[i]->pos = old->pos;
00124 }
00125 
00126 void SearchArray::generateBackground(SearchItemFactory& factory,
00127                                      const int diffusionCycles,
00128                                      const bool doFinalFill,
00129                                      const int jitterIters,
00130                                      const bool doJitterForeground,
00131                                      const int backgSeed) const
00132 {
00133   const size_t foregSize = itsArray.size();
00134 
00135   backgHexGrid(factory);
00136 
00137   rutz::urand urand(backgSeed);
00138 
00139   for (int i = 0; i < diffusionCycles; ++i)
00140     {
00141       backgJitter(urand, jitterIters, doJitterForeground);
00142       if (i+1 < diffusionCycles || doFinalFill)
00143         backgFill(factory);
00144 
00145       //      const double spacing = sqrt(2.0*itsDims.sz()/(sqrt(3.0)*itsArray.size()));
00146       //      LINFO("%"ZU" elements, ave spacing %f", itsArray.size(), spacing);
00147     }
00148 
00149   size_t insideNumber = foregSize;
00150 
00151   for (size_t n = 0; n < itsArray.size(); ++n)
00152     {
00153       if (itsArray[n]->type == SearchItem::FOREGROUND)
00154         continue;
00155 
00156       bool inside = true;
00157 
00158       for (size_t i = 0; i < foregSize; ++i)
00159         {
00160           const size_t j = (i+1) % foregSize;
00161 
00162           // This is the vector perpendicular to the line connecting
00163           // contour nodes i and j
00164           const double Yij = itsArray[i]->pos.x() - itsArray[j]->pos.x();
00165           const double Xij = itsArray[j]->pos.y() - itsArray[i]->pos.y();
00166 
00167           // This is the vector connecting from contour node i to the
00168           // background node
00169           const double Xin = itsArray[n]->pos.x() - itsArray[i]->pos.x();
00170           const double Yin = itsArray[n]->pos.y() - itsArray[i]->pos.y();
00171 
00172           // If the dot product of those two vectors is less than zero,
00173           // then the background node is "outside".
00174           const double vp = Xij*Xin + Yij*Yin;
00175 
00176           if (vp < 0.0) { inside = false; break; }
00177         }
00178 
00179       if (inside)
00180         {
00181           ++insideNumber;
00182         }
00183     }
00184 
00185   //  LINFO(" FOREG_NUMBER %"ZU"    PATCH_NUMBER %"ZU"    TOTAL_NUMBER %"ZU,
00186   //      foregSize, insideNumber, itsArray.size());
00187 }
00188 
00189 Image<byte> SearchArray::getImage(const double lo, const double hi,
00190                                   const double bg,
00191                                   bool doTagLast) const
00192 {
00193   Image<double> tmpwin(itsDims, NO_INIT);
00194   tmpwin.clear(bg);
00195 
00196   const Image<double>::iterator win = tmpwin.beginw();
00197 
00198   for (size_t i = 0; i < itsArray.size(); ++i)
00199     {
00200       const int xcenter = int(itsArray[i]->pos.x() + itsDims.w() / 2.0 + 0.5);
00201       const int ycenter = int(itsArray[i]->pos.y() + itsDims.h() / 2.0 + 0.5);
00202       
00203       const Image<double> p = itsArray[i]->getPatch();
00204 
00205       // top left:
00206       const int x0 = xcenter - p.getWidth() / 2;
00207       const int y0 = ycenter - p.getHeight() / 2;
00208       // bottom right:
00209       const int x1 = x0 + p.getWidth();
00210       const int y1 = y0 + p.getHeight();
00211 
00212       for (int y = y0; y < y1; ++y)
00213         for (int x = x0; x < x1; ++x)
00214           {
00215             if (x >= 0 && x < itsDims.w() && y >=0 && y < itsDims.h())
00216               win[x+y*itsDims.w()] += p.getVal(x-x0, y-y0);
00217           }
00218 
00219       if (doTagLast && (i+1) == itsArray.size())
00220         {
00221           const double outer = 0.4 * p.getWidth();
00222           const double inner = outer - 3;
00223 
00224           for (int y = y0; y < y1; ++y)
00225             for (int x = x0; x < x1; ++x)
00226               {
00227                 if (x >= 0 && x < itsDims.w() && y >=0 && y < itsDims.h())
00228                   {
00229                     const double r = sqrt((x-xcenter)*(x-xcenter) +
00230                                           (y-ycenter)*(y-ycenter));
00231 
00232                     if (r >= inner && r <= outer)
00233                       {
00234                         win[x+y*itsDims.w()] = 1.0;
00235                       }
00236                   }
00237               }
00238         }
00239     }
00240 
00241   Image<byte> result(tmpwin.getDims(), ZEROS);
00242 
00243   unsigned char* bytes = result.beginw();
00244 
00245   bool clip = false;
00246 
00247   const int npix = result.getSize();
00248 
00249   const double rng = hi - lo;
00250 
00251   for (int k = 0; k < npix; ++k)
00252     {
00253       int val = int((win[k]-lo)/rng*255);
00254 
00255       if      (val < 0)   { clip = true; val = 0; }
00256       else if (val > 255) { clip = true; val = 255; }
00257 
00258       GVX_ASSERT(val >= 0);
00259       GVX_ASSERT(val <= 255);
00260 
00261       *bytes++ = val;
00262     }
00263 
00264   if (clip)
00265     LERROR("some values were clipped");
00266 
00267   return result;
00268 }
00269 
00270 void SearchArray::pareBackground(const uint shrunkSize) const
00271 {
00272   // removes elements at random from the array down to the shrunkSize
00273   // elements need to be shuffled first.
00274   random_shuffle(itsArray.begin(),itsArray.end()); 
00275   while(itsArray.size() > shrunkSize)
00276     {
00277       const rutz::shared_ptr<SearchItem> last = itsArray.back();
00278       doRemoveElement(last);
00279       if (last->type == SearchItem::FOREGROUND)
00280         doAddElement(last,true);
00281     }
00282 }
00283 
00284 void SearchArray::clear() const
00285 {
00286   itsArray.clear();
00287 }
00288 
00289 void SearchArray::saveCoords(const std::string& filename) const
00290 {
00291   FILE* f = fopen(filename.c_str(), "w");
00292   if (f == 0)
00293     LFATAL("couldn't open %s for writing", filename.c_str());
00294 
00295   fprintf(f, "%% fg?   x0 y0   x1 y1\n");
00296 
00297   for (size_t i = 0; i < itsArray.size(); ++i)
00298     {
00299       const int xcenter = int(itsArray[i]->pos.x() + itsDims.w() / 2.0 + 0.5);
00300       const int ycenter = int(itsArray[i]->pos.y() + itsDims.h() / 2.0 + 0.5);
00301 
00302       const Image<double> p = itsArray[i]->getPatch();
00303 
00304       // top left:
00305       const int x0 = xcenter - p.getWidth() / 2;
00306       const int y0 = ycenter - p.getHeight() / 2;
00307       // bottom right:
00308       const int x1 = x0 + p.getWidth();
00309       const int y1 = y0 + p.getHeight();
00310 
00311       fprintf(f, "  %d   %d %d   %d %d\n",
00312               itsArray[i]->type == SearchItem::FOREGROUND ? 1 : 0,
00313               x0, y0, x1, y1);
00314     }
00315 
00316   fclose(f);
00317 }
00318 
00319 Rectangle SearchArray::itemBounds() const
00320 {
00321   Rectangle a;
00322   return a.centerDims(Point2D<int>(0,0),itsDims-itsMargin*2);
00323 }
00324 
00325 void SearchArray::doAddElement(const rutz::shared_ptr<SearchItem>& e, bool toFront) const
00326 {
00327   if (!toFront)
00328     itsArray.push_back(e);   
00329   else
00330     itsArray.insert(itsArray.begin(),e);
00331 
00332   // this double call is on purpose so that the added elements don't fly by
00333   // quite so quickly in the resulting movie
00334   if (itsDumpingFrames)
00335     {
00336       const Image<byte> bmap = getImage(true);
00337 
00338       dumpFrame(bmap);
00339       dumpFrame(bmap);
00340     }
00341 }
00342 
00343 bool SearchArray::tooClose(const vec2d& v, int except) const
00344 {
00345   if ((v.x() + itsDims.w()*0.5) < itsMargin
00346       || (itsDims.w()*0.5 - v.x()) <= itsMargin
00347       || (v.y() + itsDims.h()*0.5) < itsMargin
00348       || (itsDims.h()*0.5 - v.y()) <= itsMargin)
00349     return true;
00350 
00351   const double minSpacingSqr = itsMinSpacing*itsMinSpacing;
00352 
00353   for (size_t n = 0; n < itsArray.size(); ++n)
00354     {
00355       const double dx = itsArray[n]->pos.x() - v.x();
00356       const double dy = itsArray[n]->pos.y() - v.y();
00357 
00358       if (dx*dx+dy*dy <= minSpacingSqr && int(n) != except)
00359         return true;
00360     }
00361 
00362   return false;
00363 }
00364 
00365 void SearchArray::backgHexGrid(SearchItemFactory& factory) const
00366 {
00367   // lay down a hexagonal grid of elements
00368 
00369   const double dx = itsGridSpacing;
00370   const double dy = sqrt(3.0) * itsGridSpacing / 2.0;
00371 
00372   const int nx = int((itsDims.w() - itsMinSpacing) / dx - 0.5);
00373   const int ny = int((itsDims.h() - itsMinSpacing) / dy);
00374 
00375   double y = -0.5 * (ny-1) * dy;
00376 
00377   for (int j = 0; j < ny; ++j, y += dy)
00378     {
00379       double x = -0.5 * (nx-1) * dx - 0.25 * dx;
00380 
00381       // this is a hexagonal grid, so every other row is staggered by half
00382       // a step in the x direction
00383       if (j%2) x += 0.5*dx;
00384 
00385       for (int i = 0; i < nx; ++i, x += dx)
00386         {
00387           if (!tooClose(vec2d(x, y), -1))
00388             doAddElement(factory.make(vec2d(x, y)));
00389         }
00390     }
00391 }
00392 
00393 void SearchArray::backgFill(SearchItemFactory& factory) const
00394 {
00395   const double dx = itsMinSpacing / itsFillResolution;
00396 
00397   const double halfX = 0.5 * itsDims.w();
00398   const double halfY = 0.5 * itsDims.h();
00399 
00400   for (double x = -halfX; x <= halfX; x += dx)
00401     for (double y = -halfY; y <= halfY; y += dx)
00402       {
00403         if (!tooClose(vec2d(x, y), -1))
00404           doAddElement(factory.make(vec2d(x, y)));
00405       }
00406 }
00407 
00408 void SearchArray::backgJitter(rutz::urand& urand, const int jitterIters,
00409                               const bool doJitterForeground) const
00410 {
00411   const double jitter = (itsMinSpacing/16.0);
00412 
00413   const double halfX = 0.5 * itsDims.w();
00414   const double halfY = 0.5 * itsDims.h();
00415 
00416   for (int niter = 0; niter < jitterIters; ++niter)
00417     {
00418       for (size_t n = 0; n < itsArray.size(); ++n)
00419         {
00420           if (!doJitterForeground
00421               && itsArray[n]->type == SearchItem::FOREGROUND)
00422             continue;
00423 
00424           vec2d v;
00425           v.x() = itsArray[n]->pos.x() + jitter*(urand.fdraw_range(-1.0, 1.0));
00426           v.y() = itsArray[n]->pos.y() + jitter*(urand.fdraw_range(-1.0, 1.0));
00427 
00428           if (v.x() < -halfX) v.x() += itsDims.w();
00429           if (v.x() >  halfX) v.x() -= itsDims.w();
00430           if (v.y() < -halfY) v.y() += itsDims.h();
00431           if (v.y() >  halfY) v.y() -= itsDims.h();
00432 
00433           if (!tooClose(v, n))
00434             {
00435               itsArray[n]->pos = v;
00436             }
00437         }
00438 
00439       if (itsDumpingFrames &&
00440           niter % itsFrameDumpPeriod == 0)
00441         {
00442           const Image<byte> bmap = getImage(true);
00443           dumpFrame(bmap);
00444         }
00445     }
00446 }
00447 
00448 void SearchArray::doRemoveElement(const rutz::shared_ptr<SearchItem>& e) const
00449 {
00450   for (size_t i = 0; i < itsArray.size(); ++i)
00451     {
00452       if (e->pos.x()==itsArray[i]->pos.x() && 
00453           e->pos.y()==itsArray[i]->pos.y())
00454         {
00455           itsArray.erase(itsArray.begin()+i);
00456           break;
00457         }
00458     }
00459 
00460   // this double call is on purpose so that the added elements don't fly by
00461   // quite so quickly in the resulting movie
00462   if (itsDumpingFrames)
00463     {
00464       const Image<byte> bmap = getImage(true);
00465 
00466       dumpFrame(bmap);
00467       dumpFrame(bmap);
00468     }
00469 
00470 }
00471 
00472 // ######################################################################
00473 /* So things look consistent in everyone's emacs... */
00474 /* Local Variables: */
00475 /* mode: c++ */
00476 /* indent-tabs-mode: nil */
00477 /* End: */
00478 
00479 #endif // PSYCHO_SEARCHARRAY_C_DEFINED
Generated on Sun May 8 08:41:13 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3