Add pixel-derived image statistics via vips_stats (#915)

This commit is contained in:
Rahul Nanwani 2017-12-09 16:47:48 +05:30 committed by Lovell Fuller
parent dfaa39fa5d
commit d6aee8e5ba
9 changed files with 971 additions and 0 deletions

View File

@ -86,6 +86,7 @@
'sources': [
'src/common.cc',
'src/metadata.cc',
'src/stats.cc',
'src/operations.cc',
'src/pipeline.cc',
'src/sharp.cc',

View File

@ -67,6 +67,42 @@ image
Returns **([Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> | Sharp)**
## stats
Access to pixel-derived image statistics for every channel in the image
A Promises/A+ promise is returned when `callback` is not provided.
- `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
- `min` (minimum value in the channel)
- `max` (maximum value in the channel)
- `sum` (sum of all values in a channel)
- `squaresSum` (sum of squared values in a channel)
- `mean` (mean of the values in a channel)
- `stdev` (standard deviation for the values in a channel)
- `minX` (x-coordinate of one of the pixel where the minimum lies)
- `minY` (y-coordinate of one of the pixel where the minimum lies)
- `maxX` (x-coordinate of one of the pixel where the maximum lies)
- `maxY` (y-coordinate of one of the pixel where the maximum lies)
- `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
**Parameters**
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?** called with the arguments `(err, metadata)`
**Examples**
```javascript
const image = sharp(inputJpg);
image
.stats()
.then(function(stats) {
// stats contains the channel-wise statistics array and the isOpaque value
})
```
Returns **([Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> | Sharp)**
## limitInputPixels
Do not process input images where the number of pixels (width _ height) exceeds this limit.

View File

@ -238,6 +238,75 @@ function metadata (callback) {
}
}
/**
* Access to pixel-derived image statistics for every channel in the image
* A Promise/A+ promise is returned when `callback` is not provided.
*
* - `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
* - `min` (minimum value in the channel)
* - `max` (maximum value in the channel)
* - `sum` (sum of all values in a channel)
* - `squaresSum` (sum of squared values in a channel)
* - `mean` (mean of the values in a channel)
* - `stdev` (standard deviation for the values in a channel)
* - `minX` (x-coordinate of one of the pixel where the minimum lies)
* - `minY` (y-coordinate of one of the pixel where the minimum lies)
* - `maxX` (x-coordinate of one of the pixel where the maximum lies)
* - `maxY` (y-coordinate of one of the pixel where the maximum lies)
* - `isOpaque`: Value to identify if the image is opaque or transparent, based on the presence and use of alpha channel
*
* @example
* const image = sharp(inputJpg);
* image
* .stats()
* .then(function(stats) {
* // stats contains the channel-wise statistics array and the isOpaque value
* })
*
*
* @param {Function} [callback] - called with the arguments `(err, stats)`
* @returns {Promise<Object>|Sharp}
*/
function stats (callback) {
const that = this;
if (is.fn(callback)) {
if (this._isStreamInput()) {
this.on('finish', function () {
that._flattenBufferIn();
sharp.stats(that.options, callback);
});
} else {
sharp.stats(this.options, callback);
}
return this;
} else {
if (this._isStreamInput()) {
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
sharp.stats(that.options, function (err, stats) {
if (err) {
reject(err);
} else {
resolve(stats);
}
});
});
});
} else {
return new Promise(function (resolve, reject) {
sharp.stats(that.options, function (err, stats) {
if (err) {
reject(err);
} else {
resolve(stats);
}
});
});
}
}
}
/**
* Do not process input images where the number of pixels (width * height) exceeds this limit.
* Assumes image dimensions contained in the input metadata can be trusted.
@ -289,6 +358,7 @@ module.exports = function (Sharp) {
// Public
clone,
metadata,
stats,
limitInputPixels,
sequentialRead
].forEach(function (f) {

View File

@ -20,6 +20,7 @@
#include "metadata.h"
#include "pipeline.h"
#include "utilities.h"
#include "stats.h"
NAN_MODULE_INIT(init) {
vips_init("sharp");
@ -46,6 +47,8 @@ NAN_MODULE_INIT(init) {
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(format)).ToLocalChecked());
Nan::Set(target, Nan::New("_maxColourDistance").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(_maxColourDistance)).ToLocalChecked());
Nan::Set(target, Nan::New("stats").ToLocalChecked(),
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(stats)).ToLocalChecked());
}
NODE_MODULE(sharp, init)

188
src/stats.cc Normal file
View File

@ -0,0 +1,188 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <numeric>
#include <vector>
#include <iostream>
#include <node.h>
#include <nan.h>
#include <vips/vips8>
#include "common.h"
#include "stats.h"
class StatsWorker : public Nan::AsyncWorker {
public:
StatsWorker(
Nan::Callback *callback, StatsBaton *baton, Nan::Callback *debuglog,
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
Nan::AsyncWorker(callback), baton(baton), debuglog(debuglog),
buffersToPersist(buffersToPersist) {
// Protect Buffer objects from GC, keyed on index
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
SaveToPersistent(index, buffer);
return index + 1;
});
}
~StatsWorker() {}
const int STAT_MIN_INDEX = 0;
const int STAT_MAX_INDEX = 1;
const int STAT_SUM_INDEX = 2;
const int STAT_SQ_SUM_INDEX = 3;
const int STAT_MEAN_INDEX = 4;
const int STAT_STDEV_INDEX = 5;
const int STAT_MINX_INDEX = 6;
const int STAT_MINY_INDEX = 7;
const int STAT_MAXX_INDEX = 8;
const int STAT_MAXY_INDEX = 9;
void Execute() {
// Decrement queued task counter
g_atomic_int_dec_and_test(&sharp::counterQueue);
using Nan::New;
using Nan::Set;
using sharp::MaximumImageAlpha;
vips::VImage image;
vips::VImage stats;
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
try {
std::tie(image, imageType) = OpenInput(baton->input, baton->accessMethod);
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
if (imageType != sharp::ImageType::UNKNOWN) {
try {
stats = image.stats();
int bands = image.bands();
double const max = MaximumImageAlpha(image.interpretation());
for (int b = 1; b <= bands; b++) {
ChannelStats cStats(static_cast<int>(stats.getpoint(STAT_MIN_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAX_INDEX, b).front()),
stats.getpoint(STAT_SUM_INDEX, b).front(), stats.getpoint(STAT_SQ_SUM_INDEX, b).front(),
stats.getpoint(STAT_MEAN_INDEX, b).front(), stats.getpoint(STAT_STDEV_INDEX, b).front(),
static_cast<int>(stats.getpoint(STAT_MINX_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MINY_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAXX_INDEX, b).front()),
static_cast<int>(stats.getpoint(STAT_MAXY_INDEX, b).front()));
baton->channelStats.push_back(cStats);
}
// alpha layer is there and the last band i.e. alpha has its max value greater than 0)
if (sharp::HasAlpha(image) && stats.getpoint(STAT_MIN_INDEX, bands).front() != max) {
baton->isOpaque = false;
}
} catch (vips::VError const &err) {
(baton->err).append(err.what());
}
}
// Clean up
vips_error_clear();
vips_thread_shutdown();
}
void HandleOKCallback() {
using Nan::New;
using Nan::Set;
Nan::HandleScope();
v8::Local<v8::Value> argv[2] = { Nan::Null(), Nan::Null() };
if (!baton->err.empty()) {
argv[0] = Nan::Error(baton->err.data());
} else {
// Stats Object
v8::Local<v8::Object> info = New<v8::Object>();
v8::Local<v8::Array> channels = New<v8::Array>();
std::vector<ChannelStats>::iterator it;
int i = 0;
for (it=baton->channelStats.begin() ; it < baton->channelStats.end(); it++, i++) {
v8::Local<v8::Object> channelStat = New<v8::Object>();
Set(channelStat, New("min").ToLocalChecked(), New<v8::Number>(it->min));
Set(channelStat, New("max").ToLocalChecked(), New<v8::Number>(it->max));
Set(channelStat, New("sum").ToLocalChecked(), New<v8::Number>(it->sum));
Set(channelStat, New("squaresSum").ToLocalChecked(), New<v8::Number>(it->squaresSum));
Set(channelStat, New("mean").ToLocalChecked(), New<v8::Number>(it->mean));
Set(channelStat, New("stdev").ToLocalChecked(), New<v8::Number>(it->stdev));
Set(channelStat, New("minX").ToLocalChecked(), New<v8::Number>(it->minX));
Set(channelStat, New("minY").ToLocalChecked(), New<v8::Number>(it->minY));
Set(channelStat, New("maxX").ToLocalChecked(), New<v8::Number>(it->maxX));
Set(channelStat, New("maxY").ToLocalChecked(), New<v8::Number>(it->maxY));
channels->Set(i, channelStat);
}
Set(info, New("channels").ToLocalChecked(), channels);
Set(info, New("isOpaque").ToLocalChecked(), New<v8::Boolean>(baton->isOpaque));
argv[1] = info;
}
// Dispose of Persistent wrapper around input Buffers so they can be garbage collected
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
[this](uint32_t index, v8::Local<v8::Object> const buffer) -> uint32_t {
GetFromPersistent(index);
return index + 1;
});
delete baton->input;
delete baton;
// Handle warnings
std::string warning = sharp::VipsWarningPop();
while (!warning.empty()) {
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
debuglog->Call(1, message);
warning = sharp::VipsWarningPop();
}
// Return to JavaScript
callback->Call(2, argv);
}
private:
StatsBaton* baton;
Nan::Callback *debuglog;
std::vector<v8::Local<v8::Object>> buffersToPersist;
};
/*
stats(options, callback)
*/
NAN_METHOD(stats) {
using sharp::AttrTo;
// Input Buffers must not undergo GC compaction during processing
std::vector<v8::Local<v8::Object>> buffersToPersist;
// V8 objects are converted to non-V8 types held in the baton struct
StatsBaton *baton = new StatsBaton;
v8::Local<v8::Object> options = info[0].As<v8::Object>();
// Input
baton->input = sharp::CreateInputDescriptor(sharp::AttrAs<v8::Object>(options, "input"), buffersToPersist);
baton->accessMethod = AttrTo<bool>(options, "sequentialRead") ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_RANDOM;
// Function to notify of libvips warnings
Nan::Callback *debuglog = new Nan::Callback(sharp::AttrAs<v8::Function>(options, "debuglog"));
// Join queue for worker thread
Nan::Callback *callback = new Nan::Callback(info[1].As<v8::Function>());
Nan::AsyncQueueWorker(new StatsWorker(callback, baton, debuglog, buffersToPersist));
// Increment queued task counter
g_atomic_int_inc(&sharp::counterQueue);
}

65
src/stats.h Normal file
View File

@ -0,0 +1,65 @@
// Copyright 2013, 2014, 2015, 2016, 2017 Lovell Fuller and contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_STATS_H_
#define SRC_STATS_H_
#include <string>
#include <nan.h>
#include "./common.h"
struct ChannelStats {
// stats per channel
int min;
int max;
double sum;
double squaresSum;
double mean;
double stdev;
int minX;
int minY;
int maxX;
int maxY;
ChannelStats():
min(0), max(0), sum(0), squaresSum(0), mean(0), stdev(0)
, minX(0), minY(0), maxX(0), maxY(0) {}
ChannelStats(int minVal, int maxVal, double sumVal, double squaresSumVal,
double meanVal, double stdevVal, int minXVal, int minYVal, int maxXVal, int maxYVal):
min(minVal), max(maxVal), sum(sumVal), squaresSum(squaresSumVal),
mean(meanVal), stdev(stdevVal), minX(minXVal), minY(minYVal), maxX(maxXVal), maxY(maxYVal) {}
};
struct StatsBaton {
// Input
sharp::InputDescriptor *input;
VipsAccess accessMethod;
// Output
std::vector<ChannelStats> channelStats;
bool isOpaque;
std::string err;
StatsBaton():
input(nullptr),
isOpaque(true)
{}
};
NAN_METHOD(stats);
#endif // SRC_STATS_H_

BIN
test/fixtures/full-transparent.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 B

View File

@ -71,6 +71,7 @@ module.exports = {
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
inputPngWithTransparency: getPath('blackbug.png'), // public domain
inputPngCompleteTransparency: getPath('full-transparent.png'),
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
inputPngWithOneColor: getPath('2x2_fdcce6.png'),
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png

607
test/unit/stats.js Normal file
View File

@ -0,0 +1,607 @@
'use strict';
const fs = require('fs');
const assert = require('assert');
const sharp = require('../../');
const fixtures = require('../fixtures');
// Test Helpers
var threshold = 0.001;
function isInAcceptableRange (actual, expected) {
return actual >= ((1 - threshold) * expected) && actual <= ((1 + threshold) * expected);
}
function isInRange (actual, min, max) {
return actual >= min && actual <= max;
}
function isInteger (val) {
return Number.isInteger(val);
}
describe('Image Stats', function () {
it('JPEG', function (done) {
sharp(fixtures.inputJpg).stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(true, stats.isOpaque);
// red channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(255, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 615101275));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 83061892917));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 101.44954540768993));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 58.373870588815414));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
// green channel
assert.strictEqual(0, stats.channels[1]['min']);
assert.strictEqual(255, stats.channels[1]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 462824115));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 47083677255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 76.33425255128337));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 44.03023262954866));
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 2725));
// blue channel
assert.strictEqual(0, stats.channels[2]['min']);
assert.strictEqual(255, stats.channels[2]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 372986756));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 32151543524));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 61.51724663436759));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 38.96702865090125));
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 2725));
done();
});
});
it('PNG without transparency', function (done) {
sharp(fixtures.inputPng).stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(true, stats.isOpaque);
// red channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(255, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 1391368230));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 354798898650));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 238.8259925648822));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 62.15121915523771));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2809));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2074));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2809));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2074));
done();
});
});
it('PNG with transparency', function (done) {
sharp(fixtures.inputPngWithTransparency).stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(false, stats.isOpaque);
// red channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(255, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 795678795));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 202898092725));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 252.9394769668579));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 22.829537532816));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2048));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 1536));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2048));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 1536));
// green channel
assert.strictEqual(0, stats.channels[1]['min']);
assert.strictEqual(255, stats.channels[1]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 795678795));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 202898092725));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 252.9394769668579));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 22.829537532816));
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 2048));
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 1536));
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 2048));
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 1536));
// blue channel
assert.strictEqual(0, stats.channels[2]['min']);
assert.strictEqual(255, stats.channels[2]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 795678795));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 202898092725));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 252.9394769668579));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 22.829537532816));
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 2048));
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 1536));
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 2048));
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 1536));
// alpha channel
assert.strictEqual(0, stats.channels[3]['min']);
assert.strictEqual(255, stats.channels[3]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['sum'], 5549142));
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['squaresSum'], 1333571132));
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['mean'], 1.7640247344970703));
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['stdev'], 20.51387814157297));
assert.strictEqual(true, isInteger(stats.channels[3]['minX']));
assert.strictEqual(true, isInRange(stats.channels[3]['minX'], 0, 2048));
assert.strictEqual(true, isInteger(stats.channels[3]['minY']));
assert.strictEqual(true, isInRange(stats.channels[3]['minY'], 0, 1536));
assert.strictEqual(true, isInteger(stats.channels[3]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[3]['maxX'], 0, 2048));
assert.strictEqual(true, isInteger(stats.channels[3]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[3]['maxY'], 0, 1536));
done();
});
});
it('PNG fully transparent', function (done) {
sharp(fixtures.inputPngCompleteTransparency).stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(false, stats.isOpaque);
// alpha channel
assert.strictEqual(0, stats.channels[3]['min']);
assert.strictEqual(0, stats.channels[3]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['sum'], 0));
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['squaresSum'], 0));
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['mean'], 0));
assert.strictEqual(true, isInAcceptableRange(stats.channels[3]['stdev'], 0));
assert.strictEqual(true, isInteger(stats.channels[3]['minX']));
assert.strictEqual(true, isInRange(stats.channels[3]['minX'], 0, 300));
assert.strictEqual(true, isInteger(stats.channels[3]['minY']));
assert.strictEqual(true, isInRange(stats.channels[3]['minY'], 0, 300));
assert.strictEqual(true, isInteger(stats.channels[3]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[3]['maxX'], 0, 300));
assert.strictEqual(true, isInteger(stats.channels[3]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[3]['maxY'], 0, 300));
done();
});
});
it('Tiff', function (done) {
sharp(fixtures.inputTiff).stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(true, stats.isOpaque);
// red channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(255, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 1887266220));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 481252886100));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 235.81772349417824));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 67.25712856093298));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2464));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 3248));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2464));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 3248));
done();
});
});
it('WebP', function (done) {
sharp(fixtures.inputWebP).stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(true, stats.isOpaque);
// red channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(255, stats.channels[0]['max']);
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']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 772));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 772));
// green channel
assert.strictEqual(0, stats.channels[1]['min']);
assert.strictEqual(255, stats.channels[1]['max']);
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']));
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 772));
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 772));
// blue channel
assert.strictEqual(0, stats.channels[2]['min']);
assert.strictEqual(255, stats.channels[2]['max']);
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']));
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 772));
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 1024));
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 772));
done();
});
});
it('GIF', function (done) {
sharp(fixtures.inputGif).stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(true, stats.isOpaque);
// red channel
assert.strictEqual(35, stats.channels[0]['min']);
assert.strictEqual(254, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 56088385));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 8002132113));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 131.53936444652908));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 38.26389131415863));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 800));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 533));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 800));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 533));
// green channel
assert.strictEqual(43, stats.channels[1]['min']);
assert.strictEqual(255, stats.channels[1]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 58612156));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 8548344254));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 137.45815196998123));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 33.955424103758205));
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 800));
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 533));
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 800));
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 533));
// blue channel
assert.strictEqual(51, stats.channels[2]['min']);
assert.strictEqual(254, stats.channels[2]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 49628525));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 6450556071));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 116.38959896810506));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 39.7669551046809));
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 800));
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 533));
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 800));
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 533));
done();
});
});
it('Grayscale GIF with alpha', function (done) {
sharp(fixtures.inputGifGreyPlusAlpha).stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(false, stats.isOpaque);
// gray channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(101, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 101));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 10201));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 50.5));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 71.4177848998413));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 1));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 1));
// alpha channel
assert.strictEqual(0, stats.channels[1]['min']);
assert.strictEqual(255, stats.channels[1]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 65025));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 127.5));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 180.31222920256963));
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 2));
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 1));
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 2));
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 1));
done();
});
});
it('Stream in, Callback out', function (done) {
const readable = fs.createReadStream(fixtures.inputJpg);
const pipeline = sharp().stats(function (err, stats) {
if (err) throw err;
assert.strictEqual(true, stats.isOpaque);
// red channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(255, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 615101275));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 83061892917));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 101.44954540768993));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 58.373870588815414));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
// green channel
assert.strictEqual(0, stats.channels[1]['min']);
assert.strictEqual(255, stats.channels[1]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 462824115));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 47083677255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 76.33425255128337));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 44.03023262954866));
assert.strictEqual(true, isInteger(stats.channels[1]['minX']));
assert.strictEqual(true, isInRange(stats.channels[1]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[1]['minY']));
assert.strictEqual(true, isInRange(stats.channels[1]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[1]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[1]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[1]['maxY'], 0, 2725));
// blue channel
assert.strictEqual(0, stats.channels[2]['min']);
assert.strictEqual(255, stats.channels[2]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 372986756));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 32151543524));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 61.51724663436759));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 38.96702865090125));
assert.strictEqual(true, isInteger(stats.channels[2]['minX']));
assert.strictEqual(true, isInRange(stats.channels[2]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[2]['minY']));
assert.strictEqual(true, isInRange(stats.channels[2]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[2]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[2]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[2]['maxY'], 0, 2725));
done();
});
readable.pipe(pipeline);
});
it('Stream in, Promise out', function () {
const pipeline = sharp();
fs.createReadStream(fixtures.inputJpg).pipe(pipeline);
return pipeline.stats().then(function (stats) {
assert.strictEqual(true, stats.isOpaque);
// red channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(255, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 615101275));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 83061892917));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 101.44954540768993));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 58.373870588815414));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
// green channel
assert.strictEqual(0, stats.channels[1]['min']);
assert.strictEqual(255, stats.channels[1]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 462824115));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 47083677255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 76.33425255128337));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 44.03023262954866));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
// blue channel
assert.strictEqual(0, stats.channels[2]['min']);
assert.strictEqual(255, stats.channels[2]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 372986756));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 32151543524));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 61.51724663436759));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 38.96702865090125));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
}).catch(function (err) {
throw err;
});
});
it('File in, Promise out', function () {
return sharp(fixtures.inputJpg).stats().then(function (stats) {
assert.strictEqual(true, stats.isOpaque);
// red channel
assert.strictEqual(0, stats.channels[0]['min']);
assert.strictEqual(255, stats.channels[0]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['sum'], 615101275));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['squaresSum'], 83061892917));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['mean'], 101.44954540768993));
assert.strictEqual(true, isInAcceptableRange(stats.channels[0]['stdev'], 58.373870588815414));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
// green channel
assert.strictEqual(0, stats.channels[1]['min']);
assert.strictEqual(255, stats.channels[1]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['sum'], 462824115));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['squaresSum'], 47083677255));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['mean'], 76.33425255128337));
assert.strictEqual(true, isInAcceptableRange(stats.channels[1]['stdev'], 44.03023262954866));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
// blue channel
assert.strictEqual(0, stats.channels[2]['min']);
assert.strictEqual(255, stats.channels[2]['max']);
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['sum'], 372986756));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['squaresSum'], 32151543524));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['mean'], 61.51724663436759));
assert.strictEqual(true, isInAcceptableRange(stats.channels[2]['stdev'], 38.96702865090125));
assert.strictEqual(true, isInteger(stats.channels[0]['minX']));
assert.strictEqual(true, isInRange(stats.channels[0]['minX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['minY']));
assert.strictEqual(true, isInRange(stats.channels[0]['minY'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxX']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxX'], 0, 2725));
assert.strictEqual(true, isInteger(stats.channels[0]['maxY']));
assert.strictEqual(true, isInRange(stats.channels[0]['maxY'], 0, 2725));
}).catch(function (err) {
throw err;
});
});
it('File input with corrupt header fails gracefully', function (done) {
sharp(fixtures.inputJpgWithCorruptHeader)
.stats(function (err) {
assert.strictEqual(true, !!err);
done();
});
});
it('File input with corrupt header fails gracefully, Promise out', function () {
return sharp(fixtures.inputJpgWithCorruptHeader)
.stats().then(function (stats) {
throw new Error('Corrupt Header file');
}).catch(function (err) {
assert.ok(!!err);
});
});
it('File input with corrupt header fails gracefully, Stream In, Promise Out', function () {
const pipeline = sharp();
fs.createReadStream(fixtures.inputJpgWithCorruptHeader).pipe(pipeline);
return pipeline
.stats().then(function (stats) {
throw new Error('Corrupt Header file');
}).catch(function (err) {
assert.ok(!!err);
});
});
it('Buffer input with corrupt header fails gracefully', function (done) {
sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader))
.stats(function (err) {
assert.strictEqual(true, !!err);
done();
});
});
it('Non-existent file in, Promise out', function (done) {
sharp('fail').stats().then(function (stats) {
throw new Error('Non-existent file');
}, function (err) {
assert.ok(!!err);
done();
});
});
});