├── dog.png ├── dog_segmentation.png ├── README.md ├── test_slic.cpp ├── slic.h └── slic.cpp /dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PSMM/SLIC-Superpixels/HEAD/dog.png -------------------------------------------------------------------------------- /dog_segmentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PSMM/SLIC-Superpixels/HEAD/dog_segmentation.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SLIC Superpixel Implementation 2 | This repository contains an implementation of the SLIC Superpixel algorithm by Achanta et al. (PAMI'12, vol. 34, num. 11, pp. 2274-2282). The C++ implementation is created to work with the strutures of OpenCV. 3 | 4 | ## Exemplary result 5 | The images below shows an example of an over-segmentation using 400 superpixels and a weight factor of 40. 6 |

7 | Dog 8 | Dog Segmentation 9 |

10 | -------------------------------------------------------------------------------- /test_slic.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test_slic.cpp. 3 | * 4 | * Written by: Pascal Mettes. 5 | * 6 | * This file creates an over-segmentation of a provided image based on the SLIC 7 | * superpixel algorithm, as implemented in slic.h and slic.cpp. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | #include "slic.h" 19 | 20 | int main(int argc, char *argv[]) { 21 | /* Load the image and convert to Lab colour space. */ 22 | IplImage *image = cvLoadImage(argv[1], 1); 23 | IplImage *lab_image = cvCloneImage(image); 24 | cvCvtColor(image, lab_image, CV_BGR2Lab); 25 | 26 | /* Yield the number of superpixels and weight-factors from the user. */ 27 | int w = image->width, h = image->height; 28 | int nr_superpixels = atoi(argv[2]); 29 | int nc = atoi(argv[3]); 30 | 31 | double step = sqrt((w * h) / (double) nr_superpixels); 32 | 33 | /* Perform the SLIC superpixel algorithm. */ 34 | Slic slic; 35 | slic.generate_superpixels(lab_image, step, nc); 36 | slic.create_connectivity(lab_image); 37 | 38 | /* Display the contours and show the result. */ 39 | slic.display_contours(image, CV_RGB(255,0,0)); 40 | cvShowImage("result", image); 41 | cvWaitKey(0); 42 | cvSaveImage(argv[4], image); 43 | } 44 | -------------------------------------------------------------------------------- /slic.h: -------------------------------------------------------------------------------- 1 | #ifndef SLIC_H 2 | #define SLIC_H 3 | 4 | /* slic.h. 5 | * 6 | * Written by: Pascal Mettes. 7 | * 8 | * This file contains the class elements of the class Slic. This class is an 9 | * implementation of the SLIC Superpixel algorithm by Achanta et al. [PAMI'12, 10 | * vol. 34, num. 11, pp. 2274-2282]. 11 | * 12 | * This implementation is created for the specific purpose of creating 13 | * over-segmentations in an OpenCV-based environment. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | using namespace std; 23 | 24 | /* 2d matrices are handled by 2d vectors. */ 25 | #define vec2dd vector > 26 | #define vec2di vector > 27 | #define vec2db vector > 28 | /* The number of iterations run by the clustering algorithm. */ 29 | #define NR_ITERATIONS 10 30 | 31 | /* 32 | * class Slic. 33 | * 34 | * In this class, an over-segmentation is created of an image, provided by the 35 | * step-size (distance between initial cluster locations) and the colour 36 | * distance parameter. 37 | */ 38 | class Slic { 39 | private: 40 | /* The cluster assignments and distance values for each pixel. */ 41 | vec2di clusters; 42 | vec2dd distances; 43 | 44 | /* The LAB and xy values of the centers. */ 45 | vec2dd centers; 46 | /* The number of occurences of each center. */ 47 | vector center_counts; 48 | 49 | /* The step size per cluster, and the colour (nc) and distance (ns) 50 | * parameters. */ 51 | int step, nc, ns; 52 | 53 | /* Compute the distance between a center and an individual pixel. */ 54 | double compute_dist(int ci, CvPoint pixel, CvScalar colour); 55 | /* Find the pixel with the lowest gradient in a 3x3 surrounding. */ 56 | CvPoint find_local_minimum(IplImage *image, CvPoint center); 57 | 58 | /* Remove and initialize the 2d vectors. */ 59 | void clear_data(); 60 | void init_data(IplImage *image); 61 | 62 | public: 63 | /* Class constructors and deconstructors. */ 64 | Slic(); 65 | ~Slic(); 66 | 67 | /* Generate an over-segmentation for an image. */ 68 | void generate_superpixels(IplImage *image, int step, int nc); 69 | /* Enforce connectivity for an image. */ 70 | void create_connectivity(IplImage *image); 71 | 72 | /* Draw functions. Resp. displayal of the centers and the contours. */ 73 | void display_center_grid(IplImage *image, CvScalar colour); 74 | void display_contours(IplImage *image, CvScalar colour); 75 | void colour_with_cluster_means(IplImage *image); 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /slic.cpp: -------------------------------------------------------------------------------- 1 | #include "slic.h" 2 | 3 | /* 4 | * Constructor. Nothing is done here. 5 | */ 6 | Slic::Slic() { 7 | 8 | } 9 | 10 | /* 11 | * Destructor. Clear any present data. 12 | */ 13 | Slic::~Slic() { 14 | clear_data(); 15 | } 16 | 17 | /* 18 | * Clear the data as saved by the algorithm. 19 | * 20 | * Input : - 21 | * Output: - 22 | */ 23 | void Slic::clear_data() { 24 | clusters.clear(); 25 | distances.clear(); 26 | centers.clear(); 27 | center_counts.clear(); 28 | } 29 | 30 | /* 31 | * Initialize the cluster centers and initial values of the pixel-wise cluster 32 | * assignment and distance values. 33 | * 34 | * Input : The image (IplImage*). 35 | * Output: - 36 | */ 37 | void Slic::init_data(IplImage *image) { 38 | /* Initialize the cluster and distance matrices. */ 39 | for (int i = 0; i < image->width; i++) { 40 | vector cr; 41 | vector dr; 42 | for (int j = 0; j < image->height; j++) { 43 | cr.push_back(-1); 44 | dr.push_back(FLT_MAX); 45 | } 46 | clusters.push_back(cr); 47 | distances.push_back(dr); 48 | } 49 | 50 | /* Initialize the centers and counters. */ 51 | for (int i = step; i < image->width - step/2; i += step) { 52 | for (int j = step; j < image->height - step/2; j += step) { 53 | vector center; 54 | /* Find the local minimum (gradient-wise). */ 55 | CvPoint nc = find_local_minimum(image, cvPoint(i,j)); 56 | CvScalar colour = cvGet2D(image, nc.y, nc.x); 57 | 58 | /* Generate the center vector. */ 59 | center.push_back(colour.val[0]); 60 | center.push_back(colour.val[1]); 61 | center.push_back(colour.val[2]); 62 | center.push_back(nc.x); 63 | center.push_back(nc.y); 64 | 65 | /* Append to vector of centers. */ 66 | centers.push_back(center); 67 | center_counts.push_back(0); 68 | } 69 | } 70 | } 71 | 72 | /* 73 | * Compute the distance between a cluster center and an individual pixel. 74 | * 75 | * Input : The cluster index (int), the pixel (CvPoint), and the Lab values of 76 | * the pixel (CvScalar). 77 | * Output: The distance (double). 78 | */ 79 | double Slic::compute_dist(int ci, CvPoint pixel, CvScalar colour) { 80 | double dc = sqrt(pow(centers[ci][0] - colour.val[0], 2) + pow(centers[ci][1] 81 | - colour.val[1], 2) + pow(centers[ci][2] - colour.val[2], 2)); 82 | double ds = sqrt(pow(centers[ci][3] - pixel.x, 2) + pow(centers[ci][4] - pixel.y, 2)); 83 | 84 | return sqrt(pow(dc / nc, 2) + pow(ds / ns, 2)); 85 | 86 | //double w = 1.0 / (pow(ns / nc, 2)); 87 | //return sqrt(dc) + sqrt(ds * w); 88 | } 89 | 90 | /* 91 | * Find a local gradient minimum of a pixel in a 3x3 neighbourhood. This 92 | * method is called upon initialization of the cluster centers. 93 | * 94 | * Input : The image (IplImage*) and the pixel center (CvPoint). 95 | * Output: The local gradient minimum (CvPoint). 96 | */ 97 | CvPoint Slic::find_local_minimum(IplImage *image, CvPoint center) { 98 | double min_grad = FLT_MAX; 99 | CvPoint loc_min = cvPoint(center.x, center.y); 100 | 101 | for (int i = center.x-1; i < center.x+2; i++) { 102 | for (int j = center.y-1; j < center.y+2; j++) { 103 | CvScalar c1 = cvGet2D(image, j+1, i); 104 | CvScalar c2 = cvGet2D(image, j, i+1); 105 | CvScalar c3 = cvGet2D(image, j, i); 106 | /* Convert colour values to grayscale values. */ 107 | double i1 = c1.val[0]; 108 | double i2 = c2.val[0]; 109 | double i3 = c3.val[0]; 110 | /*double i1 = c1.val[0] * 0.11 + c1.val[1] * 0.59 + c1.val[2] * 0.3; 111 | double i2 = c2.val[0] * 0.11 + c2.val[1] * 0.59 + c2.val[2] * 0.3; 112 | double i3 = c3.val[0] * 0.11 + c3.val[1] * 0.59 + c3.val[2] * 0.3;*/ 113 | 114 | /* Compute horizontal and vertical gradients and keep track of the 115 | minimum. */ 116 | if (sqrt(pow(i1 - i3, 2)) + sqrt(pow(i2 - i3,2)) < min_grad) { 117 | min_grad = fabs(i1 - i3) + fabs(i2 - i3); 118 | loc_min.x = i; 119 | loc_min.y = j; 120 | } 121 | } 122 | } 123 | 124 | return loc_min; 125 | } 126 | 127 | /* 128 | * Compute the over-segmentation based on the step-size and relative weighting 129 | * of the pixel and colour values. 130 | * 131 | * Input : The Lab image (IplImage*), the stepsize (int), and the weight (int). 132 | * Output: - 133 | */ 134 | void Slic::generate_superpixels(IplImage *image, int step, int nc) { 135 | this->step = step; 136 | this->nc = nc; 137 | this->ns = step; 138 | 139 | /* Clear previous data (if any), and re-initialize it. */ 140 | clear_data(); 141 | init_data(image); 142 | 143 | /* Run EM for 10 iterations (as prescribed by the algorithm). */ 144 | for (int i = 0; i < NR_ITERATIONS; i++) { 145 | /* Reset distance values. */ 146 | for (int j = 0; j < image->width; j++) { 147 | for (int k = 0;k < image->height; k++) { 148 | distances[j][k] = FLT_MAX; 149 | } 150 | } 151 | 152 | for (int j = 0; j < (int) centers.size(); j++) { 153 | /* Only compare to pixels in a 2 x step by 2 x step region. */ 154 | for (int k = centers[j][3] - step; k < centers[j][3] + step; k++) { 155 | for (int l = centers[j][4] - step; l < centers[j][4] + step; l++) { 156 | 157 | if (k >= 0 && k < image->width && l >= 0 && l < image->height) { 158 | CvScalar colour = cvGet2D(image, l, k); 159 | double d = compute_dist(j, cvPoint(k,l), colour); 160 | 161 | /* Update cluster allocation if the cluster minimizes the 162 | distance. */ 163 | if (d < distances[k][l]) { 164 | distances[k][l] = d; 165 | clusters[k][l] = j; 166 | } 167 | } 168 | } 169 | } 170 | } 171 | 172 | /* Clear the center values. */ 173 | for (int j = 0; j < (int) centers.size(); j++) { 174 | centers[j][0] = centers[j][1] = centers[j][2] = centers[j][3] = centers[j][4] = 0; 175 | center_counts[j] = 0; 176 | } 177 | 178 | /* Compute the new cluster centers. */ 179 | for (int j = 0; j < image->width; j++) { 180 | for (int k = 0; k < image->height; k++) { 181 | int c_id = clusters[j][k]; 182 | 183 | if (c_id != -1) { 184 | CvScalar colour = cvGet2D(image, k, j); 185 | 186 | centers[c_id][0] += colour.val[0]; 187 | centers[c_id][1] += colour.val[1]; 188 | centers[c_id][2] += colour.val[2]; 189 | centers[c_id][3] += j; 190 | centers[c_id][4] += k; 191 | 192 | center_counts[c_id] += 1; 193 | } 194 | } 195 | } 196 | 197 | /* Normalize the clusters. */ 198 | for (int j = 0; j < (int) centers.size(); j++) { 199 | centers[j][0] /= center_counts[j]; 200 | centers[j][1] /= center_counts[j]; 201 | centers[j][2] /= center_counts[j]; 202 | centers[j][3] /= center_counts[j]; 203 | centers[j][4] /= center_counts[j]; 204 | } 205 | } 206 | } 207 | 208 | /* 209 | * Enforce connectivity of the superpixels. This part is not actively discussed 210 | * in the paper, but forms an active part of the implementation of the authors 211 | * of the paper. 212 | * 213 | * Input : The image (IplImage*). 214 | * Output: - 215 | */ 216 | void Slic::create_connectivity(IplImage *image) { 217 | int label = 0, adjlabel = 0; 218 | const int lims = (image->width * image->height) / ((int)centers.size()); 219 | 220 | const int dx4[4] = {-1, 0, 1, 0}; 221 | const int dy4[4] = { 0, -1, 0, 1}; 222 | 223 | /* Initialize the new cluster matrix. */ 224 | vec2di new_clusters; 225 | for (int i = 0; i < image->width; i++) { 226 | vector nc; 227 | for (int j = 0; j < image->height; j++) { 228 | nc.push_back(-1); 229 | } 230 | new_clusters.push_back(nc); 231 | } 232 | 233 | for (int i = 0; i < image->width; i++) { 234 | for (int j = 0; j < image->height; j++) { 235 | if (new_clusters[i][j] == -1) { 236 | vector elements; 237 | elements.push_back(cvPoint(i, j)); 238 | 239 | /* Find an adjacent label, for possible use later. */ 240 | for (int k = 0; k < 4; k++) { 241 | int x = elements[0].x + dx4[k], y = elements[0].y + dy4[k]; 242 | 243 | if (x >= 0 && x < image->width && y >= 0 && y < image->height) { 244 | if (new_clusters[x][y] >= 0) { 245 | adjlabel = new_clusters[x][y]; 246 | } 247 | } 248 | } 249 | 250 | int count = 1; 251 | for (int c = 0; c < count; c++) { 252 | for (int k = 0; k < 4; k++) { 253 | int x = elements[c].x + dx4[k], y = elements[c].y + dy4[k]; 254 | 255 | if (x >= 0 && x < image->width && y >= 0 && y < image->height) { 256 | if (new_clusters[x][y] == -1 && clusters[i][j] == clusters[x][y]) { 257 | elements.push_back(cvPoint(x, y)); 258 | new_clusters[x][y] = label; 259 | count += 1; 260 | } 261 | } 262 | } 263 | } 264 | 265 | /* Use the earlier found adjacent label if a segment size is 266 | smaller than a limit. */ 267 | if (count <= lims >> 2) { 268 | for (int c = 0; c < count; c++) { 269 | new_clusters[elements[c].x][elements[c].y] = adjlabel; 270 | } 271 | label -= 1; 272 | } 273 | label += 1; 274 | } 275 | } 276 | } 277 | } 278 | 279 | /* 280 | * Display the cluster centers. 281 | * 282 | * Input : The image to display upon (IplImage*) and the colour (CvScalar). 283 | * Output: - 284 | */ 285 | void Slic::display_center_grid(IplImage *image, CvScalar colour) { 286 | for (int i = 0; i < (int) centers.size(); i++) { 287 | cvCircle(image, cvPoint(centers[i][3], centers[i][4]), 2, colour, 2); 288 | } 289 | } 290 | 291 | /* 292 | * Display a single pixel wide contour around the clusters. 293 | * 294 | * Input : The target image (IplImage*) and contour colour (CvScalar). 295 | * Output: - 296 | */ 297 | void Slic::display_contours(IplImage *image, CvScalar colour) { 298 | const int dx8[8] = {-1, -1, 0, 1, 1, 1, 0, -1}; 299 | const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1, 1}; 300 | 301 | /* Initialize the contour vector and the matrix detailing whether a pixel 302 | * is already taken to be a contour. */ 303 | vector contours; 304 | vec2db istaken; 305 | for (int i = 0; i < image->width; i++) { 306 | vector nb; 307 | for (int j = 0; j < image->height; j++) { 308 | nb.push_back(false); 309 | } 310 | istaken.push_back(nb); 311 | } 312 | 313 | /* Go through all the pixels. */ 314 | for (int i = 0; i < image->width; i++) { 315 | for (int j = 0; j < image->height; j++) { 316 | int nr_p = 0; 317 | 318 | /* Compare the pixel to its 8 neighbours. */ 319 | for (int k = 0; k < 8; k++) { 320 | int x = i + dx8[k], y = j + dy8[k]; 321 | 322 | if (x >= 0 && x < image->width && y >= 0 && y < image->height) { 323 | if (istaken[x][y] == false && clusters[i][j] != clusters[x][y]) { 324 | nr_p += 1; 325 | } 326 | } 327 | } 328 | 329 | /* Add the pixel to the contour list if desired. */ 330 | if (nr_p >= 2) { 331 | contours.push_back(cvPoint(i,j)); 332 | istaken[i][j] = true; 333 | } 334 | } 335 | } 336 | 337 | /* Draw the contour pixels. */ 338 | for (int i = 0; i < (int)contours.size(); i++) { 339 | cvSet2D(image, contours[i].y, contours[i].x, colour); 340 | } 341 | } 342 | 343 | /* 344 | * Give the pixels of each cluster the same colour values. The specified colour 345 | * is the mean RGB colour per cluster. 346 | * 347 | * Input : The target image (IplImage*). 348 | * Output: - 349 | */ 350 | void Slic::colour_with_cluster_means(IplImage *image) { 351 | vector colours(centers.size()); 352 | 353 | /* Gather the colour values per cluster. */ 354 | for (int i = 0; i < image->width; i++) { 355 | for (int j = 0; j < image->height; j++) { 356 | int index = clusters[i][j]; 357 | CvScalar colour = cvGet2D(image, j, i); 358 | 359 | colours[index].val[0] += colour.val[0]; 360 | colours[index].val[1] += colour.val[1]; 361 | colours[index].val[2] += colour.val[2]; 362 | } 363 | } 364 | 365 | /* Divide by the number of pixels per cluster to get the mean colour. */ 366 | for (int i = 0; i < (int)colours.size(); i++) { 367 | colours[i].val[0] /= center_counts[i]; 368 | colours[i].val[1] /= center_counts[i]; 369 | colours[i].val[2] /= center_counts[i]; 370 | } 371 | 372 | /* Fill in. */ 373 | for (int i = 0; i < image->width; i++) { 374 | for (int j = 0; j < image->height; j++) { 375 | CvScalar ncolour = colours[clusters[i][j]]; 376 | cvSet2D(image, j, i, ncolour); 377 | } 378 | } 379 | } 380 | --------------------------------------------------------------------------------