Use floats for energy

This commit is contained in:
augustin64 2025-03-26 15:15:02 +01:00
parent 3984257c45
commit 1d28489c11

View File

@ -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."<<std::endl;
std::cerr << "Error while exporting the resulting image." << std::endl;
exit(errcode);
}
}
/** e_1 energy */
std::vector<unsigned char> energy_e1(std::vector<unsigned char> source, int width, int height, int nbChannels) {
std::vector<unsigned char> energy(width*height);
/** e_1 energy, energy is always normalized between 0 and 1 */
std::vector<float> energy_e1(std::vector<unsigned char> source, int width, int height, int nbChannels) {
std::vector<float> 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<unsigned char> energy_e1(std::vector<unsigned char> 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<int> optimal_vertical_seam(std::vector<unsigned char> energy, int width, int height) {
std::vector<unsigned char> dyn_energy(width*height);
std::vector<int> optimal_vertical_seam(std::vector<float> energy, int width, int height) {
std::vector<float> 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<int> optimal_vertical_seam(std::vector<unsigned char> 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<int> optimal_vertical_seam(std::vector<unsigned char> energy, int wi
std::vector<int> 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,7 +102,7 @@ std::vector<int> optimal_vertical_seam(std::vector<unsigned char> 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
@ -106,6 +113,9 @@ std::vector<int> optimal_vertical_seam(std::vector<unsigned char> energy, int wi
} 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<int> optimal_vertical_seam(std::vector<unsigned char> energy, int wi
/** Carves an image by one vertical seam. Returns the optimal seam used */
std::vector<int> carving_step_vertical(const std::vector<unsigned char> source, std::vector<unsigned char> &output, int width, int height, int nbChannels) {
std::vector<unsigned char> energy = energy_e1(source, width, height, nbChannels);
std::vector<float> energy = energy_e1(source, width, height, nbChannels);
std::vector<int> opt_seam = optimal_vertical_seam(energy, width, height);
std::vector<bool> 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<unsigned char> ini_energy = energy_e1(source_img, width, height, nbChannels);
std::vector<float> 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;