Remove many seams at once (but don't use overlapping seams)
This commit is contained in:
parent
cf35b65faf
commit
7446cabaad
@ -15,6 +15,9 @@
|
||||
bool silent;
|
||||
bool test_energy;
|
||||
|
||||
int min(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/** e_1 energy */
|
||||
std::vector<unsigned char> energy_e1(unsigned char* source, int width, int height, int nbChannels) {
|
||||
@ -22,8 +25,8 @@ std::vector<unsigned char> energy_e1(unsigned char* source, int width, int heigh
|
||||
|
||||
for (auto i=0; i < width*height; i++) { energy[i] = 0; }
|
||||
|
||||
for(auto i = 0 ; i < width ; ++i) {
|
||||
for(auto j = 0; j < height; ++j) {
|
||||
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;
|
||||
auto indexPixel_down = (j+1 < height) ? nbChannels*(width*(j+1)+i) : indexPixel;
|
||||
@ -44,6 +47,69 @@ std::vector<unsigned char> energy_e1(unsigned char* source, int width, int heigh
|
||||
return energy;
|
||||
}
|
||||
|
||||
std::vector<int> optimal_vertical_seams(std::vector<unsigned char> energy, int width, int height, int nbSeams) {
|
||||
std::vector<unsigned char> dyn_energy(width*height);
|
||||
|
||||
//* Find an end of the minimal connected vertical/horizontal seam
|
||||
for (auto i=1; i < height; i++) {
|
||||
dyn_energy[width*i] = energy[width*i];
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
dyn_energy[width*i+j] = min(
|
||||
bot_center,
|
||||
min(
|
||||
bot_left,
|
||||
bot_right
|
||||
)
|
||||
) + energy[width*i+j];
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> result(height*nbSeams);
|
||||
|
||||
// To find the nbSeams largest points, we sort the pairs (dyn_energy[idx, height-1], idx)
|
||||
std::vector<std::pair<int, int>> seamEnds(width);
|
||||
for (auto j=0; j < width; j++) {
|
||||
seamEnds[j] = {dyn_energy[(height-1)*width+j], j};
|
||||
}
|
||||
std::sort(seamEnds.begin(), seamEnds.end());
|
||||
for (auto seam=0; seam < nbSeams; seam++) {
|
||||
result[height*(seam+1)-1] = seamEnds[seam].second;
|
||||
}
|
||||
|
||||
//* Backtracking to find the path
|
||||
for (auto seam=0; seam < nbSeams; seam++) {
|
||||
int min_idx = result[height*(seam+1)-1];
|
||||
int min_val = dyn_energy[(height-1)*width+min_idx];
|
||||
|
||||
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];
|
||||
|
||||
if (dyn_energy[(i-1)+height*min_idx] == objective_energy) {
|
||||
// min_idx does not change
|
||||
min_val = dyn_energy[(i-1)+height*min_idx];
|
||||
} else if (min_idx > 0 && 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 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;
|
||||
}
|
||||
result[height*seam+i-1] = min_idx;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app{"seam-carving"};
|
||||
@ -51,6 +117,8 @@ int main(int argc, char **argv) {
|
||||
app.add_option("-s,--source", sourceImage, "Source image")->required()->check(CLI::ExistingFile);;
|
||||
std::string outputImage= "output.png";
|
||||
app.add_option("-o,--output", outputImage, "Output image")->required();
|
||||
int nbSeams = 1;
|
||||
app.add_option("-n,--nb-seams", nbSeams, "Number of seams");
|
||||
silent = false;
|
||||
app.add_flag("--silent", silent, "No verbose messages");
|
||||
test_energy = false;
|
||||
@ -66,20 +134,69 @@ int main(int argc, char **argv) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
nbSeams = min(nbSeams, width);
|
||||
int outChannels = nbChannels;
|
||||
int outHeight = height;
|
||||
int outWidth = width;
|
||||
std::vector<unsigned char> output;
|
||||
|
||||
if (test_energy) {
|
||||
outChannels = 1;
|
||||
output = energy_e1(source, width, height, nbChannels);
|
||||
//outChannels = 1;
|
||||
std::vector<unsigned char> energy = energy_e1(source, width, height, nbChannels);
|
||||
std::vector<int> opt_seam = optimal_vertical_seams(energy, width, height, nbSeams);
|
||||
|
||||
std::vector<unsigned char> output2(width*height*3);
|
||||
for (auto i=0; i < width*height; i++) {
|
||||
output2[3*i] = energy[i];
|
||||
output2[3*i+1] = energy[i];
|
||||
output2[3*i+2] = energy[i];
|
||||
}
|
||||
for (auto seam=0; seam < nbSeams; seam++) {
|
||||
for (auto i=0; i < height; i++) {
|
||||
output2[3*(i*width+opt_seam[seam*height+i])] = 255;
|
||||
output2[3*(i*width+opt_seam[seam*height+i])+1] = 0;
|
||||
output2[3*(i*width+opt_seam[seam*height+i])+2] = 0;
|
||||
}
|
||||
}
|
||||
output=output2;
|
||||
} else {
|
||||
std::cout <<"Not implemented" << std::endl;
|
||||
exit(1);
|
||||
std::vector<unsigned char> energy = energy_e1(source, width, height, nbChannels);
|
||||
std::vector<int> opt_seam = optimal_vertical_seams(energy, width, height, nbSeams);
|
||||
|
||||
std::vector<bool> blacklist(width*height);
|
||||
|
||||
for (auto k=0; k < width*height; k++) { blacklist[k] = false; }
|
||||
for (auto seam=0; seam < nbSeams; seam++) {
|
||||
bool overlap = false; // Check if 2 seams use similar pixels
|
||||
for (auto i=0; i < height; i++) {
|
||||
overlap = overlap || blacklist[i*width+opt_seam[seam*height+i]];
|
||||
}
|
||||
if (overlap) { nbSeams--; continue;}
|
||||
for (auto i=0; i < height; i++) {
|
||||
blacklist[i*width+opt_seam[seam*height+i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
outWidth = width-nbSeams; //! something is weird here
|
||||
|
||||
std::vector<unsigned char> output_l(outWidth*height*3);
|
||||
for (auto i=0; i < height; i++) {
|
||||
int cur_j = 0;
|
||||
for (auto j=0; cur_j < outWidth && j < width; j++) {
|
||||
if (!blacklist[i*width+j]) {
|
||||
output_l[3*(i*outWidth+cur_j)] = source[3*(i*width+j)];
|
||||
output_l[3*(i*outWidth+cur_j)+1] = source[3*(i*width+j)+1];
|
||||
output_l[3*(i*outWidth+cur_j)+2] = source[3*(i*width+j)+2];
|
||||
cur_j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
output = output_l;
|
||||
}
|
||||
|
||||
//Final export
|
||||
if (!silent) std::cout<<"Exporting.."<<std::endl;
|
||||
int errcode = stbi_write_png(outputImage.c_str(), width, height, outChannels, output.data(), outChannels*width);
|
||||
int errcode = stbi_write_png(outputImage.c_str(), outWidth, outHeight, outChannels, output.data(), outChannels*outWidth);
|
||||
if (!errcode) {
|
||||
std::cout<<"Error while exporting the resulting image."<<std::endl;
|
||||
exit(errcode);
|
||||
|
Loading…
x
Reference in New Issue
Block a user