diff --git a/src/seam-carving.cpp b/src/seam-carving.cpp index 4817c2d..48aaa4b 100644 --- a/src/seam-carving.cpp +++ b/src/seam-carving.cpp @@ -23,16 +23,17 @@ void export_image(const char* filename, const void* data, int width, int height, if (!silent) std::cout << "Exporting to \"" << filename << "\".." << std::endl; int errcode = stbi_write_png(filename, width, height, nbChannels, data, nbChannels*width); if (!errcode) { - std::cout<<"Error while exporting the resulting image."< energy_e1(std::vector source, int width, int height, int nbChannels) { - std::vector energy(width*height); +/** e_1 energy, energy is always normalized between 0 and 1 */ +std::vector energy_e1(std::vector source, int width, int height, int nbChannels) { + std::vector energy(width*height); - for(auto i=0 ; i < width ; ++i) { + 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; @@ -47,17 +48,23 @@ std::vector energy_e1(std::vector source, int widt +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]) - )/(nbChannels*4); + ); } + max_energy = fmax(max_energy, energy[width*j+i]); } } + if (max_energy == 0) { return energy; } + for (auto k=0; k < width*height; k++) { + energy[k] = energy[k]/max_energy; + } + return energy; } /** Given the energy value, returns the optimal vertical seam */ -std::vector optimal_vertical_seam(std::vector energy, int width, int height) { - std::vector dyn_energy(width*height); +std::vector optimal_vertical_seam(std::vector energy, int width, int height) { + std::vector dyn_energy(width*height); //* Find an end of the minimal connected vertical/horizontal seam for (auto i=1; i < height; i++) { @@ -66,13 +73,13 @@ std::vector optimal_vertical_seam(std::vector energy, int wi for (auto j=1; j < width; j++) { for (auto i=0; i < height; i++) { - int bot_center = (i > 0) ? dyn_energy[width*(i-1)+j] : INT_MAX; - int bot_left = (i > 0 && j > 0) ? dyn_energy[width*(i-1)+(j-1)] : INT_MAX; - int bot_right = (i > 0 && j+1 < width) ? dyn_energy[width*(i-1)+(j+1)] : INT_MAX; + 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__; - dyn_energy[width*i+j] = min( + dyn_energy[width*i+j] = fmin( bot_center, - min( + fmin( bot_left, bot_right ) @@ -83,7 +90,7 @@ std::vector optimal_vertical_seam(std::vector energy, int wi std::vector result(height); // Find the seam end int min_idx = -1; - int min_val = INT_MAX; + float min_val = __FLT_MAX__; for (auto j=0; j < width; j++) { if (min_val > dyn_energy[width*(height-1)+j]) { min_idx = j; @@ -95,17 +102,20 @@ std::vector optimal_vertical_seam(std::vector energy, int wi //* 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] - int objective_energy = min_val - energy[i*width+min_idx]; + float objective_energy = min_val - energy[i*width+min_idx]; //! With float, do we always have x + y - y = x ? - 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)]; + 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; } @@ -116,7 +126,7 @@ std::vector optimal_vertical_seam(std::vector energy, int wi /** Carves an image by one vertical seam. Returns the optimal seam used */ std::vector carving_step_vertical(const std::vector source, std::vector &output, int width, int height, int nbChannels) { - std::vector energy = energy_e1(source, width, height, nbChannels); + std::vector energy = energy_e1(source, width, height, nbChannels); std::vector opt_seam = optimal_vertical_seam(energy, width, height); std::vector blacklist(width*height); @@ -175,16 +185,16 @@ int main(int argc, char **argv) { for (auto k=0; k < width*height; k++) { complete_blacklist[k] = false; } for (auto i=0; i < width*height*nbChannels; i++) { source_img[i] = source[i]; } - std::vector ini_energy = energy_e1(source_img, width, height, nbChannels); + std::vector ini_energy = energy_e1(source_img, width, height, nbChannels); //* Prepare final output for (auto k=0; k < width*height; k++) { //output[3*k] = source_img[3*k]/3; //* Uncomment if you prefer to see darkened source image //output[3*k+1] = source_img[3*k+1]/3; //output[3*k+2] = source_img[3*k+2]/3; - output[3*k] = ini_energy[k]; - output[3*k+1] = ini_energy[k]; - output[3*k+2] = ini_energy[k]; + output[3*k] = ini_energy[k]*255; + output[3*k+1] = ini_energy[k]*255; + output[3*k+2] = ini_energy[k]*255; } int curWidth = width;