Use libvips' new lanczos3 kernel as default for image reduce

Deprecate interpolateWith method, now provided as an option
This commit is contained in:
Lovell Fuller
2016-05-17 22:16:47 +01:00
parent 051d022fc2
commit cdb2894bd9
32 changed files with 292 additions and 658 deletions

View File

@@ -10,10 +10,10 @@
"devDependencies": {
"async": "^1.5.2",
"benchmark": "^2.1.0",
"gm": "^1.21.0",
"gm": "^1.22.0",
"imagemagick": "^0.1.3",
"imagemagick-native": "^1.9.2",
"jimp": "^0.2.20",
"jimp": "^0.2.24",
"lwip": "^0.0.8",
"semver": "^5.1.0"
},

View File

@@ -30,18 +30,15 @@ var fixtures = require('../fixtures');
var width = 720;
var height = 480;
var magickFilterBilinear = 'Triangle';
var magickFilterBicubic = 'Lanczos';
// Disable libvips cache to ensure tests are as fair as they can be
sharp.cache(false);
// Enable use of SIMD
sharp.simd(true);
async.series({
'jpeg-linear': function(callback) {
'jpeg': function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var jpegSuite = new Benchmark.Suite('jpeg-linear');
var jpegSuite = new Benchmark.Suite('jpeg');
// jimp
jpegSuite.add('jimp-buffer-buffer', {
defer: true,
@@ -93,7 +90,7 @@ async.series({
if (err) {
throw err;
}
image.resize(width, height, 'linear', function (err, image) {
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
@@ -113,7 +110,7 @@ async.series({
if (err) {
throw err;
}
image.resize(width, height, 'linear', function (err, image) {
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
@@ -140,7 +137,7 @@ async.series({
width: width,
height: height,
format: 'jpg',
filter: magickFilterBilinear
filter: 'Lanczos'
}, function(err) {
if (err) {
throw err;
@@ -161,7 +158,7 @@ async.series({
width: width,
height: height,
format: 'JPEG',
filter: magickFilterBilinear
filter: 'Lanczos'
}, function (err, buffer) {
if (err) {
throw err;
@@ -179,7 +176,7 @@ async.series({
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
@@ -194,7 +191,7 @@ async.series({
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
@@ -210,7 +207,7 @@ async.series({
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
@@ -225,7 +222,7 @@ async.series({
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
@@ -243,7 +240,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
@@ -257,7 +253,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -272,7 +267,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
@@ -290,8 +284,7 @@ async.series({
deferred.resolve();
});
var pipeline = sharp()
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear);
.resize(width, height);
readable.pipe(pipeline).pipe(writable);
}
}).add('sharp-file-buffer', {
@@ -299,7 +292,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -314,19 +306,27 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer()
.then(function(buffer) {
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
}
}).add('sharp-sharpen-mild', {
}).on('cycle', function(event) {
console.log('jpeg ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').map('name'));
}).run();
},
// Effect of applying operations
operations: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var operationsSuite = new Benchmark.Suite('operations');
operationsSuite.add('sharp-sharpen-mild', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sharpen()
.toBuffer(function(err, buffer) {
if (err) {
@@ -342,7 +342,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sharpen(3, 1, 3)
.toBuffer(function(err, buffer) {
if (err) {
@@ -358,7 +357,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.blur()
.toBuffer(function(err, buffer) {
if (err) {
@@ -374,7 +372,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.blur(3)
.toBuffer(function(err, buffer) {
if (err) {
@@ -390,7 +387,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.gamma()
.toBuffer(function(err, buffer) {
if (err) {
@@ -406,7 +402,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.normalise()
.toBuffer(function(err, buffer) {
if (err) {
@@ -422,7 +417,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.greyscale()
.toBuffer(function(err, buffer) {
if (err) {
@@ -438,7 +432,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.gamma()
.greyscale()
.toBuffer(function(err, buffer) {
@@ -455,7 +448,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.progressive()
.toBuffer(function(err, buffer) {
if (err) {
@@ -471,7 +463,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.withoutChromaSubsampling()
.toBuffer(function(err, buffer) {
if (err) {
@@ -487,7 +478,6 @@ async.series({
fn: function(deferred) {
sharp(inputJpgBuffer)
.rotate(90)
.interpolateWith(sharp.interpolator.bilinear)
.resize(width, height)
.toBuffer(function(err, buffer) {
if (err) {
@@ -503,8 +493,6 @@ async.series({
fn: function(deferred) {
sharp.simd(false);
sharp(inputJpgBuffer)
.rotate(90)
.interpolateWith(sharp.interpolator.bilinear)
.resize(width, height)
.toBuffer(function(err, buffer) {
sharp.simd(true);
@@ -520,9 +508,8 @@ async.series({
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.sequentialRead()
.resize(width, height)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -533,189 +520,19 @@ async.series({
});
}
}).on('cycle', function(event) {
console.log('jpeg-linear ' + String(event.target));
console.log('operations ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').map('name'));
}).run();
},
'jpeg-cubic': function(callback) {
// Comparitive speed of kernels
kernels: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
var jpegSuite = new Benchmark.Suite('jpeg-cubic');
// lwip
if (typeof lwip !== 'undefined') {
jpegSuite.add('lwip-file-file', {
defer: true,
fn: function(deferred) {
lwip.open(fixtures.inputJpg, function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.writeFile(fixtures.outputJpg, {quality: 80}, function (err) {
if (err) {
throw err;
}
deferred.resolve();
});
});
});
}
}).add('lwip-buffer-buffer', {
defer: true,
fn: function(deferred) {
lwip.open(inputJpgBuffer, 'jpg', function (err, image) {
if (err) {
throw err;
}
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
image.toBuffer('jpg', {quality: 80}, function (err, buffer) {
if (err) {
throw err;
}
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
});
});
}
});
}
// imagemagick
jpegSuite.add('imagemagick-file-file', {
defer: true,
fn: function(deferred) {
imagemagick.resize({
srcPath: fixtures.inputJpg,
dstPath: fixtures.outputJpg,
quality: 0.8,
width: width,
height: height,
format: 'jpg',
filter: magickFilterBicubic
}, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
});
// imagemagick-native
if (typeof imagemagickNative !== 'undefined') {
jpegSuite.add('imagemagick-native-buffer-buffer', {
defer: true,
fn: function(deferred) {
imagemagickNative.convert({
srcData: inputJpgBuffer,
quality: 80,
width: width,
height: height,
format: 'JPEG',
filter: magickFilterBicubic
}, function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
}
// gm
jpegSuite.add('gm-buffer-file', {
defer: true,
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('gm-buffer-buffer', {
defer: true,
fn: function(deferred) {
gm(inputJpgBuffer)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('gm-file-file', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.write(fixtures.outputJpg, function (err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('gm-file-buffer', {
defer: true,
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(width, height)
.filter(magickFilterBicubic)
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
});
// sharp
jpegSuite.add('sharp-buffer-file', {
(new Benchmark.Suite('kernels')).add('sharp-cubic', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-buffer-buffer', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.resize(width, height, { kernel: 'cubic' })
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -725,39 +542,11 @@ async.series({
}
});
}
}).add('sharp-file-file', {
}).add('sharp-lanczos2', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toFile(fixtures.outputJpg, function(err) {
if (err) {
throw err;
} else {
deferred.resolve();
}
});
}
}).add('sharp-stream-stream', {
defer: true,
fn: function(deferred) {
var readable = fs.createReadStream(fixtures.inputJpg);
var writable = fs.createWriteStream(fixtures.outputJpg);
writable.on('finish', function() {
deferred.resolve();
});
var pipeline = sharp()
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic);
readable.pipe(pipeline).pipe(writable);
}
}).add('sharp-file-buffer', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
sharp(inputJpgBuffer)
.resize(width, height, { kernel: 'lanczos2' })
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -767,109 +556,11 @@ async.series({
}
});
}
}).add('sharp-promise', {
}).add('sharp-lanczos3', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toBuffer()
.then(function(buffer) {
assert.notStrictEqual(null, buffer);
deferred.resolve();
});
}
}).on('cycle', function(event) {
console.log('jpeg-cubic ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').map('name'));
}).run();
},
// Comparitive speed of pixel interpolators
interpolators: function(callback) {
var inputJpgBuffer = fs.readFileSync(fixtures.inputJpg);
(new Benchmark.Suite('interpolators')).add('sharp-nearest-neighbour', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.nearest)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-bilinear', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-vertexSplitQuadraticBasisSpline', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-bicubic', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bicubic)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-locallyBoundedBicubic', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.locallyBoundedBicubic)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).add('sharp-nohalo', {
defer: true,
fn: function(deferred) {
sharp(inputJpgBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.nohalo)
.resize(width, height, { kernel: 'lanczos3' })
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -880,12 +571,12 @@ async.series({
});
}
}).on('cycle', function(event) {
console.log('interpolators ' + String(event.target));
console.log('kernels ' + String(event.target));
}).on('complete', function() {
callback(null, this.filter('fastest').map('name'));
}).run();
},
// PNG
png: function(callback) {
var inputPngBuffer = fs.readFileSync(fixtures.inputPng);
var pngSuite = new Benchmark.Suite('png');
@@ -938,7 +629,7 @@ async.series({
if (err) {
throw err;
}
image.resize(width, height, 'linear', function (err, image) {
image.resize(width, height, 'lanczos', function (err, image) {
if (err) {
throw err;
}
@@ -963,7 +654,7 @@ async.series({
dstPath: fixtures.outputPng,
width: width,
height: height,
filter: magickFilterBilinear
filter: 'Lanczos'
}, function(err) {
if (err) {
throw err;
@@ -983,7 +674,7 @@ async.series({
width: width,
height: height,
format: 'PNG',
filter: magickFilterBilinear
filter: 'Lanczos'
});
deferred.resolve();
}
@@ -995,7 +686,7 @@ async.series({
fn: function(deferred) {
gm(fixtures.inputPng)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.write(fixtures.outputPng, function (err) {
if (err) {
throw err;
@@ -1009,7 +700,7 @@ async.series({
fn: function(deferred) {
gm(fixtures.inputPng)
.resize(width, height)
.filter(magickFilterBilinear)
.filter('Lanczos')
.toBuffer(function (err, buffer) {
if (err) {
throw err;
@@ -1026,7 +717,6 @@ async.series({
fn: function(deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputPng, function(err) {
if (err) {
throw err;
@@ -1040,7 +730,6 @@ async.series({
fn: function(deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -1055,7 +744,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputPng)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputPng, function(err) {
if (err) {
throw err;
@@ -1069,7 +757,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputPng)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -1084,7 +771,6 @@ async.series({
fn: function(deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.progressive()
.toBuffer(function(err, buffer) {
if (err) {
@@ -1100,7 +786,6 @@ async.series({
fn: function(deferred) {
sharp(inputPngBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.withoutAdaptiveFiltering()
.toBuffer(function(err, buffer) {
if (err) {
@@ -1118,7 +803,7 @@ async.series({
callback(null, this.filter('fastest').map('name'));
}).run();
},
// WebP
webp: function(callback) {
var inputWebPBuffer = fs.readFileSync(fixtures.inputWebP);
(new Benchmark.Suite('webp')).add('sharp-buffer-file', {
@@ -1126,7 +811,6 @@ async.series({
fn: function(deferred) {
sharp(inputWebPBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputWebP, function(err) {
if (err) {
throw err;
@@ -1140,7 +824,6 @@ async.series({
fn: function(deferred) {
sharp(inputWebPBuffer)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;
@@ -1155,7 +838,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputWebP)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toFile(fixtures.outputWebP, function(err) {
if (err) {
throw err;
@@ -1169,7 +851,6 @@ async.series({
fn: function(deferred) {
sharp(fixtures.inputWebp)
.resize(width, height)
.interpolateWith(sharp.interpolator.bilinear)
.toBuffer(function(err, buffer) {
if (err) {
throw err;

View File

@@ -8,14 +8,12 @@ var Benchmark = require('benchmark');
var sharp = require('../../index');
var fixtures = require('../fixtures');
sharp.cache(false);
sharp.simd(true);
var min = 320;
var max = 960;
// Nearest equivalent to bilinear
var magickFilter = 'Triangle';
var randomDimension = function() {
return Math.ceil(Math.random() * (max - min) + min);
};
@@ -30,7 +28,7 @@ new Benchmark.Suite('random').add('imagemagick', {
width: randomDimension(),
height: randomDimension(),
format: 'jpg',
filter: magickFilter
filter: 'Lanczos'
}, function(err) {
if (err) {
throw err;
@@ -44,7 +42,7 @@ new Benchmark.Suite('random').add('imagemagick', {
fn: function(deferred) {
gm(fixtures.inputJpg)
.resize(randomDimension(), randomDimension())
.filter(magickFilter)
.filter('Lanczos')
.quality(80)
.toBuffer(function (err, buffer) {
if (err) {
@@ -58,19 +56,20 @@ new Benchmark.Suite('random').add('imagemagick', {
}).add('sharp', {
defer: true,
fn: function(deferred) {
sharp(fixtures.inputJpg).resize(randomDimension(), randomDimension()).toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
sharp(fixtures.inputJpg)
.resize(randomDimension(), randomDimension())
.toBuffer(function(err, buffer) {
if (err) {
throw err;
} else {
assert.notStrictEqual(null, buffer);
deferred.resolve();
}
});
}
}).on('cycle', function(event) {
console.log(String(event.target));
}).on('complete', function() {
var winner = this.filter('fastest').map('name');
assert.strictEqual('sharp', String(winner), 'sharp was slower than ' + winner);
console.dir(sharp.cache());
}).run();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 782 B

After

Width:  |  Height:  |  Size: 988 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 789 B

After

Width:  |  Height:  |  Size: 999 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

After

Width:  |  Height:  |  Size: 839 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 B

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -18,7 +18,6 @@ var fingerprint = function(image, callback) {
.normalise()
.resize(9, 8)
.ignoreAspectRatio()
.interpolateWith(sharp.interpolator.vertexSplitQuadraticBasisSpline)
.raw()
.toBuffer(function(err, data) {
if (err) {

View File

@@ -1,48 +0,0 @@
# Interpolators
[Photo](https://www.flickr.com/photos/aotaro/21978966091) by
[aotaro](https://www.flickr.com/photos/aotaro/) is licensed under
[CC BY 2.0](https://creativecommons.org/licenses/by/2.0/).
The following examples take the 4608x3072px original image
and resize to 480x320px using various interpolators.
To fetch the original 4608x3072px image and
generate the interpolator sample images:
```sh
curl -O https://farm6.staticflickr.com/5682/21978966091_b421afe866_o.jpg
node generate.js
```
## Nearest neighbour
![Nearest neighbour interpolation](nearest.jpg)
## Bilinear
![Bilinear interpolation](bilinear.jpg)
## Bicubic
![Bicubic interpolation](bicubic.jpg)
## Locally bounded bicubic
![Locally bounded bicubic interpolation](lbb.jpg)
## Vertex-split quadratic b-splines (VSQBS)
![Vertex-split quadratic b-splines interpolation](vsqbs.jpg)
## Nohalo
![Nohalo interpolation](nohalo.jpg)
## GraphicsMagick
![GraphicsMagick](gm.jpg)
```sh
gm convert 21978966091_b421afe866_o.jpg -resize 480x320^ -gravity center -extent 480x320 -quality 95 -strip -define jpeg:optimize-coding=true gm.jpg
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

View File

@@ -1,11 +0,0 @@
'use strict';
['nearest', 'bilinear', 'bicubic', 'vsqbs', 'lbb', 'nohalo'].forEach(function(interpolator) {
require('../../')('21978966091_b421afe866_o.jpg')
.resize(480, 320)
.interpolateWith(interpolator)
.quality(95)
.toFile(interpolator + '.jpg', function(err) {
if (err) throw err;
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -45,15 +45,17 @@ describe('Alpha transparency', function() {
});
it('Flatten 16-bit PNG with transparency to orange', function(done) {
var output = fixtures.path('output.flatten-rgb16-orange.jpg');
sharp(fixtures.inputPngWithTransparency16bit)
.flatten()
.background({r: 255, g: 102, b: 0})
.toBuffer(function(err, data, info) {
.toFile(output, function(err, info) {
if (err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual(32, info.width);
assert.strictEqual(32, info.height);
fixtures.assertSimilar(fixtures.expected('flatten-rgb16-orange.jpg'), data, { threshold: 6 }, done);
fixtures.assertMaxColourDistance(output, fixtures.expected('flatten-rgb16-orange.jpg'), 25);
done();
});
});

View File

@@ -57,7 +57,7 @@ describe('Clone', function() {
var rotator = sharp().rotate(90);
// Cloned instances with differing dimensions
rotator.clone().resize(320, 240).pipe(writable1);
rotator.clone().resize(100).pipe(writable2);
rotator.clone().resize(100, 122).pipe(writable2);
// Go
fs.createReadStream(fixtures.inputJpg).pipe(rotator);
});

View File

@@ -5,100 +5,67 @@ var assert = require('assert');
var sharp = require('../../index');
var fixtures = require('../fixtures');
describe('Interpolation', function() {
describe('Interpolators and kernels', function() {
it('nearest neighbour', function(done) {
sharp(fixtures.inputJpg)
.resize(320, 240)
.interpolateWith(sharp.interpolator.nearest)
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
describe('Reducers', function() {
[
sharp.kernel.cubic,
sharp.kernel.lanczos2,
sharp.kernel.lanczos3
].forEach(function(kernel) {
it(kernel, function(done) {
sharp(fixtures.inputJpg)
.resize(320, null, { kernel: kernel })
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
fixtures.assertSimilar(fixtures.inputJpg, data, done);
});
});
});
});
it('bilinear', function(done) {
sharp(fixtures.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('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
describe('Enlargers', function() {
[
sharp.interpolator.nearest,
sharp.interpolator.bilinear,
sharp.interpolator.bicubic,
sharp.interpolator.nohalo,
sharp.interpolator.locallyBoundedBicubic,
sharp.interpolator.vertexSplitQuadraticBasisSpline
].forEach(function(interpolator) {
it(interpolator, function(done) {
sharp(fixtures.inputJpg)
.resize(320, null, { interpolator: interpolator })
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual('jpeg', info.format);
assert.strictEqual(320, info.width);
fixtures.assertSimilar(fixtures.inputJpg, data, done);
});
});
});
});
it('bicubic', function(done) {
sharp(fixtures.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('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
it('unknown kernel throws', function() {
assert.throws(function() {
sharp().resize(null, null, { kernel: 'unknown' });
});
});
it('nohalo', function(done) {
sharp(fixtures.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('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
it('unknown interpolator throws', function() {
assert.throws(function() {
sharp().resize(null, null, { interpolator: 'unknown' });
});
});
it('locally bounded bicubic (LBB)', function(done) {
sharp(fixtures.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('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
describe('deprecated interpolateWith method still works', function() {
it('resize then interpolateWith', function() {
sharp().resize(1, 1).interpolateWith('bicubic');
});
it('interpolateWith then resize', function() {
sharp().interpolateWith('bicubic').resize(1, 1);
});
});
it('vertex split quadratic basis spline (VSQBS)', function(done) {
sharp(fixtures.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('jpeg', info.format);
assert.strictEqual(320, info.width);
assert.strictEqual(240, info.height);
done();
});
});
it('unknown interpolator throws', function(done) {
var isValid = false;
try {
sharp().interpolateWith('nonexistant');
isValid = true;
} catch (e) {}
assert(!isValid);
done();
});
});

View File

@@ -1028,14 +1028,14 @@ describe('Input/output', function() {
it('Info event data', function(done) {
var readable = fs.createReadStream(fixtures.inputJPGBig);
var inPipeline = sharp()
.resize(840)
.resize(840, 472)
.raw()
.on('info', function(info) {
assert.strictEqual(840, info.width);
assert.strictEqual(472, info.height);
assert.strictEqual(3, info.channels);
});
var badPipeline = sharp(null, {raw: {width: 840, height: 473, channels: 3}})
var badPipeline = sharp(null, {raw: {width: 840, height: 500, channels: 3}})
.toFormat('jpeg')
.toBuffer(function(err, data, info) {
assert.strictEqual(err.message.indexOf('memory area too small') > 0, true);
@@ -1047,7 +1047,7 @@ describe('Input/output', function() {
done();
});
inPipeline = sharp()
.resize(840)
.resize(840, 472)
.raw();
readable.pipe(inPipeline).pipe(goodPipeline);
});

View File

@@ -96,7 +96,7 @@ describe('Threshold', function() {
.threshold()
.toBuffer(function(err, data, info) {
assert.strictEqual('webp', info.format);
fixtures.assertSimilar(fixtures.expected('threshold-128-transparency.webp'), data, { threshold: 14 }, done);
fixtures.assertSimilar(fixtures.expected('threshold-128-transparency.webp'), data, done);
});
});
}