diff --git a/docs/api.md b/docs/api.md index 2249c213d..f4bd9915e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -391,12 +391,14 @@ When a `sigma` is provided, performs a slower, more accurate sharpen of the L ch * `flat`, if present, is a Number representing the level of sharpening to apply to "flat" areas, defaulting to a value of 1.0. * `jagged`, if present, is a Number representing the level of sharpening to apply to "jagged" areas, defaulting to a value of 2.0. -#### threshold([threshold]) +#### threshold([threshold], [options]) Converts all pixels in the image to greyscale white or black. Any pixel greather-than-or-equal-to the threshold (0..255) will be white. All others will be black. * `threshold`, if present, is a Number, representing the level above which pixels will be forced to white. +* `options`, an options object that may contain a boolean `grayscale` or `greyscale`. When `grayscale` is `true`, `threshold` returns a black and white image, and when `false` a color image with each channel thresholded independently. The default is `grayscale: true` when omitted. + #### gamma([gamma]) Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma` then increasing the encoding (brighten) post-resize at a factor of `gamma`. diff --git a/index.js b/index.js index bd3f95559..a19c94759 100644 --- a/index.js +++ b/index.js @@ -85,6 +85,7 @@ var Sharp = function(input, options) { sharpenFlat: 1, sharpenJagged: 2, threshold: 0, + thresholdGrayscale: true, gamma: 0, greyscale: false, normalize: 0, @@ -487,7 +488,7 @@ Sharp.prototype.sharpen = function(sigma, flat, jagged) { return this; }; -Sharp.prototype.threshold = function(threshold) { +Sharp.prototype.threshold = function(threshold, options) { if (typeof threshold === 'undefined') { this.options.threshold = 128; } else if (typeof threshold === 'boolean') { @@ -497,6 +498,14 @@ Sharp.prototype.threshold = function(threshold) { } else { throw new Error('Invalid threshold (0 to 255) ' + threshold); } + + if(typeof options === 'undefined' || + options.greyscale === true || options.grayscale === true) { + this.options.thresholdGrayscale = true; + } else { + this.options.thresholdGrayscale = false; + } + return this; }; diff --git a/src/operations.cc b/src/operations.cc index 8e146a5f3..2cae8a889 100644 --- a/src/operations.cc +++ b/src/operations.cc @@ -327,4 +327,11 @@ namespace sharp { ); } + VImage Threshold(VImage image, double const threshold, bool const thresholdGrayscale) { + if(!thresholdGrayscale) { + return image >= threshold; + } + return image.colourspace(VIPS_INTERPRETATION_B_W) >= threshold; + } + } // namespace sharp diff --git a/src/operations.h b/src/operations.h index fa10b7d2f..044807b4e 100644 --- a/src/operations.h +++ b/src/operations.h @@ -54,6 +54,11 @@ namespace sharp { */ VImage TileCache(VImage image, double const factor); + /* + Threshold an image + */ + VImage Threshold(VImage image, double const threshold, bool const thresholdColor); + } // namespace sharp #endif // SRC_OPERATIONS_H_ diff --git a/src/pipeline.cc b/src/pipeline.cc index 074645406..85662f1ed 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -52,6 +52,7 @@ using sharp::Blur; using sharp::Sharpen; using sharp::EntropyCrop; using sharp::TileCache; +using sharp::Threshold; using sharp::ImageType; using sharp::ImageTypeId; @@ -625,7 +626,7 @@ class PipelineWorker : public AsyncWorker { // Threshold - must happen before blurring, due to the utility of blurring after thresholding if (shouldThreshold) { - image = image.colourspace(VIPS_INTERPRETATION_B_W) >= baton->threshold; + image = Threshold(image, baton->threshold, baton->thresholdGrayscale); } // Blur @@ -1107,6 +1108,7 @@ NAN_METHOD(pipeline) { baton->sharpenFlat = attrAs(options, "sharpenFlat"); baton->sharpenJagged = attrAs(options, "sharpenJagged"); baton->threshold = attrAs(options, "threshold"); + baton->thresholdGrayscale = attrAs(options, "thresholdGrayscale"); baton->gamma = attrAs(options, "gamma"); baton->greyscale = attrAs(options, "greyscale"); baton->normalize = attrAs(options, "normalize"); diff --git a/src/pipeline.h b/src/pipeline.h index 5a6b9a649..8581eedb4 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -58,6 +58,7 @@ struct PipelineBaton { double sharpenFlat; double sharpenJagged; int threshold; + bool thresholdGrayscale; double gamma; bool greyscale; bool normalize; @@ -113,6 +114,7 @@ struct PipelineBaton { sharpenFlat(1.0), sharpenJagged(2.0), threshold(0), + thresholdGrayscale(true), gamma(0.0), greyscale(false), normalize(false), diff --git a/test/fixtures/expected/threshold-color-128.jpg b/test/fixtures/expected/threshold-color-128.jpg new file mode 100644 index 000000000..54703d2e2 Binary files /dev/null and b/test/fixtures/expected/threshold-color-128.jpg differ diff --git a/test/unit/threshold.js b/test/unit/threshold.js index 7609e5825..d4fb43cbc 100644 --- a/test/unit/threshold.js +++ b/test/unit/threshold.js @@ -52,8 +52,21 @@ describe('Threshold', function() { assert.strictEqual(240, info.height); fixtures.assertSimilar(fixtures.expected('threshold-128.jpg'), data, done); }); + }); + + it('threshold grayscale: true (=128)', function(done) { + sharp(fixtures.inputJpg) + .resize(320, 240) + .threshold(128,{'grayscale':true}) + .toBuffer(function(err, data, info) { + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + fixtures.assertSimilar(fixtures.expected('threshold-128.jpg'), data, done); + }); }); + it('threshold default jpeg', function(done) { sharp(fixtures.inputJpg) .resize(320, 240) @@ -101,6 +114,18 @@ describe('Threshold', function() { }); } + it('color threshold', function(done) { + sharp(fixtures.inputJpg) + .resize(320, 240) + .threshold(128,{'grayscale':false}) + .toBuffer(function(err, data, info) { + assert.strictEqual('jpeg', info.format); + assert.strictEqual(320, info.width); + assert.strictEqual(240, info.height); + fixtures.assertSimilar(fixtures.expected('threshold-color-128.jpg'), data, done); + }); + }); + it('invalid threshold -1', function() { assert.throws(function() { sharp(fixtures.inputJpg).threshold(-1);