mirror of
https://github.com/lovell/sharp.git
synced 2025-07-11 19:40:14 +02:00
Add experimental entropy field to stats response
This commit is contained in:
parent
532de4ecab
commit
25bd2cea3e
@ -23,6 +23,8 @@ Requires libvips v8.6.1.
|
||||
* Improve install time error messages for FreeBSD users.
|
||||
[#1310](https://github.com/lovell/sharp/issues/1310)
|
||||
|
||||
* Add experimental entropy field to stats response.
|
||||
|
||||
#### v0.20.5 - 27<sup>th</sup> June 2018
|
||||
|
||||
* Expose libjpeg optimize_coding flag.
|
||||
|
@ -264,6 +264,7 @@ function metadata (callback) {
|
||||
* - `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
|
||||
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any (experimental)
|
||||
*
|
||||
* @example
|
||||
* const image = sharp(inputJpg);
|
||||
|
17
src/stats.cc
17
src/stats.cc
@ -59,7 +59,6 @@ class StatsWorker : public Nan::AsyncWorker {
|
||||
using sharp::MaximumImageAlpha;
|
||||
|
||||
vips::VImage image;
|
||||
vips::VImage stats;
|
||||
sharp::ImageType imageType = sharp::ImageType::UNKNOWN;
|
||||
|
||||
try {
|
||||
@ -69,9 +68,8 @@ class StatsWorker : public Nan::AsyncWorker {
|
||||
}
|
||||
if (imageType != sharp::ImageType::UNKNOWN) {
|
||||
try {
|
||||
stats = image.stats();
|
||||
int bands = image.bands();
|
||||
double const max = MaximumImageAlpha(image.interpretation());
|
||||
vips::VImage stats = image.stats();
|
||||
int const bands = image.bands();
|
||||
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()),
|
||||
@ -83,11 +81,15 @@ class StatsWorker : public Nan::AsyncWorker {
|
||||
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) {
|
||||
// Image is not opaque when alpha layer is present and contains a non-mamixa value
|
||||
if (sharp::HasAlpha(image)) {
|
||||
double const minAlpha = static_cast<double>(stats.getpoint(STAT_MIN_INDEX, bands).front());
|
||||
if (minAlpha != MaximumImageAlpha(image.interpretation())) {
|
||||
baton->isOpaque = false;
|
||||
}
|
||||
}
|
||||
// Estimate entropy via histogram of greyscale value frequency
|
||||
baton->entropy = std::abs(image.colourspace(VIPS_INTERPRETATION_B_W)[0].hist_find().hist_entropy());
|
||||
} catch (vips::VError const &err) {
|
||||
(baton->err).append(err.what());
|
||||
}
|
||||
@ -130,6 +132,7 @@ class StatsWorker : public Nan::AsyncWorker {
|
||||
|
||||
Set(info, New("channels").ToLocalChecked(), channels);
|
||||
Set(info, New("isOpaque").ToLocalChecked(), New<v8::Boolean>(baton->isOpaque));
|
||||
Set(info, New("entropy").ToLocalChecked(), New<v8::Number>(baton->entropy));
|
||||
argv[1] = info;
|
||||
}
|
||||
|
||||
|
@ -51,12 +51,14 @@ struct StatsBaton {
|
||||
// Output
|
||||
std::vector<ChannelStats> channelStats;
|
||||
bool isOpaque;
|
||||
double entropy;
|
||||
|
||||
std::string err;
|
||||
|
||||
StatsBaton():
|
||||
input(nullptr),
|
||||
isOpaque(true)
|
||||
isOpaque(true),
|
||||
entropy(0.0)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -24,6 +24,7 @@ describe('Image Stats', function () {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(true, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
@ -82,6 +83,7 @@ describe('Image Stats', function () {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(true, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3409031108021736));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
@ -105,7 +107,9 @@ describe('Image Stats', function () {
|
||||
it('PNG with transparency', function (done) {
|
||||
sharp(fixtures.inputPngWithTransparency).stats(function (err, stats) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(false, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.06778064835816622));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
@ -180,6 +184,7 @@ describe('Image Stats', function () {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(false, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0));
|
||||
|
||||
// alpha channel
|
||||
assert.strictEqual(0, stats.channels[3]['min']);
|
||||
@ -204,7 +209,9 @@ describe('Image Stats', function () {
|
||||
it('Tiff', function (done) {
|
||||
sharp(fixtures.inputTiff).stats(function (err, stats) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(true, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 0.3851250782608986));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
@ -231,6 +238,7 @@ describe('Image Stats', function () {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(true, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.51758075132966));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
@ -289,6 +297,7 @@ describe('Image Stats', function () {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(true, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 6.087309412541799));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(35, stats.channels[0]['min']);
|
||||
@ -345,7 +354,9 @@ describe('Image Stats', function () {
|
||||
it('Grayscale GIF with alpha', function (done) {
|
||||
sharp(fixtures.inputGifGreyPlusAlpha).stats(function (err, stats) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(false, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 1));
|
||||
|
||||
// gray channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
@ -387,7 +398,9 @@ describe('Image Stats', function () {
|
||||
const readable = fs.createReadStream(fixtures.inputJpg);
|
||||
const pipeline = sharp().stats(function (err, stats) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(true, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
@ -449,6 +462,7 @@ describe('Image Stats', function () {
|
||||
|
||||
return pipeline.stats().then(function (stats) {
|
||||
assert.strictEqual(true, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
@ -505,6 +519,7 @@ describe('Image Stats', function () {
|
||||
it('File in, Promise out', function () {
|
||||
return sharp(fixtures.inputJpg).stats().then(function (stats) {
|
||||
assert.strictEqual(true, stats.isOpaque);
|
||||
assert.strictEqual(true, isInAcceptableRange(stats.entropy, 7.319914765248541));
|
||||
|
||||
// red channel
|
||||
assert.strictEqual(0, stats.channels[0]['min']);
|
||||
|
Loading…
x
Reference in New Issue
Block a user