00001 /*!@file Gist/test-SuperPixel.C testing SuperPixel segmentation algorithm */ 00002 // //////////////////////////////////////////////////////////////////// // 00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00004 // University of Southern California (USC) and the iLab at USC. // 00005 // See http://iLab.usc.edu for information about this project. // 00006 // //////////////////////////////////////////////////////////////////// // 00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00009 // in Visual Environments, and Applications'' by Christof Koch and // 00010 // Laurent Itti, California Institute of Technology, 2001 (patent // 00011 // pending; application number 09/912,225 filed July 23, 2001; see // 00012 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00013 // //////////////////////////////////////////////////////////////////// // 00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00015 // // 00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00017 // redistribute it and/or modify it under the terms of the GNU General // 00018 // Public License as published by the Free Software Foundation; either // 00019 // version 2 of the License, or (at your option) any later version. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00024 // PURPOSE. See the GNU General Public License for more details. // 00025 // // 00026 // You should have received a copy of the GNU General Public License // 00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00029 // Boston, MA 02111-1307 USA. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // 00032 // Primary maintainer for this file: Christian Siagian <siagian@usc.edu> 00033 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Gist/SuperPixel.H $ 00034 // $Id: SuperPixel.H 14762 2011-05-03 01:13:16Z siagian $ 00035 // 00036 ////////////////////////////////////////////////////////////////////////// 00037 // 00038 // Implementation of the segmentation algorithm described in: 00039 // 00040 // Efficient Graph-Based Image Segmentation 00041 // Pedro F. Felzenszwalb and Daniel P. Huttenlocher 00042 // International Journal of Computer Vision, 59(2) September 2004. 00043 // Copyright (C) 2006 Pedro Felzenszwalb 00044 00045 #ifndef SUPERPIXEL_SEGMENT_IMAGE 00046 #define SUPERPIXEL_SEGMENT_IMAGE 00047 00048 #include <cstdlib> 00049 00050 #include "Image/Image.H" 00051 #include "Image/Pixels.H" 00052 #include "Raster/Raster.H" 00053 #include "Image/MathOps.H" 00054 #include "Image/ColorOps.H" 00055 00056 #include "Gist/SuperPixel_filter.H" 00057 #include "Gist/SuperPixel_segment_graph.H" 00058 00059 #include "Util/Timer.H" 00060 00061 // random color 00062 PixRGB<byte> random_rgb(){ 00063 PixRGB<byte> c((byte)random(), 00064 (byte)random(), 00065 (byte)random()); 00066 return c; 00067 } 00068 00069 // dissimilarity measure between pixels 00070 static inline float 00071 diff(Image<float> r, Image<float> g, Image<float> b, 00072 int x1, int y1, int x2, int y2) 00073 { 00074 return sqrt(pow(r.getVal(x1, y1) - r.getVal(x2, y2), 2.0) + 00075 pow(g.getVal(x1, y1) - g.getVal(x2, y2), 2.0) + 00076 pow(b.getVal(x1, y1) - b.getVal(x2, y2), 2.0)); 00077 } 00078 // dissimilarity measure between pixels 00079 static inline float 00080 diff(std::vector<Image<float> > channels, 00081 int x1, int y1, int x2, int y2) 00082 { 00083 float difference = 0.0; 00084 std::vector<Image<float> >::iterator 00085 it = channels.begin(), 00086 stop = channels.end(); 00087 for (;it!=stop; ++it) 00088 { 00089 Image<float> ch = *it; 00090 difference+= pow(ch.getVal(x1, y1) - ch.getVal(x2, y2), 2.0) ; 00091 } 00092 00093 return sqrt(difference); 00094 } 00095 00096 00097 00098 //! Segment an image using Super Pixel Segmentation. 00099 /*! 00100 * Returns an integer image where the value at each pixel denotes the 00101 * group it belongs to. Additionally, if a groups vector is supplied, 00102 * it will be filled with vectors of points belonging to each group. 00103 * 00104 * \param im image to segment. 00105 * \param sigma to smooth the image. 00106 * \param c constant for treshold function. 00107 * \param min_size minimum component size (enforced by post-processing stage). 00108 * \param num_ccs number of connected components in the segmentation. 00109 * \param channels :instead use default RGB channels, it takes all channels as graph base 00110 * 00111 */ 00112 00113 // ###################################################################### 00114 Image<int> SuperPixelSegment 00115 (Image<PixRGB<byte> > im, 00116 float sigma, float c, int min_size, int &num_ccs, 00117 std::vector<std::vector<Point2D<int> > > *groups , 00118 std::vector<Image<float> > channels ) 00119 { 00120 //smooth out each channel 00121 std::vector<Image<float> > smooth_channels; 00122 std::vector<Image<float> >::iterator 00123 it = channels.begin(), 00124 stop = channels.end(); 00125 for (;it!=stop; ++it) { 00126 Image<float> ch = *it; 00127 Image<float> smooth_temp = smooth(ch, sigma); 00128 smooth_channels.push_back(smooth_temp); 00129 } 00130 00131 int width = im.getWidth(); 00132 int height = im.getHeight(); 00133 00134 // build graph 00135 edge *edges = new edge[width*height*4]; 00136 int num = 0; 00137 for (int y = 0; y < height; y++) { 00138 for (int x = 0; x < width; x++) { 00139 if (x < width-1) { 00140 edges[num].a = y * width + x; 00141 edges[num].b = y * width + (x+1); 00142 edges[num].w = diff(smooth_channels, x, y, x+1, y); 00143 num++; 00144 } 00145 00146 if (y < height-1) { 00147 edges[num].a = y * width + x; 00148 edges[num].b = (y+1) * width + x; 00149 edges[num].w = diff(smooth_channels, x, y, x, y+1); 00150 num++; 00151 } 00152 00153 if ((x < width-1) && (y < height-1)) { 00154 edges[num].a = y * width + x; 00155 edges[num].b = (y+1) * width + (x+1); 00156 edges[num].w = diff(smooth_channels, x, y, x+1, y+1); 00157 num++; 00158 } 00159 00160 if ((x < width-1) && (y > 0)) { 00161 edges[num].a = y * width + x; 00162 edges[num].b = (y-1) * width + (x+1); 00163 edges[num].w = diff(smooth_channels, x, y, x+1, y-1); 00164 num++; 00165 } 00166 } 00167 } 00168 00169 // segment 00170 universe *u = segment_graph(width*height, num, edges, c); 00171 00172 // post process small components 00173 for (int i = 0; i < num; i++) { 00174 int a = u->find(edges[i].a); 00175 int b = u->find(edges[i].b); 00176 if ((a != b) && ((u->size(a) < min_size) || (u->size(b) < min_size))) 00177 u->join(a, b); 00178 } 00179 delete [] edges; 00180 num_ccs = u->num_sets(); 00181 00182 Image<int> groupImage(width, height, NO_INIT); 00183 00184 // If a group vector is provided, then let's resize it and fill it along 00185 // with the output image 00186 groups->resize(u->num_sets()); 00187 00188 // The group numbers created by the algorithm are more or less random. We 00189 // need to compress them so we can easily insert the group points into 00190 // vectors. 00191 std::map<int, int> groupLabels; 00192 int labelCount = 0; 00193 00194 for (int y = 0; y < height; y++) 00195 for (int x = 0; x < width; x++) 00196 { 00197 int groupUnorderedId = u->find(y * width + x); 00198 00199 // If we haven't seen this group before, 00200 // create a new ordered label for it 00201 if(groupLabels.find(groupUnorderedId) == groupLabels.end()) 00202 groupLabels[groupUnorderedId] = labelCount++; 00203 00204 int groupOrderedId = groupLabels[groupUnorderedId]; 00205 00206 if(groups != NULL) 00207 (*groups)[groupOrderedId].push_back(Point2D<int>(x,y)); 00208 groupImage.setVal(x, y, groupOrderedId); 00209 } 00210 00211 delete u; 00212 00213 return groupImage; 00214 } 00215 00216 // ###################################################################### 00217 Image<int> SuperPixelSegment 00218 (Image<PixRGB<byte> > im, 00219 float sigma, float c, int min_size, int &num_ccs, 00220 std::vector<std::vector<Point2D<int> > > *groups) 00221 { 00222 Image<byte> r, g, b; getComponents(im, r, g, b); 00223 00224 std::vector<Image<float> > channels; 00225 channels.push_back(r); 00226 channels.push_back(g); 00227 channels.push_back(b); 00228 00229 return SuperPixelSegment 00230 (im,sigma,c,min_size,num_ccs,groups,channels); 00231 } 00232 00233 // ###################################################################### 00234 namespace 00235 { std::vector<PixRGB<byte> > groupColors; } 00236 00237 // ###################################################################### 00238 Image<int> SuperPixelRegionSizeImage 00239 (std::vector<std::vector<Point2D<int> > > const& groups, Image<int> groupImage) 00240 { 00241 Image<int> debugSizeImage(groupImage.getDims(), NO_INIT); 00242 00243 for(size_t grpIdx=0; grpIdx < groups.size(); grpIdx++) 00244 { 00245 int regionSize = (int) groups[grpIdx].size(); 00246 for(size_t pntIdx=0; pntIdx<groups[grpIdx].size(); pntIdx++) 00247 debugSizeImage.setVal(groups[grpIdx][pntIdx], regionSize); 00248 } 00249 00250 return debugSizeImage; 00251 } 00252 00253 // ###################################################################### 00254 Image<PixRGB<byte> > SuperPixelDebugImage(Image<int> groupImage) 00255 { 00256 // pick random colors for each component 00257 if(groupColors.size() == 0) 00258 { 00259 groupColors.resize(255); 00260 std::vector<PixRGB<byte> >::iterator 00261 it = groupColors.begin(), 00262 stop = groupColors.end(); 00263 for (;it!=stop; ++it) *it = random_rgb(); 00264 } 00265 00266 Image<PixRGB<byte> > debugImage(groupImage.getDims(), NO_INIT); 00267 00268 for(size_t i=0; i<groupImage.size(); ++i) 00269 debugImage.setVal 00270 (i,groupColors[groupImage.getVal(i)%groupColors.size()]); 00271 00272 return debugImage; 00273 } 00274 00275 // ###################################################################### 00276 Image<PixRGB<byte> > SuperPixelDebugImage 00277 (std::vector<std::vector<Point2D<int> > > const& groups, 00278 Image<PixRGB<byte> > originalImage) 00279 { 00280 Image<PixRGB<byte> > debugImage(originalImage.getDims(), NO_INIT); 00281 00282 for(size_t grpIdx=0; grpIdx < groups.size(); grpIdx++) 00283 { 00284 PixRGB<long> avgColor(0,0,0); 00285 for(size_t pntIdx=0; pntIdx<groups[grpIdx].size(); pntIdx++) 00286 avgColor += originalImage.getVal(groups[grpIdx][pntIdx]); 00287 avgColor /= groups[grpIdx].size(); 00288 for(size_t pntIdx=0; pntIdx<groups[grpIdx].size(); pntIdx++) 00289 debugImage.setVal(groups[grpIdx][pntIdx], avgColor); 00290 } 00291 00292 return debugImage; 00293 } 00294 00295 /* 00296 * Segment an image 00297 * 00298 * Returns a color image representing the segmentation. 00299 * 00300 * im: image to segment. 00301 * sigma: to smooth the image. 00302 * c: constant for treshold function. 00303 * min_size: minimum component size (enforced by post-processing stage). 00304 * num_ccs: number of connected components in the segmentation. 00305 00306 00307 :::::NOTE::::: This method is now deprecated. 00308 Please use SuperPixelSegment, and SuperPixelDebugImage instead. 00309 00310 */ 00311 // ###################################################################### 00312 Image<PixRGB<byte> > segment_image 00313 (Image<PixRGB<byte> > im, float sigma, float c, int min_size, 00314 int &num_ccs) 00315 { 00316 LINFO("This method is now deprecated. " 00317 "Please use SuperPixelSegment " 00318 "and SuperPixelDebugImage instead."); 00319 std::vector<std::vector<Point2D<int> > > groups; 00320 Image<int> groupImage = 00321 SuperPixelSegment 00322 (im, sigma, c, min_size, num_ccs, &groups); 00323 return SuperPixelDebugImage(groups,im); 00324 } 00325 00326 #endif 00327 00328 // ###################################################################### 00329 /* So things look consistent in everyone's emacs... */ 00330 /* Local Variables: */ 00331 /* indent-tabs-mode: nil */ 00332 /* End: */