00001 /*!@file Channels/MotionOpticalFlowChannel.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: Christian Siagian <siagian@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Channels/MotionOpticalFlowChannel.C $ 00035 // $Id: $ 00036 // 00037 00038 #ifndef MOTION_OPTICALFLOW_CHANNEL_C_DEFINED 00039 #define MOTION_OPTICALFLOW_CHANNEL_C_DEFINED 00040 00041 #include "Channels/MotionOpticalFlowChannel.H" 00042 00043 #include "Channels/ChannelOpts.H" 00044 #include "Component/ModelOptionDef.H" 00045 00046 #include "Image/ShapeOps.H" 00047 00048 #include "rutz/trace.h" 00049 #include "Util/Timer.H" 00050 00051 #define OF_SELF_MOTION_WEIGHT 1.0F 00052 #define OF_OBJECT_MOTION_WEIGHT 1.0F 00053 #define OF_MAX_FIRING_RATE 100.0F 00054 00055 // Used by: MotionOpticalFlowChannel 00056 const ModelOptionDef OPT_OpticalFlowType = 00057 { MODOPT_ARG(OpticalFlowType), "OpticalFlowType", &MOC_CHANNEL, OPTEXP_CORE, 00058 "Type of computation used to compute the optical flow", 00059 "optical-flow-type", '\0', "<LucasKanade|HornSchunck>", 00060 "LucasKanade" }; 00061 00062 // ###################################################################### 00063 // MotionOpticalFlowChannel member definitions: 00064 // ###################################################################### 00065 00066 // ###################################################################### 00067 MotionOpticalFlowChannel::MotionOpticalFlowChannel(OptionManager& mgr) : 00068 ComplexChannel(mgr, 00069 "MotionOpticalFlow", 00070 "motionOpticalFlow", 00071 MOTIONOPTICALFLOW), 00072 itsOpticalFlowType(&OPT_OpticalFlowType, this), // see Channels/ChannelOpts.{H,C} 00073 itsNumDirs(&OPT_NumOpticalFlowDirections, this), // see Channels/ChannelOpts.{H,C} 00074 itsFoeDetector(new FoeDetector(mgr)) 00075 { 00076 GVX_TRACE(__PRETTY_FUNCTION__); 00077 00078 // let's create our subchannels (may be reconfigured later if our 00079 // number of directions changes): 00080 buildSubChans(); 00081 00082 itsWin.reset(); 00083 } 00084 00085 // ###################################################################### 00086 MotionOpticalFlowChannel::~MotionOpticalFlowChannel() 00087 { 00088 GVX_TRACE(__PRETTY_FUNCTION__); 00089 } 00090 00091 // ###################################################################### 00092 DirectionOpticalFlowChannel& MotionOpticalFlowChannel::dirChan 00093 (const uint idx) const 00094 { 00095 GVX_TRACE(__PRETTY_FUNCTION__); 00096 return *(dynCast<DirectionOpticalFlowChannel>(subChan(idx))); 00097 } 00098 00099 // ###################################################################### 00100 void MotionOpticalFlowChannel::buildSubChans() 00101 { 00102 GVX_TRACE(__PRETTY_FUNCTION__); 00103 // kill any subchans we may have had... 00104 this->removeAllSubChans(); 00105 00106 // let's instantiate our subchannels now that we know how many we 00107 // want. They will inherit the current values (typically 00108 // post-command-line parsing) of all their options as they are 00109 // constructed: 00110 LINFO("Using %d directions spanning [0..360]deg", itsNumDirs.getVal()); 00111 00112 itsDirectionOpticalFlowChannels.clear(); 00113 00114 // go through the different directions 00115 for (uint i = 0; i < itsNumDirs.getVal(); i++) 00116 { 00117 nub::ref<DirectionOpticalFlowChannel> chan = 00118 makeSharedComp(new DirectionOpticalFlowChannel 00119 (getManager(), i, 00120 360.0 * double(i)/double(itsNumDirs.getVal()), 00121 itsOpticalFlowType.getVal())); 00122 00123 itsDirectionOpticalFlowChannels.push_back(chan); 00124 00125 this->addSubChan(chan); 00126 chan->exportOptions(MC_RECURSE); 00127 } 00128 } 00129 00130 // ###################################################################### 00131 void MotionOpticalFlowChannel::paramChanged(ModelParamBase* const param, 00132 const bool valueChanged, 00133 ParamClient::ChangeStatus* status) 00134 { 00135 GVX_TRACE(__PRETTY_FUNCTION__); 00136 ComplexChannel::paramChanged(param, valueChanged, status); 00137 00138 // if the param is our number of orientations and it has become 00139 // different from our number of channels, let's reconfigure: 00140 if (param == &itsNumDirs && 00141 numChans() != itsNumDirs.getVal()) 00142 buildSubChans(); 00143 } 00144 00145 // ###################################################################### 00146 void MotionOpticalFlowChannel::doInput(const InputFrame& inframe) 00147 { 00148 GVX_TRACE(__PRETTY_FUNCTION__); 00149 ASSERT(inframe.grayFloat().initialized()); 00150 00151 Image<byte> image(inframe.grayFloat()); 00152 if(!itsCurrentImage.initialized()) 00153 { 00154 itsCurrentImage = image; return; 00155 } 00156 else 00157 itsPreviousImage = itsCurrentImage; 00158 itsCurrentImage = image; 00159 00160 // compute optical flow 00161 itsOpticalFlow = 00162 getLucasKanadeOpticFlow(itsPreviousImage, itsCurrentImage); 00163 00164 // compute directional optical flow 00165 // into several directions 00166 for (uint i = 0; i < numChans(); i++) 00167 { 00168 itsDirectionOpticalFlowChannels[i] 00169 ->setLucasKanadeOpticalFlow(itsOpticalFlow); 00170 subChan(i)->input(inframe); 00171 00172 LINFO("Motion Optical flow pyramid (%d/%d) ok.", i+1, numChans()); 00173 } 00174 00175 // compute motion conspicuity map 00176 computeConspicuityMap(); 00177 } 00178 00179 // ###################################################################### 00180 void MotionOpticalFlowChannel::computeConspicuityMap() 00181 { 00182 GVX_TRACE(__PRETTY_FUNCTION__); 00183 00184 uint cmWidth = subChan(0)->getMapDims().w(); 00185 uint cmHeight = subChan(0)->getMapDims().h(); 00186 Image<float> result(cmWidth, cmHeight, ZEROS); 00187 00188 Dims imDims = itsOpticalFlow->getImageDims(); 00189 uint mtWidth = imDims.w()/4; 00190 uint mtHeight = imDims.h()/4; 00191 00192 for (uint i = 0; i < itsNumDirs.getVal(); i++) 00193 { 00194 Image<float> tmap = itsDirectionOpticalFlowChannels[i] 00195 ->getDirectionalOpticalFlow(); 00196 00197 Image<float> submap = downSizeClean(tmap, Dims(cmWidth, cmHeight)); 00198 00199 Image<float> psubmap; 00200 if (itsUseOlderVersion.getVal()) 00201 { 00202 LDEBUG("%s[%d]: applying %s(%f .. %f)", 00203 tagName().c_str(), i, 00204 maxNormTypeName(itsNormType.getVal()), MAXNORMMIN, MAXNORMMAX); 00205 psubmap = maxNormalize(submap, MAXNORMMIN, MAXNORMMAX, 00206 itsNormType.getVal()); 00207 } 00208 else 00209 { 00210 LDEBUG("%s[%d]: applying %s(0.0 .. 0.0)", tagName().c_str(), i, 00211 maxNormTypeName(itsNormType.getVal())); 00212 psubmap = maxNormalize(submap, 0.0f, 0.0f, itsNormType.getVal()); 00213 } 00214 00215 result += psubmap; 00216 } 00217 Image<float> tsubmap = maxNormalize(result, MAXNORMMIN, MAXNORMMAX, 00218 itsNormType.getVal()); 00219 result = tsubmap * numChans(); 00220 Image<float> tres = result; 00221 result = rescale(tres, Dims(mtWidth, mtHeight)); 00222 00223 // May add a map that comes from higher level Motion areas 00224 // : MST: FOE, planar motion 00225 // : STS: Biological motion 00226 00227 // NOTE: FOE_METHOD_TEMPLATE and AVERAGE 00228 // is still fooled by Planar movement!!! 00229 Image<float> foeMap = 00230 itsFoeDetector->getFoeMap 00231 (itsOpticalFlow, FOE_METHOD_AVERAGE, false); //FOE_METHOD_TEMPLATE 00232 00233 00234 // LINFO("ORIGINAL MAP"); 00235 // if(itsWin.is_invalid()) 00236 // itsWin.reset(new XWinManaged(Dims(mtWidth*4, mtHeight*4), 00237 // 10, 0, "MotSpch: conspicuity map")); 00238 // else itsWin->setDims(Dims(mtWidth*4, mtHeight*4)); 00239 // itsWin->drawImage(zoomXY(foeMap,4),0,0); Raster::waitForKey(); 00240 00241 // float orgMax = mx; 00242 // float firingRate = 0.0; 00243 // if(orgMax > .5) 00244 // firingRate = MAX_FIRING_RATE; 00245 // else if(orgMax >= .1 && orgMax <= .5) 00246 // firingRate = (orgMax - .1)/.4 * MAX_FIRING_RATE; 00247 00248 // crazy normalizer 00249 float mn, mx; getMinMax(foeMap, mn,mx); 00250 //LINFO("org MSTd : %f %f",mn,mx); 00251 inplaceNormalize(foeMap, 0.0F, 1.0F); 00252 foeMap = toPower(foeMap, 40.0F); 00253 foeMap *= mx; 00254 00255 // weight the firing rate to the maximum possible firing rate 00256 foeMap *= (OF_SELF_MOTION_WEIGHT * OF_MAX_FIRING_RATE * numChans()); 00257 00258 // getMinMax(result,mn,mx); 00259 // LINFO("FINAL MSTv : %f %f",mn,mx); 00260 00261 // getMinMax(foeMap,mn,mx); 00262 // LINFO("FINAL MSTd : %f %f",mn,mx); 00263 00264 // if(itsWin.is_invalid()) 00265 // itsWin.reset(new XWinManaged(Dims(mtWidth*4, mtHeight*4), 00266 // 10, 0, "MotSpch: conspicuity map")); 00267 // else itsWin->setDims(Dims(mtWidth*4, mtHeight*4)); 00268 00269 // itsWin->drawImage(zoomXY(result,4),0,0); Raster::waitForKey(); 00270 // itsWin->drawImage(zoomXY(foeMap,4),0,0); Raster::waitForKey(); 00271 00272 result += foeMap; 00273 00274 // itsWin->drawImage(zoomXY(result,4),0,0); Raster::waitForKey(); 00275 00276 // resize submap to fixed scale if necessary: 00277 getMinMax(result,mn,mx); 00278 if (mtWidth > cmWidth) 00279 result = downSize(result, Dims(cmWidth, cmHeight)); 00280 else if (mtWidth < cmWidth) 00281 result = rescale(result, Dims(cmWidth, cmHeight)); 00282 inplaceNormalize(result,0.0F,mx); 00283 LINFO("Final cmap mn: %f mx: %f", mn, mx); 00284 00285 itsConspicuityMap = result; 00286 } 00287 00288 // ###################################################################### 00289 Image<float> MotionOpticalFlowChannel::getOutput() 00290 { 00291 GVX_TRACE(__PRETTY_FUNCTION__); 00292 return itsConspicuityMap; 00293 } 00294 00295 // ###################################################################### 00296 /* So things look consistent in everyone's emacs... */ 00297 /* Local Variables: */ 00298 /* indent-tabs-mode: nil */ 00299 /* End: */ 00300 00301 #endif // MOTION_OPTICALFLOW_CHANNEL_C_DEFINED