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