mirror of
https://github.com/lovell/sharp.git
synced 2025-12-19 07:15:08 +01:00
committed by
Lovell Fuller
parent
673d8278b5
commit
b69627891d
@@ -115,6 +115,10 @@ namespace sharp {
|
||||
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
|
||||
int const outWidth, int const outHeight, int const x, int const y);
|
||||
|
||||
/*
|
||||
Return the image alpha maximum. Useful for combining alpha bands. scRGB
|
||||
images are 0 - 1 for image data, but the alpha is 0 - 255.
|
||||
*/
|
||||
int MaximumImageAlpha(VipsInterpretation interpretation);
|
||||
|
||||
|
||||
|
||||
@@ -399,4 +399,56 @@ namespace sharp {
|
||||
return image.bandbool(boolean);
|
||||
}
|
||||
|
||||
VImage Trim(VImage image, int const tolerance) {
|
||||
using sharp::MaximumImageAlpha;
|
||||
// An equivalent of ImageMagick's -trim in C++ ... automatically remove
|
||||
// "boring" image edges.
|
||||
|
||||
// We use .project to sum the rows and columns of a 0/255 mask image, the first
|
||||
// non-zero row or column is the object edge. We make the mask image with an
|
||||
// amount-different-from-background image plus a threshold.
|
||||
|
||||
// find the value of the pixel at (0, 0) ... we will search for all pixels
|
||||
// significantly different from this
|
||||
std::vector<double> background = image(0, 0);
|
||||
|
||||
int max = MaximumImageAlpha(image.interpretation());
|
||||
|
||||
// we need to smooth the image, subtract the background from every pixel, take
|
||||
// the absolute value of the difference, then threshold
|
||||
VImage mask = (image.median(3) - background).abs() > (max * tolerance / 100);
|
||||
|
||||
// sum mask rows and columns, then search for the first non-zero sum in each
|
||||
// direction
|
||||
VImage rows;
|
||||
VImage columns = mask.project(&rows);
|
||||
|
||||
VImage profileLeftV;
|
||||
VImage profileLeftH = columns.profile(&profileLeftV);
|
||||
|
||||
VImage profileRightV;
|
||||
VImage profileRightH = columns.fliphor().profile(&profileRightV);
|
||||
|
||||
VImage profileTopV;
|
||||
VImage profileTopH = rows.profile(&profileTopV);
|
||||
|
||||
VImage profileBottomV;
|
||||
VImage profileBottomH = rows.flipver().profile(&profileBottomV);
|
||||
|
||||
int left = static_cast<int>(floor(profileLeftV.min()));
|
||||
int right = columns.width() - static_cast<int>(floor(profileRightV.min()));
|
||||
int top = static_cast<int>(floor(profileTopH.min()));
|
||||
int bottom = rows.height() - static_cast<int>(floor(profileBottomH.min()));
|
||||
|
||||
int width = right - left;
|
||||
int height = bottom - top;
|
||||
|
||||
if(width <= 0 || height <= 0) {
|
||||
throw VError("Unexpected error while trimming. Try to lower the tolerance");
|
||||
}
|
||||
|
||||
// and now crop the original image
|
||||
return image.extract_area(left, top, width, height);
|
||||
}
|
||||
|
||||
} // namespace sharp
|
||||
|
||||
@@ -87,6 +87,11 @@ namespace sharp {
|
||||
*/
|
||||
VImage Bandbool(VImage image, VipsOperationBoolean const boolean);
|
||||
|
||||
/*
|
||||
Trim an image
|
||||
*/
|
||||
VImage Trim(VImage image, int const tolerance);
|
||||
|
||||
} // namespace sharp
|
||||
|
||||
#endif // SRC_OPERATIONS_H_
|
||||
|
||||
@@ -56,6 +56,7 @@ using sharp::EntropyCrop;
|
||||
using sharp::TileCache;
|
||||
using sharp::Threshold;
|
||||
using sharp::Bandbool;
|
||||
using sharp::Trim;
|
||||
|
||||
using sharp::ImageType;
|
||||
using sharp::ImageTypeId;
|
||||
@@ -207,6 +208,11 @@ class PipelineWorker : public AsyncWorker {
|
||||
RemoveExifOrientation(image);
|
||||
}
|
||||
|
||||
// Trim
|
||||
if(baton->trimTolerance != 0) {
|
||||
image = Trim(image, baton->trimTolerance);
|
||||
}
|
||||
|
||||
// Pre extraction
|
||||
if (baton->topOffsetPre != -1) {
|
||||
image = image.extract_area(baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre);
|
||||
@@ -1145,6 +1151,10 @@ NAN_METHOD(pipeline) {
|
||||
baton->sharpenJagged = attrAs<double>(options, "sharpenJagged");
|
||||
baton->threshold = attrAs<int32_t>(options, "threshold");
|
||||
baton->thresholdGrayscale = attrAs<bool>(options, "thresholdGrayscale");
|
||||
baton->trimTolerance = attrAs<int32_t>(options, "trimTolerance");
|
||||
if(baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && baton->trimTolerance != 0) {
|
||||
baton->accessMethod = VIPS_ACCESS_RANDOM;
|
||||
}
|
||||
baton->gamma = attrAs<double>(options, "gamma");
|
||||
baton->greyscale = attrAs<bool>(options, "greyscale");
|
||||
baton->normalize = attrAs<bool>(options, "normalize");
|
||||
|
||||
@@ -63,6 +63,7 @@ struct PipelineBaton {
|
||||
double sharpenJagged;
|
||||
int threshold;
|
||||
bool thresholdGrayscale;
|
||||
int trimTolerance;
|
||||
double gamma;
|
||||
bool greyscale;
|
||||
bool normalize;
|
||||
@@ -127,6 +128,7 @@ struct PipelineBaton {
|
||||
sharpenJagged(2.0),
|
||||
threshold(0),
|
||||
thresholdGrayscale(true),
|
||||
trimTolerance(0),
|
||||
gamma(0.0),
|
||||
greyscale(false),
|
||||
normalize(false),
|
||||
|
||||
Reference in New Issue
Block a user