mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 02:30:12 +02:00
Switch from custom trim op to vips_find_trim #914
This commit is contained in:
parent
11900945eb
commit
21fbe546b8
@ -19,6 +19,9 @@ Requires libvips v8.7.0.
|
||||
Per-operation `background` options added to `resize`, `extend` and `flatten` operations.
|
||||
[#1392](https://github.com/lovell/sharp/issues/1392)
|
||||
|
||||
* Switch from custom trim operation to `vips_find_trim`.
|
||||
[#914](https://github.com/lovell/sharp/issues/914)
|
||||
|
||||
* Drop Node 4 support.
|
||||
[#1212](https://github.com/lovell/sharp/issues/1212)
|
||||
|
||||
|
@ -134,7 +134,7 @@ const Sharp = function (input, options) {
|
||||
sharpenJagged: 2,
|
||||
threshold: 0,
|
||||
thresholdGrayscale: true,
|
||||
trimTolerance: 0,
|
||||
trimThreshold: 0,
|
||||
gamma: 0,
|
||||
greyscale: false,
|
||||
normalise: 0,
|
||||
|
@ -359,18 +359,18 @@ function extract (options) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim "boring" pixels from all edges that contain values within a percentage similarity of the top-left pixel.
|
||||
* @param {Number} [tolerance=10] value between 1 and 99 representing the percentage similarity.
|
||||
* Trim "boring" pixels from all edges that contain values similar to the top-left pixel.
|
||||
* @param {Number} [threshold=10] the allowed difference from the top-left pixel, a number greater than zero.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
function trim (tolerance) {
|
||||
if (!is.defined(tolerance)) {
|
||||
this.options.trimTolerance = 10;
|
||||
} else if (is.integer(tolerance) && is.inRange(tolerance, 1, 99)) {
|
||||
this.options.trimTolerance = tolerance;
|
||||
function trim (threshold) {
|
||||
if (!is.defined(threshold)) {
|
||||
this.options.trimThreshold = 10;
|
||||
} else if (is.number(threshold) && threshold > 0) {
|
||||
this.options.trimThreshold = threshold;
|
||||
} else {
|
||||
throw new Error('Invalid trim tolerance (1 to 99) ' + tolerance);
|
||||
throw is.invalidParameterError('threshold', 'number greater than zero', threshold);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -324,55 +324,21 @@ namespace sharp {
|
||||
return image.boolean(imageR, 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);
|
||||
|
||||
double const 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) {
|
||||
/*
|
||||
Trim an image
|
||||
*/
|
||||
VImage Trim(VImage image, int const threshold) {
|
||||
// Top-left pixel provides the background colour
|
||||
VImage background = image.extract_area(0, 0, 1, 1);
|
||||
if (HasAlpha(background)) {
|
||||
background = background.flatten();
|
||||
}
|
||||
int top, width, height;
|
||||
int const left = image.find_trim(&top, &width, &height,
|
||||
VImage::option()->set("background", background(0, 0)));
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ namespace sharp {
|
||||
/*
|
||||
Trim an image
|
||||
*/
|
||||
VImage Trim(VImage image, int const tolerance);
|
||||
VImage Trim(VImage image, int const threshold);
|
||||
|
||||
/*
|
||||
* Linear adjustment (a * in + b)
|
||||
|
@ -100,8 +100,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
}
|
||||
|
||||
// Trim
|
||||
if (baton->trimTolerance != 0) {
|
||||
image = sharp::Trim(image, baton->trimTolerance);
|
||||
if (baton->trimThreshold > 0.0) {
|
||||
image = sharp::Trim(image, baton->trimThreshold);
|
||||
}
|
||||
|
||||
// Pre extraction
|
||||
@ -233,7 +233,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
if (
|
||||
xshrink == yshrink && xshrink >= 2 * shrink_on_load_factor &&
|
||||
(inputImageType == ImageType::JPEG || inputImageType == ImageType::WEBP) &&
|
||||
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimTolerance == 0
|
||||
baton->gamma == 0 && baton->topOffsetPre == -1 && baton->trimThreshold == 0.0
|
||||
) {
|
||||
if (xshrink >= 8 * shrink_on_load_factor) {
|
||||
xfactor = xfactor / 8;
|
||||
@ -1183,7 +1183,7 @@ NAN_METHOD(pipeline) {
|
||||
baton->sharpenJagged = AttrTo<double>(options, "sharpenJagged");
|
||||
baton->threshold = AttrTo<int32_t>(options, "threshold");
|
||||
baton->thresholdGrayscale = AttrTo<bool>(options, "thresholdGrayscale");
|
||||
baton->trimTolerance = AttrTo<int32_t>(options, "trimTolerance");
|
||||
baton->trimThreshold = AttrTo<double>(options, "trimThreshold");
|
||||
baton->gamma = AttrTo<double>(options, "gamma");
|
||||
baton->linearA = AttrTo<double>(options, "linearA");
|
||||
baton->linearB = AttrTo<double>(options, "linearB");
|
||||
@ -1293,7 +1293,7 @@ NAN_METHOD(pipeline) {
|
||||
}
|
||||
// Force random access for certain operations
|
||||
if (baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && (
|
||||
baton->trimTolerance != 0 || baton->normalise ||
|
||||
baton->trimThreshold > 0.0 || baton->normalise ||
|
||||
baton->position == 16 || baton->position == 17)) {
|
||||
baton->accessMethod = VIPS_ACCESS_RANDOM;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ struct PipelineBaton {
|
||||
double sharpenJagged;
|
||||
int threshold;
|
||||
bool thresholdGrayscale;
|
||||
int trimTolerance;
|
||||
double trimThreshold;
|
||||
double linearA;
|
||||
double linearB;
|
||||
double gamma;
|
||||
@ -176,7 +176,7 @@ struct PipelineBaton {
|
||||
sharpenJagged(2.0),
|
||||
threshold(0),
|
||||
thresholdGrayscale(true),
|
||||
trimTolerance(0),
|
||||
trimThreshold(0.0),
|
||||
linearA(1.0),
|
||||
linearB(0.0),
|
||||
gamma(0.0),
|
||||
|
@ -50,7 +50,7 @@ describe('Trim borders', function () {
|
||||
});
|
||||
|
||||
describe('Invalid thresholds', function () {
|
||||
[-1, 100, 'fail', {}].forEach(function (threshold) {
|
||||
[-1, 'fail', {}].forEach(function (threshold) {
|
||||
it(JSON.stringify(threshold), function () {
|
||||
assert.throws(function () {
|
||||
sharp().trim(threshold);
|
||||
|
Loading…
x
Reference in New Issue
Block a user