Add experimental, entropy-based auto-crop

Remove deprecated extract API
This commit is contained in:
Lovell Fuller
2016-03-05 12:29:16 +00:00
parent 38ddb3b866
commit 2034efcf55
11 changed files with 214 additions and 96 deletions

View File

@@ -170,4 +170,82 @@ namespace sharp {
}
}
/*
Calculate crop area based on image entropy
*/
std::tuple<int, int> EntropyCrop(VImage image, int const outWidth, int const outHeight) {
int left = 0;
int top = 0;
int const inWidth = image.width();
int const inHeight = image.height();
if (inWidth > outWidth) {
// Reduce width by repeated removing slices from edge with lowest entropy
int width = inWidth;
double leftEntropy = 0.0;
double rightEntropy = 0.0;
// Max width of each slice
int const maxSliceWidth = static_cast<int>(ceil((inWidth - outWidth) / 8.0));
while (width > outWidth) {
// Width of current slice
int const slice = std::min(width - outWidth, maxSliceWidth);
if (leftEntropy == 0.0) {
// Update entropy of left slice
leftEntropy = Entropy(image.extract_area(left, 0, slice, inHeight));
}
if (rightEntropy == 0.0) {
// Update entropy of right slice
rightEntropy = Entropy(image.extract_area(width - slice - 1, 0, slice, inHeight));
}
// Keep slice with highest entropy
if (leftEntropy >= rightEntropy) {
// Discard right slice
rightEntropy = 0.0;
} else {
// Discard left slice
leftEntropy = 0.0;
left = left + slice;
}
width = width - slice;
}
}
if (inHeight > outHeight) {
// Reduce height by repeated removing slices from edge with lowest entropy
int height = inHeight;
double topEntropy = 0.0;
double bottomEntropy = 0.0;
// Max height of each slice
int const maxSliceHeight = static_cast<int>(ceil((inHeight - outHeight) / 8.0));
while (height > outHeight) {
// Height of current slice
int const slice = std::min(height - outHeight, maxSliceHeight);
if (topEntropy == 0.0) {
// Update entropy of top slice
topEntropy = Entropy(image.extract_area(0, top, inWidth, slice));
}
if (bottomEntropy == 0.0) {
// Update entropy of bottom slice
bottomEntropy = Entropy(image.extract_area(0, height - slice - 1, inWidth, slice));
}
// Keep slice with highest entropy
if (topEntropy >= bottomEntropy) {
// Discard bottom slice
bottomEntropy = 0.0;
} else {
// Discard top slice
topEntropy = 0.0;
top = top + slice;
}
height = height - slice;
}
}
return std::make_tuple(left, top);
}
/*
Calculate the Shannon entropy for an image
*/
double Entropy(VImage image) {
return image.hist_find().hist_entropy();
}
} // namespace sharp

View File

@@ -33,6 +33,16 @@ namespace sharp {
*/
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged);
/*
Calculate crop area based on image entropy
*/
std::tuple<int, int> EntropyCrop(VImage image, int const outWidth, int const outHeight);
/*
Calculate the Shannon entropy for an image
*/
double Entropy(VImage image);
} // namespace sharp
#endif // SRC_OPERATIONS_H_

View File

@@ -49,6 +49,7 @@ using sharp::Normalize;
using sharp::Gamma;
using sharp::Blur;
using sharp::Sharpen;
using sharp::EntropyCrop;
using sharp::ImageType;
using sharp::ImageTypeId;
@@ -506,9 +507,15 @@ class PipelineWorker : public AsyncWorker {
// Crop/max/min
int left;
int top;
std::tie(left, top) = CalculateCrop(
image.width(), image.height(), baton->width, baton->height, baton->gravity
);
if (baton->crop < 9) {
// Gravity-based crop
std::tie(left, top) = CalculateCrop(
image.width(), image.height(), baton->width, baton->height, baton->crop
);
} else {
// Entropy-based crop
std::tie(left, top) = EntropyCrop(image, baton->width, baton->height);
}
int width = std::min(image.width(), baton->width);
int height = std::min(image.height(), baton->height);
image = image.extract_area(left, top, width, height);
@@ -996,7 +1003,7 @@ NAN_METHOD(pipeline) {
baton->overlayGravity = attrAs<int32_t>(options, "overlayGravity");
// Resize options
baton->withoutEnlargement = attrAs<bool>(options, "withoutEnlargement");
baton->gravity = attrAs<int32_t>(options, "gravity");
baton->crop = attrAs<int32_t>(options, "crop");
baton->interpolator = attrAsStr(options, "interpolator");
// Operators
baton->flatten = attrAs<bool>(options, "flatten");

View File

@@ -45,7 +45,7 @@ struct PipelineBaton {
int height;
int channels;
Canvas canvas;
int gravity;
int crop;
std::string interpolator;
double background[4];
bool flatten;
@@ -99,7 +99,7 @@ struct PipelineBaton {
topOffsetPost(-1),
channels(0),
canvas(Canvas::CROP),
gravity(0),
crop(0),
flatten(false),
negate(false),
blurSigma(0.0),