Skip to content

Commit

Permalink
Merge df2cd74 into 65b7f7d
Browse files Browse the repository at this point in the history
  • Loading branch information
kleisauke committed Jul 8, 2016
2 parents 65b7f7d + df2cd74 commit cd5958a
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 6 deletions.
30 changes: 24 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ var Sharp = function(input, options) {
sharpenJagged: 2,
threshold: 0,
thresholdGrayscale: true,
trimTolerance: 0,
gamma: 0,
greyscale: false,
normalize: 0,
Expand Down Expand Up @@ -364,13 +365,13 @@ Sharp.prototype.overlayWith = function(overlay, options) {
setTileOption(options.tile, this.options);
}
if(isDefined(options.cutout)) {
setCutoutOption(options.cutout, this.options);
setCutoutOption(options.cutout, this.options);
}
if(isDefined(options.left) || isDefined(options.top)) {
setOffsetOption(options.top, options.left, this.options);
setOffsetOption(options.top, options.left, this.options);
}
if (isDefined(options.gravity)) {
setGravityOption(options.gravity, this.options);
setGravityOption(options.gravity, this.options);
}
}
return this;
Expand All @@ -384,7 +385,7 @@ function setTileOption(tile, options) {
options.overlayTile = tile;
} else {
throw new Error('Invalid Value for tile ' + tile + ' Only Boolean Values allowed for overlay.tile.');
}
}
}

function setCutoutOption(cutout, options) {
Expand Down Expand Up @@ -553,14 +554,31 @@ Sharp.prototype.threshold = function(threshold, options) {
} 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;
};

/*
Automatically remove "boring" image edges.
tolerance - if present, is a percentaged tolerance level between 0 and 100 to trim away similar color values
Defaulting to 10 when no tolerance is given.
*/
Sharp.prototype.trim = function(tolerance) {
if (typeof tolerance === 'undefined') {
this.options.trimTolerance = 10;
} else if (isInteger(tolerance) && inRange(tolerance, 0, 100)) {
this.options.trimTolerance = tolerance;
} else {
throw new Error('Invalid trim tolerance (0 to 100) ' + tolerance);
}

return this;
};

Expand Down
4 changes: 4 additions & 0 deletions src/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ namespace sharp {
std::tuple<int, int> CalculateCrop(int const inWidth, int const inHeight,
int const outWidth, int const outHeight, int const x, int const y);

/*
Return the image alpha maximum. Useful for combining alpha bands. scRGB
images are 0 - 1 for image data, but the alpha is 0 - 255.
*/
int MaximumImageAlpha(VipsInterpretation interpretation);


Expand Down
45 changes: 45 additions & 0 deletions src/operations.cc
Original file line number Diff line number Diff line change
Expand Up @@ -399,4 +399,49 @@ namespace sharp {
return image.bandbool(boolean);
}

VImage Trim(VImage image, int const tolerance) {
using sharp::MaximumImageAlpha;
// An equivalent of ImageMagick's -trim in C++ ... automatically remove
// "boring" image edges.

// We use .project to sum the rows and columns of a 0/255 mask image, the first
// non-zero row or column is the object edge. We make the mask image with an
// amount-different-from-background image plus a threshold.

// find the value of the pixel at (0, 0) ... we will search for all pixels
// significantly different from this
std::vector<double> background = image(0, 0);

int max = MaximumImageAlpha(image.interpretation());

// we need to smooth the image, subtract the background from every pixel, take
// the absolute value of the difference, then threshold
VImage mask = (image.median(3) - background).abs() > (max * tolerance / 100);

// sum mask rows and columns, then search for the first non-zero sum in each
// direction
VImage rows;
VImage columns = mask.project(&rows);

VImage profileLeftV;
VImage profileLeftH = columns.profile(&profileLeftV);

VImage profileRightV;
VImage profileRightH = columns.fliphor().profile(&profileRightV);

VImage profileTopV;
VImage profileTopH = rows.profile(&profileTopV);

VImage profileBottomV;
VImage profileBottomH = rows.flipver().profile(&profileBottomV);

int left = static_cast<int>(floor(profileLeftV.min()));
int right = columns.width() - static_cast<int>(floor(profileRightV.min()));
int top = static_cast<int>(floor(profileTopH.min()));
int bottom = rows.height() - static_cast<int>(floor(profileBottomH.min()));

// and now crop the original image
return image.extract_area(left, top, right - left, bottom - top);
}

} // namespace sharp
5 changes: 5 additions & 0 deletions src/operations.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ namespace sharp {
*/
VImage Bandbool(VImage image, VipsOperationBoolean const boolean);

/*
Trim an image
*/
VImage Trim(VImage image, int const tolerance);

} // namespace sharp

#endif // SRC_OPERATIONS_H_
10 changes: 10 additions & 0 deletions src/pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ using sharp::EntropyCrop;
using sharp::TileCache;
using sharp::Threshold;
using sharp::Bandbool;
using sharp::Trim;

using sharp::ImageType;
using sharp::ImageTypeId;
Expand Down Expand Up @@ -207,6 +208,11 @@ class PipelineWorker : public AsyncWorker {
RemoveExifOrientation(image);
}

// Trim
if(baton->trimTolerance != 0) {
image = Trim(image, baton->trimTolerance);
}

// Pre extraction
if (baton->topOffsetPre != -1) {
image = image.extract_area(baton->leftOffsetPre, baton->topOffsetPre, baton->widthPre, baton->heightPre);
Expand Down Expand Up @@ -1145,6 +1151,10 @@ NAN_METHOD(pipeline) {
baton->sharpenJagged = attrAs<double>(options, "sharpenJagged");
baton->threshold = attrAs<int32_t>(options, "threshold");
baton->thresholdGrayscale = attrAs<bool>(options, "thresholdGrayscale");
baton->trimTolerance = attrAs<int32_t>(options, "trimTolerance");
if(baton->accessMethod == VIPS_ACCESS_SEQUENTIAL && baton->trimTolerance != 0) {
baton->accessMethod = VIPS_ACCESS_RANDOM;
}
baton->gamma = attrAs<double>(options, "gamma");
baton->greyscale = attrAs<bool>(options, "greyscale");
baton->normalize = attrAs<bool>(options, "normalize");
Expand Down
2 changes: 2 additions & 0 deletions src/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ struct PipelineBaton {
double sharpenJagged;
int threshold;
bool thresholdGrayscale;
int trimTolerance;
double gamma;
bool greyscale;
bool normalize;
Expand Down Expand Up @@ -127,6 +128,7 @@ struct PipelineBaton {
sharpenJagged(2.0),
threshold(0),
thresholdGrayscale(true),
trimTolerance(0),
gamma(0.0),
greyscale(false),
normalize(false),
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/fixtures/expected/trim-16bit-rgba.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions test/unit/trim.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
'use strict';

var assert = require('assert');

var sharp = require('../../index');
var fixtures = require('../fixtures');

describe('Trim borders', function() {

it('Threshold default', function(done) {
var expected = fixtures.expected('alpha-layer-1-fill-trim-resize.png');
sharp(fixtures.inputPngOverlayLayer1)
.resize(450, 322)
.trim()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual('png', info.format);
assert.strictEqual(450, info.width);
assert.strictEqual(322, info.height);
fixtures.assertSimilar(expected, data, done);
});
});

it('16-bit PNG with alpha channel', function(done) {
sharp(fixtures.inputPngWithTransparency16bit)
.resize(32, 32)
.trim()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('png', info.format);
assert.strictEqual(32, info.width);
assert.strictEqual(32, info.height);
assert.strictEqual(4, info.channels);
fixtures.assertSimilar(fixtures.expected('trim-16bit-rgba.png'), data, done);
});
});

});

0 comments on commit cd5958a

Please sign in to comment.