Expose optional minAmplitude parameter of blur operation (#4172)

This commit is contained in:
Marcos Casagrande 2024-07-23 12:31:11 +02:00 committed by GitHub
parent 67a5854b89
commit 735fee74db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 45 additions and 5 deletions

View File

@ -250,6 +250,7 @@ When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
| [options] | <code>Object</code> \| <code>number</code> \| <code>Boolean</code> | | | | [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.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>&quot;&#x27;integer&#x27;&quot;</code> | How accurate the operation should be, one of: integer, float, approximate. | | [options.precision] | <code>string</code> | <code>&quot;&#x27;integer&#x27;&quot;</code> | How accurate the operation should be, one of: integer, float, approximate. |
| [options.minAmplitude] | <code>number</code> | <code>0.2</code> | A value between 0.001 and 1. A smaller value will generate a larger, more accurate mask. |
**Example** **Example**
```js ```js

File diff suppressed because one or more lines are too long

View File

@ -227,6 +227,7 @@ const Sharp = function (input, options) {
medianSize: 0, medianSize: 0,
blurSigma: 0, blurSigma: 0,
precision: 'integer', precision: 'integer',
minAmpl: 0.2,
sharpenSigma: 0, sharpenSigma: 0,
sharpenM1: 1, sharpenM1: 1,
sharpenM2: 2, sharpenM2: 2,

2
lib/index.d.ts vendored
View File

@ -1347,6 +1347,8 @@ declare namespace sharp {
interface BlurOptions { interface BlurOptions {
/** A value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2` */ /** A value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2` */
sigma: number; sigma: number;
/** A value between 0.001 and 1. A smaller value will generate a larger, more accurate mask. */
minAmplitude?: number;
/** How accurate the operation should be, one of: integer, float, approximate. (optional, default "integer") */ /** How accurate the operation should be, one of: integer, float, approximate. (optional, default "integer") */
precision?: Precision | undefined; precision?: Precision | undefined;
} }

View File

@ -381,6 +381,7 @@ function median (size) {
* @param {Object|number|Boolean} [options] * @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 {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. * @param {string} [options.precision='integer'] How accurate the operation should be, one of: integer, float, approximate.
* @param {number} [options.minAmplitude=0.2] A value between 0.001 and 1. A smaller value will generate a larger, more accurate mask.
* @returns {Sharp} * @returns {Sharp}
* @throws {Error} Invalid parameters * @throws {Error} Invalid parameters
*/ */
@ -400,6 +401,13 @@ function blur (options) {
throw is.invalidParameterError('precision', 'one of: integer, float, approximate', options.precision); throw is.invalidParameterError('precision', 'one of: integer, float, approximate', options.precision);
} }
} }
if ('minAmplitude' in options) {
if (is.number(options.minAmplitude) && is.inRange(options.minAmplitude, 0.001, 1)) {
this.options.minAmpl = options.minAmplitude;
} else {
throw is.invalidParameterError('minAmplitude', 'number between 0.001 and 1', options.minAmplitude);
}
}
} }
if (!is.defined(options)) { if (!is.defined(options)) {

View File

@ -144,7 +144,7 @@ namespace sharp {
/* /*
* Gaussian blur. Use sigma of -1.0 for fast blur. * Gaussian blur. Use sigma of -1.0 for fast blur.
*/ */
VImage Blur(VImage image, double const sigma, VipsPrecision precision) { VImage Blur(VImage image, double const sigma, VipsPrecision precision, double const minAmpl) {
if (sigma == -1.0) { if (sigma == -1.0) {
// Fast, mild blur - averages neighbouring pixels // Fast, mild blur - averages neighbouring pixels
VImage blur = VImage::new_matrixv(3, 3, VImage blur = VImage::new_matrixv(3, 3,
@ -156,7 +156,8 @@ namespace sharp {
} else { } else {
// Slower, accurate Gaussian blur // Slower, accurate Gaussian blur
return StaySequential(image).gaussblur(sigma, VImage::option() return StaySequential(image).gaussblur(sigma, VImage::option()
->set("precision", precision)); ->set("precision", precision)
->set("min_ampl", minAmpl));
} }
} }

View File

@ -47,7 +47,7 @@ namespace sharp {
/* /*
* Gaussian blur. Use sigma of -1.0 for fast blur. * Gaussian blur. Use sigma of -1.0 for fast blur.
*/ */
VImage Blur(VImage image, double const sigma, VipsPrecision precision); VImage Blur(VImage image, double const sigma, VipsPrecision precision, double const minAmpl);
/* /*
* Convolution with a kernel. * Convolution with a kernel.

View File

@ -592,7 +592,7 @@ class PipelineWorker : public Napi::AsyncWorker {
// Blur // Blur
if (shouldBlur) { if (shouldBlur) {
image = sharp::Blur(image, baton->blurSigma, baton->precision); image = sharp::Blur(image, baton->blurSigma, baton->precision, baton->minAmpl);
} }
// Unflatten the image // Unflatten the image
@ -1542,6 +1542,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha"); baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma"); baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
baton->precision = sharp::AttrAsEnum<VipsPrecision>(options, "precision", VIPS_TYPE_PRECISION); baton->precision = sharp::AttrAsEnum<VipsPrecision>(options, "precision", VIPS_TYPE_PRECISION);
baton->minAmpl = sharp::AttrAsDouble(options, "minAmpl");
baton->brightness = sharp::AttrAsDouble(options, "brightness"); baton->brightness = sharp::AttrAsDouble(options, "brightness");
baton->saturation = sharp::AttrAsDouble(options, "saturation"); baton->saturation = sharp::AttrAsDouble(options, "saturation");
baton->hue = sharp::AttrAsInt32(options, "hue"); baton->hue = sharp::AttrAsInt32(options, "hue");

View File

@ -79,6 +79,7 @@ struct PipelineBaton {
bool negateAlpha; bool negateAlpha;
double blurSigma; double blurSigma;
VipsPrecision precision; VipsPrecision precision;
double minAmpl;
double brightness; double brightness;
double saturation; double saturation;
int hue; int hue;

View File

@ -63,6 +63,7 @@ sharp().blur();
sharp().blur(1); sharp().blur(1);
sharp().blur({ sigma: 1 }); sharp().blur({ sigma: 1 });
sharp().blur({ sigma: 1, precision: 'approximate' }); sharp().blur({ sigma: 1, precision: 'approximate' });
sharp().blur({ sigma: 1, minAmplitude: 0.8 });
sharp({ sharp({
create: { create: {

View File

@ -111,6 +111,16 @@ describe('Blur', function () {
}, /Expected one of: integer, float, approximate for precision but received invalid of type string/); }, /Expected one of: integer, float, approximate for precision but received invalid of type string/);
}); });
it('invalid minAmplitude', function () {
assert.throws(function () {
sharp(fixtures.inputJpg).blur({ sigma: 1, minAmplitude: 0 });
}, /Expected number between 0.001 and 1 for minAmplitude but received 0 of type number/);
assert.throws(function () {
sharp(fixtures.inputJpg).blur({ sigma: 1, minAmplitude: 1.01 });
}, /Expected number between 0.001 and 1 for minAmplitude but received 1.01 of type number/);
});
it('specific radius 10 and precision approximate', async () => { it('specific radius 10 and precision approximate', async () => {
const approximate = await sharp(fixtures.inputJpg) const approximate = await sharp(fixtures.inputJpg)
.resize(320, 240) .resize(320, 240)
@ -125,6 +135,20 @@ describe('Blur', function () {
await fixtures.assertSimilar(fixtures.expected('blur-10.jpg'), approximate); await fixtures.assertSimilar(fixtures.expected('blur-10.jpg'), approximate);
}); });
it('specific radius 10 and minAmplitude 0.01', async () => {
const minAmplitudeLow = await sharp(fixtures.inputJpg)
.resize(320, 240)
.blur({ sigma: 10, minAmplitude: 0.01 })
.toBuffer();
const minAmplitudeDefault = await sharp(fixtures.inputJpg)
.resize(320, 240)
.blur(10)
.toBuffer();
assert.notDeepEqual(minAmplitudeLow, minAmplitudeDefault);
await fixtures.assertSimilar(fixtures.expected('blur-10.jpg'), minAmplitudeLow);
});
it('options.sigma is required if options object is passed', function () { it('options.sigma is required if options object is passed', function () {
assert.throws(function () { assert.throws(function () {
sharp(fixtures.inputJpg).blur({ precision: 'invalid' }); sharp(fixtures.inputJpg).blur({ precision: 'invalid' });