mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 02:30:12 +02:00
Expose optional precision parameter of blur operation (#4168)
This commit is contained in:
parent
10c6f474d9
commit
67a4592756
@ -231,7 +231,7 @@ const output = await sharp(input).median(5).toBuffer();
|
||||
|
||||
|
||||
## blur
|
||||
> blur([sigma]) ⇒ <code>Sharp</code>
|
||||
> blur([options]) ⇒ <code>Sharp</code>
|
||||
|
||||
Blur the image.
|
||||
|
||||
@ -245,9 +245,11 @@ When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
|
||||
- <code>Error</code> Invalid parameters
|
||||
|
||||
|
||||
| Param | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [sigma] | <code>number</code> | a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`. |
|
||||
| Param | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [options] | <code>Object</code> \| <code>number</code> \| <code>Boolean</code> | | |
|
||||
| [options.sigma] | <code>number</code> | | a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`. |
|
||||
| [options.precision] | <code>string</code> | <code>"'integer'"</code> | How accurate the operation should be, one of: integer, float, approximate. |
|
||||
|
||||
**Example**
|
||||
```js
|
||||
|
File diff suppressed because one or more lines are too long
@ -226,6 +226,7 @@ const Sharp = function (input, options) {
|
||||
negateAlpha: true,
|
||||
medianSize: 0,
|
||||
blurSigma: 0,
|
||||
precision: 'integer',
|
||||
sharpenSigma: 0,
|
||||
sharpenM1: 1,
|
||||
sharpenM2: 2,
|
||||
|
11
lib/index.d.ts
vendored
11
lib/index.d.ts
vendored
@ -464,7 +464,7 @@ declare namespace sharp {
|
||||
* @throws {Error} Invalid parameters
|
||||
* @returns A sharp instance that can be used to chain operations
|
||||
*/
|
||||
blur(sigma?: number | boolean): Sharp;
|
||||
blur(sigma?: number | boolean | BlurOptions): Sharp;
|
||||
|
||||
/**
|
||||
* Merge alpha transparency channel, if any, with background.
|
||||
@ -1342,6 +1342,15 @@ declare namespace sharp {
|
||||
background?: Color | undefined;
|
||||
}
|
||||
|
||||
type Precision = 'integer' | 'float' | 'approximate';
|
||||
|
||||
interface BlurOptions {
|
||||
/** A value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2` */
|
||||
sigma: number;
|
||||
/** How accurate the operation should be, one of: integer, float, approximate. (optional, default "integer") */
|
||||
precision?: Precision | undefined;
|
||||
}
|
||||
|
||||
interface FlattenOptions {
|
||||
/** background colour, parsed by the color module, defaults to black. (optional, default {r:0,g:0,b:0}) */
|
||||
background?: Color | undefined;
|
||||
|
@ -6,6 +6,17 @@
|
||||
const color = require('color');
|
||||
const is = require('./is');
|
||||
|
||||
/**
|
||||
* How accurate an operation should be.
|
||||
* @member
|
||||
* @private
|
||||
*/
|
||||
const vipsPrecision = {
|
||||
integer: 'integer',
|
||||
float: 'float',
|
||||
approximate: 'approximate'
|
||||
};
|
||||
|
||||
/**
|
||||
* Rotate the output image by either an explicit angle
|
||||
* or auto-orient based on the EXIF `Orientation` tag.
|
||||
@ -367,23 +378,43 @@ function median (size) {
|
||||
* .blur(5)
|
||||
* .toBuffer();
|
||||
*
|
||||
* @param {number} [sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||
* @param {Object|number|Boolean} [options]
|
||||
* @param {number} [options.sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
|
||||
* @param {string} [options.precision='integer'] How accurate the operation should be, one of: integer, float, approximate.
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
function blur (sigma) {
|
||||
if (!is.defined(sigma)) {
|
||||
function blur (options) {
|
||||
let sigma;
|
||||
if (is.number(options)) {
|
||||
sigma = options;
|
||||
} else if (is.plainObject(options)) {
|
||||
if (!is.number(options.sigma)) {
|
||||
throw is.invalidParameterError('options.sigma', 'number between 0.3 and 1000', sigma);
|
||||
}
|
||||
sigma = options.sigma;
|
||||
if ('precision' in options) {
|
||||
if (is.string(vipsPrecision[options.precision])) {
|
||||
this.options.precision = vipsPrecision[options.precision];
|
||||
} else {
|
||||
throw is.invalidParameterError('precision', 'one of: integer, float, approximate', options.precision);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is.defined(options)) {
|
||||
// No arguments: default to mild blur
|
||||
this.options.blurSigma = -1;
|
||||
} else if (is.bool(sigma)) {
|
||||
} else if (is.bool(options)) {
|
||||
// Boolean argument: apply mild blur?
|
||||
this.options.blurSigma = sigma ? -1 : 0;
|
||||
this.options.blurSigma = options ? -1 : 0;
|
||||
} else if (is.number(sigma) && is.inRange(sigma, 0.3, 1000)) {
|
||||
// Numeric argument: specific sigma
|
||||
this.options.blurSigma = sigma;
|
||||
} else {
|
||||
throw is.invalidParameterError('sigma', 'number between 0.3 and 1000', sigma);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ namespace sharp {
|
||||
/*
|
||||
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
||||
*/
|
||||
VImage Blur(VImage image, double const sigma) {
|
||||
VImage Blur(VImage image, double const sigma, VipsPrecision precision) {
|
||||
if (sigma == -1.0) {
|
||||
// Fast, mild blur - averages neighbouring pixels
|
||||
VImage blur = VImage::new_matrixv(3, 3,
|
||||
@ -155,7 +155,8 @@ namespace sharp {
|
||||
return image.conv(blur);
|
||||
} else {
|
||||
// Slower, accurate Gaussian blur
|
||||
return StaySequential(image).gaussblur(sigma);
|
||||
return StaySequential(image).gaussblur(sigma, VImage::option()
|
||||
->set("precision", precision));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ namespace sharp {
|
||||
/*
|
||||
* Gaussian blur. Use sigma of -1.0 for fast blur.
|
||||
*/
|
||||
VImage Blur(VImage image, double const sigma);
|
||||
VImage Blur(VImage image, double const sigma, VipsPrecision precision);
|
||||
|
||||
/*
|
||||
* Convolution with a kernel.
|
||||
|
@ -592,7 +592,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
// Blur
|
||||
if (shouldBlur) {
|
||||
image = sharp::Blur(image, baton->blurSigma);
|
||||
image = sharp::Blur(image, baton->blurSigma, baton->precision);
|
||||
}
|
||||
|
||||
// Unflatten the image
|
||||
@ -1541,6 +1541,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->negate = sharp::AttrAsBool(options, "negate");
|
||||
baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
|
||||
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
||||
baton->precision = sharp::AttrAsEnum<VipsPrecision>(options, "precision", VIPS_TYPE_PRECISION);
|
||||
baton->brightness = sharp::AttrAsDouble(options, "brightness");
|
||||
baton->saturation = sharp::AttrAsDouble(options, "saturation");
|
||||
baton->hue = sharp::AttrAsInt32(options, "hue");
|
||||
|
@ -78,6 +78,7 @@ struct PipelineBaton {
|
||||
bool negate;
|
||||
bool negateAlpha;
|
||||
double blurSigma;
|
||||
VipsPrecision precision;
|
||||
double brightness;
|
||||
double saturation;
|
||||
int hue;
|
||||
|
@ -35,6 +35,19 @@ describe('Blur', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('specific options.sigma 10', function (done) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur({ sigma: 10 })
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('jpeg', info.format);
|
||||
assert.strictEqual(320, info.width);
|
||||
assert.strictEqual(240, info.height);
|
||||
fixtures.assertSimilar(fixtures.expected('blur-10.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('specific radius 0.3', function (done) {
|
||||
sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
@ -91,4 +104,33 @@ describe('Blur', function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('invalid precision', function () {
|
||||
assert.throws(function () {
|
||||
sharp(fixtures.inputJpg).blur({ sigma: 1, precision: 'invalid' });
|
||||
}, /Expected one of: integer, float, approximate for precision but received invalid of type string/);
|
||||
});
|
||||
|
||||
it('specific radius 10 and precision approximate', async () => {
|
||||
const approximate = await sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur({ sigma: 10, precision: 'approximate' })
|
||||
.toBuffer();
|
||||
const integer = await sharp(fixtures.inputJpg)
|
||||
.resize(320, 240)
|
||||
.blur(10)
|
||||
.toBuffer();
|
||||
|
||||
assert.notDeepEqual(approximate, integer);
|
||||
|
||||
await new Promise(resolve => {
|
||||
fixtures.assertSimilar(fixtures.expected('blur-10.jpg'), approximate, resolve);
|
||||
});
|
||||
});
|
||||
|
||||
it('options.sigma is required if options object is passed', function () {
|
||||
assert.throws(function () {
|
||||
sharp(fixtures.inputJpg).blur({ precision: 'invalid' });
|
||||
}, /Expected number between 0.3 and 1000 for options.sigma but received undefined of type undefined/);
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user