mirror of
https://github.com/python-pillow/Pillow.git
synced 2025-01-27 17:54:32 +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
|
2
|
||||||
>>> len(im.histogram())
|
>>> len(im.histogram())
|
||||||
768
|
768
|
||||||
|
>>> '%.7f' % im.entropy()
|
||||||
|
'8.8212866'
|
||||||
>>> _info(im.point(list(range(256))*3))
|
>>> _info(im.point(list(range(256))*3))
|
||||||
(None, 'RGB', (128, 128))
|
(None, 'RGB', (128, 128))
|
||||||
>>> _info(im.resize((64, 64)))
|
>>> _info(im.resize((64, 64)))
|
||||||
|
|
|
@ -1405,6 +1405,7 @@ class Image(object):
|
||||||
bi-level image (mode "1") or a greyscale image ("L").
|
bi-level image (mode "1") or a greyscale image ("L").
|
||||||
|
|
||||||
:param mask: An optional mask.
|
:param mask: An optional mask.
|
||||||
|
:param extrema: An optional tuple of manually-specified extrema.
|
||||||
:returns: A list containing pixel counts.
|
:returns: A list containing pixel counts.
|
||||||
"""
|
"""
|
||||||
self.load()
|
self.load()
|
||||||
|
@ -1417,6 +1418,32 @@ class Image(object):
|
||||||
return self.im.histogram(extrema)
|
return self.im.histogram(extrema)
|
||||||
return self.im.histogram()
|
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):
|
def offset(self, xoffset, yoffset=None):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
"offset() has been removed. Please call ImageChops.offset() instead."
|
"offset() has been removed. Please call ImageChops.offset() instead."
|
||||||
|
|
140
src/_imaging.c
140
src/_imaging.c
|
@ -86,6 +86,9 @@
|
||||||
|
|
||||||
#include "py3.h"
|
#include "py3.h"
|
||||||
|
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
/* Configuration stuff. Feel free to undef things you don't need. */
|
/* Configuration stuff. Feel free to undef things you don't need. */
|
||||||
#define WITH_IMAGECHOPS /* ImageChops support */
|
#define WITH_IMAGECHOPS /* ImageChops support */
|
||||||
#define WITH_IMAGEDRAW /* ImageDraw support */
|
#define WITH_IMAGEDRAW /* ImageDraw support */
|
||||||
|
@ -1176,59 +1179,68 @@ _getpixel(ImagingObject* self, PyObject* args)
|
||||||
return getpixel(self->image, self->access, x, y);
|
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*
|
static PyObject*
|
||||||
_histogram(ImagingObject* self, PyObject* args)
|
_histogram(ImagingObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
ImagingHistogram h;
|
ImagingHistogram h;
|
||||||
PyObject* list;
|
PyObject* list;
|
||||||
int i;
|
int i;
|
||||||
union {
|
union hist_extrema extrema;
|
||||||
UINT8 u[2];
|
union hist_extrema* ep;
|
||||||
INT32 i[2];
|
|
||||||
FLOAT32 f[2];
|
|
||||||
} extrema;
|
|
||||||
void* ep;
|
|
||||||
int i0, i1;
|
|
||||||
double f0, f1;
|
|
||||||
|
|
||||||
PyObject* extremap = NULL;
|
PyObject* extremap = NULL;
|
||||||
ImagingObject* maskp = NULL;
|
ImagingObject* maskp = NULL;
|
||||||
if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp))
|
if (!PyArg_ParseTuple(args, "|OO!", &extremap, &Imaging_Type, &maskp))
|
||||||
return NULL;
|
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);
|
h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
|
||||||
|
|
||||||
if (!h)
|
if (!h)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Build an integer list containing the histogram */
|
/* Build an integer list containing the histogram */
|
||||||
list = PyList_New(h->bands * 256);
|
list = PyList_New(h->bands * 256);
|
||||||
|
@ -1243,11 +1255,59 @@ _histogram(ImagingObject* self, PyObject* args)
|
||||||
PyList_SetItem(list, i, item);
|
PyList_SetItem(list, i, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Destroy the histogram structure */
|
||||||
ImagingHistogramDelete(h);
|
ImagingHistogramDelete(h);
|
||||||
|
|
||||||
return list;
|
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
|
#ifdef WITH_MODEFILTER
|
||||||
static PyObject*
|
static PyObject*
|
||||||
_modefilter(ImagingObject* self, PyObject* args)
|
_modefilter(ImagingObject* self, PyObject* args)
|
||||||
|
@ -3193,6 +3253,7 @@ static struct PyMethodDef methods[] = {
|
||||||
{"expand", (PyCFunction)_expand_image, 1},
|
{"expand", (PyCFunction)_expand_image, 1},
|
||||||
{"filter", (PyCFunction)_filter, 1},
|
{"filter", (PyCFunction)_filter, 1},
|
||||||
{"histogram", (PyCFunction)_histogram, 1},
|
{"histogram", (PyCFunction)_histogram, 1},
|
||||||
|
{"entropy", (PyCFunction)_entropy, 1},
|
||||||
#ifdef WITH_MODEFILTER
|
#ifdef WITH_MODEFILTER
|
||||||
{"modefilter", (PyCFunction)_modefilter, 1},
|
{"modefilter", (PyCFunction)_modefilter, 1},
|
||||||
#endif
|
#endif
|
||||||
|
@ -3918,4 +3979,3 @@ init_imaging(void)
|
||||||
setup_module(m);
|
setup_module(m);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user