Add max_step argument

This commit is contained in:
augustin64 2025-04-01 15:14:20 +02:00
parent b99ee153f0
commit 4049dc4510

View File

@ -16,7 +16,6 @@
bool silent; bool silent;
bool test_energy; 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 // Get index for any table indexed by [width*(i : height) + (j : width)], but a : dim_long, b : dim_large
#define im_index(a, b) \ #define im_index(a, b) \
@ -60,7 +59,7 @@ std::vector<float> energy_e1(std::vector<unsigned char> source, int width, int h
+fabs((float)source[indexPixel_right+ch] - source[indexPixel+ch]) +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<float> energy_e1(std::vector<unsigned char> source, int width, int h
} }
/** Given the energy value, returns the optimal seam */ /** Given the energy value, returns the optimal seam */
std::vector<int> optimal_seam(std::vector<float> energy, int width, int height, bool vertical) { std::vector<int> optimal_seam(std::vector<float> energy, int width, int height, bool vertical, int max_step=1) {
// dyn_energy is indexed by [dim_large*(i : dim_long) + (j : dim_large)] // dyn_energy is indexed by [dim_large*(i : dim_long) + (j : dim_large)]
std::vector<float> dyn_energy(width*height); std::vector<float> dyn_energy(width*height);
@ -87,17 +86,18 @@ std::vector<int> optimal_seam(std::vector<float> energy, int width, int height,
for (auto i=1; i < dim_long; i++) { // Propagate dyn_energy for (auto i=1; i < dim_long; i++) { // Propagate dyn_energy
for (auto j=0; j < dim_large; j++) { for (auto j=0; j < dim_large; j++) {
float bot_center = dyn_energy[dim_large*(i-1) + j]; dyn_energy[dim_large*i+j] = __FLT_MAX__;
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] = fmin( int lower_bound = std::max(j - max_step, 0);
bot_center, int upper_bound = std::min(j+max_step, dim_large-1);
fmin(
bot_left, for (auto k=lower_bound; k <= upper_bound; k++) { // Compute energy based on predecessors
bot_right dyn_energy[dim_large*i+j] = std::min(
) dyn_energy[dim_large*i+j],
) + energy[im_index(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<int> optimal_seam(std::vector<float> energy, int width, int height,
#define is_next_idx(idx) \ #define is_next_idx(idx) \
(dyn_energy[(i-1)*dim_large + idx]+energy[im_index(i, min_idx)] == min_val) (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)) { if (is_next_idx(min_idx)) {
// min_idx does not change min_val = dyn_energy[(i-1)*dim_large+min_idx];
min_val = dyn_energy[(i-1)*dim_large + min_idx]; found = true;
} else if (min_idx > 0 && is_next_idx(min_idx-1)) { }
min_val = dyn_energy[(i-1)*dim_large + (min_idx-1)]; for (auto k=1; !found && k <= max_step; k++) {
min_idx = min_idx - 1; if (min_idx+k < dim_large && is_next_idx(min_idx+k)) {
} else if (min_idx+1 < dim_large && is_next_idx(min_idx+1)) { min_val = dyn_energy[(i-1)*dim_large+min_idx+k];
min_val = dyn_energy[(i-1)*dim_large + (min_idx+1)]; min_idx = min_idx+k;
min_idx = min_idx + 1;
} else { 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; std::cerr << "Unable to backtrack path !" << std::endl;
exit(1); exit(1);
} }
@ -143,9 +153,9 @@ std::vector<int> optimal_seam(std::vector<float> energy, int width, int height,
} }
/** Carves an image by one seam. Returns the optimal seam used */ /** Carves an image by one seam. Returns the optimal seam used */
std::vector<int> carving_step(const std::vector<unsigned char> source, std::vector<unsigned char> &output, int width, int height, int nbChannels, bool vertical) { std::vector<int> carving_step(const std::vector<unsigned char> source, std::vector<unsigned char> &output, int width, int height, int nbChannels, bool vertical, int max_step=1) {
std::vector<float> energy = energy_e1(source, width, height, nbChannels); std::vector<float> energy = energy_e1(source, width, height, nbChannels);
std::vector<int> opt_seam = optimal_seam(energy, width, height, vertical); std::vector<int> opt_seam = optimal_seam(energy, width, height, vertical, max_step=max_step);
std::vector<bool> blacklist(width*height); std::vector<bool> blacklist(width*height);
int dim_large = vertical ? width : height; int dim_large = vertical ? width : height;
@ -174,7 +184,7 @@ std::vector<int> carving_step(const std::vector<unsigned char> 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 nbColorChannels = nbChannels > 3 ? 3 : nbChannels;
int curWidth = width; int curWidth = width;
int curHeight = height; int curHeight = height;
@ -209,7 +219,7 @@ void seam_carving(unsigned char* source, int width, int height, int nbChannels,
SimpleProgressBar::ProgressBar bar(nbSeams); SimpleProgressBar::ProgressBar bar(nbSeams);
for (auto seam=0; seam < nbSeams; seam++) { for (auto seam=0; seam < nbSeams; seam++) {
std::vector<int> opt_seam = carving_step(source_img, carve_output, curWidth, curHeight, nbChannels, vertical); std::vector<int> 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()); std::copy(carve_output.begin(), carve_output.end(), source_img.begin());
if (vertical) // We just reduced the dimension 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(); app.add_option("-o,--output", outputImage, "Output image")->required();
int nbSeams = 1; int nbSeams = 1;
app.add_option("-n,--nb-seams", nbSeams, "Number of seams"); 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; bool vertical = false;
app.add_flag("--vertical", vertical, "Vertical carving"); app.add_flag("--vertical", vertical, "Vertical carving");
silent = false; silent = false;
@ -261,11 +273,11 @@ int main(int argc, char **argv) {
//Image loading //Image loading
int width, height, nbChannels; int width, height, nbChannels;
unsigned char *source = stbi_load(sourceImage.c_str(), &width, &height, &nbChannels, 0); 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; //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); stbi_image_free(source);
exit(0); exit(0);