mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-11-04 09:57:43 +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."
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										136
									
								
								src/_imaging.c
									
									
									
									
									
								
							
							
						
						
									
										136
									
								
								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,55 +1179,64 @@ _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) {
 | 
					    /* Using a var to avoid allocations. */
 | 
				
			||||||
        ep = &extrema;
 | 
					    ep = parse_histogram_extremap(self, extremap, &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;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
 | 
					    h = ImagingGetHistogram(self->image, (maskp) ? maskp->image : NULL, ep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!h)
 | 
					    if (!h)
 | 
				
			||||||
| 
						 | 
					@ -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