mirror of
https://github.com/lovell/sharp.git
synced 2025-07-11 19:40:14 +02:00
Break existing sharpen API to accept sigma and improve precision
This commit is contained in:
parent
ee21d2991c
commit
b7a098fb28
@ -374,15 +374,15 @@ When used without parameters, performs a fast, mild blur of the output image. Th
|
||||
|
||||
When a `sigma` is provided, performs a slower, more accurate Gaussian blur. This typically reduces performance by 25%.
|
||||
|
||||
* `sigma`, if present, is a Number between 0.3 and 1000 representing the approximate blur radius in pixels.
|
||||
* `sigma`, if present, is a Number between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||
|
||||
#### sharpen([radius], [flat], [jagged])
|
||||
#### sharpen([sigma], [flat], [jagged])
|
||||
|
||||
When used without parameters, performs a fast, mild sharpen of the output image. This typically reduces performance by 10%.
|
||||
|
||||
When a `radius` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space. Separate control over the level of sharpening in "flat" and "jagged" areas is available. This typically reduces performance by 50%.
|
||||
When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space. Separate control over the level of sharpening in "flat" and "jagged" areas is available. This typically reduces performance by 50%.
|
||||
|
||||
* `radius`, if present, is an integral Number representing the sharpen mask radius in pixels.
|
||||
* `sigma`, if present, is a Number representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||
* `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0.
|
||||
* `jagged`, if present, is a Number representing the level of sharpening to apply to "jagged" areas, defaulting to a value of 2.0.
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
* Take advantage of libvips 8.3 features.
|
||||
Use shrink-on-load for WebP input.
|
||||
Break existing sharpen API to accept sigma and improve precision.
|
||||
[#369](https://github.com/lovell/sharp/issues/369)
|
||||
|
||||
### v0.14 - "*needle*"
|
||||
|
50
index.js
50
index.js
@ -80,7 +80,7 @@ var Sharp = function(input, options) {
|
||||
flatten: false,
|
||||
negate: false,
|
||||
blurSigma: 0,
|
||||
sharpenRadius: 0,
|
||||
sharpenSigma: 0,
|
||||
sharpenFlat: 1,
|
||||
sharpenJagged: 2,
|
||||
threshold: 0,
|
||||
@ -154,14 +154,20 @@ var isDefined = function(val) {
|
||||
var isObject = function(val) {
|
||||
return typeof val === 'object';
|
||||
};
|
||||
var isBoolean = function(val) {
|
||||
return typeof val === 'boolean';
|
||||
};
|
||||
var isBuffer = function(val) {
|
||||
return typeof val === 'object' && val instanceof Buffer;
|
||||
};
|
||||
var isString = function(val) {
|
||||
return typeof val === 'string' && val.length > 0;
|
||||
};
|
||||
var isNumber = function(val) {
|
||||
return typeof val === 'number' && !Number.isNaN(val);
|
||||
};
|
||||
var isInteger = function(val) {
|
||||
return typeof val === 'number' && !Number.isNaN(val) && val % 1 === 0;
|
||||
return isNumber(val) && val % 1 === 0;
|
||||
};
|
||||
var inRange = function(val, min, max) {
|
||||
return val >= min && val <= max;
|
||||
@ -406,17 +412,17 @@ Sharp.prototype.withoutEnlargement = function(withoutEnlargement) {
|
||||
Call with a sigma to use a slower, more accurate Gaussian blur.
|
||||
*/
|
||||
Sharp.prototype.blur = function(sigma) {
|
||||
if (typeof sigma === 'undefined') {
|
||||
if (!isDefined(sigma)) {
|
||||
// No arguments: default to mild blur
|
||||
this.options.blurSigma = -1;
|
||||
} else if (typeof sigma === 'boolean') {
|
||||
} else if (isBoolean(sigma)) {
|
||||
// Boolean argument: apply mild blur?
|
||||
this.options.blurSigma = sigma ? -1 : 0;
|
||||
} else if (typeof sigma === 'number' && !Number.isNaN(sigma) && sigma >= 0.3 && sigma <= 1000) {
|
||||
} else if (isNumber(sigma) && inRange(sigma, 0.3, 1000)) {
|
||||
// Numeric argument: specific sigma
|
||||
this.options.blurSigma = sigma;
|
||||
} else {
|
||||
throw new Error('Invalid blur sigma (0.3 to 1000.0) ' + sigma);
|
||||
throw new Error('Invalid blur sigma (0.3 - 1000.0) ' + sigma);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
@ -425,38 +431,38 @@ Sharp.prototype.blur = function(sigma) {
|
||||
Sharpen the output image.
|
||||
Call without a radius to use a fast, mild sharpen.
|
||||
Call with a radius to use a slow, accurate sharpen using the L of LAB colour space.
|
||||
radius - size of mask in pixels, must be integer
|
||||
sigma - sigma of mask
|
||||
flat - level of "flat" area sharpen, default 1
|
||||
jagged - level of "jagged" area sharpen, default 2
|
||||
*/
|
||||
Sharp.prototype.sharpen = function(radius, flat, jagged) {
|
||||
if (typeof radius === 'undefined') {
|
||||
Sharp.prototype.sharpen = function(sigma, flat, jagged) {
|
||||
if (!isDefined(sigma)) {
|
||||
// No arguments: default to mild sharpen
|
||||
this.options.sharpenRadius = -1;
|
||||
} else if (typeof radius === 'boolean') {
|
||||
this.options.sharpenSigma = -1;
|
||||
} else if (isBoolean(sigma)) {
|
||||
// Boolean argument: apply mild sharpen?
|
||||
this.options.sharpenRadius = radius ? -1 : 0;
|
||||
} else if (typeof radius === 'number' && !Number.isNaN(radius) && (radius % 1 === 0) && radius >= 1) {
|
||||
// Numeric argument: specific radius
|
||||
this.options.sharpenRadius = radius;
|
||||
this.options.sharpenSigma = sigma ? -1 : 0;
|
||||
} else if (isNumber(sigma) && inRange(sigma, 0.01, 10000)) {
|
||||
// Numeric argument: specific sigma
|
||||
this.options.sharpenSigma = sigma;
|
||||
// Control over flat areas
|
||||
if (typeof flat !== 'undefined' && flat !== null) {
|
||||
if (typeof flat === 'number' && !Number.isNaN(flat) && flat >= 0) {
|
||||
if (isDefined(flat)) {
|
||||
if (isNumber(flat) && inRange(flat, 0, 10000)) {
|
||||
this.options.sharpenFlat = flat;
|
||||
} else {
|
||||
throw new Error('Invalid sharpen level for flat areas ' + flat + ' (expected >= 0)');
|
||||
throw new Error('Invalid sharpen level for flat areas (0 - 10000) ' + flat);
|
||||
}
|
||||
}
|
||||
// Control over jagged areas
|
||||
if (typeof jagged !== 'undefined' && jagged !== null) {
|
||||
if (typeof jagged === 'number' && !Number.isNaN(jagged) && jagged >= 0) {
|
||||
if (isDefined(jagged)) {
|
||||
if (isNumber(jagged) && inRange(jagged, 0, 10000)) {
|
||||
this.options.sharpenJagged = jagged;
|
||||
} else {
|
||||
throw new Error('Invalid sharpen level for jagged areas ' + jagged + ' (expected >= 0)');
|
||||
throw new Error('Invalid sharpen level for jagged areas (0 - 10000) ' + jagged);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error('Invalid sharpen radius ' + radius + ' (expected integer >= 1)');
|
||||
throw new Error('Invalid sharpen sigma (0.01 - 10000) ' + sigma);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
@ -135,10 +135,10 @@ namespace sharp {
|
||||
}
|
||||
|
||||
/*
|
||||
* Gaussian blur (use sigma <0 for fast blur)
|
||||
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
||||
*/
|
||||
VImage Blur(VImage image, double const sigma) {
|
||||
if (sigma < 0.0) {
|
||||
if (sigma == -1.0) {
|
||||
// Fast, mild blur - averages neighbouring pixels
|
||||
VImage blur = VImage::new_matrixv(3, 3,
|
||||
1.0, 1.0, 1.0,
|
||||
@ -153,10 +153,10 @@ namespace sharp {
|
||||
}
|
||||
|
||||
/*
|
||||
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
|
||||
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
|
||||
*/
|
||||
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged) {
|
||||
if (radius == -1) {
|
||||
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged) {
|
||||
if (sigma == -1.0) {
|
||||
// Fast, mild sharpen
|
||||
VImage sharpen = VImage::new_matrixv(3, 3,
|
||||
-1.0, -1.0, -1.0,
|
||||
@ -167,7 +167,7 @@ namespace sharp {
|
||||
} else {
|
||||
// Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas
|
||||
return image.sharpen(
|
||||
VImage::option()->set("radius", radius)->set("m1", flat)->set("m2", jagged)
|
||||
VImage::option()->set("sigma", sigma)->set("m1", flat)->set("m2", jagged)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,14 +25,14 @@ namespace sharp {
|
||||
VImage Gamma(VImage image, double const exponent);
|
||||
|
||||
/*
|
||||
* Gaussian blur. Use sigma of -1 for fast blur.
|
||||
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
||||
*/
|
||||
VImage Blur(VImage image, double const sigma);
|
||||
|
||||
/*
|
||||
* Sharpen flat and jagged areas. Use radius of -1 for fast sharpen.
|
||||
* Sharpen flat and jagged areas. Use sigma of -1.0 for fast sharpen.
|
||||
*/
|
||||
VImage Sharpen(VImage image, int const radius, double const flat, double const jagged);
|
||||
VImage Sharpen(VImage image, double const sigma, double const flat, double const jagged);
|
||||
|
||||
/*
|
||||
Calculate crop area based on image entropy
|
||||
|
@ -444,7 +444,7 @@ class PipelineWorker : public AsyncWorker {
|
||||
|
||||
bool shouldAffineTransform = xresidual != 1.0 || yresidual != 1.0;
|
||||
bool shouldBlur = baton->blurSigma != 0.0;
|
||||
bool shouldSharpen = baton->sharpenRadius != 0;
|
||||
bool shouldSharpen = baton->sharpenSigma != 0.0;
|
||||
bool shouldThreshold = baton->threshold != 0;
|
||||
bool shouldPremultiplyAlpha = HasAlpha(image) &&
|
||||
(shouldAffineTransform || shouldBlur || shouldSharpen || hasOverlay);
|
||||
@ -598,7 +598,7 @@ class PipelineWorker : public AsyncWorker {
|
||||
|
||||
// Sharpen
|
||||
if (shouldSharpen) {
|
||||
image = Sharpen(image, baton->sharpenRadius, baton->sharpenFlat, baton->sharpenJagged);
|
||||
image = Sharpen(image, baton->sharpenSigma, baton->sharpenFlat, baton->sharpenJagged);
|
||||
}
|
||||
|
||||
// Composite with overlay, if present
|
||||
@ -1053,7 +1053,7 @@ NAN_METHOD(pipeline) {
|
||||
baton->flatten = attrAs<bool>(options, "flatten");
|
||||
baton->negate = attrAs<bool>(options, "negate");
|
||||
baton->blurSigma = attrAs<double>(options, "blurSigma");
|
||||
baton->sharpenRadius = attrAs<int32_t>(options, "sharpenRadius");
|
||||
baton->sharpenSigma = attrAs<double>(options, "sharpenSigma");
|
||||
baton->sharpenFlat = attrAs<double>(options, "sharpenFlat");
|
||||
baton->sharpenJagged = attrAs<double>(options, "sharpenJagged");
|
||||
baton->threshold = attrAs<int32_t>(options, "threshold");
|
||||
|
@ -51,7 +51,7 @@ struct PipelineBaton {
|
||||
bool flatten;
|
||||
bool negate;
|
||||
double blurSigma;
|
||||
int sharpenRadius;
|
||||
double sharpenSigma;
|
||||
double sharpenFlat;
|
||||
double sharpenJagged;
|
||||
int threshold;
|
||||
@ -104,7 +104,7 @@ struct PipelineBaton {
|
||||
flatten(false),
|
||||
negate(false),
|
||||
blurSigma(0.0),
|
||||
sharpenRadius(0),
|
||||
sharpenSigma(0.0),
|
||||
sharpenFlat(1.0),
|
||||
sharpenJagged(2.0),
|
||||
threshold(0),
|
||||
|
@ -7,10 +7,10 @@ var fixtures = require('../fixtures');
|
||||
|
||||
describe('Sharpen', function() {
|
||||
|
||||
it('specific radius 10', function(done) {
|
||||
it('specific radius 10 (sigma 6)', function(done) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.sharpen(10)
|
||||
.sharpen(6)
|
||||
.toBuffer(function(err, data, info) {
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@ -19,10 +19,10 @@ describe('Sharpen', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('specific radius 3 and levels 0.5, 2.5', function(done) {
|
||||
it('specific radius 3 (sigma 1.5) and levels 0.5, 2.5', function(done) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.sharpen(3, 0.5, 2.5)
|
||||
.sharpen(1.5, 0.5, 2.5)
|
||||
.toBuffer(function(err, data, info) {
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@ -31,10 +31,10 @@ describe('Sharpen', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('specific radius 5 and levels 2, 4', function(done) {
|
||||
it('specific radius 5 (sigma 3.5) and levels 2, 4', function(done) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.sharpen(5, 2, 4)
|
||||
.sharpen(3.5, 2, 4)
|
||||
.toBuffer(function(err, data, info) {
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
@ -55,9 +55,9 @@ describe('Sharpen', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid radius', function() {
|
||||
it('invalid sigma', function() {
|
||||
assert.throws(function() {
|
||||
sharp(fixtures.inputJpg).sharpen(1.5);
|
||||
sharp(fixtures.inputJpg).sharpen(-1.5);
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user