Upgrade to libvips 8.10.5, AVIF support in prebuilt binaries

Remove experimental status from HEIF, changing defaults
to prefer royalty-free AV1 over patent-encumbered HEVC
This commit is contained in:
Lovell Fuller 2020-11-17 14:32:05 +00:00
parent a0d89ed514
commit 103ec0d58f
20 changed files with 254 additions and 125 deletions

View File

@ -45,43 +45,43 @@ jobs:
install: sudo docker exec sharp bash -c "npm install --build-from-source --unsafe-perm"
script: sudo docker exec sharp bash -c "npm test"
- name: "Linux x64 (Alpine 3.9, musl 1.1.20) - Node.js 10"
- name: "Linux x64 (Alpine 3.11, musl 1.1.24) - Node.js 10"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --env TRAVIS_TAG --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10.17.0-alpine3.9 # https://github.com/nodejs/docker-node/issues/1158
- sudo docker run -dit --name sharp --env CI --env TRAVIS_TAG --env prebuild_upload --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:10-alpine3.11
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 12"
- name: "Linux x64 (Alpine 3.11, musl 1.1.24) - Node.js 12"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12.0-alpine
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:12-alpine3.11
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 14"
- name: "Linux x64 (Alpine 3.11, musl 1.1.24) - Node.js 14"
os: linux
dist: bionic
language: shell
before_install:
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14.0-alpine
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:14-alpine3.11
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"
- name: "Linux x64 (Alpine 3.11, musl 1.1.20) - Node.js 15"
- name: "Linux x64 (Alpine 3.11, musl 1.1.24) - Node.js 15"
os: linux
dist: bionic
language: shell
before_install:
- sudo chown 0.0 ${PWD}
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:15.0-alpine
- sudo docker run -dit --name sharp --env CI --volume "${PWD}:/mnt/sharp" --workdir /mnt/sharp node:15-alpine3.11
- sudo docker exec sharp apk add build-base git python2 --update-cache
install: sudo docker exec sharp sh -c "npm install --build-from-source --unsafe-perm"
script: sudo docker exec sharp sh -c "npm test"

View File

@ -4,7 +4,7 @@
The typical use case for this high speed Node.js module
is to convert large images in common formats to
smaller, web-friendly JPEG, PNG and WebP images of varying dimensions.
smaller, web-friendly JPEG, PNG, AVIF and WebP images of varying dimensions.
Resizing an image is typically 4x-5x faster than using the
quickest ImageMagick and GraphicsMagick settings
@ -21,9 +21,9 @@ do not require any additional install or runtime dependencies.
### Formats
This module supports reading JPEG, PNG, WebP, TIFF, GIF and SVG images.
This module supports reading JPEG, PNG, WebP, AVIF, TIFF, GIF and SVG images.
Output images can be in JPEG, PNG, WebP and TIFF formats as well as uncompressed raw pixel data.
Output images can be in JPEG, PNG, WebP, AVIF and TIFF formats as well as uncompressed raw pixel data.
Streams, Buffer objects and the filesystem can be used for input and output.

View File

@ -299,23 +299,40 @@ sharp('input.svg')
Returns **Sharp**
## heif
## avif
Use these HEIF options for output image.
Support for HEIF (HEIC/AVIF) is experimental.
Do not use this in production systems.
Requires a custom, globally-installed libvips compiled with support for libheif.
Most versions of libheif support only the patent-encumbered HEVC compression format.
Use these AVIF options for output image.
### Parameters
- `options` **[Object][6]?** output options
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `80`)
- `options.compression` **[boolean][7]** compression format: hevc, avc, jpeg, av1 (optional, default `'hevc'`)
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
- `options.speed` **[boolean][7]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
- Throws **[Error][4]** Invalid options
Returns **Sharp**
**Meta**
- **since**: 0.27.0
## heif
Use these HEIF options for output image.
Support for patent-encumbered HEIC images requires the use of a
globally-installed libvips compiled with support for libheif, libde265 and x265.
### Parameters
- `options` **[Object][6]?** output options
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
- `options.compression` **[boolean][7]** compression format: av1, hevc (optional, default `'av1'`)
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
- `options.speed` **[boolean][7]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
- Throws **[Error][4]** Invalid options

View File

@ -1,5 +1,15 @@
# Changelog
## v0.27 - *avif*
Requires libvips v8.10.5
### v0.27.0 - TBD
* Add support for AVIF to prebuilt binaries.
* Remove experimental status from `heif` output, defaults are now AVIF-centric.
## v0.26 - *zoom*
Requires libvips v8.10.0

View File

@ -20,21 +20,24 @@ Node.js v10+ on the most common platforms:
* macOS x64 (>= 10.13)
* Linux x64 (glibc >= 2.17, musl >= 1.1.24)
* Linux ARM64 (glibc >= 2.29)
* Windows
* Windows x64
* Windows x86
A 7-8MB tarball containing libvips and its most commonly used dependencies
A ~9MB tarball containing libvips and its most commonly used dependencies
is downloaded via HTTPS and stored within `node_modules/sharp/vendor` during `npm install`.
This provides support for the
JPEG, PNG, WebP, TIFF, GIF (input) and SVG (input) image formats.
JPEG, PNG, WebP, AVIF, TIFF, GIF (input) and SVG (input) image formats.
The following platforms have prebuilt libvips but not sharp:
* Linux ARMv6
* Linux ARMv7 (glibc >= 2.28)
* Windows ARM64
The following platforms require compilation of both libvips and sharp from source:
* macOS ARM64
* Linux x86
* Linux x64 (glibc <= 2.16, includes RHEL/CentOS 6)
* Linux ARM64 (glibc <= 2.28, musl)
@ -69,7 +72,7 @@ The use of a globally-installed libvips is unsupported on Windows.
This module will be compiled from source at `npm install` time when:
* a globally-installed libvips is detected (set the `SHARP_IGNORE_GLOBAL_LIBVIPS` environment variable to skip this),
* prebuilt binaries do not exist for the current platform and Node.js version, or
* prebuilt sharp binaries do not exist for the current platform, or
* when the `npm install --build-from-source` flag is used.
Building from source requires:

File diff suppressed because one or more lines are too long

View File

@ -233,9 +233,10 @@ const Sharp = function (input, options) {
tiffTileWidth: 256,
tiffXres: 1.0,
tiffYres: 1.0,
heifQuality: 80,
heifQuality: 50,
heifLossless: false,
heifCompression: 'hevc',
heifCompression: 'av1',
heifSpeed: 5,
tileSize: 256,
tileOverlap: 0,
tileContainer: 'fs',

View File

@ -6,6 +6,7 @@ const sharp = require('../build/Release/sharp.node');
const formats = new Map([
['heic', 'heif'],
['heif', 'heif'],
['avif', 'avif'],
['jpeg', 'jpeg'],
['jpg', 'jpeg'],
['png', 'png'],
@ -556,29 +557,39 @@ function tiff (options) {
return this._updateFormatOut('tiff', options);
}
/**
* Use these AVIF options for output image.
*
* @since 0.27.0
*
* @param {Object} [options] - output options
* @param {number} [options.quality=50] - quality, integer 1-100
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {boolean} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function avif (options) {
return this.heif({ ...options, compression: 'av1' });
}
/**
* Use these HEIF options for output image.
*
* Support for HEIF (HEIC/AVIF) is experimental.
* Do not use this in production systems.
*
* Requires a custom, globally-installed libvips compiled with support for libheif.
*
* Most versions of libheif support only the patent-encumbered HEVC compression format.
* Support for patent-encumbered HEIC images requires the use of a
* globally-installed libvips compiled with support for libheif, libde265 and x265.
*
* @since 0.23.0
*
* @param {Object} [options] - output options
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1
* @param {number} [options.quality=50] - quality, integer 1-100
* @param {boolean} [options.compression='av1'] - compression format: av1, hevc
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {boolean} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function heif (options) {
if (!this.constructor.format.heif.output.buffer) {
throw new Error('The heif operation requires libvips to have been installed with support for libheif');
}
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
@ -595,10 +606,17 @@ function heif (options) {
}
}
if (is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['hevc', 'avc', 'jpeg', 'av1'])) {
if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
this.options.heifCompression = options.compression;
} else {
throw is.invalidParameterError('compression', 'one of: hevc, avc, jpeg, av1', options.compression);
throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
}
}
if (is.defined(options.speed)) {
if (is.integer(options.speed) && is.inRange(options.speed, 0, 8)) {
this.options.heifSpeed = options.speed;
} else {
throw is.invalidParameterError('speed', 'integer between 0 and 8', options.speed);
}
}
}
@ -891,6 +909,7 @@ module.exports = function (Sharp) {
png,
webp,
tiff,
avif,
heif,
gif,
raw,

View File

@ -1,7 +1,7 @@
{
"name": "sharp",
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
"version": "0.26.3",
"version": "0.27.0-alpha1",
"author": "Lovell Fuller <npm@lovell.info>",
"homepage": "https://github.com/lovell/sharp",
"contributors": [
@ -100,6 +100,7 @@
"jpeg",
"png",
"webp",
"avif",
"tiff",
"gif",
"svg",
@ -116,10 +117,10 @@
"array-flatten": "^3.0.0",
"color": "^3.1.3",
"detect-libc": "^1.0.3",
"node-addon-api": "^3.0.2",
"node-addon-api": "^3.1.0",
"npmlog": "^4.1.2",
"prebuild-install": "^6.0.0",
"semver": "^7.3.2",
"semver": "^7.3.4",
"simple-get": "^4.0.0",
"tar-fs": "^2.1.1",
"tunnel-agent": "^0.6.0"
@ -141,7 +142,7 @@
},
"license": "Apache-2.0",
"config": {
"libvips": "8.10.0",
"libvips": "8.10.5-alpha2",
"runtime": "napi",
"target": 3
},

View File

@ -24,8 +24,10 @@
// Verify platform and compiler compatibility
#if (VIPS_MAJOR_VERSION < 8 || (VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10))
#error "libvips version 8.10.0+ is required - please see https://sharp.pixelplumbing.com/install"
#if (VIPS_MAJOR_VERSION < 8) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION < 10) || \
(VIPS_MAJOR_VERSION == 8 && VIPS_MINOR_VERSION == 10 && VIPS_MICRO_VERSION < 5)
#error "libvips version 8.10.5+ is required - please see https://sharp.pixelplumbing.com/install"
#endif
#if ((!defined(__clang__)) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6)))

View File

@ -837,6 +837,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("strip", !baton->withMetadata)
->set("compression", baton->heifCompression)
->set("Q", baton->heifQuality)
->set("speed", baton->heifSpeed)
->set("lossless", baton->heifLossless)));
baton->bufferOut = static_cast<char*>(area->data);
baton->bufferOutLength = area->length;
@ -885,7 +886,7 @@ class PipelineWorker : public Napi::AsyncWorker {
bool const isV = sharp::IsV(baton->fileOut);
bool const mightMatchInput = baton->formatOut == "input";
bool const willMatchInput = mightMatchInput &&
!(isJpeg || isPng || isWebp || isGif || isTiff || isDz || isDzZip || isV);
!(isJpeg || isPng || isWebp || isGif || isTiff || isHeif || isDz || isDzZip || isV);
if (baton->formatOut == "jpeg" || (mightMatchInput && isJpeg) ||
(willMatchInput && inputImageType == sharp::ImageType::JPEG)) {
@ -966,13 +967,11 @@ class PipelineWorker : public Napi::AsyncWorker {
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
// Write HEIF to file
if (sharp::IsAvif(baton->fileOut)) {
baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
}
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata)
->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression)
->set("speed", baton->heifSpeed)
->set("lossless", baton->heifLossless));
baton->formatOut = "heif";
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
@ -1395,6 +1394,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
sharp::AttrAsStr(options, "heifCompression").data()));
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
// Animated output
if (sharp::HasAttr(options, "pageHeight")) {

View File

@ -159,6 +159,7 @@ struct PipelineBaton {
double tiffYres;
int heifQuality;
VipsForeignHeifCompression heifCompression;
int heifSpeed;
bool heifLossless;
std::string err;
bool withMetadata;
@ -276,8 +277,9 @@ struct PipelineBaton {
tiffTileWidth(256),
tiffXres(1.0),
tiffYres(1.0),
heifQuality(80),
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_HEVC),
heifQuality(50),
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
heifSpeed(5),
heifLossless(false),
withMetadata(false),
withMetadataOrientation(-1),

View File

@ -22,6 +22,7 @@
#include "stats.h"
static void* sharp_vips_init(void*) {
g_setenv("VIPS_MIN_STACK_SIZE", "2m", FALSE);
vips_init("sharp");
return nullptr;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -109,6 +109,7 @@ module.exports = {
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
inputSvgSmallViewBox: getPath('circle.svg'),
inputSvgWithEmbeddedImages: getPath('struct-image-04-t.svg'), // https://dev.w3.org/SVG/profiles/1.2T/test/svg/struct-image-04-t.svg
inputAvif: getPath('cosmos_frame12924_yuv420_10bpc_bt2020_pq_q50.avif'), // CC by-nc-nd https://github.com/AOMediaCodec/av1-avif/tree/master/testFiles/Netflix
inputJPGBig: getPath('flowers.jpeg'),

84
test/unit/avif.js Normal file
View File

@ -0,0 +1,84 @@
'use strict';
const assert = require('assert');
const sharp = require('../../');
const { inputAvif, inputJpg } = require('../fixtures');
describe('AVIF', () => {
it('called without options does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().avif();
});
});
it('can passthrough AVIF', async () => {
const data = await sharp(inputAvif)
.resize(32)
.toBuffer();
const metadata = await sharp(data)
.metadata();
const { size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
channels: 3,
depth: 'uchar',
format: 'heif',
hasAlpha: false,
hasProfile: false,
height: 12,
isProgressive: false,
pageHeight: 12,
pagePrimary: 0,
pages: 1,
space: 'srgb',
width: 32
});
});
it('can convert AVIF to JPEG', async () => {
const data = await sharp(inputAvif)
.resize(32)
.jpeg()
.toBuffer();
const metadata = await sharp(data)
.metadata();
const { size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
channels: 3,
chromaSubsampling: '4:2:0',
density: 72,
depth: 'uchar',
format: 'jpeg',
hasAlpha: false,
hasProfile: false,
height: 13,
isProgressive: false,
space: 'srgb',
width: 32
});
});
it('can convert JPEG to AVIF', async () => {
const data = await sharp(inputJpg)
.resize(32)
.avif()
.toBuffer();
const metadata = await sharp(data)
.metadata();
const { size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
channels: 3,
depth: 'uchar',
format: 'heif',
hasAlpha: false,
hasProfile: false,
height: 26,
isProgressive: false,
pageHeight: 26,
pagePrimary: 0,
pages: 1,
space: 'srgb',
width: 32
});
});
});

View File

@ -4,76 +4,65 @@ const assert = require('assert');
const sharp = require('../../');
const formatHeifOutputBuffer = sharp.format.heif.output.buffer;
describe('HEIF (experimental)', () => {
describe('Stubbed without support for HEIF', () => {
before(() => {
sharp.format.heif.output.buffer = false;
});
after(() => {
sharp.format.heif.output.buffer = formatHeifOutputBuffer;
});
it('should throw an error', () => {
assert.throws(() => {
sharp().heif();
});
describe('HEIF', () => {
it('called without options does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif();
});
});
describe('Stubbed with support for HEIF', () => {
before(() => {
sharp.format.heif.output.buffer = true;
it('valid quality does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ quality: 80 });
});
after(() => {
sharp.format.heif.output.buffer = formatHeifOutputBuffer;
});
it('invalid quality should throw an error', () => {
assert.throws(() => {
sharp().heif({ quality: 101 });
});
it('called without options does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif();
});
});
it('non-numeric quality should throw an error', () => {
assert.throws(() => {
sharp().heif({ quality: 'fail' });
});
it('valid quality does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ quality: 50 });
});
});
it('valid lossless does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ lossless: true });
});
it('invalid quality should throw an error', () => {
assert.throws(() => {
sharp().heif({ quality: 101 });
});
});
it('non-boolean lossless should throw an error', () => {
assert.throws(() => {
sharp().heif({ lossless: 'fail' });
});
it('non-numeric quality should throw an error', () => {
assert.throws(() => {
sharp().heif({ quality: 'fail' });
});
});
it('valid compression does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ compression: 'hevc' });
});
it('valid lossless does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ lossless: true });
});
});
it('unknown compression should throw an error', () => {
assert.throws(() => {
sharp().heif({ compression: 'fail' });
});
it('non-boolean lossless should throw an error', () => {
assert.throws(() => {
sharp().heif({ lossless: 'fail' });
});
});
it('invalid compression should throw an error', () => {
assert.throws(() => {
sharp().heif({ compression: 1 });
});
it('valid compression does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ compression: 'avc' });
});
});
it('valid speed does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ speed: 6 });
});
it('unknown compression should throw an error', () => {
assert.throws(() => {
sharp().heif({ compression: 'fail' });
});
});
it('out of range speed should throw an error', () => {
assert.throws(() => {
sharp().heif({ speed: 9 });
});
it('invalid compression should throw an error', () => {
assert.throws(() => {
sharp().heif({ compression: 1 });
});
});
it('invalid speed should throw an error', () => {
assert.throws(() => {
sharp().heif({ compression: 'fail' });
});
});
});

View File

@ -302,7 +302,6 @@ describe('Input/output', function () {
});
it('Fail when input is empty Buffer', function (done) {
if (sharp.format.magick.input.buffer) return this.skip(); // can be removed with libvips 8.10.1+
sharp(Buffer.alloc(0)).toBuffer().then(function () {
assert(false);
done();

View File

@ -279,10 +279,10 @@ describe('Image Stats', function () {
// red channel
assert.strictEqual(0, stats.channels[0].min);
assert.strictEqual(true, isInRange(stats.channels[0].max, 254, 255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 82506996));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11213984832));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 104.36947963892487));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.379896254993135));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].sum, 83291370));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].squaresSum, 11379783198));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].mean, 105.36169496842616));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0].stdev, 57.39412151419967));
assert.strictEqual(true, isInteger(stats.channels[0].minX));
assert.strictEqual(true, isInRange(stats.channels[0].minX, 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[0].minY));
@ -295,10 +295,10 @@ describe('Image Stats', function () {
// green channel
assert.strictEqual(0, stats.channels[1].min);
assert.strictEqual(true, isInRange(stats.channels[1].max, 254, 255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 120089056));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20533721114));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 151.90993361398964));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.83370206587037));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].sum, 120877425));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].squaresSum, 20774687595));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].mean, 152.9072025279307));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1].stdev, 53.84143349689916));
assert.strictEqual(true, isInteger(stats.channels[1].minX));
assert.strictEqual(true, isInRange(stats.channels[1].minX, 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[1].minY));
@ -311,10 +311,10 @@ describe('Image Stats', function () {
// blue channel
assert.strictEqual(0, stats.channels[2].min);
assert.strictEqual(true, isInRange(stats.channels[2].max, 254, 255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 138153653));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28172033081));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 174.76123932359133));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.38276338513747));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].sum, 138938859));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].squaresSum, 28449125593));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].mean, 175.75450711423252));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2].stdev, 71.39929031070358));
assert.strictEqual(true, isInteger(stats.channels[2].minX));
assert.strictEqual(true, isInRange(stats.channels[2].minX, 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[2].minY));