diff --git a/src/seam-carving.cpp b/src/seam-carving.cpp index 511872a..1c90c7a 100644 --- a/src/seam-carving.cpp +++ b/src/seam-carving.cpp @@ -15,6 +15,7 @@ // Global flag to silent verbose messages bool silent; bool test_energy; +bool energy_recompute_all = false; #define min(a, b) \ { (a < b ? a : b) } @@ -39,6 +40,33 @@ void export_image(const char *filename, const void *data, int width, int height, exit(errcode); } } + +#define compute_energy_for_pixel(source, width, height, i, j, nbChannels, \ + nbColorChannels, energy) \ + auto indexPixel = (nbChannels) * (width * (j) + (i)); \ + auto indexPixel_up = \ + ((j) - 1 > 0) ? (nbChannels) * (width * ((j) - 1) + (i)) : indexPixel; \ + auto indexPixel_down = ((j) + 1 < height) \ + ? (nbChannels) * (width * ((j) + 1) + (i)) \ + : indexPixel; \ + auto indexPixel_left = \ + ((i) - 1 > 0) ? (nbChannels) * (width * (j) + ((i) - 1)) : indexPixel; \ + auto indexPixel_right = ((i) + 1 < width) \ + ? (nbChannels) * (width * (j) + ((i) + 1)) \ + : indexPixel; \ + energy[width * j + i] = 0; \ + for (auto ch = 0; ch < (nbColorChannels); ch++) { \ + energy[(width) * (j) + (i)] += \ + (fabs((float)source[indexPixel_up + ch] - source[indexPixel + ch]) + \ + fabs((float)source[indexPixel_down + ch] - source[indexPixel + ch]) + \ + fabs((float)source[indexPixel_left + ch] - source[indexPixel + ch]) + \ + fabs((float)source[indexPixel_right + ch] - \ + source[indexPixel + ch])); \ + } \ +// Le alpha n'est pas pris en compte dans l'énergie +// Here, we use this /ugly/ macro to avoid defining a function that would be way +// longer... + /** e_1 energy, energy is always normalized between 0 and 1 */ std::vector energy_e1(std::vector source, int width, int height, int nbChannels) { @@ -48,38 +76,10 @@ std::vector energy_e1(std::vector source, int width, float max_energy = 0; for (auto i = 0; i < width; ++i) { for (auto j = 0; j < height; ++j) { - auto indexPixel = nbChannels * (width * j + i); - auto indexPixel_up = - (j - 1 > 0) ? nbChannels * (width * (j - 1) + i) : indexPixel; - auto indexPixel_down = - (j + 1 < height) ? nbChannels * (width * (j + 1) + i) : indexPixel; - auto indexPixel_left = - (i - 1 > 0) ? nbChannels * (width * j + (i - 1)) : indexPixel; - auto indexPixel_right = - (i + 1 < width) ? nbChannels * (width * j + (i + 1)) : indexPixel; - energy[width * j + i] = 0; - for (auto ch = 0; ch < nbColorChannels; - ch++) { // Le alpha n'est pas pris en compte dans l'énergie - energy[width * j + i] += - (fabs((float)source[indexPixel_up + ch] - source[indexPixel + ch]) + - fabs((float)source[indexPixel_down + ch] - - source[indexPixel + ch]) + - fabs((float)source[indexPixel_left + ch] - - source[indexPixel + ch]) + - fabs((float)source[indexPixel_right + ch] - - source[indexPixel + ch])); - } - max_energy = fmax(max_energy, energy[width * j + i]); + compute_energy_for_pixel(source, width, height, i, j, nbChannels, + nbColorChannels, energy); } } - - if (max_energy == 0) { - return energy; - } - for (auto k = 0; k < width * height; k++) { - energy[k] = energy[k] / max_energy; - } - return energy; } @@ -154,13 +154,14 @@ std::vector optimal_seam(std::vector energy, int width, int height, return result; } -void remove_seam(const std::vector source, - std::vector &output, int width, int height, - int nbChannels, bool vertical, const std::vector seam) { +template +void remove_seam(const std::vector source, std::vector &output, int width, + int height, int nbChannels, bool vertical, + const std::vector seam) { // remove the given seam from the image, the result is in output + // the output must have at least the right size! int dim_large = vertical ? width : height; int dim_long = vertical ? height : width; - for (auto i = 0; i < dim_long; i++) { int cur_j = 0; for (auto j = 0; cur_j < dim_large - 1 && j < dim_large; j++) { @@ -168,7 +169,6 @@ void remove_seam(const std::vector source, int out_pixelIndex = nbChannels * (vertical ? ((width - 1) * i + cur_j) : (width * cur_j + i)); int src_pixelIndex = nbChannels * im_index(i, j); - for (auto ch = 0; ch < nbChannels; ch++) output[out_pixelIndex + ch] = source[src_pixelIndex + ch]; cur_j++; @@ -177,13 +177,43 @@ void remove_seam(const std::vector source, } } -std::vector carving_step(const std::vector source, - std::vector energy, - std::vector &output, int width, +std::vector carving_step(const std::vector source_img, + std::vector source_energy, + std::vector &output_img, + std::vector &output_energy, int width, int height, int nbChannels, bool vertical) { - /** Carves an image by one seam. Returns the optimal seam used */ - std::vector opt_seam = optimal_seam(energy, width, height, vertical); - remove_seam(source, output, width, height, nbChannels, vertical, opt_seam); + /** Carves an image and its energy by one seam, and recomputes the energy. + Returns the optimal seam used */ + std::vector opt_seam = + optimal_seam(source_energy, width, height, vertical); + remove_seam(source_img, output_img, width, height, nbChannels, vertical, + opt_seam); + + remove_seam(source_energy, output_energy, width, height, 1, vertical, + opt_seam); + + // Recompute the energy along the seam + if (energy_recompute_all) { + std::vector energy = + energy_e1(output_img, vertical ? width - 1 : width, + vertical ? height : height - 1, nbChannels); + std::copy(energy.begin(), energy.end(), output_energy.begin()); + } else { + int nbColorChannels = 3; + + std::cout << "starting to recompute the energy along the seam" << std::endl; + // ASSUME WE ARE DOING A VERTICAL SEAM + for (auto j = 0; j < height; j++) { + for (auto i = -1; i < 2; i++) { + if ((0 < (opt_seam[j] + i)) && ((opt_seam[j] + i) < width)) { + compute_energy_for_pixel(output_img, (width - 1), height, + (opt_seam[j] + i), j, nbChannels, + nbColorChannels, output_energy); + } + } + } + std::cout << "DONE" << std::endl; + } return opt_seam; } @@ -198,10 +228,14 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels, int dim_long = vertical ? height : width; // dim_long=longueur des seam - std::vector carve_output(width * height * nbChannels); - // Receives at each step the newly carved image std::vector source_img(width * height * nbChannels); // Contains at each step the carved image + std::vector source_energy(width * height); + // Contains at each step the carved energy + std::vector output_img(width * height * nbChannels); + // Receives at each step the newly carved image + std::vector output_energy(width * height); + // Contains at each step the carved energy std::vector complete_blacklist(width * height); // Contains all removed pixels, for "test_energy" std::vector ini_energy; @@ -213,6 +247,8 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels, source_img[i] = source[i]; } + source_energy = energy_e1(source_img, width, height, nbChannels); + if (test_energy) { ini_energy = energy_e1(source_img, width, height, nbChannels); for (auto k = 0; k < width * height; k++) { @@ -220,10 +256,21 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels, } //* Prepare final output + + float max_energy=__FLT_MAX__; + for (auto k=0;k energy = - energy_e1(source_img, width, height, nbChannels); + for (auto seam_index = 0; seam_index < nbSeams; seam_index++) { std::vector opt_seam = - carving_step(source_img, energy, carve_output, curWidth, curHeight, - nbChannels, vertical); - std::copy(carve_output.begin(), carve_output.end(), source_img.begin()); + carving_step(source_img, source_energy, output_img, output_energy, + curWidth, curHeight, nbChannels, vertical); + std::copy(output_img.begin(), output_img.end(), source_img.begin()); + std::copy(output_energy.begin(), output_energy.end(), + source_energy.begin()); if (vertical) // We just reduced the dimension curWidth--; @@ -247,6 +294,7 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels, curHeight--; if (test_energy) { // Update blacklist + for (auto i = 0; i < dim_long; i++) { int j, cur_j = 0; // cur_j is the index relative to the current carved // image. j is absolute in the source image @@ -263,6 +311,7 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels, 255; // Set carved pixel to red } } + bar.increment(); bar.print(); } @@ -289,12 +338,15 @@ int main(int argc, char **argv) { int nbSeams = 1; app.add_option("-n,--nb-seams", nbSeams, "Number of seams"); bool vertical = false; - app.add_flag("--vertical", vertical, "Vertical carving"); + app.add_flag("--vertical", vertical, + "Vertical carving (remove vertical seams)"); silent = false; app.add_flag("--silent", silent, "No verbose messages"); test_energy = false; app.add_flag("--test-energy", test_energy, "Don't resize image, just try the specified energy function"); + app.add_flag("--energy-recompute-all", energy_recompute_all, + "recompute the whole energy at each step"); CLI11_PARSE(app, argc, argv); // Image loading @@ -304,7 +356,6 @@ int main(int argc, char **argv) { nbSeams = min(nbSeams, width); // std::cout << "channels: " << nbChannels << std::endl; - seam_carving(source, width, height, nbChannels, outputImage.c_str(), nbSeams, vertical, test_energy = test_energy);