mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add pixel-derived image statistics via vips_stats (#915)
This commit is contained in:
parent
dfaa39fa5d
commit
d6aee8e5ba
@ -86,6 +86,7 @@
|
|||||||
'sources': [
|
'sources': [
|
||||||
'src/common.cc',
|
'src/common.cc',
|
||||||
'src/metadata.cc',
|
'src/metadata.cc',
|
||||||
|
'src/stats.cc',
|
||||||
'src/operations.cc',
|
'src/operations.cc',
|
||||||
'src/pipeline.cc',
|
'src/pipeline.cc',
|
||||||
'src/sharp.cc',
|
'src/sharp.cc',
|
||||||
|
@ -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)**
|
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
|
## limitInputPixels
|
||||||
|
|
||||||
Do not process input images where the number of pixels (width _ height) exceeds this limit.
|
Do not process input images where the number of pixels (width _ height) exceeds this limit.
|
||||||
|
70
lib/input.js
70
lib/input.js
@ -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.
|
* 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.
|
* Assumes image dimensions contained in the input metadata can be trusted.
|
||||||
@ -289,6 +358,7 @@ module.exports = function (Sharp) {
|
|||||||
// Public
|
// Public
|
||||||
clone,
|
clone,
|
||||||
metadata,
|
metadata,
|
||||||
|
stats,
|
||||||
limitInputPixels,
|
limitInputPixels,
|
||||||
sequentialRead
|
sequentialRead
|
||||||
].forEach(function (f) {
|
].forEach(function (f) {
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include "metadata.h"
|
#include "metadata.h"
|
||||||
#include "pipeline.h"
|
#include "pipeline.h"
|
||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
#include "stats.h"
|
||||||
|
|
||||||
NAN_MODULE_INIT(init) {
|
NAN_MODULE_INIT(init) {
|
||||||
vips_init("sharp");
|
vips_init("sharp");
|
||||||
@ -46,6 +47,8 @@ NAN_MODULE_INIT(init) {
|
|||||||
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(format)).ToLocalChecked());
|
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(format)).ToLocalChecked());
|
||||||
Nan::Set(target, Nan::New("_maxColourDistance").ToLocalChecked(),
|
Nan::Set(target, Nan::New("_maxColourDistance").ToLocalChecked(),
|
||||||
Nan::GetFunction(Nan::New<v8::FunctionTemplate>(_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)
|
NODE_MODULE(sharp, init)
|
||||||
|
188
src/stats.cc
Normal file
188
src/stats.cc
Normal 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
65
src/stats.h
Normal 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
BIN
test/fixtures/full-transparent.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 148 B |
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@ -71,6 +71,7 @@ module.exports = {
|
|||||||
|
|
||||||
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
|
||||||
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
inputPngWithTransparency: getPath('blackbug.png'), // public domain
|
||||||
|
inputPngCompleteTransparency: getPath('full-transparent.png'),
|
||||||
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
inputPngWithGreyAlpha: getPath('grey-8bit-alpha.png'),
|
||||||
inputPngWithOneColor: getPath('2x2_fdcce6.png'),
|
inputPngWithOneColor: getPath('2x2_fdcce6.png'),
|
||||||
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png
|
inputPngWithTransparency16bit: getPath('tbgn2c16.png'), // http://www.schaik.com/pngsuite/tbgn2c16.png
|
||||||
|
607
test/unit/stats.js
Normal file
607
test/unit/stats.js
Normal 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user