From 4049dc4510e4b8faefde0f0ba86eacf8762dbddd Mon Sep 17 00:00:00 2001 From: augustin64 Date: Tue, 1 Apr 2025 15:14:20 +0200 Subject: [PATCH] Add max_step argument --- src/seam-carving.cpp | 68 ++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/src/seam-carving.cpp b/src/seam-carving.cpp index 77abee8..e08fce6 100644 --- a/src/seam-carving.cpp +++ b/src/seam-carving.cpp @@ -16,7 +16,6 @@ bool silent; bool test_energy; -#define min(a, b) { (a < b ? a : b) } // Get index for any table indexed by [width*(i : height) + (j : width)], but a : dim_long, b : dim_large #define im_index(a, b) \ @@ -60,7 +59,7 @@ std::vector energy_e1(std::vector source, int width, int h +fabs((float)source[indexPixel_right+ch] - source[indexPixel+ch]) ); } - max_energy = fmax(max_energy, energy[width*j+i]); + max_energy = std::max(max_energy, energy[width*j+i]); } } @@ -73,7 +72,7 @@ std::vector energy_e1(std::vector source, int width, int h } /** Given the energy value, returns the optimal seam */ -std::vector optimal_seam(std::vector energy, int width, int height, bool vertical) { +std::vector optimal_seam(std::vector energy, int width, int height, bool vertical, int max_step=1) { // dyn_energy is indexed by [dim_large*(i : dim_long) + (j : dim_large)] std::vector dyn_energy(width*height); @@ -87,17 +86,18 @@ std::vector optimal_seam(std::vector energy, int width, int height, for (auto i=1; i < dim_long; i++) { // Propagate dyn_energy for (auto j=0; j < dim_large; j++) { - float bot_center = dyn_energy[dim_large*(i-1) + j]; - float bot_left = (j > 0) ? dyn_energy[dim_large*(i-1) + (j-1)] : __FLT_MAX__; - float bot_right = (j+1 < dim_large) ? dyn_energy[dim_large*(i-1) + (j+1)] : __FLT_MAX__; + dyn_energy[dim_large*i+j] = __FLT_MAX__; - dyn_energy[dim_large*i+j] = fmin( - bot_center, - fmin( - bot_left, - bot_right - ) - ) + energy[im_index(i, j)]; + int lower_bound = std::max(j - max_step, 0); + int upper_bound = std::min(j+max_step, dim_large-1); + + for (auto k=lower_bound; k <= upper_bound; k++) { // Compute energy based on predecessors + dyn_energy[dim_large*i+j] = std::min( + dyn_energy[dim_large*i+j], + dyn_energy[dim_large*(i-1)+k] + ); + } + dyn_energy[dim_large*i+j] += energy[im_index(i, j)]; } } @@ -123,16 +123,26 @@ std::vector optimal_seam(std::vector energy, int width, int height, #define is_next_idx(idx) \ (dyn_energy[(i-1)*dim_large + idx]+energy[im_index(i, min_idx)] == min_val) + // This is not the nicest way to do thiss but we want to check in priority at the center to have straight seams + bool found = false; if (is_next_idx(min_idx)) { - // min_idx does not change - min_val = dyn_energy[(i-1)*dim_large + min_idx]; - } else if (min_idx > 0 && is_next_idx(min_idx-1)) { - min_val = dyn_energy[(i-1)*dim_large + (min_idx-1)]; - min_idx = min_idx - 1; - } else if (min_idx+1 < dim_large && is_next_idx(min_idx+1)) { - min_val = dyn_energy[(i-1)*dim_large + (min_idx+1)]; - min_idx = min_idx + 1; - } else { + min_val = dyn_energy[(i-1)*dim_large+min_idx]; + found = true; + } + for (auto k=1; !found && k <= max_step; k++) { + if (min_idx+k < dim_large && is_next_idx(min_idx+k)) { + min_val = dyn_energy[(i-1)*dim_large+min_idx+k]; + min_idx = min_idx+k; + + found = true; + } else if (min_idx-k >= 0 && is_next_idx(min_idx-k)) { + min_val = dyn_energy[(i-1)*dim_large+min_idx-k]; + min_idx = min_idx-k; + + found = true; + } + } + if (!found) { std::cerr << "Unable to backtrack path !" << std::endl; exit(1); } @@ -143,9 +153,9 @@ std::vector optimal_seam(std::vector energy, int width, int height, } /** Carves an image by one seam. Returns the optimal seam used */ -std::vector carving_step(const std::vector source, std::vector &output, int width, int height, int nbChannels, bool vertical) { +std::vector carving_step(const std::vector source, std::vector &output, int width, int height, int nbChannels, bool vertical, int max_step=1) { std::vector energy = energy_e1(source, width, height, nbChannels); - std::vector opt_seam = optimal_seam(energy, width, height, vertical); + std::vector opt_seam = optimal_seam(energy, width, height, vertical, max_step=max_step); std::vector blacklist(width*height); int dim_large = vertical ? width : height; @@ -174,7 +184,7 @@ std::vector carving_step(const std::vector source, std::vect } -void seam_carving(unsigned char* source, int width, int height, int nbChannels, const char* out_filename, int nbSeams, bool vertical, bool test_energy=false) { +void seam_carving(unsigned char* source, int width, int height, int nbChannels, const char* out_filename, int nbSeams, bool vertical, bool test_energy=false, int max_step=1) { int nbColorChannels = nbChannels > 3 ? 3 : nbChannels; int curWidth = width; int curHeight = height; @@ -209,7 +219,7 @@ void seam_carving(unsigned char* source, int width, int height, int nbChannels, SimpleProgressBar::ProgressBar bar(nbSeams); for (auto seam=0; seam < nbSeams; seam++) { - std::vector opt_seam = carving_step(source_img, carve_output, curWidth, curHeight, nbChannels, vertical); + std::vector opt_seam = carving_step(source_img, carve_output, curWidth, curHeight, nbChannels, vertical, max_step=max_step); std::copy(carve_output.begin(), carve_output.end(), source_img.begin()); if (vertical) // We just reduced the dimension @@ -250,6 +260,8 @@ int main(int argc, char **argv) { app.add_option("-o,--output", outputImage, "Output image")->required(); int nbSeams = 1; app.add_option("-n,--nb-seams", nbSeams, "Number of seams"); + int max_step = 1; + app.add_option("--max-step", max_step, "Max width of step to find a seam"); bool vertical = false; app.add_flag("--vertical", vertical, "Vertical carving"); silent = false; @@ -261,11 +273,11 @@ int main(int argc, char **argv) { //Image loading int width, height, nbChannels; unsigned char *source = stbi_load(sourceImage.c_str(), &width, &height, &nbChannels, 0); - nbSeams = min(nbSeams, width); + nbSeams = std::min(nbSeams, width); //std::cout << "channels: " << nbChannels << std::endl; - seam_carving(source, width, height, nbChannels, outputImage.c_str(), nbSeams, vertical, test_energy=test_energy); + seam_carving(source, width, height, nbChannels, outputImage.c_str(), nbSeams, vertical, test_energy=test_energy, max_step=max_step); stbi_image_free(source); exit(0);