Nv2LabelReader.C

Go to the documentation of this file.
00001 /*!@file NeovisionII/Nv2LabelReader.C */
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/NeovisionII/Nv2LabelReader.C $
00035 // $Id: Nv2LabelReader.C 14252 2010-11-19 17:43:55Z pez $
00036 //
00037 
00038 #ifndef NEOVISIONII_NV2LABELREADER_C_DEFINED
00039 #define NEOVISIONII_NV2LABELREADER_C_DEFINED
00040 
00041 
00042 #include <deque>
00043 #include <string>
00044 
00045 #include "NeovisionII/Nv2LabelReader.H"
00046 
00047 #include "Image/CutPaste.H"
00048 #include "Image/DrawOps.H"
00049 #include "NeovisionII/nv2_label_reader.h"
00050 #include "Util/StringConversions.H"
00051 #include "Util/StringUtil.H"
00052 #include "Util/sformat.H"
00053 
00054 // ######################################################################
00055 template <class T>
00056 void writeText2(Image<T>& dst,
00057                const Point2D<int>& pt, const char* text,
00058                const T col, const T bgcol, const SimpleFont& f,
00059                const double bg_alpha,
00060                const TextAnchor anchor)
00061 {
00062   const int textwidth = strlen(text) * f.w();
00063   const int textheight = f.h();
00064 
00065   const Point2D<int> top_left
00066     =    anchor == ANCHOR_BOTTOM_RIGHT ?    pt - Point2D<int>(textwidth, textheight)
00067     :    anchor == ANCHOR_BOTTOM_LEFT  ?    pt - Point2D<int>(0, textheight)
00068     :    anchor == ANCHOR_TOP_RIGHT    ?    pt - Point2D<int>(textwidth, 0)
00069     : /* anchor == ANCHOR_TOP_LEFT     ? */ pt;
00070 
00071   Point2D<int> p = top_left; // copy for modif
00072   const int ww = dst.getWidth(), hh = dst.getHeight();
00073   const int len = int(strlen(text));
00074 
00075   for (int i = 0; i < len; i ++)
00076     {
00077       const unsigned char *ptr = f.charptr(text[i]);
00078 
00079       for (int y = 0; y < int(f.h()); y ++)
00080         for (int x = 0; x < int(f.w()); x ++)
00081           if (p.i + x >= 0 && p.i + x < ww && p.j + y >= 0 && p.j + y < hh)
00082             {
00083               if (!ptr[y * f.w() + x])
00084                 dst.setVal(p.i + x, p.j + y, col);
00085               else
00086                 dst.setVal(p.i + x, p.j + y,
00087                            bgcol * (1.0 - bg_alpha)
00088                            + dst.getVal(p.i + x, p.j + y) * bg_alpha);
00089             }
00090       p.i += f.w();
00091     }
00092 }
00093 
00094 // ######################################################################
00095 Nv2LabelReader::Nv2LabelReader(const PixRGB<byte> color_,
00096                                const int label_reader_port,
00097                                const std::string& remote_patch_reader)
00098   :
00099   reader(),
00100   color(color_),
00101   lastConfidence()
00102 {
00103   std::vector<std::string> parts;
00104   split(remote_patch_reader, ":", std::back_inserter(parts));
00105   if (parts.size() != 2 && parts.size() != 3)
00106     LFATAL("couldn't parse addr:port[:pixtype] from '%s'",
00107            remote_patch_reader.c_str());
00108 
00109   const std::string remote_patch_reader_addr = parts[0];
00110   const int remote_patch_reader_port = fromStr<int>(parts[1]);
00111 
00112   //this->pixtype = NV2_PIXEL_TYPE_GRAY8;
00113   this->pixtype = NV2_PIXEL_TYPE_RGB24;
00114   if (parts.size() >= 3)
00115     {
00116       if (parts[2].compare("gray8") == 0)
00117         this->pixtype = NV2_PIXEL_TYPE_GRAY8;
00118       else if (parts[2].compare("rgb24") == 0)
00119         this->pixtype = NV2_PIXEL_TYPE_RGB24;
00120       else
00121         LFATAL("invalid pixel type %s (expected gray8 or rgb24",
00122                parts[2].c_str());
00123     }
00124 
00125   reader = nv2_label_reader_create(label_reader_port,
00126                                    remote_patch_reader_addr.c_str(),
00127                                    remote_patch_reader_port);
00128 
00129   LINFO("label reader at %s:%d, "
00130         "listening for labels on port %d",
00131         remote_patch_reader_addr.c_str(),
00132         remote_patch_reader_port,
00133         label_reader_port);
00134 }
00135 
00136 // ######################################################################
00137 Nv2LabelReader::~Nv2LabelReader()
00138 {
00139   nv2_label_reader_destroy(reader);
00140 }
00141 
00142 // ######################################################################
00143 void Nv2LabelReader::sendPatch(const uint32_t id,
00144                                const Image<PixRGB<byte> >& fullimg,
00145                                const Rectangle& foa,
00146                                const Image<PixRGB<byte> >& foapatch,
00147                                const rutz::time& qtime,
00148                                bool is_training_image,
00149                                const std::string& training_label,
00150                                const std::string& remote_command,
00151                                Point2D<int> fixLoc)
00152 {
00153   {
00154     const size_t npix = foapatch.getSize();
00155 
00156     nv2_image_patch patch;
00157     patch.protocol_version = NV2_PATCH_PROTOCOL_VERSION;
00158     patch.width = foapatch.getWidth();
00159     patch.height = foapatch.getHeight();
00160     patch.fix_x = fixLoc.i;
00161     patch.fix_y = fixLoc.j;
00162     patch.id = id;
00163     patch.is_training_image = is_training_image ? 1 : 0;
00164     patch.type = this->pixtype;
00165     nv2_image_patch_set_training_label(&patch, training_label.c_str());
00166     nv2_image_patch_set_remote_command(&patch, remote_command.c_str());
00167 
00168     switch (this->pixtype)
00169       {
00170       case NV2_PIXEL_TYPE_NONE:
00171         patch.data = 0;
00172         break;
00173 
00174       case NV2_PIXEL_TYPE_GRAY8:
00175         {
00176           patch.data = (unsigned char*) malloc(npix * sizeof(byte));
00177           if (patch.data == 0)
00178             LFATAL("malloc() failed");
00179 
00180           const Image<PixRGB<byte> >::const_iterator foaptr =
00181             foapatch.begin();
00182 
00183           for (size_t i = 0; i < npix; ++i)
00184             patch.data[i] = foaptr[i].luminance();
00185         }
00186         break;
00187 
00188       case NV2_PIXEL_TYPE_RGB24:
00189         {
00190           patch.data = (unsigned char*) malloc(3 * npix * sizeof(byte));
00191           if (patch.data == 0)
00192             LFATAL("malloc() failed");
00193 
00194           memcpy(&patch.data[0], foapatch.getArrayPtr(),
00195                  3 * npix * sizeof(byte));
00196         }
00197         break;
00198       }
00199 
00200     nv2_label_reader_send_patch(reader, &patch);
00201   }
00202 
00203   PendingImage qimg;
00204   qimg.fullimg = fullimg;
00205   qimg.foa = foa;
00206   qimg.patch_id = id;
00207   qimg.qtime = rutz::time::wall_clock_now();
00208 
00209   imgq.push_back(qimg);
00210 
00211   // if the queue gets too large, just drop some old frames so that
00212   // we don't allow unbounded memory usage
00213   while (imgq.size() > 60)
00214     {
00215       imgq.pop_front();
00216     }
00217 }
00218 
00219 // ######################################################################
00220 // Added by PEZ, not fully tested yet
00221 // ######################################################################
00222 
00223 class FilterLabel
00224 {
00225 private:
00226   float itsMaxCenterDist2;
00227   int itsForgetIfMissingFor;
00228   int itsFilterLength;
00229   
00230   struct CacheItem
00231   {
00232     CacheItem(const Point2D<int>& center, const std::string& label, int frameNumber) :
00233       itsCenter(center),
00234       itsLabel(label),
00235       itsFrameNumber(frameNumber)
00236     {
00237     }
00238     
00239     CacheItem(const CacheItem& that) :
00240       itsCenter(that.itsCenter),
00241       itsLabel(that.itsLabel),
00242       itsFrameNumber(that.itsFrameNumber)
00243     {
00244     }
00245     
00246     CacheItem& operator=(const CacheItem& that)
00247     {
00248       itsCenter = that.itsCenter;
00249       itsLabel = that.itsLabel;
00250       itsFrameNumber = that.itsFrameNumber;
00251       
00252       return *this;
00253     }
00254     
00255     float dist2(const Point2D<int>& center)
00256     {
00257       float dx = center.i - itsCenter.i;
00258       float dy = center.j - itsCenter.j;
00259       return dx * dx + dy * dy;
00260     }
00261     
00262     float dist2(const Rectangle& rect)
00263     {
00264       return dist2(rect.center());
00265     }
00266 
00267     Point2D<int> itsCenter;
00268     std::string itsLabel;
00269     int itsFrameNumber;
00270   };
00271   
00272   typedef std::deque<CacheItem> cacheSingleItem;
00273   std::list<cacheSingleItem> cache;
00274 
00275 public:
00276   FilterLabel(float maxCenterDist = 64.0f, int forgetIfMissingFor = 10, int filterLength = 9) :
00277     itsMaxCenterDist2(maxCenterDist * maxCenterDist),
00278     itsForgetIfMissingFor(forgetIfMissingFor),
00279     itsFilterLength(filterLength)
00280   {
00281   }
00282   
00283   virtual ~FilterLabel()
00284   {
00285   }
00286   
00287   std::string findLabel(cacheSingleItem& singleItem)
00288   {
00289     std::map<std::string,int> count;
00290     
00291     std::deque<CacheItem>::iterator itr = singleItem.begin();
00292     const std::deque<CacheItem>::iterator end = singleItem.end();
00293     
00294     for (; itr != end; ++itr)
00295     {
00296       ++count[itr->itsLabel];
00297     }
00298     
00299     std::map<std::string,int>::iterator itr2 = count.begin();
00300     std::map<std::string,int>::iterator end2 = count.end();
00301     
00302     std::string label;
00303     int max = 0;
00304     for (; itr2 != end2; ++itr2)
00305     {
00306       if (max == 0 || max < (*itr2).second)
00307       {
00308         max = (*itr2).second;
00309         label =(*itr2).first;
00310       }
00311     }
00312     
00313     return label;
00314   }
00315   
00316   std::string push(const Rectangle& rect, const std::string& label, int frameNumber)
00317   {
00318     Point2D<int> center = rect.center();
00319     
00320     std::list<cacheSingleItem>::iterator itr = cache.begin();
00321     const std::list<cacheSingleItem>::iterator end = cache.end();
00322     std::list<cacheSingleItem>::iterator best = end;
00323     float bestDist2 = itsMaxCenterDist2 * 2;
00324     
00325     for (; itr != end; ++itr)
00326     {
00327       cacheSingleItem& singleItem = *itr;
00328       float d2 = singleItem.back().dist2(center);
00329       if (best == end || d2 < bestDist2)
00330       {
00331         bestDist2 = d2;
00332         best = itr;
00333       }
00334     }
00335     
00336     if (best == end || bestDist2 > itsMaxCenterDist2)
00337     {  // add this new item
00338       cacheSingleItem singleItem;
00339       singleItem.push_back(CacheItem(center, label, frameNumber));
00340       cache.push_back(singleItem);
00341       return label;
00342     }
00343     
00344     if (best->size() >= static_cast<unsigned int>(itsFilterLength))
00345       best->pop_front(); // remove the old data from cache
00346       
00347     best->push_back(CacheItem(center, label, frameNumber));
00348     
00349     return findLabel(*best);
00350   }
00351   
00352   void prune(int currentFrame)
00353   {
00354     std::list<cacheSingleItem>::iterator itr = cache.begin();
00355     const std::list<cacheSingleItem>::iterator end = cache.end();
00356     
00357     while (itr != end)
00358     {
00359       cacheSingleItem& singleItem = *itr;
00360       if (singleItem.back().itsFrameNumber < currentFrame - itsForgetIfMissingFor)
00361       { // too old, let's forget it
00362         itr = cache.erase(itr);
00363       }
00364       else
00365       {
00366         ++itr;
00367       }
00368     }
00369   }
00370 };
00371 
00372 FilterLabel filterLabel;
00373 
00374 // ######################################################################
00375 Nv2LabelReader::LabeledImage
00376 Nv2LabelReader::getNextLabeledImage(bool ignore_nomatch,
00377                                     const size_t text_length,
00378                                     int FrameNumber)
00379 {
00380   LabeledImage result;
00381 
00382   if (imgq.size() == 0)
00383     return result; // with a still-empty image
00384 
00385   struct nv2_patch_label label;
00386   const int gotit =
00387     nv2_label_reader_get_current_label(reader, &label);
00388 
00389   if (!gotit)
00390     return result; // with a still-empty image
00391 
00392   // else ...
00393 
00394   lastConfidence.atomic_set(label.confidence);
00395 
00396   result.ident = label.source;
00397   result.label = label.name;
00398   
00399 
00400   while (imgq.size() > 0 && imgq.front().patch_id < label.patch_id)
00401     // forget about patches that have been skipped by the label
00402     // server:
00403     imgq.pop_front();
00404 
00405   if (imgq.size() == 0 || imgq.front().patch_id > label.patch_id)
00406     return result; // with a still-empty image
00407 
00408   ASSERT(imgq.size() > 0 && imgq.front().patch_id == label.patch_id);
00409 
00410   PendingImage qimg = imgq.front();
00411   imgq.pop_front();
00412 
00413   if (ignore_nomatch &&
00414       (strncmp(label.name, "nomatch", 7) == 0 ||
00415        strncmp(label.name, "none", 4) == 0))
00416     return result; // with a still-empty image
00417 
00418   LINFO("label.name = '%s'", label.name);
00419 
00420   const rutz::time now = rutz::time::wall_clock_now();
00421 
00422   times.push_back(now);
00423 
00424   const double fps =
00425     times.size() >= 2
00426     ? (times.size() - 1) / (times.back() - times.front()).sec()
00427     : 0.0;
00428 
00429   if (times.size() > 2 && (times.back() - times.front()).sec() > 3.0)
00430     times.pop_front();
00431 
00432   if (!qimg.foa.isValid())
00433     return result;
00434 
00435   // added filter by PEZ
00436   if (FrameNumber >= 0)
00437   {
00438     std::string tempLabel = filterLabel.push(qimg.foa, result.label, FrameNumber);
00439     if (result.label != tempLabel)
00440     {
00441       LINFO("Label '%s' is relaced by '%s'", result.label.c_str(), tempLabel.c_str());
00442       result.label = tempLabel;
00443       strncpy(label.name, tempLabel.c_str(), sizeof(label.name));
00444       label.name[sizeof(label.name)-1]=0;
00445       label.confidence = 0.0;
00446     }
00447     filterLabel.prune(FrameNumber);
00448   }
00449 
00450   drawRectSquareCorners(qimg.fullimg, qimg.foa, this->color, 3);
00451 
00452   Point2D<int> textpos((qimg.foa.left() + qimg.foa.rightO()) / 2,
00453                   (qimg.foa.top() + qimg.foa.bottomO()) / 2);
00454 
00455   TextAnchor textloc = ANCHOR_TOP_LEFT;
00456 
00457   if (textpos.i < qimg.fullimg.getWidth() / 2)
00458     {
00459       if (textpos.j < qimg.fullimg.getHeight() / 2)
00460         {
00461           textloc = ANCHOR_TOP_LEFT;
00462           textpos.i += 10;
00463           textpos.j += 10;
00464         }
00465       else
00466         {
00467           textloc = ANCHOR_BOTTOM_LEFT;
00468           textpos.i += 10;
00469           textpos.j -= 10;
00470         }
00471     }
00472   else
00473     {
00474       if (textpos.j < qimg.fullimg.getHeight() / 2)
00475         {
00476           textloc = ANCHOR_TOP_RIGHT;
00477           textpos.i -= 10;
00478           textpos.j += 10;
00479         }
00480       else
00481         {
00482           textloc = ANCHOR_BOTTOM_RIGHT;
00483           textpos.i -= 10;
00484           textpos.j -= 10;
00485         }
00486     }
00487 
00488   writeText2(qimg.fullimg,
00489             textpos,
00490             sformat("%s (%.2f)", label.name, double(label.confidence) / double(NV2_MAX_LABEL_CONFIDENCE)).c_str(),
00491             this->color, PixRGB<byte>(0,0,0),
00492             SimpleFont::FIXED(14),
00493             0.5,
00494             textloc);
00495 
00496   const std::string lines[3] =
00497     {
00498       sformat("[c=%4.2f] %s",
00499               double(label.confidence) / double(NV2_MAX_LABEL_CONFIDENCE),
00500               label.name),
00501       sformat("%s", label.extra_info),
00502       sformat("%s: lag %06.3fs #%06u [%5.2ffps]",
00503               label.source,
00504               (now - qimg.qtime).sec(),
00505               (unsigned int) label.patch_id,
00506               fps)
00507     };
00508 
00509   const Image<PixRGB<byte> > textarea =
00510     makeMultilineTextBox(qimg.fullimg.getWidth(), &lines[0], 3,
00511                          this->color, PixRGB<byte>(0,0,0),
00512                          text_length);
00513 
00514   result.img = concatY(qimg.fullimg, textarea);
00515   return result;
00516 }
00517 
00518 // ######################################################################
00519 double Nv2LabelReader::getLastConfidence() const
00520 {
00521   return (double(lastConfidence.atomic_get())
00522           / double(NV2_MAX_LABEL_CONFIDENCE));
00523 }
00524 
00525 // ######################################################################
00526 /* So things look consistent in everyone's emacs... */
00527 /* Local Variables: */
00528 /* mode: c++ */
00529 /* indent-tabs-mode: nil */
00530 /* End: */
00531 
00532 #endif // NEOVISIONII_NV2LABELREADER_C_DEFINED
Generated on Sun May 8 08:41:02 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3