Does not recompute energy for vertical seams
This commit is contained in:
parent
e7ab4f0bd6
commit
93e14375dd
@ -15,6 +15,7 @@
|
|||||||
// Global flag to silent verbose messages
|
// Global flag to silent verbose messages
|
||||||
bool silent;
|
bool silent;
|
||||||
bool test_energy;
|
bool test_energy;
|
||||||
|
bool energy_recompute_all = false;
|
||||||
|
|
||||||
#define min(a, b) \
|
#define min(a, b) \
|
||||||
{ (a < b ? a : b) }
|
{ (a < b ? a : b) }
|
||||||
@ -39,6 +40,33 @@ void export_image(const char *filename, const void *data, int width, int height,
|
|||||||
exit(errcode);
|
exit(errcode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define compute_energy_for_pixel(source, width, height, i, j, nbChannels, \
|
||||||
|
nbColorChannels, energy) \
|
||||||
|
auto indexPixel = (nbChannels) * (width * (j) + (i)); \
|
||||||
|
auto indexPixel_up = \
|
||||||
|
((j) - 1 > 0) ? (nbChannels) * (width * ((j) - 1) + (i)) : indexPixel; \
|
||||||
|
auto indexPixel_down = ((j) + 1 < height) \
|
||||||
|
? (nbChannels) * (width * ((j) + 1) + (i)) \
|
||||||
|
: indexPixel; \
|
||||||
|
auto indexPixel_left = \
|
||||||
|
((i) - 1 > 0) ? (nbChannels) * (width * (j) + ((i) - 1)) : indexPixel; \
|
||||||
|
auto indexPixel_right = ((i) + 1 < width) \
|
||||||
|
? (nbChannels) * (width * (j) + ((i) + 1)) \
|
||||||
|
: indexPixel; \
|
||||||
|
energy[width * j + i] = 0; \
|
||||||
|
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||||
|
energy[(width) * (j) + (i)] += \
|
||||||
|
(fabs((float)source[indexPixel_up + ch] - source[indexPixel + ch]) + \
|
||||||
|
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])); \
|
||||||
|
} \
|
||||||
|
// Le alpha n'est pas pris en compte dans l'énergie
|
||||||
|
// Here, we use this /ugly/ macro to avoid defining a function that would be way
|
||||||
|
// longer...
|
||||||
|
|
||||||
/** e_1 energy, energy is always normalized between 0 and 1 */
|
/** e_1 energy, energy is always normalized between 0 and 1 */
|
||||||
std::vector<float> energy_e1(std::vector<unsigned char> source, int width,
|
std::vector<float> energy_e1(std::vector<unsigned char> source, int width,
|
||||||
int height, int nbChannels) {
|
int height, int nbChannels) {
|
||||||
@ -48,38 +76,10 @@ std::vector<float> energy_e1(std::vector<unsigned char> source, int width,
|
|||||||
float max_energy = 0;
|
float max_energy = 0;
|
||||||
for (auto i = 0; i < width; ++i) {
|
for (auto i = 0; i < width; ++i) {
|
||||||
for (auto j = 0; j < height; ++j) {
|
for (auto j = 0; j < height; ++j) {
|
||||||
auto indexPixel = nbChannels * (width * j + i);
|
compute_energy_for_pixel(source, width, height, i, j, nbChannels,
|
||||||
auto indexPixel_up =
|
nbColorChannels, energy);
|
||||||
(j - 1 > 0) ? nbChannels * (width * (j - 1) + i) : indexPixel;
|
|
||||||
auto indexPixel_down =
|
|
||||||
(j + 1 < height) ? nbChannels * (width * (j + 1) + i) : indexPixel;
|
|
||||||
auto indexPixel_left =
|
|
||||||
(i - 1 > 0) ? nbChannels * (width * j + (i - 1)) : indexPixel;
|
|
||||||
auto indexPixel_right =
|
|
||||||
(i + 1 < width) ? nbChannels * (width * j + (i + 1)) : indexPixel;
|
|
||||||
energy[width * j + i] = 0;
|
|
||||||
for (auto ch = 0; ch < nbColorChannels;
|
|
||||||
ch++) { // Le alpha n'est pas pris en compte dans l'énergie
|
|
||||||
energy[width * j + i] +=
|
|
||||||
(fabs((float)source[indexPixel_up + ch] - source[indexPixel + ch]) +
|
|
||||||
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]));
|
|
||||||
}
|
|
||||||
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;
|
return energy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,13 +154,14 @@ std::vector<int> optimal_seam(std::vector<float> energy, int width, int height,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_seam(const std::vector<unsigned char> source,
|
template <typename T>
|
||||||
std::vector<unsigned char> &output, int width, int height,
|
void remove_seam(const std::vector<T> source, std::vector<T> &output, int width,
|
||||||
int nbChannels, bool vertical, const std::vector<int> seam) {
|
int height, int nbChannels, bool vertical,
|
||||||
|
const std::vector<int> seam) {
|
||||||
// remove the given seam from the image, the result is in output
|
// remove the given seam from the image, the result is in output
|
||||||
|
// the output must have at least the right size!
|
||||||
int dim_large = vertical ? width : height;
|
int dim_large = vertical ? width : height;
|
||||||
int dim_long = vertical ? height : width;
|
int dim_long = vertical ? height : width;
|
||||||
|
|
||||||
for (auto i = 0; i < dim_long; i++) {
|
for (auto i = 0; i < dim_long; i++) {
|
||||||
int cur_j = 0;
|
int cur_j = 0;
|
||||||
for (auto j = 0; cur_j < dim_large - 1 && j < dim_large; j++) {
|
for (auto j = 0; cur_j < dim_large - 1 && j < dim_large; j++) {
|
||||||
@ -168,7 +169,6 @@ void remove_seam(const std::vector<unsigned char> source,
|
|||||||
int out_pixelIndex = nbChannels * (vertical ? ((width - 1) * i + cur_j)
|
int out_pixelIndex = nbChannels * (vertical ? ((width - 1) * i + cur_j)
|
||||||
: (width * cur_j + i));
|
: (width * cur_j + i));
|
||||||
int src_pixelIndex = nbChannels * im_index(i, j);
|
int src_pixelIndex = nbChannels * im_index(i, j);
|
||||||
|
|
||||||
for (auto ch = 0; ch < nbChannels; ch++)
|
for (auto ch = 0; ch < nbChannels; ch++)
|
||||||
output[out_pixelIndex + ch] = source[src_pixelIndex + ch];
|
output[out_pixelIndex + ch] = source[src_pixelIndex + ch];
|
||||||
cur_j++;
|
cur_j++;
|
||||||
@ -177,13 +177,43 @@ void remove_seam(const std::vector<unsigned char> source,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<int> carving_step(const std::vector<unsigned char> source,
|
std::vector<int> carving_step(const std::vector<unsigned char> source_img,
|
||||||
std::vector<float> energy,
|
std::vector<float> source_energy,
|
||||||
std::vector<unsigned char> &output, int width,
|
std::vector<unsigned char> &output_img,
|
||||||
|
std::vector<float> &output_energy, int width,
|
||||||
int height, int nbChannels, bool vertical) {
|
int height, int nbChannels, bool vertical) {
|
||||||
/** Carves an image by one seam. Returns the optimal seam used */
|
/** Carves an image and its energy by one seam, and recomputes the energy.
|
||||||
std::vector<int> opt_seam = optimal_seam(energy, width, height, vertical);
|
Returns the optimal seam used */
|
||||||
remove_seam(source, output, width, height, nbChannels, vertical, opt_seam);
|
std::vector<int> opt_seam =
|
||||||
|
optimal_seam(source_energy, width, height, vertical);
|
||||||
|
remove_seam(source_img, output_img, width, height, nbChannels, vertical,
|
||||||
|
opt_seam);
|
||||||
|
|
||||||
|
remove_seam(source_energy, output_energy, width, height, 1, vertical,
|
||||||
|
opt_seam);
|
||||||
|
|
||||||
|
// Recompute the energy along the seam
|
||||||
|
if (energy_recompute_all) {
|
||||||
|
std::vector<float> energy =
|
||||||
|
energy_e1(output_img, vertical ? width - 1 : width,
|
||||||
|
vertical ? height : height - 1, nbChannels);
|
||||||
|
std::copy(energy.begin(), energy.end(), output_energy.begin());
|
||||||
|
} else {
|
||||||
|
int nbColorChannels = 3;
|
||||||
|
|
||||||
|
std::cout << "starting to recompute the energy along the seam" << std::endl;
|
||||||
|
// ASSUME WE ARE DOING A VERTICAL SEAM
|
||||||
|
for (auto j = 0; j < height; j++) {
|
||||||
|
for (auto i = -1; i < 2; i++) {
|
||||||
|
if ((0 < (opt_seam[j] + i)) && ((opt_seam[j] + i) < width)) {
|
||||||
|
compute_energy_for_pixel(output_img, (width - 1), height,
|
||||||
|
(opt_seam[j] + i), j, nbChannels,
|
||||||
|
nbColorChannels, output_energy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "DONE" << std::endl;
|
||||||
|
}
|
||||||
return opt_seam;
|
return opt_seam;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,10 +228,14 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
|||||||
int dim_long = vertical ? height : width;
|
int dim_long = vertical ? height : width;
|
||||||
// dim_long=longueur des seam
|
// dim_long=longueur des seam
|
||||||
|
|
||||||
std::vector<unsigned char> carve_output(width * height * nbChannels);
|
|
||||||
// Receives at each step the newly carved image
|
|
||||||
std::vector<unsigned char> source_img(width * height * nbChannels);
|
std::vector<unsigned char> source_img(width * height * nbChannels);
|
||||||
// Contains at each step the carved image
|
// Contains at each step the carved image
|
||||||
|
std::vector<float> source_energy(width * height);
|
||||||
|
// Contains at each step the carved energy
|
||||||
|
std::vector<unsigned char> output_img(width * height * nbChannels);
|
||||||
|
// Receives at each step the newly carved image
|
||||||
|
std::vector<float> output_energy(width * height);
|
||||||
|
// Contains at each step the carved energy
|
||||||
std::vector<bool> complete_blacklist(width * height);
|
std::vector<bool> complete_blacklist(width * height);
|
||||||
// Contains all removed pixels, for "test_energy"
|
// Contains all removed pixels, for "test_energy"
|
||||||
std::vector<float> ini_energy;
|
std::vector<float> ini_energy;
|
||||||
@ -213,6 +247,8 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
|||||||
source_img[i] = source[i];
|
source_img[i] = source[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
source_energy = energy_e1(source_img, width, height, nbChannels);
|
||||||
|
|
||||||
if (test_energy) {
|
if (test_energy) {
|
||||||
ini_energy = energy_e1(source_img, width, height, nbChannels);
|
ini_energy = energy_e1(source_img, width, height, nbChannels);
|
||||||
for (auto k = 0; k < width * height; k++) {
|
for (auto k = 0; k < width * height; k++) {
|
||||||
@ -220,10 +256,21 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
|||||||
}
|
}
|
||||||
|
|
||||||
//* Prepare final output
|
//* Prepare final output
|
||||||
|
|
||||||
|
float max_energy=__FLT_MAX__;
|
||||||
|
for (auto k=0;k<width*height;k++){
|
||||||
|
max_energy=fmax(max_energy, ini_energy[k]);
|
||||||
|
}
|
||||||
|
if (max_energy!=0){
|
||||||
|
for (auto k=0;k<width*height;k++){
|
||||||
|
ini_energy[k]/=max_energy;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (auto k = 0; k < width * height; k++) {
|
for (auto k = 0; k < width * height; k++) {
|
||||||
// for (auto i=0; i < nbColorChannels; i++) //* Uncomment if you prefer to
|
// for (auto i=0; i < nbColorChannels; i++) //* Uncomment if you prefer to
|
||||||
// see darkened source image
|
// see darkened source image
|
||||||
// output[nbChannels*k+i] = source_img[nbChannels*k+i]/nbChannels;
|
// output[nbChannels*k+i] = source_img[nbChannels*k+i]/nbChannels;
|
||||||
|
|
||||||
for (auto i = 0; i < nbColorChannels; i++)
|
for (auto i = 0; i < nbColorChannels; i++)
|
||||||
test_energy_output[nbChannels * k + i] = ini_energy[k] * 255;
|
test_energy_output[nbChannels * k + i] = ini_energy[k] * 255;
|
||||||
|
|
||||||
@ -233,13 +280,13 @@ 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_index = 0; seam_index < nbSeams; seam_index++) {
|
||||||
std::vector<float> energy =
|
|
||||||
energy_e1(source_img, width, height, nbChannels);
|
|
||||||
std::vector<int> opt_seam =
|
std::vector<int> opt_seam =
|
||||||
carving_step(source_img, energy, carve_output, curWidth, curHeight,
|
carving_step(source_img, source_energy, output_img, output_energy,
|
||||||
nbChannels, vertical);
|
curWidth, curHeight, nbChannels, vertical);
|
||||||
std::copy(carve_output.begin(), carve_output.end(), source_img.begin());
|
std::copy(output_img.begin(), output_img.end(), source_img.begin());
|
||||||
|
std::copy(output_energy.begin(), output_energy.end(),
|
||||||
|
source_energy.begin());
|
||||||
|
|
||||||
if (vertical) // We just reduced the dimension
|
if (vertical) // We just reduced the dimension
|
||||||
curWidth--;
|
curWidth--;
|
||||||
@ -247,6 +294,7 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
|||||||
curHeight--;
|
curHeight--;
|
||||||
|
|
||||||
if (test_energy) { // Update blacklist
|
if (test_energy) { // Update blacklist
|
||||||
|
|
||||||
for (auto i = 0; i < dim_long; i++) {
|
for (auto i = 0; i < dim_long; i++) {
|
||||||
int j, cur_j = 0; // cur_j is the index relative to the current carved
|
int j, cur_j = 0; // cur_j is the index relative to the current carved
|
||||||
// image. j is absolute in the source image
|
// image. j is absolute in the source image
|
||||||
@ -263,6 +311,7 @@ void seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
|||||||
255; // Set carved pixel to red
|
255; // Set carved pixel to red
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bar.increment();
|
bar.increment();
|
||||||
bar.print();
|
bar.print();
|
||||||
}
|
}
|
||||||
@ -289,12 +338,15 @@ int main(int argc, char **argv) {
|
|||||||
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");
|
||||||
bool vertical = false;
|
bool vertical = false;
|
||||||
app.add_flag("--vertical", vertical, "Vertical carving");
|
app.add_flag("--vertical", vertical,
|
||||||
|
"Vertical carving (remove vertical seams)");
|
||||||
silent = false;
|
silent = false;
|
||||||
app.add_flag("--silent", silent, "No verbose messages");
|
app.add_flag("--silent", silent, "No verbose messages");
|
||||||
test_energy = false;
|
test_energy = false;
|
||||||
app.add_flag("--test-energy", test_energy,
|
app.add_flag("--test-energy", test_energy,
|
||||||
"Don't resize image, just try the specified energy function");
|
"Don't resize image, just try the specified energy function");
|
||||||
|
app.add_flag("--energy-recompute-all", energy_recompute_all,
|
||||||
|
"recompute the whole energy at each step");
|
||||||
CLI11_PARSE(app, argc, argv);
|
CLI11_PARSE(app, argc, argv);
|
||||||
|
|
||||||
// Image loading
|
// Image loading
|
||||||
@ -304,7 +356,6 @@ int main(int argc, char **argv) {
|
|||||||
nbSeams = min(nbSeams, width);
|
nbSeams = 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,
|
seam_carving(source, width, height, nbChannels, outputImage.c_str(), nbSeams,
|
||||||
vertical, test_energy = test_energy);
|
vertical, test_energy = test_energy);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user