Merge branch 'mask'
This commit is contained in:
commit
14cccecfa1
@ -18,10 +18,8 @@ bool silent = false;
|
|||||||
bool test_energy = false;
|
bool test_energy = false;
|
||||||
bool until_mask_removal = false;
|
bool until_mask_removal = false;
|
||||||
int max_step = 1;
|
int max_step = 1;
|
||||||
|
std::string function = "grad";
|
||||||
|
|
||||||
// Get index for any table indexed by [width*(i : height) + (j : width)], but a
|
|
||||||
// : 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,
|
void export_image(const char *filename, const void *data, int width, int height,
|
||||||
int nbChannels) {
|
int nbChannels) {
|
||||||
@ -35,92 +33,93 @@ 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, \
|
#define compute_energy_for_pixel( \
|
||||||
nbChannels, /* computes the energy at pixel i,j, i.e. energy[width*j+i]*/ \
|
source, width, height, i, j, \
|
||||||
nbColorChannels, dest) \
|
nbChannels, /* computes the energy at pixel i,j, i.e. energy[width*j+i]*/ \
|
||||||
auto indexPixel = (nbChannels) * (width * (j) + (i)); \
|
nbColorChannels, dest) \
|
||||||
auto indexPixel_up = \
|
auto indexPixel = (nbChannels) * (width * (j) + (i)); \
|
||||||
((j) - 1 > 0) ? (nbChannels) * (width * ((j) - 1) + (i)) : indexPixel; \
|
auto indexPixel_up = \
|
||||||
auto indexPixel_down = ((j) + 1 < height) \
|
((j) - 1 > 0) ? (nbChannels) * (width * ((j) - 1) + (i)) : indexPixel; \
|
||||||
? (nbChannels) * (width * ((j) + 1) + (i)) \
|
auto indexPixel_down = ((j) + 1 < height) \
|
||||||
: indexPixel; \
|
? (nbChannels) * (width * ((j) + 1) + (i)) \
|
||||||
auto indexPixel_left = \
|
: indexPixel; \
|
||||||
((i) - 1 > 0) ? (nbChannels) * (width * (j) + ((i) - 1)) : indexPixel; \
|
auto indexPixel_left = \
|
||||||
auto indexPixel_right = ((i) + 1 < width) \
|
((i) - 1 > 0) ? (nbChannels) * (width * (j) + ((i) - 1)) : indexPixel; \
|
||||||
? (nbChannels) * (width * (j) + ((i) + 1)) \
|
auto indexPixel_right = ((i) + 1 < width) \
|
||||||
: indexPixel; \
|
? (nbChannels) * (width * (j) + ((i) + 1)) \
|
||||||
dest = 0; \
|
: indexPixel; \
|
||||||
if (function == "grad") { \
|
dest = 0; \
|
||||||
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
if (function == "grad") { \
|
||||||
dest += ((fabs((float)source[indexPixel_up + ch] - \
|
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||||
source[indexPixel + ch])) + \
|
dest += ((fabs((float)source[indexPixel_up + ch] - \
|
||||||
\
|
source[indexPixel + ch])) + \
|
||||||
(fabs((float)source[indexPixel_down + 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_left + ch] - \
|
||||||
(fabs((float)source[indexPixel_right + ch] - \
|
source[indexPixel + ch])) + \
|
||||||
source[indexPixel + ch]))); \
|
(fabs((float)source[indexPixel_right + ch] - \
|
||||||
} \
|
source[indexPixel + ch]))); \
|
||||||
} else if (function == "gradnorm") { \
|
} \
|
||||||
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
} else if (function == "gradnorm") { \
|
||||||
dest += (std::pow(fabs((float)source[indexPixel_up + ch] - \
|
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||||
source[indexPixel + ch]), \
|
dest += (std::pow(fabs((float)source[indexPixel_up + ch] - \
|
||||||
2) + \
|
source[indexPixel + ch]), \
|
||||||
\
|
2) + \
|
||||||
std::pow(fabs((float)source[indexPixel_down + ch] - \
|
\
|
||||||
source[indexPixel + ch]), \
|
std::pow(fabs((float)source[indexPixel_down + ch] - \
|
||||||
2) + \
|
source[indexPixel + ch]), \
|
||||||
\
|
2) + \
|
||||||
std::pow(fabs((float)source[indexPixel_left + ch] - \
|
\
|
||||||
source[indexPixel + ch]), \
|
std::pow(fabs((float)source[indexPixel_left + ch] - \
|
||||||
2) + \
|
source[indexPixel + ch]), \
|
||||||
std::pow(fabs((float)source[indexPixel_right + ch] - \
|
2) + \
|
||||||
source[indexPixel + ch]), \
|
std::pow(fabs((float)source[indexPixel_right + ch] - \
|
||||||
2)); \
|
source[indexPixel + ch]), \
|
||||||
} \
|
2)); \
|
||||||
} else if (function == "gradhoriz") { \
|
} \
|
||||||
/* take the gradient along the horizontal only*/ \
|
} else if (function == "gradhoriz") { \
|
||||||
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
/* take the gradient along the horizontal only*/ \
|
||||||
dest += ((fabs((float)source[indexPixel_left + ch] - \
|
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||||
source[indexPixel + ch])) + \
|
dest += ((fabs((float)source[indexPixel_left + ch] - \
|
||||||
(fabs((float)source[indexPixel_right + ch] - \
|
source[indexPixel + ch])) + \
|
||||||
source[indexPixel + ch]))); \
|
(fabs((float)source[indexPixel_right + ch] - \
|
||||||
} \
|
source[indexPixel + ch]))); \
|
||||||
} else if (function == "gradvertic") { \
|
} \
|
||||||
/* take the gradient along the vertical only*/ \
|
} else if (function == "gradvertic") { \
|
||||||
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
/* take the gradient along the vertical only*/ \
|
||||||
dest += ((fabs((float)source[indexPixel_up + ch] - \
|
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||||
source[indexPixel + ch])) + \
|
dest += ((fabs((float)source[indexPixel_up + ch] - \
|
||||||
(fabs((float)source[indexPixel_down + ch] - \
|
source[indexPixel + ch])) + \
|
||||||
source[indexPixel + ch]))); \
|
(fabs((float)source[indexPixel_down + ch] - \
|
||||||
} \
|
source[indexPixel + ch]))); \
|
||||||
} else if (function == "gradnorminf") { \
|
} \
|
||||||
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
} else if (function == "gradnorminf") { \
|
||||||
dest += std::max(std::max((fabs((float)source[indexPixel_up + ch] - \
|
for (auto ch = 0; ch < (nbColorChannels); ch++) { \
|
||||||
source[indexPixel + ch])), \
|
dest += std::max(std::max((fabs((float)source[indexPixel_up + ch] - \
|
||||||
\
|
source[indexPixel + ch])), \
|
||||||
(fabs((float)source[indexPixel_down + ch] - \
|
\
|
||||||
source[indexPixel + ch]))), \
|
(fabs((float)source[indexPixel_down + ch] - \
|
||||||
std::max( \
|
source[indexPixel + ch]))), \
|
||||||
\
|
std::max( \
|
||||||
(fabs((float)source[indexPixel_left + ch] - \
|
\
|
||||||
source[indexPixel + ch])), \
|
(fabs((float)source[indexPixel_left + ch] - \
|
||||||
(fabs((float)source[indexPixel_right + ch] - \
|
source[indexPixel + ch])), \
|
||||||
source[indexPixel + ch])))); \
|
(fabs((float)source[indexPixel_right + ch] - \
|
||||||
} \
|
source[indexPixel + ch])))); \
|
||||||
} else { \
|
} \
|
||||||
std::cerr << "function " << function << " not available" << std::endl; \
|
} else { \
|
||||||
exit(1); \
|
std::cerr << "function " << function << " not available" << std::endl; \
|
||||||
};
|
exit(1); \
|
||||||
|
};
|
||||||
// Le alpha n'est pas pris en compte dans l'énergie
|
// 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
|
// Here, we use this /ugly/ macro to avoid defining a function that would be way
|
||||||
// slower...
|
// slower...
|
||||||
|
|
||||||
|
|
||||||
/** e_1 energy*/
|
/** e_1 energy*/
|
||||||
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) {
|
||||||
@ -160,8 +159,8 @@ std::vector<int> optimal_seam(std::vector<T> energy, int width, int height,
|
|||||||
int lower_bound = std::max(j - max_step, 0);
|
int lower_bound = std::max(j - max_step, 0);
|
||||||
int upper_bound = std::min(j + max_step, dim_large - 1);
|
int upper_bound = std::min(j + max_step, dim_large - 1);
|
||||||
|
|
||||||
for (auto k = lower_bound; k <= upper_bound;
|
// Compute energy based on predecessors
|
||||||
k++) { // Compute energy based on predecessors
|
for (auto k = lower_bound; k <= upper_bound; k++) {
|
||||||
if (dyn_energy[dim_large * (i - 1) + k] <
|
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 + j] = dyn_energy[dim_large * (i - 1) + k];
|
dyn_energy[dim_large * i + j] = dyn_energy[dim_large * (i - 1) + k];
|
||||||
@ -177,7 +176,7 @@ std::vector<int> optimal_seam(std::vector<T> energy, int width, int height,
|
|||||||
T min_val = limits::max_energy();
|
T min_val = limits::max_energy();
|
||||||
|
|
||||||
for (auto j = 0; j < dim_large; j++) {
|
for (auto j = 0; j < dim_large; j++) {
|
||||||
if (min_val > dyn_energy[dim_large * (dim_long - 1) + j]) {
|
if (dyn_energy[dim_large * (dim_long - 1) + j] < min_val) {
|
||||||
min_idx = j;
|
min_idx = j;
|
||||||
min_val = dyn_energy[dim_large * (dim_long - 1) + j];
|
min_val = dyn_energy[dim_large * (dim_long - 1) + j];
|
||||||
}
|
}
|
||||||
@ -306,7 +305,7 @@ void recompute_energy_along_seam(
|
|||||||
(((0 < j0 + j_offset) && (j0 + j_offset < dim_long)))) {
|
(((0 < j0 + j_offset) && (j0 + j_offset < dim_long)))) {
|
||||||
compute_energy_for_pixel(carved_img, newWidth, newHeight, x, y,
|
compute_energy_for_pixel(carved_img, newWidth, newHeight, x, y,
|
||||||
nbChannels, nbColorChannels,
|
nbChannels, nbColorChannels,
|
||||||
output_energy[width * y + x].first);
|
output_energy[width * y + x].second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -338,30 +337,6 @@ std::vector<int> carving_step(const std::vector<unsigned char> source_img,
|
|||||||
return opt_seam;
|
return opt_seam;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<int, float>> mask_energy(std::vector<float> energy,
|
|
||||||
int width, int height,
|
|
||||||
unsigned char *mask) {
|
|
||||||
std::vector<std::pair<int, float>> output(width * height);
|
|
||||||
|
|
||||||
for (auto i = 0; i < width * height; i++) {
|
|
||||||
output[i] = {mask[i], energy[i]};
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
auto seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
||||||
int nbSeams, bool vertical, unsigned char *mask = nullptr) {
|
int nbSeams, bool vertical, unsigned char *mask = nullptr) {
|
||||||
@ -492,9 +467,7 @@ auto seam_carving(unsigned char *source, int width, int height, int nbChannels,
|
|||||||
std::cout << std::endl; // Add newline after ProgressBar
|
std::cout << std::endl; // Add newline after ProgressBar
|
||||||
|
|
||||||
if (test_energy) {
|
if (test_energy) {
|
||||||
|
|
||||||
return std::make_tuple(test_energy_output, width, height, nbChannels);
|
return std::make_tuple(test_energy_output, width, height, nbChannels);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return std::make_tuple(source_img, curWidth, curHeight, nbChannels);
|
return std::make_tuple(source_img, curWidth, curHeight, nbChannels);
|
||||||
}
|
}
|
||||||
@ -564,8 +537,8 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
mask[i] = positive ? 2 : (negative ? 0 : 1);
|
mask[i] = positive ? 2 : (negative ? 0 : 1);
|
||||||
}
|
}
|
||||||
//* From now on, mask has the same dimensions as source and one single
|
//* "mask" has the same dimensions as source and one single channel
|
||||||
// channel
|
|
||||||
//* The values are:
|
//* The values are:
|
||||||
//* . (2) we want to keep the pixel
|
//* . (2) we want to keep the pixel
|
||||||
//* . (1) nothing in particular
|
//* . (1) nothing in particular
|
||||||
|
@ -1,17 +1,42 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <random>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb_image.h>
|
#include <stb_image.h>
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include "utils.hpp"
|
||||||
#include <random>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
bool nearly_equal(float a, float b) {
|
bool nearly_equal(float a, float b) {
|
||||||
return std::nextafter(a, std::numeric_limits<float>::lowest()) <= b &&
|
return std::nextafter(a, std::numeric_limits<float>::lowest()) <= b &&
|
||||||
std::nextafter(a, std::numeric_limits<float>::max()) >= b;
|
std::nextafter(a, std::numeric_limits<float>::max()) >= b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<int, float>> mask_energy(std::vector<float> energy,
|
||||||
|
int width, int height, unsigned char* mask) {
|
||||||
|
std::vector<std::pair<int, float>> output(width*height);
|
||||||
|
|
||||||
|
for (auto i=0; i < width*height; i++) {
|
||||||
|
output[i] = {mask[i], energy[i]};
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<int, float> operator+(std::pair<int, float>& p1, std::pair<int, float>& p2) {
|
std::pair<int, float> operator+(std::pair<int, float>& p1, std::pair<int, float>& p2) {
|
||||||
return {
|
return {
|
||||||
// If one of the two pixels is "protected" (ie 2), we want to prevent this line removing
|
// If one of the two pixels is "protected" (ie 2), we want to prevent this line removing
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
|
// Get index for any table indexed by [width*(i : height) + (j : width)], but a
|
||||||
|
// : dim_long, b : dim_large
|
||||||
|
#define im_index(a, b) (vertical ? (width * a + b) : (width * b + a))
|
||||||
|
|
||||||
bool nearly_equal(float a, float b);
|
bool nearly_equal(float a, float b);
|
||||||
|
|
||||||
@ -7,6 +10,12 @@ std::pair<int, float> operator+(std::pair<int, float>& p1, std::pair<int, float>
|
|||||||
|
|
||||||
void operator+=(std::pair<int, float>& p1, std::pair<int, float>& p2);
|
void operator+=(std::pair<int, float>& p1, std::pair<int, float>& p2);
|
||||||
|
|
||||||
|
bool does_seam_remove_mask(unsigned char* mask, int width, int height, int nbChannels,
|
||||||
|
std::vector<int> opt_seam, bool vertical);
|
||||||
|
|
||||||
|
std::vector<std::pair<int, float>> mask_energy(std::vector<float> energy,
|
||||||
|
int width, int height, unsigned char* mask);
|
||||||
|
|
||||||
namespace limits {
|
namespace limits {
|
||||||
struct max_energy {
|
struct max_energy {
|
||||||
template<class T> operator T() {
|
template<class T> operator T() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user