00001 /*!@file AppNeuro/app-Contour.C 00002 */ 00003 00004 // //////////////////////////////////////////////////////////////////// // 00005 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00006 // University of Southern California (USC) and the iLab at USC. // 00007 // See http://iLab.usc.edu for information about this project. // 00008 // //////////////////////////////////////////////////////////////////// // 00009 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00010 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00011 // in Visual Environments, and Applications'' by Christof Koch and // 00012 // Laurent Itti, California Institute of Technology, 2001 (patent // 00013 // pending; filed July 23, 2001, following provisional applications // 00014 // No. 60/274,674 filed March 8, 2001 and 60/288,724 filed May 4, 2001).// 00015 // //////////////////////////////////////////////////////////////////// // 00016 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00017 // // 00018 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00019 // redistribute it and/or modify it under the terms of the GNU General // 00020 // Public License as published by the Free Software Foundation; either // 00021 // version 2 of the License, or (at your option) any later version. // 00022 // // 00023 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00024 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00025 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00026 // PURPOSE. See the GNU General Public License for more details. // 00027 // // 00028 // You should have received a copy of the GNU General Public License // 00029 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00030 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00031 // Boston, MA 02111-1307 USA. // 00032 // //////////////////////////////////////////////////////////////////// // 00033 // 00034 // Primary maintainer for this file: Rob Peters <rjpeters@klab.caltech.edu> 00035 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppNeuro/app-Contour.C $ 00036 // $Id: app-Contour.C 9412 2008-03-10 23:10:15Z farhan $ 00037 // 00038 00039 #include "Channels/ChannelBase.H" 00040 #include "Channels/ChannelOpts.H" 00041 #include "Channels/ContourChannel.H" 00042 #include "Component/ModelManager.H" 00043 #include "Image/CutPaste.H" // for crop() etc. 00044 #include "Image/Image.H" 00045 #include "Image/MathOps.H" 00046 #include "Media/FrameSeries.H" 00047 #include "Raster/Raster.H" 00048 #include "Util/Assert.H" 00049 #include "Util/CpuTimer.H" 00050 #include "Util/SimTime.H" 00051 #include "Util/log.H" 00052 #include "rutz/compat_snprintf.h" 00053 #include "rutz/shared_ptr.h" 00054 00055 #include <exception> 00056 #include <iostream> 00057 00058 #ifdef HAVE_FENV_H 00059 #include <fenv.h> 00060 #endif 00061 00062 namespace 00063 { 00064 // Parameters 00065 double OVERLAP_FRACTION = 0.2; 00066 00067 // If SPLIT_SIZE is negative, then just do a "dry run" with 00068 // abs(SPLIT_SIZE) but without actually running the contour channel 00069 int SPLIT_SIZE = 1400000; 00070 00071 int NEST_DEPTH = 0; 00072 00073 // Prototypes 00074 Image<float> processWholeImage(const Image<byte>& img, 00075 ModelManager& manager, 00076 const std::string& saveprefix, 00077 int& pixused, 00078 nub::ref<FrameOstream> ofs); 00079 00080 Image<float> processSplitImage(const Image<byte>& img, 00081 ModelManager& manager, 00082 const std::string& saveprefix, 00083 int& pixused, 00084 nub::ref<FrameOstream> ofs); 00085 00086 // Implementations 00087 Image<float> processImage(const Image<byte>& img, 00088 ModelManager& manager, 00089 const std::string& saveprefix, 00090 int& pixused, 00091 nub::ref<FrameOstream> ofs) 00092 { 00093 Image<float> output; 00094 00095 if (img.getSize() > abs(SPLIT_SIZE)) 00096 { 00097 LINFO("%*sin PARTS (%s, w x h = %d x %d = %d)", 00098 NEST_DEPTH*4, "", 00099 saveprefix.c_str(), img.getWidth(), img.getHeight(), 00100 img.getSize()); 00101 00102 ++NEST_DEPTH; 00103 00104 output = processSplitImage(img, manager, saveprefix, 00105 pixused, ofs); 00106 00107 LINFO("%*seffective tiling ratio: %d/%d = %.4f", 00108 NEST_DEPTH*4, "", 00109 pixused, img.getSize(), 00110 double(pixused) / double(img.getSize())); 00111 00112 --NEST_DEPTH; 00113 } 00114 else 00115 { 00116 LINFO("%*sin whole (%s, w x h = %d x %d = %d)", 00117 NEST_DEPTH*4, "", 00118 saveprefix.c_str(), img.getWidth(), img.getHeight(), 00119 img.getSize()); 00120 output = processWholeImage(img, manager, saveprefix, 00121 pixused, ofs); 00122 } 00123 00124 ASSERT(output.initialized()); 00125 00126 return output; 00127 } 00128 00129 00130 Image<float> processWholeImage(const Image<byte>& img, 00131 ModelManager& manager, 00132 const std::string& saveprefix, 00133 int& pixused, 00134 nub::ref<FrameOstream> ofs) 00135 { 00136 pixused = img.getSize(); 00137 00138 if (SPLIT_SIZE > 0) 00139 { 00140 nub::ref<ChannelBase> contourChan = 00141 makeContourChannel(manager, saveprefix); 00142 00143 manager.addSubComponent(contourChan); 00144 contourChan->exportOptions(MC_RECURSE); 00145 00146 contourChan->start(); 00147 00148 const Image<float> gray(img); 00149 contourChan->input(InputFrame::fromGrayFloat(&gray, SimTime::ZERO())); 00150 00151 const Image<float> result = contourChan->getOutput(); 00152 00153 contourChan->saveResults(ofs); 00154 00155 contourChan->stop(); 00156 manager.removeSubComponent(*contourChan); 00157 00158 return result; 00159 } 00160 else 00161 { 00162 return Image<float>(img); 00163 } 00164 } 00165 00166 Image<float> processSplitImage(const Image<byte>& input, 00167 ModelManager& manager, 00168 const std::string& saveprefix, 00169 int& pixused, 00170 nub::ref<FrameOstream> ofs) 00171 { 00172 // split image into 2x2 temporary image files 00173 const int w = input.getWidth(); 00174 const int h = input.getHeight(); 00175 00176 /* 00177 00178 0 w1 wm w2 w 0 w1 wm w2 w 00179 0 +-----+--+--+-----+ 0 +-----+--+--+-----+ 00180 | ://| | | |//: | 00181 | ://| | | |//: | 00182 | ://| | | |//: | 00183 h1 + 1 ://| + h1 + |//: 2 + 00184 | ://| | | |//: | 00185 hm +........://| | hm + |//:........+ 00186 |///////////| | | |///////////| 00187 h2 +-----------+ + h2 + +-----------+ 00188 | | | | 00189 | | | | 00190 | | | | 00191 h +-----+--+--+-----+ h +-----+-----+-----+ 00192 00193 00194 0 w1 wm w2 w 0 w1 wm w2 w 00195 0 +-----+--+--+-----+ 0 +-----+--+--+-----+ 00196 | | | | 00197 | | | | 00198 | | | | 00199 h1 +-----------+ + h1 + +-----------+ 00200 |///////////| | | |///////////| 00201 hm +........://| | hm + |//:........+ 00202 | ://| | | |//: | 00203 h2 + 3 ://| + h2 + |//: 4 + 00204 | ://| | | |//: | 00205 | ://| | | |//: | 00206 | ://| | | |//: | 00207 h +-----+--+--+-----+ h +-----+--+--+-----+ 00208 00209 00210 Regions hatched with //////// are regions of overlap between 00211 adjacent tiles; these regions are discarded when the final output 00212 image is re-joined. 00213 00214 */ 00215 00216 const int wm = w/2; 00217 const int hm = h/2; 00218 00219 ASSERT(OVERLAP_FRACTION >= 0.0); 00220 ASSERT(OVERLAP_FRACTION <= 1.0); 00221 00222 const int wtile = int(w*(0.5+0.5*OVERLAP_FRACTION)); 00223 const int htile = int(h*(0.5+0.5*OVERLAP_FRACTION)); 00224 00225 LINFO("%*stile area is %dx%d", 00226 NEST_DEPTH*4, "", wtile, htile); 00227 00228 const int w1 = w - wtile; 00229 const int w2 = 0 + wtile - 1; 00230 00231 const int h1 = h - htile; 00232 const int h2 = 0 + htile - 1; 00233 00234 const Rectangle input_rect[4] = { 00235 Rectangle::tlbrI (0, 0, h2, w2), 00236 Rectangle::tlbrI (0, w1, h2, w-1 ), 00237 Rectangle::tlbrI (h1, 0, h-1, w2), 00238 Rectangle::tlbrI (h1, w1, h-1, w-1 ) 00239 }; 00240 00241 for (size_t i = 0; i < 4; ++i) 00242 { 00243 LDEBUG("input_rect[%"ZU"].dims() = %dx%d", 00244 i, input_rect[i].dims().w(), input_rect[i].dims().h()); 00245 ASSERT(input.rectangleOk(input_rect[i])); 00246 ASSERT(input_rect[i].dims() == Dims(wtile, htile)); 00247 } 00248 00249 const Rectangle subcrop_rect[4] = { 00250 Rectangle (Point2D<int>(0, 0), Dims(wm, hm) ), 00251 Rectangle (Point2D<int>(wm-w1, 0), Dims(w-wm, hm) ), 00252 Rectangle (Point2D<int>(0, hm-h1), Dims(wm, h-hm) ), 00253 Rectangle (Point2D<int>(wm-w1, hm-h1), Dims(w-wm, h-hm) ), 00254 }; 00255 00256 const Point2D<int> subcrop_paste_pt[4] = { 00257 Point2D<int>(0, 0 ), 00258 Point2D<int>(wm, 0 ), 00259 Point2D<int>(0, hm), 00260 Point2D<int>(wm, hm) 00261 }; 00262 00263 Image<float> output(input.getDims(), NO_INIT); 00264 00265 // Use this to reconstruct the input image using the same 00266 // crop/paste functions as we are using to reconstruct the output, 00267 // so that we can check the reconstructed input against the 00268 // original input to make sure we haven't screwed anything up 00269 Image<byte> reconstruct_input(input.getDims(), ZEROS); 00270 00271 pixused = 0; 00272 00273 // process those four images 00274 for (int i = 0; i < 4; ++i) 00275 { 00276 const Image<byte> subinput = 00277 crop(input, 00278 input_rect[i].topLeft(), 00279 input_rect[i].dims()); 00280 00281 const std::string subprefix = 00282 sformat("%s-sub%d", saveprefix.c_str(), i); 00283 00284 int subpix = 0; 00285 00286 const Image<float> suboutput = 00287 processImage(subinput, manager, subprefix, subpix, ofs); 00288 00289 if (suboutput.getDims() != subinput.getDims()) 00290 LFATAL("Oops! In order to handle a subdivided image, the " 00291 "contour channel must give output the same size as " 00292 "its input (%dx%d), but the actual output was %dx%d. " 00293 "Make sure that the --levelspec option includes a " 00294 "map level of 0 (e.g., --levelspec=2,4,3,4,0); its " 00295 "current value is --levelspec=%s.", 00296 subinput.getWidth(), subinput.getHeight(), 00297 suboutput.getWidth(), suboutput.getHeight(), 00298 manager.getOptionValString(&OPT_LevelSpec).c_str()); 00299 00300 pixused += subpix; 00301 00302 const Image<float> subsuboutput = 00303 crop(suboutput, 00304 subcrop_rect[i].topLeft(), 00305 subcrop_rect[i].dims()); 00306 00307 const Image<float> subsubinput = 00308 crop(subinput, 00309 subcrop_rect[i].topLeft(), 00310 subcrop_rect[i].dims()); 00311 00312 inplacePaste(output, subsuboutput, subcrop_paste_pt[i]); 00313 inplacePaste(reconstruct_input, Image<byte>(subsubinput), 00314 subcrop_paste_pt[i]); 00315 } 00316 00317 if (!(reconstruct_input == input)) 00318 { 00319 LFATAL("crop/paste error: reconstructed input differs from original input"); 00320 } 00321 00322 return output; 00323 } 00324 } 00325 00326 int main(int argc, char* argv[]) 00327 { 00328 try 00329 { 00330 ModelManager manager("Contour"); 00331 00332 nub::ref<OutputFrameSeries> ofs(new OutputFrameSeries(manager)); 00333 manager.addSubComponent(ofs); 00334 00335 // make a dummy contour channel to get the command-line options 00336 // exported 00337 nub::ref<ChannelBase> dummyContourChannel = 00338 makeContourChannel(manager); 00339 manager.addSubComponent(dummyContourChannel); 00340 00341 // force the map level to 0 so that we get full-sized output 00342 // from the ContourChannel 00343 manager.setOptionValString(&OPT_LevelSpec, "2,4,3,4,0"); 00344 manager.setOptionValString(&OPT_MaxNormType, "Ignore"); 00345 00346 MYLOGVERB = LOG_DEBUG; 00347 00348 if (manager.parseCommandLine(argc, argv, 00349 "input.pnm [save_prefix] " 00350 "[split_size] [overlap_fraction]", 00351 1, 4) == false) 00352 return 1; 00353 00354 #ifdef HAVE_FEENABLEEXCEPT 00355 fedisableexcept(FE_ALL_EXCEPT); 00356 feenableexcept(FE_DIVBYZERO|FE_INVALID); 00357 #endif 00358 00359 const Image<byte> input = Raster::ReadGray(manager.getExtraArg(0)); 00360 00361 const std::string saveprefix = 00362 (manager.numExtraArgs() >= 2 && manager.getExtraArg(1).length() > 0) 00363 ? manager.getExtraArg(1) : "contourout"; 00364 00365 if (manager.numExtraArgs() >= 3 && manager.getExtraArg(2).length() > 0) 00366 SPLIT_SIZE = manager.getExtraArgAs<int>(2); 00367 00368 if (manager.numExtraArgs() >= 4 && manager.getExtraArg(3).length() > 0) 00369 OVERLAP_FRACTION = manager.getExtraArgAs<double>(3); 00370 00371 manager.start(); 00372 00373 CpuTimer t; 00374 00375 int pixused = 0; 00376 const Image<float> output = 00377 processImage(input, manager, saveprefix, pixused, ofs); 00378 00379 t.mark(); 00380 t.report("full contour operation"); 00381 00382 if (SPLIT_SIZE < 0) 00383 { 00384 ofs->writeGray(Image<byte>(output), 00385 sformat("%s.output", saveprefix.c_str())); 00386 } 00387 else 00388 { 00389 ofs->writeFloat(output, 00390 FLOAT_NORM_0_255, 00391 sformat("%s.output", saveprefix.c_str())); 00392 00393 ofs->writeFloat(output, 00394 FLOAT_NORM_0_255|FLOAT_NORM_WITH_SCALE, 00395 sformat("%s.output-s", saveprefix.c_str())); 00396 } 00397 00398 manager.stop(); 00399 00400 return 0; 00401 } 00402 catch (...) 00403 { 00404 REPORT_CURRENT_EXCEPTION; 00405 } 00406 00407 return 1; 00408 } 00409 00410 // ###################################################################### 00411 /* So things look consistent in everyone's emacs... */ 00412 /* Local Variables: */ 00413 /* indent-tabs-mode: nil */ 00414 /* End: */