00001 /*!@file Neuro/EnvSegmenterColorRegion.C FOA segmenter */ 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: Rob Peters <rjpeters at usc dot edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Neuro/EnvSegmenterColorRegion.C $ 00035 // $Id: EnvSegmenterColorRegion.C 9740 2008-05-10 03:20:14Z rjpeters $ 00036 // 00037 00038 #ifndef NEURO_ENVSEGMENTERCOLORREGION_C_DEFINED 00039 #define NEURO_ENVSEGMENTERCOLORREGION_C_DEFINED 00040 00041 #include "Neuro/EnvSegmenterColorRegion.H" 00042 00043 #include "Component/ModelOptionDef.H" 00044 #include "Image/ColorOps.H" 00045 #include "Image/Image.H" 00046 #include "Image/ImageSet.H" 00047 #include "Image/LowPass.H" 00048 #include "Image/MathOps.H" 00049 #include "Image/Normalize.H" 00050 #include "Image/Pixels.H" 00051 #include "Image/PyramidOps.H" 00052 #include "Neuro/NeuroOpts.H" 00053 #include "Util/MathFunctions.H" 00054 00055 static const ModelOptionDef OPT_EseDynamicFoa = 00056 { MODOPT_FLAG, "EseDynamicFoa", &MOC_ITC, OPTEXP_CORE, 00057 "Whether to use a dynamically-sized FOA obtained from color-based " 00058 "segmentation", 00059 "ese-dynamic-foa", '\0', "", "true" }; 00060 00061 static const ModelOptionDef OPT_EseFoaSize = 00062 { MODOPT_ARG(int), "EseFoaSize", &MOC_ITC, OPTEXP_CORE, 00063 "Size, in pixels, of the FOA box for non-dynamically-sized FOAs", 00064 "ese-foa-size", '\0', "int", "180" }; 00065 00066 static const ModelOptionDef OPT_EseDynamicFoaMinSize = 00067 { MODOPT_ARG(int), "EseFoaMinSize", &MOC_ITC, OPTEXP_CORE, 00068 "Minimum size, in pixels, of the FOA box for dynamically-sized FOAs", 00069 "ese-foa-min-size", '\0', "int", "32" }; 00070 00071 static const ModelOptionDef OPT_EseDynamicFoaMaxSize = 00072 { MODOPT_ARG(int), "EseFoaMaxSize", &MOC_ITC, OPTEXP_CORE, 00073 "Maximum size, in pixels, of the FOA box for dynamically-sized FOAs", 00074 "ese-foa-max-size", '\0', "int", "360" }; 00075 00076 static const ModelOptionDef OPT_EseSegmentationThresh = 00077 { MODOPT_ARG(double), "EseSegmentationThresh", &MOC_ITC, OPTEXP_CORE, 00078 "Pixel-similarity threshold for object segmentation (0.0 .. 1.0)", 00079 "ese-segmentation-thresh", '\0', "double", "0.5" }; 00080 00081 static const ModelOptionDef OPT_EseSegmentationScale = 00082 { MODOPT_ARG(size_t), "EseSegmentationScale", &MOC_ITC, OPTEXP_CORE, 00083 "Pyramid scale at which to compute object segmentation", 00084 "ese-segmentation-scale", '\0', "uint", "4" }; 00085 00086 namespace 00087 { 00088 Rectangle segmentColor(const Image<PixRGB<byte> >& rgbin, 00089 const Point2D<int>& foacenter, 00090 const size_t segmentationScale, 00091 const float segmentationThresh, 00092 const int foaminsize, 00093 const int foamaxsize, 00094 Image<float>& distmap, 00095 Image<byte>& mask) 00096 { 00097 /* color-based target segmentation: 00098 00099 (1) scale rgb input down by (1 << segmentationScale), e.g. 16 00100 (2) convert to h2sv1 color space 00101 (3) spatially smooth the h1, h2, s, and v maps 00102 (4) find the color values at the foa center 00103 (5) compute a distance map reflecting both the distance in 00104 color space from the foa center as well as the distance in 00105 retinotopic space -- we want to find pixels that have a 00106 similar color to the target and that are also spatially 00107 close to the target 00108 (6) start a flood from the foa center in the distance map 00109 looking for connected pixels whose similarity (inverse 00110 distance) to the target is above some threshold 00111 (7) the foa is smallest rectangular bounding box that contains 00112 the flooded pixels, and optionally constrain the foa to 00113 given min/max dimensions 00114 */ 00115 00116 const ImageSet<PixRGB<byte> > pyr = 00117 buildPyrLocalAvg2x2(rgbin, segmentationScale+1); 00118 00119 Image<PixH2SV1<float> > h2sv1(pyr[segmentationScale]); 00120 Image<float> h1 = lowPass9(getPixelComponentImage(h2sv1, 0)); 00121 Image<float> h2 = lowPass9(getPixelComponentImage(h2sv1, 1)); 00122 Image<float> s = lowPass9(getPixelComponentImage(h2sv1, 2)); 00123 Image<float> v = lowPass9(getPixelComponentImage(h2sv1, 3)); 00124 00125 const PixH2SV1<float> target(h1.getVal(foacenter), 00126 h2.getVal(foacenter), 00127 s.getVal(foacenter), 00128 v.getVal(foacenter)); 00129 distmap = Image<float>(h2sv1.getDims(), NO_INIT); 00130 00131 Image<PixH2SV1<float> >::const_iterator ptr = h2sv1.begin(); 00132 Image<float>::iterator bptr = distmap.beginw(); 00133 00134 Point2D<int> p; 00135 for (p.j = 0; p.j < distmap.getHeight(); ++p.j) 00136 for (p.i = 0; p.i < distmap.getWidth(); ++p.i) 00137 { 00138 const float dist = p.distance(foacenter) / 15.0; 00139 *bptr = 00140 0.2 * exp(-dist*dist) 00141 + 00142 0.8 * exp(-sqrt(0.3 * squareOf(ptr->p[0] - target.p[0]) 00143 + 00144 0.3 * squareOf(ptr->p[1] - target.p[1]) 00145 + 00146 0.3 * squareOf(ptr->p[2] - target.p[2]) 00147 + 00148 0.1 * squareOf(ptr->p[3] - target.p[3]) 00149 )); 00150 00151 ++bptr; 00152 ++ptr; 00153 } 00154 00155 distmap = lowPass9(distmap); 00156 00157 float mi, ma, avg; getMinMaxAvg(distmap, mi, ma, avg); 00158 00159 const float thresh = avg + segmentationThresh * (ma - avg); 00160 00161 mask = Image<byte>(distmap.getDims(), ZEROS); 00162 00163 std::vector<Point2D<int> > pts; 00164 pts.push_back(foacenter); 00165 mask[foacenter] = 255; 00166 00167 #if 0 00168 const Point2D<int> offsets[8] = 00169 { 00170 Point2D<int>(-1, -1), Point2D<int>(0, -1), Point2D<int>(1, -1), 00171 Point2D<int>(-1, 0), Point2D<int>(1, 0), 00172 Point2D<int>(-1, 1), Point2D<int>(0, 1), Point2D<int>(1, 1) 00173 }; 00174 #endif 00175 00176 const Point2D<int> offsets[4] = 00177 { 00178 Point2D<int>(0, -1), 00179 Point2D<int>(-1, 0), Point2D<int>(1, 0), 00180 Point2D<int>(0, 1), 00181 }; 00182 00183 int left = foacenter.i, top = foacenter.j, right = foacenter.i, bottom = foacenter.j; 00184 00185 while (pts.size() > 0) 00186 { 00187 const Point2D<int> p = pts.back(); 00188 pts.pop_back(); 00189 00190 if (p.i < left) left = p.i; 00191 else if (p.i > right) right = p.i; 00192 if (p.j < top) top = p.j; 00193 else if (p.j > bottom) bottom = p.j; 00194 00195 for (size_t i = 0; i < sizeof(offsets) / sizeof(offsets[0]); ++i) 00196 if (distmap.coordsOk(p+offsets[i]) && 00197 distmap[p+offsets[i]] >= thresh && 00198 mask[p+offsets[i]] == 0) 00199 { 00200 pts.push_back(p+offsets[i]); 00201 mask[p+offsets[i]] = 255; 00202 } 00203 } 00204 00205 ASSERT(left >= 0); 00206 ASSERT(right < mask.getWidth()); 00207 ASSERT(top >= 0); 00208 ASSERT(bottom < mask.getHeight()); 00209 00210 const Rectangle rawfoa = 00211 Rectangle::tlbrO(top << segmentationScale, 00212 left << segmentationScale, 00213 (bottom + 1) << segmentationScale, 00214 (right + 1) << segmentationScale); 00215 00216 return constrainRect(rawfoa, rgbin.getBounds(), 00217 foaminsize, foamaxsize); 00218 } 00219 } 00220 00221 // ###################################################################### 00222 EnvSegmenterColorRegion::EnvSegmenterColorRegion(OptionManager& mgr) 00223 : 00224 EnvSegmenter(mgr, "Embeddable Color-Region FOA Segmenter", "EnvSegmenterColorRegion"), 00225 itsDynamicFoa(&OPT_EseDynamicFoa, this, ALLOW_ONLINE_CHANGES), 00226 itsFoaSize(&OPT_EseFoaSize, this, ALLOW_ONLINE_CHANGES), 00227 itsFoaMinSize(&OPT_EseDynamicFoaMinSize, this, ALLOW_ONLINE_CHANGES), 00228 itsFoaMaxSize(&OPT_EseDynamicFoaMaxSize, this, ALLOW_ONLINE_CHANGES), 00229 itsSegmentationThresh(&OPT_EseSegmentationThresh, this, ALLOW_ONLINE_CHANGES), 00230 itsSegmentationScale(&OPT_EseSegmentationScale, this, ALLOW_ONLINE_CHANGES) 00231 {} 00232 00233 // ###################################################################### 00234 EnvSegmenterColorRegion::~EnvSegmenterColorRegion() 00235 {} 00236 00237 // ###################################################################### 00238 void EnvSegmenterColorRegion::paramChanged(ModelParamBase* const param, 00239 const bool valueChanged, 00240 ParamClient::ChangeStatus* status) 00241 { 00242 EnvSegmenter::paramChanged(param, valueChanged, status); 00243 00244 if (param == &itsDynamicFoa) 00245 { 00246 const bool dyn = itsDynamicFoa.getVal(); 00247 00248 itsFoaSize.setInactive(dyn); 00249 itsFoaMinSize.setInactive(!dyn); 00250 itsFoaMaxSize.setInactive(!dyn); 00251 itsSegmentationThresh.setInactive(!dyn); 00252 } 00253 else if (param == &itsFoaSize) 00254 { 00255 if (itsFoaSize.getVal() < 16) 00256 *status = ParamClient::CHANGE_REJECTED; 00257 } 00258 else if (param == &itsFoaMinSize) 00259 { 00260 if (itsFoaMinSize.getVal() < 16) 00261 *status = ParamClient::CHANGE_REJECTED; 00262 } 00263 else if (param == &itsFoaMaxSize) 00264 { 00265 if (itsFoaMaxSize.getVal() < itsFoaMinSize.getVal()) 00266 *status = ParamClient::CHANGE_REJECTED; 00267 } 00268 else if (param == &itsSegmentationScale) 00269 { 00270 if (itsSegmentationScale.getVal() > 8) 00271 *status = ParamClient::CHANGE_REJECTED; 00272 } 00273 } 00274 00275 // ###################################################################### 00276 Rectangle EnvSegmenterColorRegion::getFoa(const Image<PixRGB<byte> >& rgbin, 00277 const Point2D<int>& center, 00278 Image<byte>* foamask, 00279 Image<PixRGB<byte> >* segmentdisp) const 00280 { 00281 *foamask = Image<byte>(); 00282 *segmentdisp = Image<PixRGB<byte> >(); 00283 00284 if (itsDynamicFoa.getVal()) 00285 { 00286 const int seg_zoom = (1 << itsSegmentationScale.getVal()); 00287 00288 const Point2D<int> seg_scaled_maxpos = center / seg_zoom; 00289 00290 Image<float> distmap; 00291 const Rectangle foa = 00292 segmentColor(rgbin, seg_scaled_maxpos, 00293 itsSegmentationScale.getVal(), 00294 itsSegmentationThresh.getVal(), 00295 itsFoaMinSize.getVal(), 00296 itsFoaMaxSize.getVal(), 00297 distmap, *foamask); 00298 00299 *segmentdisp = normalizeFloat(distmap, FLOAT_NORM_0_255); 00300 00301 return foa; 00302 } 00303 else 00304 { 00305 Point2D<int> foatopleft = center - itsFoaSize.getVal()/2; 00306 foatopleft.clampToDims(rgbin.getDims()); 00307 00308 const Rectangle foa = 00309 Rectangle(foatopleft, Dims(itsFoaSize.getVal(), itsFoaSize.getVal())) 00310 .getOverlap(rgbin.getBounds()); 00311 00312 return foa; 00313 } 00314 } 00315 00316 // ###################################################################### 00317 /* So things look consistent in everyone's emacs... */ 00318 /* Local Variables: */ 00319 /* mode: c++ */ 00320 /* indent-tabs-mode: nil */ 00321 /* End: */ 00322 00323 #endif // NEURO_ENVSEGMENTERCOLORREGION_C_DEFINED