mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Expose erode and dilate operations #4243
This commit is contained in:
parent
03e1b19764
commit
031c808aa5
@ -314,3 +314,6 @@ GitHub: https://github.com/happycollision
|
|||||||
|
|
||||||
Name: Florent Zabera
|
Name: Florent Zabera
|
||||||
GitHub: https://github.com/florentzabera
|
GitHub: https://github.com/florentzabera
|
||||||
|
|
||||||
|
Name: Quentin Pinçon
|
||||||
|
GitHub: https://github.com/qpincon
|
||||||
|
@ -284,6 +284,52 @@ const gaussianBlurred = await sharp(input)
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## dilate
|
||||||
|
> dilate([width]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
|
Expand foreground objects using the dilate morphological operator.
|
||||||
|
|
||||||
|
|
||||||
|
**Throws**:
|
||||||
|
|
||||||
|
- <code>Error</code> Invalid parameters
|
||||||
|
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| [width] | <code>Number</code> | <code>1</code> | dilation width in pixels. |
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
```js
|
||||||
|
const output = await sharp(input)
|
||||||
|
.dilate()
|
||||||
|
.toBuffer();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## erode
|
||||||
|
> erode([width]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
|
Shrink foreground objects using the erode morphological operator.
|
||||||
|
|
||||||
|
|
||||||
|
**Throws**:
|
||||||
|
|
||||||
|
- <code>Error</code> Invalid parameters
|
||||||
|
|
||||||
|
|
||||||
|
| Param | Type | Default | Description |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| [width] | <code>Number</code> | <code>1</code> | erosion width in pixels. |
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
```js
|
||||||
|
const output = await sharp(input)
|
||||||
|
.erode()
|
||||||
|
.toBuffer();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## flatten
|
## flatten
|
||||||
> flatten([options]) ⇒ <code>Sharp</code>
|
> flatten([options]) ⇒ <code>Sharp</code>
|
||||||
|
|
||||||
|
@ -45,6 +45,10 @@ Requires libvips v8.16.1
|
|||||||
[#4207](https://github.com/lovell/sharp/pull/4207)
|
[#4207](https://github.com/lovell/sharp/pull/4207)
|
||||||
[@calebmer](https://github.com/calebmer)
|
[@calebmer](https://github.com/calebmer)
|
||||||
|
|
||||||
|
* Expose erode and dilate operations.
|
||||||
|
[#4243](https://github.com/lovell/sharp/pull/4243)
|
||||||
|
[@qpincon](https://github.com/qpincon)
|
||||||
|
|
||||||
* Add support for RGBE images. Requires libvips compiled with radiance support.
|
* Add support for RGBE images. Requires libvips compiled with radiance support.
|
||||||
[#4316](https://github.com/lovell/sharp/pull/4316)
|
[#4316](https://github.com/lovell/sharp/pull/4316)
|
||||||
[@florentzabera](https://github.com/florentzabera)
|
[@florentzabera](https://github.com/florentzabera)
|
||||||
|
@ -263,6 +263,8 @@ const Sharp = function (input, options) {
|
|||||||
trimBackground: [],
|
trimBackground: [],
|
||||||
trimThreshold: -1,
|
trimThreshold: -1,
|
||||||
trimLineArt: false,
|
trimLineArt: false,
|
||||||
|
dilateWidth: 0,
|
||||||
|
erodeWidth: 0,
|
||||||
gamma: 0,
|
gamma: 0,
|
||||||
gammaOut: 0,
|
gammaOut: 0,
|
||||||
greyscale: false,
|
greyscale: false,
|
||||||
|
16
lib/index.d.ts
vendored
16
lib/index.d.ts
vendored
@ -504,6 +504,22 @@ declare namespace sharp {
|
|||||||
*/
|
*/
|
||||||
blur(sigma?: number | boolean | BlurOptions): Sharp;
|
blur(sigma?: number | boolean | BlurOptions): Sharp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand foreground objects using the dilate morphological operator.
|
||||||
|
* @param {Number} [width=1] dilation width in pixels.
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
* @returns A sharp instance that can be used to chain operations
|
||||||
|
*/
|
||||||
|
dilate(width?: number): Sharp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shrink foreground objects using the erode morphological operator.
|
||||||
|
* @param {Number} [width=1] erosion width in pixels.
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
* @returns A sharp instance that can be used to chain operations
|
||||||
|
*/
|
||||||
|
erode(width?: number): Sharp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge alpha transparency channel, if any, with background.
|
* Merge alpha transparency channel, if any, with background.
|
||||||
* @param flatten true to enable and false to disable (defaults to true)
|
* @param flatten true to enable and false to disable (defaults to true)
|
||||||
|
@ -443,6 +443,52 @@ function blur (options) {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand foreground objects using the dilate morphological operator.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const output = await sharp(input)
|
||||||
|
* .dilate()
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
|
* @param {Number} [width=1] dilation width in pixels.
|
||||||
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
*/
|
||||||
|
function dilate (width) {
|
||||||
|
if (!is.defined(width)) {
|
||||||
|
this.options.dilateWidth = 1;
|
||||||
|
} else if (is.integer(width) && width > 0) {
|
||||||
|
this.options.dilateWidth = width;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('dilate', 'positive integer', dilate);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shrink foreground objects using the erode morphological operator.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const output = await sharp(input)
|
||||||
|
* .erode()
|
||||||
|
* .toBuffer();
|
||||||
|
*
|
||||||
|
* @param {Number} [width=1] erosion width in pixels.
|
||||||
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid parameters
|
||||||
|
*/
|
||||||
|
function erode (width) {
|
||||||
|
if (!is.defined(width)) {
|
||||||
|
this.options.erodeWidth = 1;
|
||||||
|
} else if (is.integer(width) && width > 0) {
|
||||||
|
this.options.erodeWidth = width;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('erode', 'positive integer', erode);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
|
||||||
*
|
*
|
||||||
@ -958,6 +1004,8 @@ module.exports = function (Sharp) {
|
|||||||
flop,
|
flop,
|
||||||
affine,
|
affine,
|
||||||
sharpen,
|
sharpen,
|
||||||
|
erode,
|
||||||
|
dilate,
|
||||||
median,
|
median,
|
||||||
blur,
|
blur,
|
||||||
flatten,
|
flatten,
|
||||||
|
@ -472,4 +472,26 @@ namespace sharp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dilate an image
|
||||||
|
*/
|
||||||
|
VImage Dilate(VImage image, int const width) {
|
||||||
|
int const maskWidth = 2 * width + 1;
|
||||||
|
VImage mask = VImage::new_matrix(maskWidth, maskWidth);
|
||||||
|
return image.morph(
|
||||||
|
mask,
|
||||||
|
VIPS_OPERATION_MORPHOLOGY_DILATE).invert();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Erode an image
|
||||||
|
*/
|
||||||
|
VImage Erode(VImage image, int const width) {
|
||||||
|
int const maskWidth = 2 * width + 1;
|
||||||
|
VImage mask = VImage::new_matrix(maskWidth, maskWidth);
|
||||||
|
return image.morph(
|
||||||
|
mask,
|
||||||
|
VIPS_OPERATION_MORPHOLOGY_ERODE).invert();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
@ -120,6 +120,15 @@ namespace sharp {
|
|||||||
VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
|
VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
|
||||||
VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight);
|
VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dilate an image
|
||||||
|
*/
|
||||||
|
VImage Dilate(VImage image, int const maskWidth);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Erode an image
|
||||||
|
*/
|
||||||
|
VImage Erode(VImage image, int const maskWidth);
|
||||||
} // namespace sharp
|
} // namespace sharp
|
||||||
|
|
||||||
#endif // SRC_OPERATIONS_H_
|
#endif // SRC_OPERATIONS_H_
|
||||||
|
@ -609,6 +609,16 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|||||||
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
|
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dilate - must happen before blurring, due to the utility of dilating after thresholding
|
||||||
|
if (baton->dilateWidth != 0) {
|
||||||
|
image = sharp::Dilate(image, baton->dilateWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erode - must happen before blurring, due to the utility of eroding after thresholding
|
||||||
|
if (baton->erodeWidth != 0) {
|
||||||
|
image = sharp::Erode(image, baton->erodeWidth);
|
||||||
|
}
|
||||||
|
|
||||||
// Blur
|
// Blur
|
||||||
if (shouldBlur) {
|
if (shouldBlur) {
|
||||||
image = sharp::Blur(image, baton->blurSigma, baton->precision, baton->minAmpl);
|
image = sharp::Blur(image, baton->blurSigma, baton->precision, baton->minAmpl);
|
||||||
@ -1621,6 +1631,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|||||||
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
|
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
|
||||||
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
|
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
|
||||||
baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
|
baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
|
||||||
|
baton->dilateWidth = sharp::AttrAsUint32(options, "dilateWidth");
|
||||||
|
baton->erodeWidth = sharp::AttrAsUint32(options, "erodeWidth");
|
||||||
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
|
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
|
||||||
baton->normalise = sharp::AttrAsBool(options, "normalise");
|
baton->normalise = sharp::AttrAsBool(options, "normalise");
|
||||||
baton->normaliseLower = sharp::AttrAsUint32(options, "normaliseLower");
|
baton->normaliseLower = sharp::AttrAsUint32(options, "normaliseLower");
|
||||||
|
@ -101,6 +101,8 @@ struct PipelineBaton {
|
|||||||
int trimOffsetTop;
|
int trimOffsetTop;
|
||||||
std::vector<double> linearA;
|
std::vector<double> linearA;
|
||||||
std::vector<double> linearB;
|
std::vector<double> linearB;
|
||||||
|
int dilateWidth;
|
||||||
|
int erodeWidth;
|
||||||
double gamma;
|
double gamma;
|
||||||
double gammaOut;
|
double gammaOut;
|
||||||
bool greyscale;
|
bool greyscale;
|
||||||
@ -274,6 +276,8 @@ struct PipelineBaton {
|
|||||||
trimOffsetTop(0),
|
trimOffsetTop(0),
|
||||||
linearA{},
|
linearA{},
|
||||||
linearB{},
|
linearB{},
|
||||||
|
dilateWidth(0),
|
||||||
|
erodeWidth(0),
|
||||||
gamma(0.0),
|
gamma(0.0),
|
||||||
greyscale(false),
|
greyscale(false),
|
||||||
normalise(false),
|
normalise(false),
|
||||||
|
BIN
test/fixtures/dot-and-lines.png
vendored
Normal file
BIN
test/fixtures/dot-and-lines.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 463 B |
BIN
test/fixtures/expected/dilate-1.png
vendored
Normal file
BIN
test/fixtures/expected/dilate-1.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 540 B |
BIN
test/fixtures/expected/erode-1.png
vendored
Normal file
BIN
test/fixtures/expected/erode-1.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 450 B |
2
test/fixtures/index.js
vendored
2
test/fixtures/index.js
vendored
@ -128,6 +128,8 @@ module.exports = {
|
|||||||
|
|
||||||
inputJPGBig: getPath('flowers.jpeg'),
|
inputJPGBig: getPath('flowers.jpeg'),
|
||||||
|
|
||||||
|
inputPngDotAndLines: getPath('dot-and-lines.png'),
|
||||||
|
|
||||||
inputPngStripesV: getPath('stripesV.png'),
|
inputPngStripesV: getPath('stripesV.png'),
|
||||||
inputPngStripesH: getPath('stripesH.png'),
|
inputPngStripesH: getPath('stripesH.png'),
|
||||||
|
|
||||||
|
@ -740,3 +740,8 @@ sharp([input, input], {
|
|||||||
valign: 'bottom'
|
valign: 'bottom'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
sharp().erode();
|
||||||
|
sharp().erode(1);
|
||||||
|
sharp().dilate();
|
||||||
|
sharp().dilate(1);
|
||||||
|
38
test/unit/dilate.js
Normal file
38
test/unit/dilate.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('Dilate', function () {
|
||||||
|
it('dilate 1 png', function (done) {
|
||||||
|
sharp(fixtures.inputPngDotAndLines)
|
||||||
|
.dilate(1)
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('dilate-1.png'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('dilate 1 png - default width', function (done) {
|
||||||
|
sharp(fixtures.inputPngDotAndLines)
|
||||||
|
.dilate()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('dilate-1.png'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid dilation width', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpg).dilate(-1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
38
test/unit/erode.js
Normal file
38
test/unit/erode.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
const fixtures = require('../fixtures');
|
||||||
|
|
||||||
|
describe('Erode', function () {
|
||||||
|
it('erode 1 png', function (done) {
|
||||||
|
sharp(fixtures.inputPngDotAndLines)
|
||||||
|
.erode(1)
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('erode-1.png'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('erode 1 png - default width', function (done) {
|
||||||
|
sharp(fixtures.inputPngDotAndLines)
|
||||||
|
.erode()
|
||||||
|
.toBuffer(function (err, data, info) {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('png', info.format);
|
||||||
|
assert.strictEqual(100, info.width);
|
||||||
|
assert.strictEqual(100, info.height);
|
||||||
|
fixtures.assertSimilar(fixtures.expected('erode-1.png'), data, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid erosion width', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp(fixtures.inputJpg).erode(-1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user