mirror of
https://github.com/lovell/sharp.git
synced 2025-12-19 07:15:08 +01:00
Add experimental, entropy-based auto-crop
Remove deprecated extract API
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user