format
This commit is contained in:
parent
739d45e7cc
commit
f6e45e62b2
@ -1,5 +1,5 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -7,9 +7,9 @@
|
||||
#include <CLI11.hpp>
|
||||
|
||||
// Image filtering and I/O
|
||||
#include <SimpleProgressBar.hpp>
|
||||
#include "utils.hpp"
|
||||
#include "image.h"
|
||||
#include "utils.hpp"
|
||||
#include <SimpleProgressBar.hpp>
|
||||
|
||||
#define DEFAULT_SEAMS 1
|
||||
|
||||
@ -23,7 +23,6 @@ int max_step = 1;
|
||||
// : dim_long, b : dim_large
|
||||
#define im_index(a, b) (vertical ? (width * a + b) : (width * b + a))
|
||||
|
||||
|
||||
void export_image(const char *filename, const void *data, int width, int height,
|
||||
int nbChannels) {
|
||||
if (!silent)
|
||||
@ -38,8 +37,9 @@ void export_image(const char *filename, const void *data, int width, int height,
|
||||
|
||||
std::string function = "grad";
|
||||
|
||||
#define compute_energy_for_pixel(source, width, height, i, j, nbChannels, \
|
||||
/* computes the energy at pixel i,j, i.e. energy[width*j+i]*/ \
|
||||
#define compute_energy_for_pixel( \
|
||||
source, width, height, i, j, \
|
||||
nbChannels, /* computes the energy at pixel i,j, i.e. energy[width*j+i]*/ \
|
||||
nbColorChannels, dest) \
|
||||
auto indexPixel = (nbChannels) * (width * (j) + (i)); \
|
||||
auto indexPixel_up = \
|
||||
@ -55,8 +55,7 @@ std::string function = "grad";
|
||||
dest = 0; \
|
||||
if (function == "gradnorm") { \
|
||||
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||
dest+= \
|
||||
(std::pow(fabs((float)source[indexPixel_up + ch] - \
|
||||
dest += (std::pow(fabs((float)source[indexPixel_up + ch] - \
|
||||
source[indexPixel + ch]), \
|
||||
2) + \
|
||||
\
|
||||
@ -73,8 +72,7 @@ std::string function = "grad";
|
||||
} \
|
||||
} else if (function == "grad") { \
|
||||
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||
dest += \
|
||||
((fabs((float)source[indexPixel_up + ch] - \
|
||||
dest += ((fabs((float)source[indexPixel_up + ch] - \
|
||||
source[indexPixel + ch])) + \
|
||||
\
|
||||
(fabs((float)source[indexPixel_down + ch] - \
|
||||
@ -87,8 +85,7 @@ std::string function = "grad";
|
||||
} \
|
||||
} else if (function == "gradnorminf") { \
|
||||
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||
dest+= \
|
||||
std::max(std::max((fabs((float)source[indexPixel_up + ch] - \
|
||||
dest += std::max(std::max((fabs((float)source[indexPixel_up + ch] - \
|
||||
source[indexPixel + ch])), \
|
||||
\
|
||||
(fabs((float)source[indexPixel_down + ch] - \
|
||||
@ -118,17 +115,16 @@ std::vector<float> energy_e1(std::vector<unsigned char> source, int width,
|
||||
|
||||
for (auto i = 0; i < width; i++) {
|
||||
for (auto j = 0; j < height; j++) {
|
||||
compute_energy_for_pixel(
|
||||
source, width, height,
|
||||
i, j, nbChannels, nbColorChannels, energy[width*j+i]
|
||||
);
|
||||
compute_energy_for_pixel(source, width, height, i, j, nbChannels,
|
||||
nbColorChannels, energy[width * j + i]);
|
||||
}
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
/** Given the energy value, returns the optimal seam */
|
||||
template <typename T> std::vector<int> optimal_seam(std::vector<T> energy, int width, int height,
|
||||
template <typename T>
|
||||
std::vector<int> optimal_seam(std::vector<T> energy, int width, int height,
|
||||
bool vertical) {
|
||||
|
||||
// dyn_energy is indexed by [dim_large*(i : dim_long) + (j : dim_large)]
|
||||
@ -150,7 +146,8 @@ template <typename T> std::vector<int> optimal_seam(std::vector<T> energy, int w
|
||||
|
||||
for (auto k = lower_bound; k <= upper_bound;
|
||||
k++) { // Compute energy based on predecessors
|
||||
if (dyn_energy[dim_large*(i-1)+k] < dyn_energy[dim_large*i+j]) {
|
||||
if (dyn_energy[dim_large * (i - 1) + k] <
|
||||
dyn_energy[dim_large * i + j]) {
|
||||
dyn_energy[dim_large * i + j] = dyn_energy[dim_large * (i - 1) + k];
|
||||
}
|
||||
}
|
||||
@ -239,12 +236,13 @@ void remove_seam(const std::vector<T> source, std::vector<T> &output, int width,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// It would be preferable to use templates only for the value assignation but this is in fact far less efficient
|
||||
void recompute_energy_along_seam(
|
||||
std::vector<unsigned char> carved_img, std::vector<float> &output_energy, std::vector<int> opt_seam,
|
||||
int width, int height, int nbChannels, int nbColorChannels, bool vertical
|
||||
) {
|
||||
// It would be preferable to use templates only for the value assignation but
|
||||
// this is in fact far less efficient
|
||||
void recompute_energy_along_seam(std::vector<unsigned char> carved_img,
|
||||
std::vector<float> &output_energy,
|
||||
std::vector<int> opt_seam, int width,
|
||||
int height, int nbChannels,
|
||||
int nbColorChannels, bool vertical) {
|
||||
int dim_large = vertical ? width : height;
|
||||
int dim_long = vertical ? height : width;
|
||||
|
||||
@ -256,20 +254,19 @@ void recompute_energy_along_seam(
|
||||
int x = vertical ? (opt_seam[j] + i) : j;
|
||||
int y = vertical ? j : (opt_seam[j] + i);
|
||||
if ((0 < (opt_seam[j] + i)) && ((opt_seam[j] + i) < dim_large - 1)) {
|
||||
compute_energy_for_pixel(
|
||||
carved_img, newWidth, newHeight,
|
||||
x, y, nbChannels,
|
||||
nbColorChannels, output_energy[width*y+x]
|
||||
);
|
||||
compute_energy_for_pixel(carved_img, newWidth, newHeight, x, y,
|
||||
nbChannels, nbColorChannels,
|
||||
output_energy[width * y + x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recompute_energy_along_seam(
|
||||
std::vector<unsigned char> carved_img, std::vector<std::pair<int, float>> &output_energy, std::vector<int> opt_seam,
|
||||
int width, int height, int nbChannels, int nbColorChannels, bool vertical
|
||||
) {
|
||||
std::vector<unsigned char> carved_img,
|
||||
std::vector<std::pair<int, float>> &output_energy,
|
||||
std::vector<int> opt_seam, int width, int height, int nbChannels,
|
||||
int nbColorChannels, bool vertical) {
|
||||
int dim_large = vertical ? width : height;
|
||||
int dim_long = vertical ? height : width;
|
||||
|
||||
@ -281,17 +278,14 @@ void recompute_energy_along_seam(
|
||||
int x = vertical ? (opt_seam[j] + i) : j;
|
||||
int y = vertical ? j : (opt_seam[j] + i);
|
||||
if ((0 < (opt_seam[j] + i)) && ((opt_seam[j] + i) < dim_large - 1)) {
|
||||
compute_energy_for_pixel(
|
||||
carved_img, newWidth, newHeight,
|
||||
x, y, nbChannels,
|
||||
nbColorChannels, output_energy[width*y+x].first
|
||||
);
|
||||
compute_energy_for_pixel(carved_img, newWidth, newHeight, x, y,
|
||||
nbChannels, nbColorChannels,
|
||||
output_energy[width * y + x].first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Carves an image and its energy by one seam, and recomputes the energy.
|
||||
Returns the optimal seam used */
|
||||
template <typename T>
|
||||
@ -309,22 +303,17 @@ std::vector<int> carving_step(const std::vector<unsigned char> source_img,
|
||||
opt_seam);
|
||||
remove_seam(source_energy, output_energy, width, height, 1, vertical,
|
||||
opt_seam);
|
||||
// Recompute the energy along the seam, we need a separate function for templating
|
||||
recompute_energy_along_seam(
|
||||
source_img, output_energy, opt_seam,
|
||||
width, height, nbChannels, nbColorChannels,
|
||||
vertical
|
||||
);
|
||||
|
||||
// Recompute the energy along the seam, we need a separate function for
|
||||
// templating
|
||||
recompute_energy_along_seam(source_img, output_energy, opt_seam, width,
|
||||
height, nbChannels, nbColorChannels, vertical);
|
||||
|
||||
return opt_seam;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<std::pair<int, float>> mask_energy(std::vector<float> energy,
|
||||
int width, int height, unsigned char* mask) {
|
||||
int width, int height,
|
||||
unsigned char *mask) {
|
||||
std::vector<std::pair<int, float>> output(width * height);
|
||||
|
||||
for (auto i = 0; i < width * height; i++) {
|
||||
@ -334,14 +323,15 @@ std::vector<std::pair<int, float>> mask_energy(std::vector<float> energy,
|
||||
return output;
|
||||
}
|
||||
|
||||
bool does_seam_remove_mask(unsigned char* mask, int width, int height, int nbChannels,
|
||||
std::vector<int> opt_seam, bool vertical)
|
||||
{
|
||||
bool does_seam_remove_mask(unsigned char *mask, int width, int height,
|
||||
int nbChannels, std::vector<int> opt_seam,
|
||||
bool vertical) {
|
||||
int dim_large = vertical ? width : height;
|
||||
int dim_long = vertical ? height : width;
|
||||
|
||||
for (int i = 0; i < dim_long; i++) {
|
||||
if (mask[im_index(i, opt_seam[i])] == 0) return true;
|
||||
if (mask[im_index(i, opt_seam[i])] == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -428,21 +418,19 @@ auto seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
||||
while (seam_index++ < nbSeams || until_mask_removal) {
|
||||
std::vector<int> opt_seam;
|
||||
if (mask) {
|
||||
opt_seam = carving_step(
|
||||
source_img, masked_energy, output_img, output_masked_energy,
|
||||
curWidth, curHeight, nbChannels, nbColorChannels, vertical
|
||||
);
|
||||
opt_seam = carving_step(source_img, masked_energy, output_img,
|
||||
output_masked_energy, curWidth, curHeight,
|
||||
nbChannels, nbColorChannels, vertical);
|
||||
|
||||
if (
|
||||
until_mask_removal &&
|
||||
!does_seam_remove_mask(mask, width, height, nbChannels, opt_seam, vertical)
|
||||
) break;
|
||||
if (until_mask_removal &&
|
||||
!does_seam_remove_mask(mask, width, height, nbChannels, opt_seam,
|
||||
vertical))
|
||||
break;
|
||||
|
||||
} else {
|
||||
opt_seam = carving_step(
|
||||
source_img, source_energy, output_img, output_energy,
|
||||
curWidth, curHeight, nbChannels, nbColorChannels, vertical
|
||||
);
|
||||
opt_seam = carving_step(source_img, source_energy, output_img,
|
||||
output_energy, curWidth, curHeight, nbChannels,
|
||||
nbColorChannels, vertical);
|
||||
}
|
||||
std::copy(output_img.begin(), output_img.end(), source_img.begin());
|
||||
std::copy(output_energy.begin(), output_energy.end(),
|
||||
@ -512,7 +500,8 @@ int main(int argc, char **argv) {
|
||||
"Don't resize image, just try the specified energy function");
|
||||
app.add_option("-f,--function", function,
|
||||
"The function to apply to compute the energy");
|
||||
app.add_flag("-u,--until-mask-removal", until_mask_removal,
|
||||
app.add_flag(
|
||||
"-u,--until-mask-removal", until_mask_removal,
|
||||
"Carve the image until there are no more red pixels in the mask");
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
@ -542,12 +531,14 @@ int main(int argc, char **argv) {
|
||||
r = mask[maskChannels * i];
|
||||
g = mask[maskChannels * i + 1];
|
||||
b = mask[maskChannels * i + 2];
|
||||
bool positive = (g > r && g > b && g > 100); // Mask images are not always the cleanest
|
||||
bool positive = (g > r && g > b &&
|
||||
g > 100); // Mask images are not always the cleanest
|
||||
bool negative = (r > g && r > b && r > 100);
|
||||
|
||||
mask[i] = positive ? 2 : (negative ? 0 : 1);
|
||||
}
|
||||
//* From now on, mask has the same dimensions as source and one single channel
|
||||
//* From now on, mask has the same dimensions as source and one single
|
||||
//channel
|
||||
//* The values are:
|
||||
//* . (2) we want to keep the pixel
|
||||
//* . (1) nothing in particular
|
||||
@ -558,9 +549,9 @@ int main(int argc, char **argv) {
|
||||
until_mask_removal = false;
|
||||
}
|
||||
if (until_mask_removal && nbSeams != DEFAULT_SEAMS) {
|
||||
std::cerr << "Flag --nb-seams specified but --until-mask-removal provided." << std::endl;
|
||||
std::cerr << "Flag --nb-seams specified but --until-mask-removal provided."
|
||||
<< std::endl;
|
||||
nbSeams = DEFAULT_SEAMS;
|
||||
|
||||
}
|
||||
|
||||
nbSeams = std::min(
|
||||
|
Loading…
x
Reference in New Issue
Block a user