Skip to content

Commit 031c808

Browse files
qpinconlovell
authored andcommitted
Expose erode and dilate operations #4243
1 parent 03e1b19 commit 031c808

File tree

17 files changed

+249
-0
lines changed

17 files changed

+249
-0
lines changed

docs/public/humans.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,6 @@ GitHub: https://github.com/happycollision
314314

315315
Name: Florent Zabera
316316
GitHub: https://github.com/florentzabera
317+
318+
Name: Quentin Pinçon
319+
GitHub: https://github.com/qpincon

docs/src/content/docs/api-operation.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,52 @@ const gaussianBlurred = await sharp(input)
284284
```
285285

286286

287+
## dilate
288+
> dilate([width]) ⇒ <code>Sharp</code>
289+
290+
Expand foreground objects using the dilate morphological operator.
291+
292+
293+
**Throws**:
294+
295+
- <code>Error</code> Invalid parameters
296+
297+
298+
| Param | Type | Default | Description |
299+
| --- | --- | --- | --- |
300+
| [width] | <code>Number</code> | <code>1</code> | dilation width in pixels. |
301+
302+
**Example**
303+
```js
304+
const output = await sharp(input)
305+
.dilate()
306+
.toBuffer();
307+
```
308+
309+
310+
## erode
311+
> erode([width]) ⇒ <code>Sharp</code>
312+
313+
Shrink foreground objects using the erode morphological operator.
314+
315+
316+
**Throws**:
317+
318+
- <code>Error</code> Invalid parameters
319+
320+
321+
| Param | Type | Default | Description |
322+
| --- | --- | --- | --- |
323+
| [width] | <code>Number</code> | <code>1</code> | erosion width in pixels. |
324+
325+
**Example**
326+
```js
327+
const output = await sharp(input)
328+
.erode()
329+
.toBuffer();
330+
```
331+
332+
287333
## flatten
288334
> flatten([options]) ⇒ <code>Sharp</code>
289335

docs/src/content/docs/changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ Requires libvips v8.16.1
4545
[#4207](https://github.com/lovell/sharp/pull/4207)
4646
[@calebmer](https://github.com/calebmer)
4747

48+
* Expose erode and dilate operations.
49+
[#4243](https://github.com/lovell/sharp/pull/4243)
50+
[@qpincon](https://github.com/qpincon)
51+
4852
* Add support for RGBE images. Requires libvips compiled with radiance support.
4953
[#4316](https://github.com/lovell/sharp/pull/4316)
5054
[@florentzabera](https://github.com/florentzabera)

lib/constructor.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,8 @@ const Sharp = function (input, options) {
263263
trimBackground: [],
264264
trimThreshold: -1,
265265
trimLineArt: false,
266+
dilateWidth: 0,
267+
erodeWidth: 0,
266268
gamma: 0,
267269
gammaOut: 0,
268270
greyscale: false,

lib/index.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,22 @@ declare namespace sharp {
504504
*/
505505
blur(sigma?: number | boolean | BlurOptions): Sharp;
506506

507+
/**
508+
* Expand foreground objects using the dilate morphological operator.
509+
* @param {Number} [width=1] dilation width in pixels.
510+
* @throws {Error} Invalid parameters
511+
* @returns A sharp instance that can be used to chain operations
512+
*/
513+
dilate(width?: number): Sharp;
514+
515+
/**
516+
* Shrink foreground objects using the erode morphological operator.
517+
* @param {Number} [width=1] erosion width in pixels.
518+
* @throws {Error} Invalid parameters
519+
* @returns A sharp instance that can be used to chain operations
520+
*/
521+
erode(width?: number): Sharp;
522+
507523
/**
508524
* Merge alpha transparency channel, if any, with background.
509525
* @param flatten true to enable and false to disable (defaults to true)

lib/operation.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,52 @@ function blur (options) {
443443
return this;
444444
}
445445

446+
/**
447+
* Expand foreground objects using the dilate morphological operator.
448+
*
449+
* @example
450+
* const output = await sharp(input)
451+
* .dilate()
452+
* .toBuffer();
453+
*
454+
* @param {Number} [width=1] dilation width in pixels.
455+
* @returns {Sharp}
456+
* @throws {Error} Invalid parameters
457+
*/
458+
function dilate (width) {
459+
if (!is.defined(width)) {
460+
this.options.dilateWidth = 1;
461+
} else if (is.integer(width) && width > 0) {
462+
this.options.dilateWidth = width;
463+
} else {
464+
throw is.invalidParameterError('dilate', 'positive integer', dilate);
465+
}
466+
return this;
467+
}
468+
469+
/**
470+
* Shrink foreground objects using the erode morphological operator.
471+
*
472+
* @example
473+
* const output = await sharp(input)
474+
* .erode()
475+
* .toBuffer();
476+
*
477+
* @param {Number} [width=1] erosion width in pixels.
478+
* @returns {Sharp}
479+
* @throws {Error} Invalid parameters
480+
*/
481+
function erode (width) {
482+
if (!is.defined(width)) {
483+
this.options.erodeWidth = 1;
484+
} else if (is.integer(width) && width > 0) {
485+
this.options.erodeWidth = width;
486+
} else {
487+
throw is.invalidParameterError('erode', 'positive integer', erode);
488+
}
489+
return this;
490+
}
491+
446492
/**
447493
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
448494
*
@@ -958,6 +1004,8 @@ module.exports = function (Sharp) {
9581004
flop,
9591005
affine,
9601006
sharpen,
1007+
erode,
1008+
dilate,
9611009
median,
9621010
blur,
9631011
flatten,

src/operations.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,4 +472,26 @@ namespace sharp {
472472
}
473473
}
474474

475+
/*
476+
* Dilate an image
477+
*/
478+
VImage Dilate(VImage image, int const width) {
479+
int const maskWidth = 2 * width + 1;
480+
VImage mask = VImage::new_matrix(maskWidth, maskWidth);
481+
return image.morph(
482+
mask,
483+
VIPS_OPERATION_MORPHOLOGY_DILATE).invert();
484+
}
485+
486+
/*
487+
* Erode an image
488+
*/
489+
VImage Erode(VImage image, int const width) {
490+
int const maskWidth = 2 * width + 1;
491+
VImage mask = VImage::new_matrix(maskWidth, maskWidth);
492+
return image.morph(
493+
mask,
494+
VIPS_OPERATION_MORPHOLOGY_ERODE).invert();
495+
}
496+
475497
} // namespace sharp

src/operations.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ namespace sharp {
120120
VImage EmbedMultiPage(VImage image, int left, int top, int width, int height,
121121
VipsExtend extendWith, std::vector<double> background, int nPages, int *pageHeight);
122122

123+
/*
124+
* Dilate an image
125+
*/
126+
VImage Dilate(VImage image, int const maskWidth);
127+
128+
/*
129+
* Erode an image
130+
*/
131+
VImage Erode(VImage image, int const maskWidth);
123132
} // namespace sharp
124133

125134
#endif // SRC_OPERATIONS_H_

src/pipeline.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,16 @@ class PipelineWorker : public Napi::AsyncWorker {
609609
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
610610
}
611611

612+
// Dilate - must happen before blurring, due to the utility of dilating after thresholding
613+
if (baton->dilateWidth != 0) {
614+
image = sharp::Dilate(image, baton->dilateWidth);
615+
}
616+
617+
// Erode - must happen before blurring, due to the utility of eroding after thresholding
618+
if (baton->erodeWidth != 0) {
619+
image = sharp::Erode(image, baton->erodeWidth);
620+
}
621+
612622
// Blur
613623
if (shouldBlur) {
614624
image = sharp::Blur(image, baton->blurSigma, baton->precision, baton->minAmpl);
@@ -1621,6 +1631,8 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
16211631
baton->gammaOut = sharp::AttrAsDouble(options, "gammaOut");
16221632
baton->linearA = sharp::AttrAsVectorOfDouble(options, "linearA");
16231633
baton->linearB = sharp::AttrAsVectorOfDouble(options, "linearB");
1634+
baton->dilateWidth = sharp::AttrAsUint32(options, "dilateWidth");
1635+
baton->erodeWidth = sharp::AttrAsUint32(options, "erodeWidth");
16241636
baton->greyscale = sharp::AttrAsBool(options, "greyscale");
16251637
baton->normalise = sharp::AttrAsBool(options, "normalise");
16261638
baton->normaliseLower = sharp::AttrAsUint32(options, "normaliseLower");

src/pipeline.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ struct PipelineBaton {
101101
int trimOffsetTop;
102102
std::vector<double> linearA;
103103
std::vector<double> linearB;
104+
int dilateWidth;
105+
int erodeWidth;
104106
double gamma;
105107
double gammaOut;
106108
bool greyscale;
@@ -274,6 +276,8 @@ struct PipelineBaton {
274276
trimOffsetTop(0),
275277
linearA{},
276278
linearB{},
279+
dilateWidth(0),
280+
erodeWidth(0),
277281
gamma(0.0),
278282
greyscale(false),
279283
normalise(false),

0 commit comments

Comments
 (0)