Content-Aware-Image-Resizer / src / processing.cpp
processing.cpp
Raw
#include <cassert>
#include "processing.h"

using namespace std;

// REQUIRES: img points to a valid Image
// MODIFIES: *img
// EFFECTS:  The image is rotated 90 degrees to the left (counterclockwise).
void rotate_left(Image* img) {

  // for convenience
  int width = Image_width(img);
  int height = Image_height(img);

  // auxiliary image to temporarily store rotated image
  Image *aux = new Image;
  Image_init(aux, height, width); // width and height switched

  // iterate through pixels and place each where it goes in temp
  for (int r = 0; r < height; ++r) {
    for (int c = 0; c < width; ++c) {
      Image_set_pixel(aux, width - 1 - c, r, Image_get_pixel(img, r, c));
    }
  }

  // Copy data back into original
  *img = *aux;
  delete aux;
}

// REQUIRES: img points to a valid Image.
// MODIFIES: *img
// EFFECTS:  The image is rotated 90 degrees to the right (clockwise).
void rotate_right(Image* img){

  // for convenience
  int width = Image_width(img);
  int height = Image_height(img);

  // auxiliary image to temporarily store rotated image
  Image *aux = new Image;
  Image_init(aux, height, width); // width and height switched

  // iterate through pixels and place each where it goes in temp
  for (int r = 0; r < height; ++r) {
    for (int c = 0; c < width; ++c) {
      Image_set_pixel(aux, c, height - 1 - r, Image_get_pixel(img, r, c));
    }
  }

  // Copy data back into original
  *img = *aux;
  delete aux;
}

static int squared_difference(Pixel p1, Pixel p2) {
  int dr = p2.r - p1.r;
  int dg = p2.g - p1.g;
  int db = p2.b - p1.b;
  // Divide by 100 is to avoid possible overflows
  // later on in the algorithm.
  return (dr*dr + dg*dg + db*db) / 100;
}

// REQUIRES: img points to a valid Image.
//           energy points to a Matrix.
// MODIFIES: *energy
// EFFECTS:  energy serves as an "output parameter".
//           The Matrix pointed to by energy is initialized to be the same
//           size as the given Image, and then the energy matrix for that
//           image is computed and written into it.

void compute_energy_matrix(const Image* img, Matrix* energy) {
    Matrix_init(energy, Image_width(img), Image_height(img));
    Matrix_fill(energy, 0);
    for (int r = 1; r < Matrix_height(energy) - 1; r++) {
        for (int c = 1; c < Matrix_width(energy) - 1; c++)  {
            Pixel pn = Image_get_pixel(img, r - 1, c);
            Pixel ps = Image_get_pixel(img, r + 1, c);
            Pixel pw = Image_get_pixel(img, r, c - 1);
            Pixel pe = Image_get_pixel(img, r, c + 1);
            *Matrix_at(energy, r, c) = 
              squared_difference(pn, ps) + squared_difference(pw, pe);
        }
    }
    Matrix_fill_border(energy, Matrix_max(energy));
}


// REQUIRES: energy points to a valid Matrix.
//           cost points to a Matrix.
//           energy and cost aren't pointing to the same Matrix
// MODIFIES: *cost
// EFFECTS:  cost serves as an "output parameter".
//           The Matrix pointed to by cost is initialized to be the same
//           size as the given energy Matrix, and then the cost matrix is
//           computed and written into it.

void compute_vertical_cost_matrix(const Matrix* energy, Matrix *cost) {
    Matrix_init(cost, Matrix_width(energy), Matrix_height(energy));
    for (int c = 0; c < Matrix_width(cost); c++)   {
        *Matrix_at(cost, 0, c) = *Matrix_at(energy, 0, c);
    }
    for (int r = 1; r < Matrix_height(cost); r++)  {
        for (int c = 0; c < Matrix_width(cost); c++)   {
            if (c == 0) {
                *Matrix_at(cost, r, c) = *Matrix_at(energy, r, c) 
                  + Matrix_min_value_in_row(cost, r - 1, c, c + 2);
            }
            else if (c == Matrix_width(cost) - 1)   {
                *Matrix_at(cost, r, c) = *Matrix_at(energy, r, c) 
                  + Matrix_min_value_in_row(cost, r - 1, c - 1, c + 1);
            }
            else    {
                *Matrix_at(cost, r, c) = *Matrix_at(energy, r, c) 
                  + Matrix_min_value_in_row(cost, r - 1, c - 1, c + 2);
            }
        }
    }
}


// REQUIRES: cost points to a valid Matrix
//           seam points to an array
//           the size of seam is >= Matrix_height(cost)
// MODIFIES: seam[0]...seam[Matrix_height(cost)-1]
// EFFECTS:  seam serves as an "output parameter".
//           The vertical seam with the minimal cost according to the given
//           cost matrix is found and the seam array is filled with the column
//           numbers for each pixel along the seam, with the pixel for each
//           row r placed at seam[r]. While determining the seam, if any pixels
//           tie for lowest cost, the leftmost one (i.e. with the lowest
//           column number) is used.

void find_minimal_vertical_seam(const Matrix* cost, int seam[]) {
    int i = Matrix_height(cost) - 1;
    seam[i] = Matrix_column_of_min_value_in_row
      (cost, Matrix_height(cost) - 1, 0, Matrix_width(cost) - 1);
    i -= 1; 
    for (int r = Matrix_height(cost) - 2; r >= 0; r--)  {
        int c = seam[i + 1];
        if (c == 0)  {
            seam[i] = Matrix_column_of_min_value_in_row(cost, r, c, c + 2);
            i -= 1;
        }
        else if (c == Matrix_width(cost) - 1) {
            seam[i] = Matrix_column_of_min_value_in_row(cost, r, c - 1, c + 1);
            i -= 1;
        }
        else    {
            seam[i] = Matrix_column_of_min_value_in_row(cost, r, c - 1, c + 2);
            i -= 1;
        }
    }
}


// REQUIRES: img points to a valid Image with width >= 2
//           seam points to an array
//           the size of seam is == Image_height(img)
//           each element x in seam satisfies 0 <= x < Image_width(img)
// MODIFIES: *img
// EFFECTS:  Removes the given vertical seam from the Image. That is, one
//           pixel will be removed from every row in the image. The pixel
//           removed from row r will be the one with column equal to seam[r].
//           The width of the image will be one less than before.
/
void remove_vertical_seam(Image *img, const int seam[]) {
    Image *aux_img = new Image;
    Image_init(aux_img, Image_width(img) - 1, Image_height(img));
    for (int r = 0; r < Image_height(aux_img); r++) {
        for (int c = 0; c < Image_width(aux_img); c++)  {
            if (c < seam[r])    {
              Image_set_pixel(aux_img, r, c, Image_get_pixel(img, r, c));
            }
            if (c >= seam[r])   {
              Image_set_pixel(aux_img, r, c, Image_get_pixel(img, r, c + 1));
            }
        }
    }
    *img = *aux_img;
    delete aux_img;
}


// REQUIRES: img points to a valid Image
//           0 < newWidth && newWidth <= Image_width(img)
// MODIFIES: *img
// EFFECTS:  Reduces the width of the given Image to be newWidth by using
//           the seam carving algorithm. 

void seam_carve_width(Image *img, int newWidth) {
  assert(0 < newWidth && newWidth <= Image_width(img));

  Matrix *energy = new Matrix;
  Matrix *cost = new Matrix;
  int *seam = new int[Image_height(img)];

  while (Image_width(img) > newWidth) {
    compute_energy_matrix(img, energy);
    compute_vertical_cost_matrix(energy, cost);
    find_minimal_vertical_seam(cost, seam);
    remove_vertical_seam(img, seam);
  }

  delete energy;
  delete cost;
  delete[] seam;
}

// REQUIRES: img points to a valid Image
//           0 < newHeight && newHeight <= Image_height(img)
// MODIFIES: *img
// EFFECTS:  Reduces the height of the given Image to be newHeight.

void seam_carve_height(Image *img, int newHeight) {
  assert(0 < newHeight && newHeight <= Image_height(img));

  rotate_left(img);
  seam_carve_width(img, newHeight);
  rotate_right(img);
}

// REQUIRES: img points to a valid Image
//           0 < newWidth && newWidth <= Image_width(img)
//           0 < newHeight && newHeight <= Image_height(img)
// MODIFIES: *img
// EFFECTS:  Reduces the width and height of the given Image to be newWidth
//           and newHeight, respectively.

void seam_carve(Image *img, int newWidth, int newHeight) {
  assert(0 < newWidth && newWidth <= Image_width(img));
  assert(0 < newHeight && newHeight <= Image_height(img));

  seam_carve_width(img, newWidth);
  seam_carve_height(img, newHeight);
}