app-Contour.C

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:40:08 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3