slic.c
Go to the documentation of this file.
00001 
00006 /*
00007 Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
00008 All rights reserved.
00009 
00010 This file is part of the VLFeat library and is made available under
00011 the terms of the BSD license (see the COPYING file).
00012 */
00013 
00145 #include "slic.h"
00146 #include "mathop.h"
00147 #include <math.h>
00148 #include <string.h>
00149 
00169 void
00170 vl_slic_segment (vl_uint32 * segmentation,
00171                  float const * image,
00172                  vl_size width,
00173                  vl_size height,
00174                  vl_size numChannels,
00175                  vl_size regionSize,
00176                  float regularization,
00177                  vl_size minRegionSize)
00178 {
00179   vl_index i, x, y, u, v, k, region ;
00180   vl_uindex iter ;
00181   vl_size const numRegionsX = (vl_size) ceil((double) width / regionSize) ;
00182   vl_size const numRegionsY = (vl_size) ceil((double) height / regionSize) ;
00183   vl_size const numRegions = numRegionsX * numRegionsY ;
00184   vl_size const numPixels = width * height ;
00185   float * centers ;
00186   float * edgeMap ;
00187   float previousEnergy = VL_INFINITY_F ;
00188   float startingEnergy ;
00189   vl_uint32 * masses ;
00190   vl_size const maxNumIterations = 100 ;
00191 
00192   assert(segmentation) ;
00193   assert(image) ;
00194   assert(width >= 1) ;
00195   assert(height >= 1) ;
00196   assert(numChannels >= 1) ;
00197   assert(regionSize >= 1) ;
00198   assert(regularization >= 0) ;
00199 
00200 #define atimage(x,y,k) image[(x)+(y)*width+(k)*width*height]
00201 #define atEdgeMap(x,y) edgeMap[(x)+(y)*width]
00202 
00203   edgeMap = vl_calloc(numPixels, sizeof(float)) ;
00204   masses = vl_malloc(sizeof(vl_uint32) * numPixels) ;
00205   centers = vl_malloc(sizeof(float) * (2 + numChannels) * numRegions) ;
00206 
00207   /* compute edge map (gradient strength) */
00208   for (k = 0 ; k < (signed)numChannels ; ++k) {
00209     for (y = 1 ; y < (signed)height-1 ; ++y) {
00210       for (x = 1 ; x < (signed)width-1 ; ++x) {
00211         float a = atimage(x-1,y,k) ;
00212         float b = atimage(x+1,y,k) ;
00213         float c = atimage(x,y+1,k) ;
00214         float d = atimage(x,y-1,k) ;
00215         atEdgeMap(x,y) += (a - b)  * (a - b) + (c - d) * (c - d) ;
00216       }
00217     }
00218   }
00219 
00220   /* initialize K-means centers */
00221   i = 0 ;
00222   for (v = 0 ; v < (signed)numRegionsY ; ++v) {
00223     for (u = 0 ; u < (signed)numRegionsX ; ++u) {
00224       vl_index xp ;
00225       vl_index yp ;
00226       vl_index centerx = 0 ;
00227       vl_index centery = 0 ;
00228       float minEdgeValue = VL_INFINITY_F ;
00229 
00230       x = (vl_index) vl_round_d(regionSize * (u + 0.5)) ;
00231       y = (vl_index) vl_round_d(regionSize * (v + 0.5)) ;
00232 
00233       x = VL_MAX(VL_MIN(x, (signed)width-1),0) ;
00234       y = VL_MAX(VL_MIN(y, (signed)height-1),0) ;
00235 
00236       /* search in a 3x3 neighbourhood the smallest edge response */
00237       for (yp = VL_MAX(0, y-1) ; yp <= VL_MIN((signed)height-1, y+1) ; ++ yp) {
00238         for (xp = VL_MAX(0, x-1) ; xp <= VL_MIN((signed)width-1, x+1) ; ++ xp) {
00239           float thisEdgeValue = atEdgeMap(xp,yp) ;
00240           if (thisEdgeValue < minEdgeValue) {
00241             minEdgeValue = thisEdgeValue ;
00242             centerx = xp ;
00243             centery = yp ;
00244           }
00245         }
00246       }
00247 
00248       /* initialize the new center at this location */
00249       centers[i++] = (float) centerx ;
00250       centers[i++] = (float) centery ;
00251       for (k  = 0 ; k < (signed)numChannels ; ++k) {
00252         centers[i++] = atimage(centerx,centery,k) ;
00253       }
00254     }
00255   }
00256 
00257   /* run k-means iterations */
00258   for (iter = 0 ; iter < maxNumIterations ; ++iter) {
00259     float factor = regularization / (regionSize * regionSize) ;
00260     float energy = 0 ;
00261 
00262     /* assign pixels to centers */
00263     for (y = 0 ; y < (signed)height ; ++y) {
00264       for (x = 0 ; x < (signed)width ; ++x) {
00265         vl_index u = floor((double)x / regionSize - 0.5) ;
00266         vl_index v = floor((double)y / regionSize - 0.5) ;
00267         vl_index up, vp ;
00268         float minDistance = VL_INFINITY_F ;
00269 
00270         for (vp = VL_MAX(0, v) ; vp <= VL_MIN((signed)numRegionsY-1, v+1) ; ++vp) {
00271           for (up = VL_MAX(0, u) ; up <= VL_MIN((signed)numRegionsX-1, u+1) ; ++up) {
00272             vl_index region = up  + vp * numRegionsX ;
00273             float centerx = centers[(2 + numChannels) * region + 0]  ;
00274             float centery = centers[(2 + numChannels) * region + 1] ;
00275             float spatial = (x - centerx) * (x - centerx) + (y - centery) * (y - centery) ;
00276             float appearance = 0 ;
00277             float distance ;
00278             for (k = 0 ; k < (signed)numChannels ; ++k) {
00279               float centerz = centers[(2 + numChannels) * region + k + 2]  ;
00280               float z = atimage(x,y,k) ;
00281               appearance += (z - centerz) * (z - centerz) ;
00282             }
00283             distance = appearance + factor * spatial ;
00284             if (minDistance > distance) {
00285               minDistance = distance ;
00286               segmentation[x + y * width] = (vl_uint32)region ;
00287             }
00288           }
00289         }
00290         energy += minDistance ;
00291       }
00292     }
00293 
00294     /*
00295      VL_PRINTF("vl:slic: iter %d: energy: %g\n", iter, energy) ;
00296     */
00297 
00298     /* check energy termination conditions */
00299     if (iter == 0) {
00300       startingEnergy = energy ;
00301     } else {
00302       if ((previousEnergy - energy) < 1e-5 * (startingEnergy - energy)) {
00303         break ;
00304       }
00305     }
00306     previousEnergy = energy ;
00307 
00308     /* recompute centers */
00309     memset(masses, 0, sizeof(vl_uint32) * width * height) ;
00310     memset(centers, 0, sizeof(float) * (2 + numChannels) * numRegions) ;
00311 
00312     for (y = 0 ; y < (signed)height ; ++y) {
00313       for (x = 0 ; x < (signed)width ; ++x) {
00314         vl_index pixel = x + y * width ;
00315         vl_index region = segmentation[pixel] ;
00316         masses[region] ++ ;
00317         centers[region * (2 + numChannels) + 0] += x ;
00318         centers[region * (2 + numChannels) + 1] += y ;
00319         for (k = 0 ; k < (signed)numChannels ; ++k) {
00320           centers[region * (2 + numChannels) + k + 2] += atimage(x,y,k) ;
00321         }
00322       }
00323     }
00324 
00325     for (region = 0 ; region < (signed)numRegions ; ++region) {
00326       float mass = VL_MAX(masses[region], 1e-8) ;
00327       for (i = (2 + numChannels) * region ;
00328            i < (signed)(2 + numChannels) * (region + 1) ;
00329            ++i) {
00330         centers[i] /= mass ;
00331       }
00332     }
00333   }
00334 
00335   vl_free(masses) ;
00336   vl_free(centers) ;
00337   vl_free(edgeMap) ;
00338 
00339   /* elimiate small regions */
00340   {
00341     vl_uint32 * cleaned = vl_calloc(numPixels, sizeof(vl_uint32)) ;
00342     vl_uindex * segment = vl_malloc(sizeof(vl_uindex) * numPixels) ;
00343     vl_size segmentSize ;
00344     vl_uint32 label ;
00345     vl_uint32 cleanedLabel ;
00346     vl_size numExpanded ;
00347     vl_index const dx [] = {+1, -1,  0,  0} ;
00348     vl_index const dy [] = { 0,  0, +1, -1} ;
00349     vl_index direction ;
00350     vl_index pixel ;
00351 
00352     for (pixel = 0 ; pixel < (signed)numPixels ; ++pixel) {
00353       if (cleaned[pixel]) continue ;
00354       label = segmentation[pixel] ;
00355       numExpanded = 0 ;
00356       segmentSize = 0 ;
00357       segment[segmentSize++] = pixel ;
00358 
00359       /*
00360        find cleanedLabel as the label of an already cleaned
00361        region neihbour of this pixel
00362        */
00363       cleanedLabel = label + 1 ;
00364       cleaned[pixel] = label + 1 ;
00365       x = pixel % width ;
00366       y = pixel / width ;
00367       for (direction = 0 ; direction < 4 ; ++direction) {
00368         vl_index xp = x + dx[direction] ;
00369         vl_index yp = y + dy[direction] ;
00370         vl_index neighbor = xp + yp * width ;
00371         if (0 <= xp && xp < (signed)width &&
00372             0 <= yp && yp < (signed)height &&
00373             cleaned[neighbor]) {
00374           cleanedLabel = cleaned[neighbor] ;
00375         }
00376       }
00377 
00378       /* expand the segment */
00379       while (numExpanded < segmentSize) {
00380         vl_index open = segment[numExpanded++] ;
00381         x = open % width ;
00382         y = open / width ;
00383         for (direction = 0 ; direction < 4 ; ++direction) {
00384           vl_index xp = x + dx[direction] ;
00385           vl_index yp = y + dy[direction] ;
00386           vl_index neighbor = xp + yp * width ;
00387           if (0 <= xp && xp < (signed)width &&
00388               0 <= yp && yp < (signed)height &&
00389               cleaned[neighbor] == 0 &&
00390               segmentation[neighbor] == label) {
00391             cleaned[neighbor] = label + 1 ;
00392             segment[segmentSize++] = neighbor ;
00393           }
00394         }
00395       }
00396 
00397       /* change label to cleanedLabel if the semgent is too small */
00398       if (segmentSize < minRegionSize) {
00399         while (segmentSize > 0) {
00400           cleaned[segment[--segmentSize]] = cleanedLabel ;
00401         }
00402       }
00403     }
00404     /* restore base 0 indexing of the regions */
00405     for (pixel = 0 ; pixel < (signed)numPixels ; ++pixel) cleaned[pixel] -- ;
00406 
00407     memcpy(segmentation, cleaned, numPixels * sizeof(vl_uint32)) ;
00408     vl_free(cleaned) ;
00409     vl_free(segment) ;
00410   }
00411 }


libvlfeat
Author(s): Andrea Vedaldi
autogenerated on Thu Jun 6 2019 20:25:51