Expose libvips interpolators #69

This commit is contained in:
Lovell Fuller 2014-08-22 16:50:24 +01:00
parent 6498fc3a9e
commit 017bf1e905
4 changed files with 112 additions and 46 deletions

View File

@ -110,21 +110,33 @@ image.metadata(function(err, metadata) {
```
```javascript
var pipeline = sharp().rotate().resize(null, 200).progressive().toBuffer(function(err, outputBuffer, info) {
if (err) {
throw err;
}
// outputBuffer contains 200px high progressive JPEG image data, auto-rotated using EXIF Orientation tag
// info.width and info.height contain the final pixel dimensions of the resized image
});
var pipeline = sharp()
.rotate()
.resize(null, 200)
.progressive()
.toBuffer(function(err, outputBuffer, info) {
if (err) {
throw err;
}
// outputBuffer contains 200px high progressive JPEG image data,
// auto-rotated using EXIF Orientation tag
// info.width and info.height contain the dimensions of the resized image
});
readableStream.pipe(pipeline);
```
```javascript
sharp('input.png').rotate(180).resize(300).sharpen().quality(90).webp().toBuffer().then(function(outputBuffer, info) {
// outputBuffer contains 300px wide, upside down, sharpened, 90% quality WebP image data
// info.width and info.height contain the final pixel dimensions of the resized image
});
sharp('input.png')
.rotate(180)
.resize(300)
.sharpen()
.quality(90)
.webp()
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains 300px wide, upside down, sharpened,
// 90% quality WebP image data
});
```
```javascript
@ -137,10 +149,16 @@ http.createServer(function(request, response) {
```
```javascript
sharp(inputBuffer).resize(200, 300).bicubicInterpolation().embedWhite().toFile('output.tiff').then(function() {
// output.tiff is a 200 pixels wide and 300 pixels high image containing a bicubic scaled
// version, embedded on a white canvas, of the image data in buffer
});
sharp(inputBuffer)
.resize(200, 300)
.interpolateWith(sharp.interpolator.nohalo)
.embedWhite()
.toFile('output.tiff')
.then(function() {
// output.tiff is a 200 pixels wide and 300 pixels high image
// containing a bicubic scaled version, embedded on a white canvas,
// of the image data in inputBuffer
});
```
```javascript
@ -247,17 +265,17 @@ This is equivalent to GraphicsMagick's `>` geometry option: "change the dimensio
Perform a mild sharpen of the resultant image. This typically reduces performance by 30%.
#### bilinearInterpolation()
#### interpolateWith(interpolator)
Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation) for image resizing, the default (and fastest) interpolation if none is specified.
Use the given interpolator for image resizing, where `interpolator` is an attribute of the `sharp.interpolator` Object e.g. `sharp.interpolator.bicubic`.
#### bicubicInterpolation()
Possible interpolators, in order of performance, are:
Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) for image resizing. This typically reduces performance by 5%.
#### nohaloInterpolation()
Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/) for image resizing. This typically reduces performance by a factor of 2.
* `bilinear`: Use [bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation), the default (and fastest) interpolation.
* `bicubic`: Use [bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation), which typically reduces performance by 5%.
* `vertexSplitQuadraticBasisSpline`: Use [VSQBS interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/vsqbs.cpp#L48), which prevents "staircasing" when enlarging and typically reduces performance by 5%.
* `locallyBoundedBicubic`: Use [LBB interpolation](https://github.com/jcupitt/libvips/blob/master/libvips/resample/lbb.cpp#L100), which prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" and typically reduces performance by a factor of 2.
* `nohalo`: Use [Nohalo interpolation](http://eprints.soton.ac.uk/268086/), which prevents acutance and typically reduces performance by a factor of 3.
### Output options

View File

@ -135,28 +135,32 @@ Sharp.prototype.sharpen = function(sharpen) {
};
/*
Use bilinear interpolation for the affine transformation (fastest, default)
Set the interpolator to use for the affine transformation
*/
Sharp.prototype.bilinearInterpolation = function() {
this.options.interpolator = 'bilinear';
module.exports.interpolator = {
bilinear: 'bilinear',
bicubic: 'bicubic',
nohalo: 'nohalo',
locallyBoundedBicubic: 'lbb',
vertexSplitQuadraticBasisSpline: 'vsqbs'
};
Sharp.prototype.interpolateWith = function(interpolator) {
this.options.interpolator = interpolator;
return this;
};
/*
Use bicubic interpolation for the affine transformation
Deprecated interpolation methods, to be removed in v0.7.0
*/
Sharp.prototype.bicubicInterpolation = function() {
this.options.interpolator = 'bicubic';
return this;
};
/*
Use Nohalo interpolation for the affine transformation
*/
Sharp.prototype.nohaloInterpolation = function() {
this.options.interpolator = 'nohalo';
return this;
};
Sharp.prototype.bilinearInterpolation = util.deprecate(function() {
return this.interpolateWith(module.exports.interpolator.bilinear);
}, 'bilinearInterpolation() is deprecated, use interpolateWith(sharp.interpolator.bilinear) instead');
Sharp.prototype.bicubicInterpolation = util.deprecate(function() {
return this.interpolateWith(module.exports.interpolator.bicubic);
}, 'bicubicInterpolation() is deprecated, use interpolateWith(sharp.interpolator.bicubic) instead');
Sharp.prototype.nohaloInterpolation = util.deprecate(function() {
return this.interpolateWith(module.exports.interpolator.nohalo);
}, 'nohaloInterpolation() is deprecated, use interpolateWith(sharp.interpolator.nohalo) instead');
Sharp.prototype.progressive = function(progressive) {
this.options.progressive = (typeof progressive === 'boolean') ? progressive : true;

View File

@ -188,7 +188,7 @@ async.series({
}).add("sharp-file-buffer-bicubic", {
defer: true,
fn: function(deferred) {
sharp(inputJpg).resize(width, height).bicubicInterpolation().toBuffer(function(err, buffer) {
sharp(inputJpg).resize(width, height).interpolateWith(sharp.interpolator.bicubic).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
@ -200,7 +200,31 @@ async.series({
}).add("sharp-file-buffer-nohalo", {
defer: true,
fn: function(deferred) {
sharp(inputJpg).resize(width, height).nohaloInterpolation().toBuffer(function(err, buffer) {
sharp(inputJpg).resize(width, height).interpolateWith(sharp.interpolator.nohalo).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add("sharp-file-buffer-locallyBoundedBicubic", {
defer: true,
fn: function(deferred) {
sharp(inputJpg).resize(width, height).interpolateWith(sharp.interpolator.locallyBoundedBicubic).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add("sharp-file-buffer-vertexSplitQuadraticBasisSpline", {
defer: true,
fn: function(deferred) {
sharp(inputJpg).resize(width, height).interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {

View File

@ -249,7 +249,7 @@ async.series([
},
// Interpolation: bilinear
function(done) {
sharp(inputJpg).resize(320, 240).bilinearInterpolation().toBuffer(function(err, data, info) {
sharp(inputJpg).resize(320, 240).interpolateWith(sharp.interpolator.bilinear).toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(320, info.width);
@ -259,7 +259,7 @@ async.series([
},
// Interpolation: bicubic
function(done) {
sharp(inputJpg).resize(320, 240).bicubicInterpolation().toBuffer(function(err, data, info) {
sharp(inputJpg).resize(320, 240).interpolateWith(sharp.interpolator.bicubic).toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(320, info.width);
@ -269,7 +269,27 @@ async.series([
},
// Interpolation: nohalo
function(done) {
sharp(inputJpg).resize(320, 240).nohaloInterpolation().toBuffer(function(err, data, info) {
sharp(inputJpg).resize(320, 240).interpolateWith(sharp.interpolator.nohalo).toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
},
// Interpolation: locally bounded bicubic (LBB)
function(done) {
sharp(inputJpg).resize(320, 240).interpolateWith(sharp.interpolator.locallyBoundedBicubic).toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
},
// Interpolation: vertex split quadratic basis spline (VSQBS)
function(done) {
sharp(inputJpg).resize(320, 240).interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline).toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual(320, info.width);
@ -346,7 +366,7 @@ async.series([
});
});
var pipeline = sharp().resize(320, 240);
readable.pipe(pipeline).pipe(writable)
readable.pipe(pipeline).pipe(writable);
},
// Crop, gravity=north
function(done) {
@ -516,7 +536,7 @@ async.series([
assert.strictEqual(1362, info.width);
assert.strictEqual(1112, info.height);
done();
})
});
});
},
// Verify internal counters