00001 /*!@file Apps/BorderWatch/BorderWatch.C Border watch */ 00002 00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003 // 00004 // by the University of Southern California (USC) and the iLab at USC. // 00005 // See http://iLab.usc.edu for information about this project. // 00006 // //////////////////////////////////////////////////////////////////// // 00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00009 // in Visual Environments, and Applications'' by Christof Koch and // 00010 // Laurent Itti, California Institute of Technology, 2001 (patent // 00011 // pending; application number 09/912,225 filed July 23, 2001; see // 00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00013 // //////////////////////////////////////////////////////////////////// // 00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00015 // // 00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00017 // redistribute it and/or modify it under the terms of the GNU General // 00018 // Public License as published by the Free Software Foundation; either // 00019 // version 2 of the License, or (at your option) any later version. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00024 // PURPOSE. See the GNU General Public License for more details. // 00025 // // 00026 // You should have received a copy of the GNU General Public License // 00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00029 // Boston, MA 02111-1307 USA. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // Primary maintainer for this file: Lior Elazary <elazary@usc.edu> 00032 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Apps/BorderWatch/BorderWatch.C $ 00033 // $Id: BorderWatch.C 13039 2010-03-23 02:06:32Z itti $ 00034 00035 #include "Component/JobServerConfigurator.H" 00036 #include "Component/ModelManager.H" 00037 #include "Component/GlobalOpts.H" // for OPT_UseRandom 00038 #include "Channels/ChannelOpts.H" // for OPT_LevelSpec 00039 #include "Media/FrameSeries.H" 00040 #include "Transport/FrameInfo.H" 00041 #include "Raster/GenericFrame.H" 00042 #include "Image/Image.H" 00043 #include "Image/ImageSet.H" 00044 #include "Image/DrawOps.H" 00045 #include "Image/ShapeOps.H" 00046 #include "Image/Layout.H" 00047 #include "GUI/ImageDisplayStream.H" 00048 #include "GUI/XWinManaged.H" 00049 #include "GUI/DebugWin.H" 00050 #include "GUI/SimpleMeter.H" 00051 #include "Neuro/EnvOpts.H" // for OPT_EnvLevelSpec, etc 00052 #include "Neuro/NeuroOpts.H" // for OPT_VisualCortexType 00053 #include "Apps/BorderWatch/ImageInfo.H" 00054 #include "Simulation/SimulationOpts.H" 00055 #include "Simulation/SimEventQueue.H" 00056 #include "Simulation/SimEventQueueConfigurator.H" 00057 #include "Util/Timer.H" 00058 #include <queue> 00059 #include <cmath> 00060 #include <cstdio> 00061 #include <fstream> 00062 00063 const ModelOptionCateg MOC_BORDERWATCH = { 00064 MOC_SORTPRI_3, "BorderWatch-Related Options" }; 00065 00066 static const ModelOptionDef OPT_ShowDebugWin = 00067 { MODOPT_FLAG, "ShowDebugWin", &MOC_BORDERWATCH, OPTEXP_CORE, 00068 "Whether to show the input image, the saliency maps, and the beliefs. " 00069 "This is used for debugging and setting the thresholds. ", 00070 "show-debug-win", '\0', "", "false" }; 00071 00072 static const ModelOptionDef OPT_ShowThumbs = 00073 { MODOPT_FLAG, "ShowThumbs", &MOC_BORDERWATCH, OPTEXP_CORE, 00074 "Show thumbnails of the most recent frames that had surprise above threshold", 00075 "show-thumbs", '\0', "", "false" }; 00076 00077 static const ModelOptionDef OPT_Threshold = 00078 { MODOPT_ARG(float), "Threshold", &MOC_BORDERWATCH, OPTEXP_CORE, 00079 "The threshold level at which to save images to disk. ", 00080 "threshold", '\0', "<float>", "3.5e-10" }; 00081 00082 static const ModelOptionDef OPT_ImgQLen = 00083 { MODOPT_ARG(uint), "ImgQLen", &MOC_BORDERWATCH, OPTEXP_CORE, 00084 "Length of the queue of images for movie context, in frames", 00085 "imgqlen", '\0', "<uint>", "50" }; 00086 00087 const ModelOptionDef OPT_OutFname = 00088 { MODOPT_ARG_STRING, "OutFname", &MOC_BORDERWATCH, OPTEXP_CORE, 00089 "File name for text output data (or empty to not save output data)", 00090 "out-fname", '\0', "<file>", "" }; 00091 00092 // ###################################################################### 00093 void displayOutput(const Image<PixRGB<byte> >& img, nub::ref<OutputFrameSeries> ofs, bool showThumbs) 00094 { 00095 static const uint nx = 15, ny = 12; // hardcoded for 30in display 00096 static ImageSet<PixRGB<byte> > thumbs(nx*ny, Dims(img.getWidth()/2, img.getHeight()/2), ZEROS); 00097 00098 // do we want to show thumbnail displays? 00099 if (showThumbs) { 00100 thumbs.push_back(quickLocalAvg2x2(img)); while(thumbs.size() > nx*ny) thumbs.pop_front(); 00101 Layout<PixRGB<byte> > t = arrcat(thumbs, nx); 00102 ofs->writeRgbLayout(t, "Thumbnails", FrameInfo("Thumbnails", SRC_POS)); 00103 } 00104 00105 // display the full-resolution image: 00106 ofs->writeRGB(img, "Output", FrameInfo("Output", SRC_POS)); 00107 ofs->updateNext(); 00108 } 00109 00110 // ###################################################################### 00111 int main(const int argc, const char **argv) 00112 { 00113 MYLOGVERB = LOG_INFO; 00114 ModelManager mgr("Border Watch"); 00115 00116 nub::ref<JobServerConfigurator> jsc(new JobServerConfigurator(mgr)); 00117 mgr.addSubComponent(jsc); 00118 00119 nub::ref<SimEventQueueConfigurator> seqc(new SimEventQueueConfigurator(mgr)); 00120 mgr.addSubComponent(seqc); 00121 00122 nub::ref<OutputFrameSeries> ofs(new OutputFrameSeries(mgr)); 00123 mgr.addSubComponent(ofs); 00124 00125 nub::ref<InputFrameSeries> ifs(new InputFrameSeries(mgr)); 00126 mgr.addSubComponent(ifs); 00127 00128 nub::ref<ImageInfo> imageInfo(new ImageInfo(mgr)); 00129 mgr.addSubComponent(imageInfo); 00130 00131 OModelParam<bool> optShowDebugWin(&OPT_ShowDebugWin, &mgr); 00132 OModelParam<bool> optShowThumbs(&OPT_ShowThumbs, &mgr); 00133 OModelParam<float> optThreshold(&OPT_Threshold, &mgr); 00134 OModelParam<uint> optQLen(&OPT_ImgQLen, &mgr); 00135 OModelParam<std::string> optOutFname(&OPT_OutFname, &mgr); 00136 00137 mgr.exportOptions(MC_RECURSE); 00138 mgr.setOptionValString(&OPT_SimulationTimeStep, "30ms"); // avoid feeding inputs too fast 00139 mgr.setOptionValString(&OPT_LevelSpec, "1,3,3,4,4"); // use higher saliency resolution than usual 00140 mgr.setOptionValString(&OPT_VisualCortexType, "Env"); // use envision 00141 mgr.setOptionValString(&OPT_EvcMultithreaded, "true"); // use envision multi-threaded 00142 mgr.setOptionValString(&OPT_EnvLevelSpec, "1,3,3,4,4"); // use higher saliency resolution than usual 00143 mgr.setOptionValString(&OPT_UseRandom, "false"); // enough noise in the videos, no need to add more 00144 00145 if (mgr.parseCommandLine(argc, argv, "", 0, 0) == false) 00146 { LFATAL("This program takes no non-option command-line argument; try --help"); return 1; } 00147 00148 nub::ref<SimEventQueue> seq = seqc->getQ(); 00149 00150 // open output file, if any: 00151 std::ofstream *outFile = 0; 00152 if (optOutFname.getVal().empty() == false) 00153 { 00154 outFile = new std::ofstream(optOutFname.getVal().c_str()); 00155 if (outFile->is_open() == false) 00156 LFATAL("Cannot open '%s' for writing", optOutFname.getVal().c_str()); 00157 } 00158 00159 // get started: 00160 mgr.start(); 00161 MYLOGVERB = LOG_CRIT; // get less verbose... 00162 00163 std::queue<Image<PixRGB<byte> > > imgQueue; 00164 SimStatus status = SIM_CONTINUE; 00165 Timer tim; 00166 00167 // main loop: 00168 while(status == SIM_CONTINUE) { 00169 ifs->updateNext(); 00170 00171 // grab the images: 00172 GenericFrame input = ifs->readFrame(); if (!input.initialized()) break; 00173 Image<PixRGB<byte> > img = input.asRgb(); 00174 00175 // process the image and get the results: 00176 ImageInfo::ImageStats stats = imageInfo->update(seq, img, ifs->frame()); 00177 00178 float mi, ma; 00179 Image<float> smapf = stats.smap; inplaceNormalize(smapf, 0.0F, 255.0F, mi, ma); 00180 Image<byte> smap = smapf; smap = rescaleNI(smap, img.getDims()); 00181 00182 // time-stamp the image: 00183 char msg[255]; time_t rawtime; time(&rawtime); 00184 struct tm *timeinfo; timeinfo = localtime(&rawtime); 00185 sprintf(msg, " %s- S=%g %d", asctime(timeinfo), stats.score, ifs->frame()); 00186 writeText(img, Point2D<int>(0,0), msg, PixRGB<byte>(255,0,0), PixRGB<byte>(0), SimpleFont::FIXED(6), true); 00187 sprintf(msg, " Saliency [%.4g .. %.4g] %d ", mi, ma, ifs->frame()); 00188 writeText(smap, Point2D<int>(0,0), msg, byte(255), byte(0), SimpleFont::FIXED(6)); 00189 00190 // mark most salient point: 00191 const Point2D<int> sp = stats.salpoint * img.getWidth() / smapf.getWidth(); 00192 if (stats.score > optThreshold.getVal()) drawCircle(img, sp, 15, PixRGB<byte>(255,255,0), 2); 00193 else drawCircle(img, sp, 5, PixRGB<byte>(0,128,0), 1); 00194 00195 // push the image into our context queue: 00196 imgQueue.push(img); 00197 if (imgQueue.size() > optQLen.getVal()) imgQueue.pop(); 00198 00199 // show a debug window with saliency maps and such? 00200 if (optShowDebugWin.getVal()) 00201 { 00202 if (stats.belief1.initialized() && stats.belief2.initialized()) { 00203 Image<float> b1f = stats.belief1; inplaceNormalize(b1f, 0.0F, 255.0F, mi, ma); 00204 Image<byte> b1 = b1f; b1 = rescaleNI(b1, img.getDims()); 00205 sprintf(msg, " Beliefs 1 [%.4g .. %.4g] ", mi, ma); 00206 writeText(b1, Point2D<int>(0,0), msg, byte(255), byte(0), SimpleFont::FIXED(6)); 00207 drawLine(b1, Point2D<int>(0,0), Point2D<int>(0, b1.getHeight()-1), byte(255)); 00208 00209 Image<float> b2f = stats.belief2; inplaceNormalize(b2f, 0.0F, 255.0F, mi, ma); 00210 Image<byte> b2 = b2f; b2 = rescaleNI(b1, img.getDims()); 00211 sprintf(msg, " Beliefs 2 [%.4g .. %.4g] ", mi, ma); 00212 writeText(b2, Point2D<int>(0,0), msg, byte(255), byte(0), SimpleFont::FIXED(6)); 00213 drawLine(b2, Point2D<int>(0,0), Point2D<int>(0, b2.getHeight()-1), byte(255)); 00214 00215 Layout<byte> smapDisp; 00216 smapDisp = hcat(hcat(smap, b1), b2); 00217 ofs->writeGrayLayout(smapDisp, "Smap", FrameInfo("Smap", SRC_POS)); 00218 } else ofs->writeGray(smap, "Smap", FrameInfo("Smap", SRC_POS)); 00219 00220 const MeterInfo infos[] = { 00221 { "", stats.score, optThreshold.getVal()*3, optThreshold.getVal(), PixRGB<byte>(0, 255, 0) } 00222 }; 00223 Image<PixRGB<byte> > meterImg = 00224 drawMeters(&infos[0], sizeof(infos) / sizeof(infos[0]), 1, Dims(img.getWidth(),20)); 00225 00226 Layout<PixRGB<byte> > inputDisp; 00227 inputDisp = vcat(img, meterImg); 00228 ofs->writeRgbLayout(inputDisp, "Input", FrameInfo("Input", SRC_POS)); 00229 } 00230 00231 if (stats.score > optThreshold.getVal()) 00232 { 00233 if (imgQueue.size() == optQLen.getVal()) // buffer is full, place a title 00234 { 00235 Image<PixRGB<byte> > title = imgQueue.back(); 00236 00237 sprintf(msg, "Date: %s ", asctime(timeinfo)); 00238 writeText(title, Point2D<int>(0,title.getHeight()-40), msg, PixRGB<byte>(255), PixRGB<byte>(0)); 00239 sprintf(msg, "Surprise: %e ", stats.score); 00240 writeText(title, Point2D<int>(0,title.getHeight()-20), msg, PixRGB<byte>(255), PixRGB<byte>(0)); 00241 00242 for (int i = 0; i < 3; ++i) displayOutput(title, ofs, optShowThumbs.getVal()); 00243 } 00244 00245 while(!imgQueue.empty()) 00246 { 00247 Image<PixRGB<byte> > tImg = imgQueue.front(); imgQueue.pop(); 00248 displayOutput(tImg, ofs, optShowThumbs.getVal()); 00249 } 00250 } 00251 00252 // print framerate: 00253 if (ifs->frame() > 0 && (ifs->frame() % 100) == 0) { 00254 printf("Frame %06d - %.2ffps\n", ifs->frame(), 100.0 / tim.getSecs()); 00255 tim.reset(); 00256 } 00257 00258 // save text output if desired, for later use: 00259 if (outFile) 00260 (*outFile)<<sformat("of=%i if=%i score=%e x=%d y=%d sal=%e ener=%e uniq=%e entr=%e rand=%e kls=%e msds=%e %s", 00261 ofs->frame(), ifs->frame(), stats.score, sp.i, sp.j, stats.saliency, stats.energy, 00262 stats.uniqueness, stats.entropy, stats.rand, stats.KLsurprise, stats.MSDsurprise, 00263 asctime(timeinfo)); 00264 00265 // Evolve for one time step and switch to the next one: 00266 status = seq->evolve(); 00267 } 00268 00269 LINFO("Simulation terminated."); 00270 mgr.stop(); 00271 if (outFile) { outFile->close(); delete outFile; } 00272 00273 return 0; 00274 } 00275 00276