Spaces:
Running
Running
File size: 5,485 Bytes
3f7c971 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
#
# The Python Imaging Library.
# $Id$
#
# global image statistics
#
# History:
# 1996-04-05 fl Created
# 1997-05-21 fl Added mask; added rms, var, stddev attributes
# 1997-08-05 fl Added median
# 1998-07-05 hk Fixed integer overflow error
#
# Notes:
# This class shows how to implement delayed evaluation of attributes.
# To get a certain value, simply access the corresponding attribute.
# The __getattr__ dispatcher takes care of the rest.
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996-97.
#
# See the README file for information on usage and redistribution.
#
from __future__ import annotations
import math
from functools import cached_property
from . import Image
class Stat:
def __init__(
self, image_or_list: Image.Image | list[int], mask: Image.Image | None = None
) -> None:
"""
Calculate statistics for the given image. If a mask is included,
only the regions covered by that mask are included in the
statistics. You can also pass in a previously calculated histogram.
:param image: A PIL image, or a precalculated histogram.
.. note::
For a PIL image, calculations rely on the
:py:meth:`~PIL.Image.Image.histogram` method. The pixel counts are
grouped into 256 bins, even if the image has more than 8 bits per
channel. So ``I`` and ``F`` mode images have a maximum ``mean``,
``median`` and ``rms`` of 255, and cannot have an ``extrema`` maximum
of more than 255.
:param mask: An optional mask.
"""
if isinstance(image_or_list, Image.Image):
self.h = image_or_list.histogram(mask)
elif isinstance(image_or_list, list):
self.h = image_or_list
else:
msg = "first argument must be image or list" # type: ignore[unreachable]
raise TypeError(msg)
self.bands = list(range(len(self.h) // 256))
@cached_property
def extrema(self) -> list[tuple[int, int]]:
"""
Min/max values for each band in the image.
.. note::
This relies on the :py:meth:`~PIL.Image.Image.histogram` method, and
simply returns the low and high bins used. This is correct for
images with 8 bits per channel, but fails for other modes such as
``I`` or ``F``. Instead, use :py:meth:`~PIL.Image.Image.getextrema` to
return per-band extrema for the image. This is more correct and
efficient because, for non-8-bit modes, the histogram method uses
:py:meth:`~PIL.Image.Image.getextrema` to determine the bins used.
"""
def minmax(histogram: list[int]) -> tuple[int, int]:
res_min, res_max = 255, 0
for i in range(256):
if histogram[i]:
res_min = i
break
for i in range(255, -1, -1):
if histogram[i]:
res_max = i
break
return res_min, res_max
return [minmax(self.h[i:]) for i in range(0, len(self.h), 256)]
@cached_property
def count(self) -> list[int]:
"""Total number of pixels for each band in the image."""
return [sum(self.h[i : i + 256]) for i in range(0, len(self.h), 256)]
@cached_property
def sum(self) -> list[float]:
"""Sum of all pixels for each band in the image."""
v = []
for i in range(0, len(self.h), 256):
layer_sum = 0.0
for j in range(256):
layer_sum += j * self.h[i + j]
v.append(layer_sum)
return v
@cached_property
def sum2(self) -> list[float]:
"""Squared sum of all pixels for each band in the image."""
v = []
for i in range(0, len(self.h), 256):
sum2 = 0.0
for j in range(256):
sum2 += (j**2) * float(self.h[i + j])
v.append(sum2)
return v
@cached_property
def mean(self) -> list[float]:
"""Average (arithmetic mean) pixel level for each band in the image."""
return [self.sum[i] / self.count[i] for i in self.bands]
@cached_property
def median(self) -> list[int]:
"""Median pixel level for each band in the image."""
v = []
for i in self.bands:
s = 0
half = self.count[i] // 2
b = i * 256
for j in range(256):
s = s + self.h[b + j]
if s > half:
break
v.append(j)
return v
@cached_property
def rms(self) -> list[float]:
"""RMS (root-mean-square) for each band in the image."""
return [math.sqrt(self.sum2[i] / self.count[i]) for i in self.bands]
@cached_property
def var(self) -> list[float]:
"""Variance for each band in the image."""
return [
(self.sum2[i] - (self.sum[i] ** 2.0) / self.count[i]) / self.count[i]
for i in self.bands
]
@cached_property
def stddev(self) -> list[float]:
"""Standard deviation for each band in the image."""
return [math.sqrt(self.var[i]) for i in self.bands]
Global = Stat # compatibility
|