00001 /*!@file Channels/OptimalGains.C Compute the optimal gains that maximize SNR */ 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: 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Channels/OptimalGains.C $ 00035 // $Id: OptimalGains.C 10794 2009-02-08 06:21:09Z itti $ 00036 // 00037 00038 #ifndef CHANNELS_OPTIMALGAINS_C_DEFINED 00039 #define CHANNELS_OPTIMALGAINS_C_DEFINED 00040 00041 #include "Channels/OptimalGains.H" 00042 00043 #include "Channels/ChannelFacets.H" 00044 #include "Channels/ComplexChannel.H" 00045 #include "Channels/SingleChannel.H" 00046 #include "Component/ParamMap.H" 00047 #include "Image/MathOps.H" 00048 #include "Image/ShapeOps.H" // for rescale() 00049 #include "Util/sformat.H" 00050 00051 #include <vector> 00052 00053 // ###################################################################### 00054 OptimalGainsFinder::OptimalGainsFinder(const Image<byte>& targetMask, 00055 const Image<byte>& distractorMask, 00056 rutz::shared_ptr<ParamMap> pmap, 00057 const bool doMax) 00058 : 00059 itsTargetMask(targetMask), 00060 itsDistractorMask(distractorMask), 00061 itsPmap(pmap), 00062 itsDoMax(doMax) 00063 { } 00064 00065 // ###################################################################### 00066 OptimalGainsFinder::~OptimalGainsFinder() 00067 { } 00068 00069 // ###################################################################### 00070 void OptimalGainsFinder::visitChannelBase(ChannelBase& chan) 00071 { LFATAL("don't know how to handle %s", chan.tagName().c_str()); } 00072 00073 // ###################################################################### 00074 void OptimalGainsFinder::visitSingleChannel(SingleChannel& chan) 00075 { 00076 // get or install some ChannelFacet for the gains: 00077 rutz::shared_ptr<ChannelFacetGainSingle> gfacet; 00078 if (chan.hasFacet<ChannelFacetGainSingle>()) 00079 gfacet = chan.getFacet<ChannelFacetGainSingle>(); 00080 else 00081 { gfacet.reset(new ChannelFacetGainSingle(chan)); chan.setFacet(gfacet); } 00082 00083 /* 1. for each submap i, find sT and sD, the salience of the target T 00084 and distractors D 00085 2. find SNR 00086 3. find g = SNR / (sum(SNR_j) / n) where n is the number of submaps. */ 00087 00088 const uint num = chan.numSubmaps(); 00089 float sumSNR = 0.0f, SNR[num]; 00090 00091 Image<byte> tmap, dmap; 00092 if (itsTargetMask.initialized()) 00093 tmap = rescale(itsTargetMask, chan.getMapDims()); 00094 if (itsDistractorMask.initialized()) 00095 dmap = rescale(itsDistractorMask, chan.getMapDims()); 00096 00097 for (uint idx = 0; idx < num; idx ++) 00098 { 00099 const Image<float> submap = chan.getSubmap(idx); 00100 float sT = 0.0f, sD = 0.0f; // default values if some mask is missing 00101 float junk1, junk2, junk3; 00102 00103 // how to compute SNR? max(sT) / max(sD) or mean(sT) / mean(sD)?? 00104 if (itsDoMax) 00105 { 00106 // SNR = max(sT) / max(sD) 00107 // sT is max salience within target object 00108 if (tmap.initialized()) 00109 getMaskedMinMax(submap, tmap, junk1, sT, junk2, junk3); 00110 // sD is max salience within distractor object 00111 if (dmap.initialized()) 00112 getMaskedMinMax(submap, dmap, junk1, sD, junk2, junk3); 00113 } 00114 else 00115 { 00116 // SNR = mean(sT) / mean(sD) 00117 // sT is mean salience within target object 00118 if (tmap.initialized()) 00119 getMaskedMinMaxAvg(submap, tmap, junk1, junk2, sT); 00120 // sD is mean salience within distractor object 00121 if (dmap.initialized()) 00122 getMaskedMinMaxAvg(submap, dmap, junk1, junk2, sD); 00123 } 00124 00125 SNR[idx] = (sT + OPTIGAIN_BG_FIRING) / (sD + OPTIGAIN_BG_FIRING); 00126 sumSNR += SNR[idx]; 00127 00128 // store these salience values so that they can be written out later 00129 itsPmap->putDoubleParam(sformat("salienceT(%d)", idx), sT); 00130 itsPmap->putDoubleParam(sformat("salienceD(%d)", idx), sD); 00131 00132 uint c = 0, s = 0; chan.getLevelSpec().indexToCS(idx, c, s); 00133 LDEBUG("%s(%d,%d): sT=%f, sD=%f", chan.tagName().c_str(), c, s, sT, sD); 00134 } 00135 sumSNR /= num; 00136 00137 // find the optimal gains g 00138 for (uint idx = 0; idx < num; idx ++) 00139 { 00140 const float gain = SNR[idx] / sumSNR; 00141 uint clev = 0, slev = 0; chan.getLevelSpec().indexToCS(idx, clev, slev); 00142 LINFO("%s(%d,%d): gain = %f, SNR = %f", chan.tagName().c_str(), 00143 clev, slev, gain, SNR[idx]); 00144 gfacet->setVal(idx, gain); 00145 } 00146 00147 // find the biased saliency map for this single channel and cache it 00148 // so that the parent complex channels can use it to find SNR: 00149 chan.killCaches(); 00150 (void) chan.getOutput(); 00151 } 00152 00153 // ###################################################################### 00154 void OptimalGainsFinder::visitComplexChannel(ComplexChannel& chan) 00155 { 00156 // get or install some ChannelFacet for the gains: 00157 rutz::shared_ptr<ChannelFacetGainComplex> gfacet; 00158 if (chan.hasFacet<ChannelFacetGainComplex>()) 00159 gfacet = chan.getFacet<ChannelFacetGainComplex>(); 00160 else 00161 { gfacet.reset(new ChannelFacetGainComplex(chan)); chan.setFacet(gfacet); } 00162 00163 // first find the optimal gains within sub channels 00164 const uint num = chan.numChans(); 00165 rutz::shared_ptr<ParamMap> pmapsave = itsPmap; 00166 for (uint idx = 0; idx < num; idx ++) 00167 { 00168 // visit the subchan, it will update itsPmap: 00169 itsPmap.reset(new ParamMap()); 00170 chan.subChan(idx)->accept(*this); 00171 00172 // store subchan index, for human consumption: 00173 itsPmap->putIntParam("subchanidx", idx); 00174 00175 // store the submap for the subchan: 00176 pmapsave->putSubpmap(chan.subChan(idx)->tagName(), itsPmap); 00177 } 00178 itsPmap.swap(pmapsave); 00179 00180 /* 1. for each subchannel i, find sT and sD, the salience of the target T 00181 and distractors D 00182 2. find SNR 00183 3. find g = SNR / (sum(SNR_j) / n) where n is the number of subchannels */ 00184 float sumSNR = 0.0f, SNR[num]; 00185 00186 Image<byte> tmap, dmap; 00187 if (itsTargetMask.initialized()) 00188 tmap = rescale(itsTargetMask, chan.getMapDims()); 00189 if (itsDistractorMask.initialized()) 00190 dmap = rescale(itsDistractorMask, chan.getMapDims()); 00191 00192 // next, find the optimal gains for the sub channels 00193 for (uint idx = 0; idx < num; idx ++) 00194 { 00195 // find the biased saliency maps for the sub channels 00196 const Image<float> submap = chan.subChan(idx)->getOutput(); 00197 float sT = 0.0f, sD = 0.0f; // default values if some mask is missing 00198 float junk1, junk2, junk3; 00199 00200 // how to compute SNR? max(sT) / max(sD) or mean(sT) / mean(sD)?? 00201 if (itsDoMax) 00202 { 00203 // SNR = max(sT) / max(sD) 00204 // sT is max salience within target object 00205 if (tmap.initialized()) 00206 getMaskedMinMax(submap, tmap, junk1, sT, junk2, junk3); 00207 // sD is max salience within distractor object 00208 if (dmap.initialized()) 00209 getMaskedMinMax(submap, dmap, junk1, sD, junk2, junk3); 00210 } 00211 else 00212 { 00213 // SNR = mean(sT) / mean(sD) 00214 // sT is mean salience within target object 00215 if (tmap.initialized()) 00216 getMaskedMinMaxAvg(submap, tmap, junk1, junk2, sT); 00217 // sD is mean salience within distractor object 00218 if (dmap.initialized()) 00219 getMaskedMinMaxAvg(submap, dmap, junk1, junk2, sD); 00220 } 00221 00222 SNR[idx] = (sT + OPTIGAIN_BG_FIRING) / (sD + OPTIGAIN_BG_FIRING); 00223 sumSNR += SNR[idx]; 00224 00225 // store these salience values so that they can be written out later 00226 itsPmap->putDoubleParam(sformat("salienceT(%d)", idx), sT); 00227 itsPmap->putDoubleParam(sformat("salienceD(%d)", idx), sD); 00228 00229 LDEBUG("%s: sT=%f, sD=%f", chan.subChan(idx)->tagName().c_str(), sT, sD); 00230 } 00231 sumSNR /= num; 00232 00233 // find the optimal gains 00234 for (uint idx = 0; idx < num; idx ++) 00235 { 00236 const float gain = SNR[idx] / sumSNR; 00237 LINFO("%s: gain = %f, SNR = %f", chan.subChan(idx)->tagName().c_str(), 00238 gain, SNR[idx]); 00239 gfacet->setVal(idx, gain); 00240 } 00241 00242 // find the biased saliency map and cache it so that visual cortex 00243 // may use it to compute SNR: 00244 chan.killCaches(); 00245 (void) chan.getOutput(); 00246 } 00247 00248 // ###################################################################### 00249 rutz::shared_ptr<ParamMap> OptimalGainsFinder::pmap() const 00250 { return itsPmap; } 00251 00252 00253 // ###################################################################### 00254 /* So things look consistent in everyone's emacs... */ 00255 /* Local Variables: */ 00256 /* mode: c++ */ 00257 /* indent-tabs-mode: nil */ 00258 /* End: */ 00259 00260 #endif // CHANNELS_OPTIMALGAINS_C_DEFINED