Skip to content

Commit 875937e

Browse files
BiancoAlovell
authored andcommitted
Expose libvips' median filter operation (lovell#1161)
1 parent f880adb commit 875937e

12 files changed

Lines changed: 105 additions & 1 deletion

File tree

lib/constructor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ const Sharp = function (input, options) {
153153
background: [0, 0, 0, 255],
154154
flatten: false,
155155
negate: false,
156+
medianSize: 0,
156157
blurSigma: 0,
157158
sharpenSigma: 0,
158159
sharpenFlat: 1,

lib/operation.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,26 @@ function sharpen (sigma, flat, jagged) {
156156
return this;
157157
}
158158

159+
/**
160+
* Apply median filter using vips_rank( in, out, m, m, m * m / 2 );
161+
* when used witout parameters the defaul window is 3x3
162+
* @param {Number} [size] square mask size: size x size
163+
* @returns {Sharp}
164+
* @throws {Error} Invalid parameters
165+
*/
166+
function median (size) {
167+
if (!is.defined(size)) {
168+
// No arguments: default to 3x3
169+
this.options.medianSize = 3;
170+
} else if (is.integer(size) && is.inRange(size, 1, 1000)) {
171+
// Numeric argument: specific sigma
172+
this.options.medianSize = size;
173+
} else {
174+
throw new Error('Invalid median size ' + size);
175+
}
176+
return this;
177+
}
178+
159179
/**
160180
* Blur the image.
161181
* When used without parameters, performs a fast, mild blur of the output image.
@@ -444,6 +464,7 @@ module.exports = function (Sharp) {
444464
flip,
445465
flop,
446466
sharpen,
467+
median,
447468
blur,
448469
extend,
449470
flatten,

src/pipeline.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@ class PipelineWorker : public Nan::AsyncWorker {
359359
bool const shouldBlur = baton->blurSigma != 0.0;
360360
bool const shouldConv = baton->convKernelWidth * baton->convKernelHeight > 0;
361361
bool const shouldSharpen = baton->sharpenSigma != 0.0;
362+
bool const shouldApplyMedian = baton->medianSize > 0;
363+
362364
bool const shouldPremultiplyAlpha = HasAlpha(image) &&
363365
(shouldResize || shouldBlur || shouldConv || shouldSharpen || shouldOverlayWithAlpha);
364366

@@ -544,7 +546,10 @@ class PipelineWorker : public Nan::AsyncWorker {
544546
image = image.embed(baton->extendLeft, baton->extendTop, baton->width, baton->height,
545547
VImage::option()->set("extend", VIPS_EXTEND_BACKGROUND)->set("background", background));
546548
}
547-
549+
// Median - must happen before blurring, due to the utility of blurring after thresholding
550+
if (shouldApplyMedian) {
551+
image = image.median(baton->medianSize);
552+
}
548553
// Threshold - must happen before blurring, due to the utility of blurring after thresholding
549554
if (baton->threshold != 0) {
550555
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
@@ -1194,6 +1199,7 @@ NAN_METHOD(pipeline) {
11941199
baton->flatten = AttrTo<bool>(options, "flatten");
11951200
baton->negate = AttrTo<bool>(options, "negate");
11961201
baton->blurSigma = AttrTo<double>(options, "blurSigma");
1202+
baton->medianSize = AttrTo<uint32_t>(options, "medianSize");
11971203
baton->sharpenSigma = AttrTo<double>(options, "sharpenSigma");
11981204
baton->sharpenFlat = AttrTo<double>(options, "sharpenFlat");
11991205
baton->sharpenJagged = AttrTo<double>(options, "sharpenJagged");

src/pipeline.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct PipelineBaton {
7373
bool flatten;
7474
bool negate;
7575
double blurSigma;
76+
int medianSize;
7677
double sharpenSigma;
7778
double sharpenFlat;
7879
double sharpenJagged;
@@ -157,6 +158,7 @@ struct PipelineBaton {
157158
flatten(false),
158159
negate(false),
159160
blurSigma(0.0),
161+
medianSize(0),
160162
sharpenSigma(0.0),
161163
sharpenFlat(1.0),
162164
sharpenJagged(2.0),
19.9 KB
Loading
833 Bytes
Loading
640 Bytes
Loading
11.9 KB
Loading

test/fixtures/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ module.exports = {
6969
inputJpgOverlayLayer2: getPath('alpha-layer-2-ink.jpg'),
7070
inputJpgTruncated: getPath('truncated.jpg'), // head -c 10000 2569067123_aca715a2ee_o.jpg > truncated.jpg
7171
inputJpgCenteredImage: getPath('centered_image.jpeg'),
72+
inputJpgRandom: getPath('random.jpg'), // convert -size 200x200 xc: +noise Random random.jpg
73+
inputJpgThRandom: getPath('thRandom.jpg'), // convert random.jpg -channel G -threshold 5% -separate +channel -negate thRandom.jpg
7274

7375
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
7476
inputPngWithTransparency: getPath('blackbug.png'), // public domain

test/fixtures/random.jpg

68.4 KB
Loading

0 commit comments

Comments
 (0)