diff --git a/src/seam-carving.cpp b/src/seam-carving.cpp index 48aaa4b..58fd6ea 100644 --- a/src/seam-carving.cpp +++ b/src/seam-carving.cpp @@ -19,6 +19,11 @@ int min(int a, int b) { return a < b ? a : b; } +bool nearly_equal(float a, float b) { + return std::nextafter(a, std::numeric_limits::lowest()) <= b + && std::nextafter(a, std::numeric_limits::max()) >= b; +} + void export_image(const char* filename, const void* data, int width, int height, int nbChannels) { if (!silent) std::cout << "Exporting to \"" << filename << "\".." << std::endl; int errcode = stbi_write_png(filename, width, height, nbChannels, data, nbChannels*width); @@ -67,15 +72,15 @@ std::vector optimal_vertical_seam(std::vector energy, int width, int std::vector dyn_energy(width*height); //* Find an end of the minimal connected vertical/horizontal seam - for (auto i=1; i < height; i++) { - dyn_energy[width*i] = energy[width*i]; + for (auto i=0; i < width; i++) { + dyn_energy[i] = energy[i]; } - for (auto j=1; j < width; j++) { - for (auto i=0; i < height; i++) { - float bot_center = (i > 0) ? dyn_energy[width*(i-1)+j] : __FLT_MAX__; - float bot_left = (i > 0 && j > 0) ? dyn_energy[width*(i-1)+(j-1)] : __FLT_MAX__; - float bot_right = (i > 0 && j+1 < width) ? dyn_energy[width*(i-1)+(j+1)] : __FLT_MAX__; + for (auto i=1; i < height; i++) { + for (auto j=0; j < width; j++) { + float bot_center = dyn_energy[width*(i-1) + j]; + float bot_left = (j > 0) ? dyn_energy[width*(i-1) + (j-1)] : __FLT_MAX__; + float bot_right = (j+1 < width) ? dyn_energy[width*(i-1) + (j+1)] : __FLT_MAX__; dyn_energy[width*i+j] = fmin( bot_center, @@ -83,7 +88,7 @@ std::vector optimal_vertical_seam(std::vector energy, int width, int bot_left, bot_right ) - ) + energy[width*i+j]; + ) + energy[width*i + j]; } } @@ -92,9 +97,9 @@ std::vector optimal_vertical_seam(std::vector energy, int width, int int min_idx = -1; float min_val = __FLT_MAX__; for (auto j=0; j < width; j++) { - if (min_val > dyn_energy[width*(height-1)+j]) { + if (min_val > dyn_energy[width*(height-1) + j]) { min_idx = j; - min_val = dyn_energy[width*(height-1)+j]; + min_val = dyn_energy[width*(height-1) + j]; } } result[height-1] = min_idx; @@ -102,22 +107,25 @@ std::vector optimal_vertical_seam(std::vector energy, int width, int //* Backtracking to find the path for (auto i=height-1; i > 0; i--) { // We want to find either (bot_l, bot_c, bot_r) with dyn_energy[.] = min_val - energy[cur] - float objective_energy = min_val - energy[i*width+min_idx]; //! With float, do we always have x + y - y = x ? + float objective_energy = min_val - energy[width*i + min_idx]; - if (dyn_energy[(i-1)+height*min_idx] == objective_energy) { - // min_idx does not change - min_val = dyn_energy[(i-1)+height*min_idx]; - } else if (min_idx > 0 && dyn_energy[(i-1)+height*(min_idx-1)] == objective_energy) { - min_val = dyn_energy[(i-1)+height*(min_idx - 1)]; - min_idx = min_idx - 1; - } else if (min_idx+1 < width && dyn_energy[(i-1)+height*(min_idx+1)] == objective_energy) { - min_val = dyn_energy[(i-1)+height*(min_idx + 1)]; - min_idx = min_idx + 1; - } else { - std::cerr << "Unable to backtrack path !" << std::endl; - exit(1); - } - result[i-1] = min_idx; + //! With float, we don't always have x + y - y = x + if (nearly_equal(dyn_energy[width*(i-1) + min_idx], objective_energy)) { + // min_idx does not change + min_val = dyn_energy[(i-1)*width + min_idx]; + } else if (min_idx > 0 && nearly_equal(dyn_energy[(i-1)*width + (min_idx-1)], objective_energy)) { + min_val = dyn_energy[(i-1)*width + (min_idx-1)]; + min_idx = min_idx - 1; + } else if (min_idx+1 < width && nearly_equal(dyn_energy[(i-1)*width + (min_idx+1)], objective_energy)) { + min_val = dyn_energy[(i-1)*width + (min_idx+1)]; + min_idx = min_idx + 1; + } else { + std::cerr << dyn_energy[(i-1)*width + min_idx] <<", "<< dyn_energy[(i-1)*width + (min_idx-1)] + <<", "<< dyn_energy[(i-1)*width + (min_idx+1)] <<" != "<< objective_energy << std::endl; + std::cerr << "Unable to backtrack path !" << std::endl; + exit(1); + } + result[i-1] = min_idx; } return result;