mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-13 18:56:17 +03:00
Added an image.entropy()
method (#3608)
Added an `image.entropy()` method
This commit is contained in:
commit
08c47925d0
17
Tests/test_image_entropy.py
Normal file
17
Tests/test_image_entropy.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from .helper import PillowTestCase, hopper
|
||||
|
||||
|
||||
class TestImageEntropy(PillowTestCase):
|
||||
def test_entropy(self):
|
||||
def entropy(mode):
|
||||
return hopper(mode).entropy()
|
||||
|
||||
self.assertAlmostEqual(entropy("1"), 0.9138803254693582)
|
||||
self.assertAlmostEqual(entropy("L"), 7.06650513081286)
|
||||
self.assertAlmostEqual(entropy("I"), 7.06650513081286)
|
||||
self.assertAlmostEqual(entropy("F"), 7.06650513081286)
|
||||
self.assertAlmostEqual(entropy("P"), 5.0530452472519745)
|
||||
self.assertAlmostEqual(entropy("RGB"), 8.821286587714319)
|
||||
self.assertAlmostEqual(entropy("RGBA"), 7.42724306524488)
|
||||
self.assertAlmostEqual(entropy("CMYK"), 7.4272430652448795)
|
||||
self.assertAlmostEqual(entropy("YCbCr"), 7.698360534903628)
|
|
@ -90,6 +90,8 @@ def testimage():
|
|||
2
|
||||
>>> len(im.histogram())
|
||||
768
|
||||
>>> '%.7f' % im.entropy()
|
||||
'8.8212866'
|
||||
>>> _info(im.point(list(range(256))*3))
|
||||
(None, 'RGB', (128, 128))
|
||||
>>> _info(im.resize((64, 64)))
|
||||
|
|
|
@ -1405,6 +1405,7 @@ class Image(object):
|
|||
bi-level image (mode "1") or a greyscale image ("L").
|
||||
|
||||
:param mask: An optional mask.
|
||||
:param extrema: An optional tuple of manually-specified extrema.
|
||||
:returns: A list containing pixel counts.
|
||||
"""
|
||||
self.load()
|
||||
|
@ -1417,6 +1418,32 @@ class Image(object):
|
|||
return self.im.histogram(extrema)
|
||||
return self.im.histogram()
|
||||
|
||||
def entropy(self, mask=None, extrema=None):
|
||||
"""
|
||||
Calculates and returns the entropy for the image.
|
||||
|
||||
A bilevel image (mode "1") is treated as a greyscale ("L")
|
||||
image by this method.
|
||||
|
||||
If a mask is provided, the method employs the histogram for
|
||||
those parts of the image where the mask image is non-zero.
|
||||
The mask image must have the same size as the image, and be
|
||||
either a bi-level image (mode "1") or a greyscale image ("L").
|
||||
|
||||
:param mask: An optional mask.
|
||||
:param extrema: An optional tuple of manually-specified extrema.
|
||||
:returns: A float value representing the image entropy
|
||||
"""
|
||||
self.load()
|
||||
if mask:
|
||||
mask.load()
|
||||
return self.im.entropy((0, 0), mask.im)
|
||||
if self.mode in ("I", "F"):
|
||||
if extrema is None:
|
||||
extrema = self.getextrema()
|
||||
return self.im.entropy(extrema)
|
||||
return self.im.entropy()
|
||||
|
||||
def offset(self, xoffset, yoffset=None):
|
||||
raise NotImplementedError(
|
||||
"offset() has been removed. Please call ImageChops.offset() instead."
|
||||
|
|
136
src/_imaging.c
136
src/_imaging.c
|
@ -86,6 +86,9 @@
|
|||
|
||||
#include "py3.h"
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
|
||||
/* Configuration stuff. Feel free to undef things you don't need. */
|
||||
#define WITH_IMAGECHOPS /* ImageChops support */
|
||||
#define WITH_IMAGEDRAW /* ImageDraw support */
|
||||
|
@ -1176,55 +1179,64 @@ _getpixel(ImagingObject* self, PyObject* args)
|
|||
return getpixel(self->image, self->access, x, y);
|
||||
}
|
||||
|
||||
union hist_extrema {
|
||||
UINT8 u[2];
|
||||
INT32 i[2];
|
||||
FLOAT32 f[2];
|
||||
};
|
||||
|
||||
static union hist_extrema*
|
||||
parse_histogram_extremap(ImagingObject* self, PyObject* extremap,
|
||||
union hist_extrema* ep)
|
||||
{
|
||||
int i0, i1;
|
||||
double f0, f1;
|
||||
|
||||
if (extremap) {
|
||||
switch (self->image->type) {
|
||||
case IMAGING_TYPE_UINT8:
|
||||
if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1))
|
||||
return NULL;
|
||||
ep->u[0] = CLIP8(i0);
|
||||
ep->u[1] = CLIP8(i1);
|
||||
break;
|
||||
case IMAGING_TYPE_INT32:
|
||||
if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1))
|
||||
return NULL;
|
||||
ep->i[0] = i0;
|
||||
ep->i[1] = i1;
|
||||
break;
|
||||
case IMAGING_TYPE_FLOAT32:
|
||||
if (!PyArg_ParseTuple(extremap, "dd", &f0, &f1))
|
||||
return NULL;
|
||||
ep->f[0] = (FLOAT32) f0;
|
||||
ep->f[1] = (FLOAT32) f1;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
return ep;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
_histogram(ImagingObject* self, PyObject* args)
|
||||
{
|
||||
ImagingHistogram h;
|
||||
PyObject* list;
|
||||
int i;
|
||||
union {
|
||||
UINT8 u[2];
|
||||
INT32 i[2];
|
||||
FLOAT32 f[2];
|
||||
} extrema;
|
||||
void* ep;
|
||||
int i0, i1;
|
||||
double f0, f1;
|
||||
union hist_extrema extrema;
|
||||
union hist_extrema* ep;
|
||||
|
||||
PyObject* extremap = NULL;
|
||||
ImagingObject* maskp = NULL;
|
||||
if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp))
|
||||
return NULL;
|
||||
|
||||
if (extremap) {
|
||||
ep = &extrema;
|
||||
switch (self->image->type) {
|
||||
case IMAGING_TYPE_UINT8:
|
||||
if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1))
|
||||
return NULL;
|
||||
/* FIXME: clip */
|
||||
extrema.u[0] = i0;
|
||||
extrema.u[1] = i1;
|
||||
break;
|
||||
case IMAGING_TYPE_INT32:
|
||||
if (!PyArg_ParseTuple(extremap, "ii", &i0, &i1))
|
||||
return NULL;
|
||||
extrema.i[0] = i0;
|
||||
extrema.i[1] = i1;
|
||||
break;
|
||||
case IMAGING_TYPE_FLOAT32:
|
||||
if (!PyArg_ParseTuple(extremap, "dd", &f0, &f1))
|
||||
return NULL;
|
||||
extrema.f[0] = (FLOAT32) f0;
|
||||
extrema.f[1] = (FLOAT32) f1;
|
||||
break;
|
||||
default:
|
||||
ep = NULL;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
ep = NULL;
|
||||
|
||||
/* Using a var to avoid allocations. */
|
||||
ep = parse_histogram_extremap(self, extremap, &extrema);
|
||||
h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
|
||||
|
||||
if (!h)
|
||||
|
@ -1243,11 +1255,59 @@ _histogram(ImagingObject* self, PyObject* args)
|
|||
PyList_SetItem(list, i, item);
|
||||
}
|
||||
|
||||
/* Destroy the histogram structure */
|
||||
ImagingHistogramDelete(h);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
_entropy(ImagingObject* self, PyObject* args)
|
||||
{
|
||||
ImagingHistogram h;
|
||||
int idx, length;
|
||||
long sum;
|
||||
double entropy, fsum, p;
|
||||
union hist_extrema extrema;
|
||||
union hist_extrema* ep;
|
||||
|
||||
PyObject* extremap = NULL;
|
||||
ImagingObject* maskp = NULL;
|
||||
if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp))
|
||||
return NULL;
|
||||
|
||||
/* Using a local var to avoid allocations. */
|
||||
ep = parse_histogram_extremap(self, extremap, &extrema);
|
||||
h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
|
||||
|
||||
if (!h)
|
||||
return NULL;
|
||||
|
||||
/* Calculate the histogram entropy */
|
||||
/* First, sum the histogram data */
|
||||
length = h->bands * 256;
|
||||
sum = 0;
|
||||
for (idx = 0; idx < length; idx++) {
|
||||
sum += h->histogram[idx];
|
||||
}
|
||||
|
||||
/* Next, normalize the histogram data, */
|
||||
/* using the histogram sum value */
|
||||
fsum = (double)sum;
|
||||
entropy = 0.0;
|
||||
for (idx = 0; idx < length; idx++) {
|
||||
p = (double)h->histogram[idx] / fsum;
|
||||
if (p != 0.0) {
|
||||
entropy += p * log(p) * M_LOG2E;
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroy the histogram structure */
|
||||
ImagingHistogramDelete(h);
|
||||
|
||||
return PyFloat_FromDouble(-entropy);
|
||||
}
|
||||
|
||||
#ifdef WITH_MODEFILTER
|
||||
static PyObject*
|
||||
_modefilter(ImagingObject* self, PyObject* args)
|
||||
|
@ -3193,6 +3253,7 @@ static struct PyMethodDef methods[] = {
|
|||
{"expand", (PyCFunction)_expand_image, 1},
|
||||
{"filter", (PyCFunction)_filter, 1},
|
||||
{"histogram", (PyCFunction)_histogram, 1},
|
||||
{"entropy", (PyCFunction)_entropy, 1},
|
||||
#ifdef WITH_MODEFILTER
|
||||
{"modefilter", (PyCFunction)_modefilter, 1},
|
||||
#endif
|
||||
|
@ -3918,4 +3979,3 @@ init_imaging(void)
|
|||
setup_module(m);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user