mirror of
				https://github.com/python-pillow/Pillow.git
				synced 2025-10-31 16:07:30 +03:00 
			
		
		
		
	Merge pull request #7786 from radarhere/type_hints_imageops
Added type hints to ImageOps
This commit is contained in:
		
						commit
						80d4fc14b8
					
				|  | @ -1430,7 +1430,7 @@ class Image: | |||
|             root = ElementTree.fromstring(xmp_tags) | ||||
|             return {get_name(root.tag): get_value(root)} | ||||
| 
 | ||||
|     def getexif(self): | ||||
|     def getexif(self) -> Exif: | ||||
|         """ | ||||
|         Gets EXIF data from the image. | ||||
| 
 | ||||
|  | @ -1438,7 +1438,6 @@ class Image: | |||
|         """ | ||||
|         if self._exif is None: | ||||
|             self._exif = Exif() | ||||
|             self._exif._loaded = False | ||||
|         elif self._exif._loaded: | ||||
|             return self._exif | ||||
|         self._exif._loaded = True | ||||
|  | @ -1525,7 +1524,7 @@ class Image: | |||
|         self.load() | ||||
|         return self.im.ptr | ||||
| 
 | ||||
|     def getpalette(self, rawmode="RGB"): | ||||
|     def getpalette(self, rawmode: str | None = "RGB") -> list[int] | None: | ||||
|         """ | ||||
|         Returns the image palette as a list. | ||||
| 
 | ||||
|  | @ -1615,7 +1614,7 @@ class Image: | |||
|         x, y = self.im.getprojection() | ||||
|         return list(x), list(y) | ||||
| 
 | ||||
|     def histogram(self, mask=None, extrema=None): | ||||
|     def histogram(self, mask=None, extrema=None) -> list[int]: | ||||
|         """ | ||||
|         Returns a histogram for the image. The histogram is returned as a | ||||
|         list of pixel counts, one for each pixel value in the source | ||||
|  | @ -1804,7 +1803,7 @@ class Image: | |||
|         result = alpha_composite(background, overlay) | ||||
|         self.paste(result, box) | ||||
| 
 | ||||
|     def point(self, lut, mode=None): | ||||
|     def point(self, lut, mode: str | None = None) -> Image: | ||||
|         """ | ||||
|         Maps this image through a lookup table or function. | ||||
| 
 | ||||
|  | @ -1928,7 +1927,7 @@ class Image: | |||
| 
 | ||||
|         self.im.putdata(data, scale, offset) | ||||
| 
 | ||||
|     def putpalette(self, data, rawmode="RGB"): | ||||
|     def putpalette(self, data, rawmode="RGB") -> None: | ||||
|         """ | ||||
|         Attaches a palette to this image.  The image must be a "P", "PA", "L" | ||||
|         or "LA" image. | ||||
|  | @ -2108,7 +2107,7 @@ class Image: | |||
|             min(self.size[1], math.ceil(box[3] + support_y)), | ||||
|         ) | ||||
| 
 | ||||
|     def resize(self, size, resample=None, box=None, reducing_gap=None): | ||||
|     def resize(self, size, resample=None, box=None, reducing_gap=None) -> Image: | ||||
|         """ | ||||
|         Returns a resized copy of this image. | ||||
| 
 | ||||
|  | @ -2200,10 +2199,11 @@ class Image: | |||
|             if factor_x > 1 or factor_y > 1: | ||||
|                 reduce_box = self._get_safe_box(size, resample, box) | ||||
|                 factor = (factor_x, factor_y) | ||||
|                 if callable(self.reduce): | ||||
|                     self = self.reduce(factor, box=reduce_box) | ||||
|                 else: | ||||
|                     self = Image.reduce(self, factor, box=reduce_box) | ||||
|                 self = ( | ||||
|                     self.reduce(factor, box=reduce_box) | ||||
|                     if callable(self.reduce) | ||||
|                     else Image.reduce(self, factor, box=reduce_box) | ||||
|                 ) | ||||
|                 box = ( | ||||
|                     (box[0] - reduce_box[0]) / factor_x, | ||||
|                     (box[1] - reduce_box[1]) / factor_y, | ||||
|  | @ -2818,7 +2818,7 @@ class Image: | |||
| 
 | ||||
|         self.im.transform2(box, image.im, method, data, resample, fill) | ||||
| 
 | ||||
|     def transpose(self, method): | ||||
|     def transpose(self, method: Transpose) -> Image: | ||||
|         """ | ||||
|         Transpose image (flip or rotate in 90 degree steps) | ||||
| 
 | ||||
|  | @ -2870,7 +2870,9 @@ class ImagePointHandler: | |||
|     (for use with :py:meth:`~PIL.Image.Image.point`) | ||||
|     """ | ||||
| 
 | ||||
|     pass | ||||
|     @abc.abstractmethod | ||||
|     def point(self, im: Image) -> Image: | ||||
|         pass | ||||
| 
 | ||||
| 
 | ||||
| class ImageTransformHandler: | ||||
|  | @ -3690,6 +3692,7 @@ class Exif(_ExifBase): | |||
| 
 | ||||
|     endian = None | ||||
|     bigtiff = False | ||||
|     _loaded = False | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         self._data = {} | ||||
|  | @ -3805,7 +3808,7 @@ class Exif(_ExifBase): | |||
| 
 | ||||
|         return merged_dict | ||||
| 
 | ||||
|     def tobytes(self, offset=8): | ||||
|     def tobytes(self, offset: int = 8) -> bytes: | ||||
|         from . import TiffImagePlugin | ||||
| 
 | ||||
|         head = self._get_head() | ||||
|  | @ -3960,7 +3963,7 @@ class Exif(_ExifBase): | |||
|             del self._info[tag] | ||||
|         self._data[tag] = value | ||||
| 
 | ||||
|     def __delitem__(self, tag): | ||||
|     def __delitem__(self, tag: int) -> None: | ||||
|         if self._info is not None and tag in self._info: | ||||
|             del self._info[tag] | ||||
|         else: | ||||
|  |  | |||
|  | @ -124,7 +124,7 @@ def getrgb(color): | |||
| 
 | ||||
| 
 | ||||
| @lru_cache | ||||
| def getcolor(color, mode): | ||||
| def getcolor(color, mode: str) -> tuple[int, ...]: | ||||
|     """ | ||||
|     Same as :py:func:`~PIL.ImageColor.getrgb` for most modes. However, if | ||||
|     ``mode`` is HSV, converts the RGB value to a HSV value, or if ``mode`` is | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ from __future__ import annotations | |||
| import functools | ||||
| import operator | ||||
| import re | ||||
| from typing import Protocol, Sequence, cast | ||||
| 
 | ||||
| from . import ExifTags, Image, ImagePalette | ||||
| 
 | ||||
|  | @ -28,7 +29,7 @@ from . import ExifTags, Image, ImagePalette | |||
| # helpers | ||||
| 
 | ||||
| 
 | ||||
| def _border(border): | ||||
| def _border(border: int | tuple[int, ...]) -> tuple[int, int, int, int]: | ||||
|     if isinstance(border, tuple): | ||||
|         if len(border) == 2: | ||||
|             left, top = right, bottom = border | ||||
|  | @ -39,7 +40,7 @@ def _border(border): | |||
|     return left, top, right, bottom | ||||
| 
 | ||||
| 
 | ||||
| def _color(color, mode): | ||||
| def _color(color: str | int | tuple[int, ...], mode: str) -> int | tuple[int, ...]: | ||||
|     if isinstance(color, str): | ||||
|         from . import ImageColor | ||||
| 
 | ||||
|  | @ -47,7 +48,7 @@ def _color(color, mode): | |||
|     return color | ||||
| 
 | ||||
| 
 | ||||
| def _lut(image, lut): | ||||
| def _lut(image: Image.Image, lut: list[int]) -> Image.Image: | ||||
|     if image.mode == "P": | ||||
|         # FIXME: apply to lookup table, not image data | ||||
|         msg = "mode P support coming soon" | ||||
|  | @ -65,7 +66,13 @@ def _lut(image, lut): | |||
| # actions | ||||
| 
 | ||||
| 
 | ||||
| def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False): | ||||
| def autocontrast( | ||||
|     image: Image.Image, | ||||
|     cutoff: float | tuple[float, float] = 0, | ||||
|     ignore: int | Sequence[int] | None = None, | ||||
|     mask: Image.Image | None = None, | ||||
|     preserve_tone: bool = False, | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Maximize (normalize) image contrast. This function calculates a | ||||
|     histogram of the input image (or mask region), removes ``cutoff`` percent of the | ||||
|  | @ -97,10 +104,9 @@ def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False): | |||
|         h = histogram[layer : layer + 256] | ||||
|         if ignore is not None: | ||||
|             # get rid of outliers | ||||
|             try: | ||||
|             if isinstance(ignore, int): | ||||
|                 h[ignore] = 0 | ||||
|             except TypeError: | ||||
|                 # assume sequence | ||||
|             else: | ||||
|                 for ix in ignore: | ||||
|                     h[ix] = 0 | ||||
|         if cutoff: | ||||
|  | @ -112,7 +118,7 @@ def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False): | |||
|             for ix in range(256): | ||||
|                 n = n + h[ix] | ||||
|             # remove cutoff% pixels from the low end | ||||
|             cut = n * cutoff[0] // 100 | ||||
|             cut = int(n * cutoff[0] // 100) | ||||
|             for lo in range(256): | ||||
|                 if cut > h[lo]: | ||||
|                     cut = cut - h[lo] | ||||
|  | @ -123,7 +129,7 @@ def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False): | |||
|                 if cut <= 0: | ||||
|                     break | ||||
|             # remove cutoff% samples from the high end | ||||
|             cut = n * cutoff[1] // 100 | ||||
|             cut = int(n * cutoff[1] // 100) | ||||
|             for hi in range(255, -1, -1): | ||||
|                 if cut > h[hi]: | ||||
|                     cut = cut - h[hi] | ||||
|  | @ -156,7 +162,15 @@ def autocontrast(image, cutoff=0, ignore=None, mask=None, preserve_tone=False): | |||
|     return _lut(image, lut) | ||||
| 
 | ||||
| 
 | ||||
| def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoint=127): | ||||
| def colorize( | ||||
|     image: Image.Image, | ||||
|     black: str | tuple[int, ...], | ||||
|     white: str | tuple[int, ...], | ||||
|     mid: str | int | tuple[int, ...] | None = None, | ||||
|     blackpoint: int = 0, | ||||
|     whitepoint: int = 255, | ||||
|     midpoint: int = 127, | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Colorize grayscale image. | ||||
|     This function calculates a color wedge which maps all black pixels in | ||||
|  | @ -188,10 +202,9 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi | |||
|         assert 0 <= blackpoint <= midpoint <= whitepoint <= 255 | ||||
| 
 | ||||
|     # Define colors from arguments | ||||
|     black = _color(black, "RGB") | ||||
|     white = _color(white, "RGB") | ||||
|     if mid is not None: | ||||
|         mid = _color(mid, "RGB") | ||||
|     rgb_black = cast(Sequence[int], _color(black, "RGB")) | ||||
|     rgb_white = cast(Sequence[int], _color(white, "RGB")) | ||||
|     rgb_mid = cast(Sequence[int], _color(mid, "RGB")) if mid is not None else None | ||||
| 
 | ||||
|     # Empty lists for the mapping | ||||
|     red = [] | ||||
|  | @ -200,18 +213,24 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi | |||
| 
 | ||||
|     # Create the low-end values | ||||
|     for i in range(0, blackpoint): | ||||
|         red.append(black[0]) | ||||
|         green.append(black[1]) | ||||
|         blue.append(black[2]) | ||||
|         red.append(rgb_black[0]) | ||||
|         green.append(rgb_black[1]) | ||||
|         blue.append(rgb_black[2]) | ||||
| 
 | ||||
|     # Create the mapping (2-color) | ||||
|     if mid is None: | ||||
|     if rgb_mid is None: | ||||
|         range_map = range(0, whitepoint - blackpoint) | ||||
| 
 | ||||
|         for i in range_map: | ||||
|             red.append(black[0] + i * (white[0] - black[0]) // len(range_map)) | ||||
|             green.append(black[1] + i * (white[1] - black[1]) // len(range_map)) | ||||
|             blue.append(black[2] + i * (white[2] - black[2]) // len(range_map)) | ||||
|             red.append( | ||||
|                 rgb_black[0] + i * (rgb_white[0] - rgb_black[0]) // len(range_map) | ||||
|             ) | ||||
|             green.append( | ||||
|                 rgb_black[1] + i * (rgb_white[1] - rgb_black[1]) // len(range_map) | ||||
|             ) | ||||
|             blue.append( | ||||
|                 rgb_black[2] + i * (rgb_white[2] - rgb_black[2]) // len(range_map) | ||||
|             ) | ||||
| 
 | ||||
|     # Create the mapping (3-color) | ||||
|     else: | ||||
|  | @ -219,26 +238,36 @@ def colorize(image, black, white, mid=None, blackpoint=0, whitepoint=255, midpoi | |||
|         range_map2 = range(0, whitepoint - midpoint) | ||||
| 
 | ||||
|         for i in range_map1: | ||||
|             red.append(black[0] + i * (mid[0] - black[0]) // len(range_map1)) | ||||
|             green.append(black[1] + i * (mid[1] - black[1]) // len(range_map1)) | ||||
|             blue.append(black[2] + i * (mid[2] - black[2]) // len(range_map1)) | ||||
|             red.append( | ||||
|                 rgb_black[0] + i * (rgb_mid[0] - rgb_black[0]) // len(range_map1) | ||||
|             ) | ||||
|             green.append( | ||||
|                 rgb_black[1] + i * (rgb_mid[1] - rgb_black[1]) // len(range_map1) | ||||
|             ) | ||||
|             blue.append( | ||||
|                 rgb_black[2] + i * (rgb_mid[2] - rgb_black[2]) // len(range_map1) | ||||
|             ) | ||||
|         for i in range_map2: | ||||
|             red.append(mid[0] + i * (white[0] - mid[0]) // len(range_map2)) | ||||
|             green.append(mid[1] + i * (white[1] - mid[1]) // len(range_map2)) | ||||
|             blue.append(mid[2] + i * (white[2] - mid[2]) // len(range_map2)) | ||||
|             red.append(rgb_mid[0] + i * (rgb_white[0] - rgb_mid[0]) // len(range_map2)) | ||||
|             green.append( | ||||
|                 rgb_mid[1] + i * (rgb_white[1] - rgb_mid[1]) // len(range_map2) | ||||
|             ) | ||||
|             blue.append(rgb_mid[2] + i * (rgb_white[2] - rgb_mid[2]) // len(range_map2)) | ||||
| 
 | ||||
|     # Create the high-end values | ||||
|     for i in range(0, 256 - whitepoint): | ||||
|         red.append(white[0]) | ||||
|         green.append(white[1]) | ||||
|         blue.append(white[2]) | ||||
|         red.append(rgb_white[0]) | ||||
|         green.append(rgb_white[1]) | ||||
|         blue.append(rgb_white[2]) | ||||
| 
 | ||||
|     # Return converted image | ||||
|     image = image.convert("RGB") | ||||
|     return _lut(image, red + green + blue) | ||||
| 
 | ||||
| 
 | ||||
| def contain(image, size, method=Image.Resampling.BICUBIC): | ||||
| def contain( | ||||
|     image: Image.Image, size: tuple[int, int], method: int = Image.Resampling.BICUBIC | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Returns a resized version of the image, set to the maximum width and height | ||||
|     within the requested size, while maintaining the original aspect ratio. | ||||
|  | @ -267,7 +296,9 @@ def contain(image, size, method=Image.Resampling.BICUBIC): | |||
|     return image.resize(size, resample=method) | ||||
| 
 | ||||
| 
 | ||||
| def cover(image, size, method=Image.Resampling.BICUBIC): | ||||
| def cover( | ||||
|     image: Image.Image, size: tuple[int, int], method: int = Image.Resampling.BICUBIC | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Returns a resized version of the image, so that the requested size is | ||||
|     covered, while maintaining the original aspect ratio. | ||||
|  | @ -296,7 +327,13 @@ def cover(image, size, method=Image.Resampling.BICUBIC): | |||
|     return image.resize(size, resample=method) | ||||
| 
 | ||||
| 
 | ||||
| def pad(image, size, method=Image.Resampling.BICUBIC, color=None, centering=(0.5, 0.5)): | ||||
| def pad( | ||||
|     image: Image.Image, | ||||
|     size: tuple[int, int], | ||||
|     method: int = Image.Resampling.BICUBIC, | ||||
|     color: str | int | tuple[int, ...] | None = None, | ||||
|     centering: tuple[float, float] = (0.5, 0.5), | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Returns a resized and padded version of the image, expanded to fill the | ||||
|     requested aspect ratio and size. | ||||
|  | @ -334,7 +371,7 @@ def pad(image, size, method=Image.Resampling.BICUBIC, color=None, centering=(0.5 | |||
|     return out | ||||
| 
 | ||||
| 
 | ||||
| def crop(image, border=0): | ||||
| def crop(image: Image.Image, border: int = 0) -> Image.Image: | ||||
|     """ | ||||
|     Remove border from image.  The same amount of pixels are removed | ||||
|     from all four sides.  This function works on all image modes. | ||||
|  | @ -349,7 +386,9 @@ def crop(image, border=0): | |||
|     return image.crop((left, top, image.size[0] - right, image.size[1] - bottom)) | ||||
| 
 | ||||
| 
 | ||||
| def scale(image, factor, resample=Image.Resampling.BICUBIC): | ||||
| def scale( | ||||
|     image: Image.Image, factor: float, resample: int = Image.Resampling.BICUBIC | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Returns a rescaled image by a specific factor given in parameter. | ||||
|     A factor greater than 1 expands the image, between 0 and 1 contracts the | ||||
|  | @ -372,7 +411,19 @@ def scale(image, factor, resample=Image.Resampling.BICUBIC): | |||
|         return image.resize(size, resample) | ||||
| 
 | ||||
| 
 | ||||
| def deform(image, deformer, resample=Image.Resampling.BILINEAR): | ||||
| class _SupportsGetMesh(Protocol): | ||||
|     def getmesh( | ||||
|         self, image: Image.Image | ||||
|     ) -> list[ | ||||
|         tuple[tuple[int, int, int, int], tuple[int, int, int, int, int, int, int, int]] | ||||
|     ]: ... | ||||
| 
 | ||||
| 
 | ||||
| def deform( | ||||
|     image: Image.Image, | ||||
|     deformer: _SupportsGetMesh, | ||||
|     resample: int = Image.Resampling.BILINEAR, | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Deform the image. | ||||
| 
 | ||||
|  | @ -388,7 +439,7 @@ def deform(image, deformer, resample=Image.Resampling.BILINEAR): | |||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def equalize(image, mask=None): | ||||
| def equalize(image: Image.Image, mask: Image.Image | None = None) -> Image.Image: | ||||
|     """ | ||||
|     Equalize the image histogram. This function applies a non-linear | ||||
|     mapping to the input image, in order to create a uniform | ||||
|  | @ -419,7 +470,11 @@ def equalize(image, mask=None): | |||
|     return _lut(image, lut) | ||||
| 
 | ||||
| 
 | ||||
| def expand(image, border=0, fill=0): | ||||
| def expand( | ||||
|     image: Image.Image, | ||||
|     border: int | tuple[int, ...] = 0, | ||||
|     fill: str | int | tuple[int, ...] = 0, | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Add border to the image | ||||
| 
 | ||||
|  | @ -445,7 +500,13 @@ def expand(image, border=0, fill=0): | |||
|     return out | ||||
| 
 | ||||
| 
 | ||||
| def fit(image, size, method=Image.Resampling.BICUBIC, bleed=0.0, centering=(0.5, 0.5)): | ||||
| def fit( | ||||
|     image: Image.Image, | ||||
|     size: tuple[int, int], | ||||
|     method: int = Image.Resampling.BICUBIC, | ||||
|     bleed: float = 0.0, | ||||
|     centering: tuple[float, float] = (0.5, 0.5), | ||||
| ) -> Image.Image: | ||||
|     """ | ||||
|     Returns a resized and cropped version of the image, cropped to the | ||||
|     requested aspect ratio and size. | ||||
|  | @ -479,13 +540,12 @@ def fit(image, size, method=Image.Resampling.BICUBIC, bleed=0.0, centering=(0.5, | |||
|     # kevin@cazabon.com | ||||
|     # https://www.cazabon.com | ||||
| 
 | ||||
|     # ensure centering is mutable | ||||
|     centering = list(centering) | ||||
|     centering_x, centering_y = centering | ||||
| 
 | ||||
|     if not 0.0 <= centering[0] <= 1.0: | ||||
|         centering[0] = 0.5 | ||||
|     if not 0.0 <= centering[1] <= 1.0: | ||||
|         centering[1] = 0.5 | ||||
|     if not 0.0 <= centering_x <= 1.0: | ||||
|         centering_x = 0.5 | ||||
|     if not 0.0 <= centering_y <= 1.0: | ||||
|         centering_y = 0.5 | ||||
| 
 | ||||
|     if not 0.0 <= bleed < 0.5: | ||||
|         bleed = 0.0 | ||||
|  | @ -522,8 +582,8 @@ def fit(image, size, method=Image.Resampling.BICUBIC, bleed=0.0, centering=(0.5, | |||
|         crop_height = live_size[0] / output_ratio | ||||
| 
 | ||||
|     # make the crop | ||||
|     crop_left = bleed_pixels[0] + (live_size[0] - crop_width) * centering[0] | ||||
|     crop_top = bleed_pixels[1] + (live_size[1] - crop_height) * centering[1] | ||||
|     crop_left = bleed_pixels[0] + (live_size[0] - crop_width) * centering_x | ||||
|     crop_top = bleed_pixels[1] + (live_size[1] - crop_height) * centering_y | ||||
| 
 | ||||
|     crop = (crop_left, crop_top, crop_left + crop_width, crop_top + crop_height) | ||||
| 
 | ||||
|  | @ -531,7 +591,7 @@ def fit(image, size, method=Image.Resampling.BICUBIC, bleed=0.0, centering=(0.5, | |||
|     return image.resize(size, method, box=crop) | ||||
| 
 | ||||
| 
 | ||||
| def flip(image): | ||||
| def flip(image: Image.Image) -> Image.Image: | ||||
|     """ | ||||
|     Flip the image vertically (top to bottom). | ||||
| 
 | ||||
|  | @ -541,7 +601,7 @@ def flip(image): | |||
|     return image.transpose(Image.Transpose.FLIP_TOP_BOTTOM) | ||||
| 
 | ||||
| 
 | ||||
| def grayscale(image): | ||||
| def grayscale(image: Image.Image) -> Image.Image: | ||||
|     """ | ||||
|     Convert the image to grayscale. | ||||
| 
 | ||||
|  | @ -551,7 +611,7 @@ def grayscale(image): | |||
|     return image.convert("L") | ||||
| 
 | ||||
| 
 | ||||
| def invert(image): | ||||
| def invert(image: Image.Image) -> Image.Image: | ||||
|     """ | ||||
|     Invert (negate) the image. | ||||
| 
 | ||||
|  | @ -562,7 +622,7 @@ def invert(image): | |||
|     return image.point(lut) if image.mode == "1" else _lut(image, lut) | ||||
| 
 | ||||
| 
 | ||||
| def mirror(image): | ||||
| def mirror(image: Image.Image) -> Image.Image: | ||||
|     """ | ||||
|     Flip image horizontally (left to right). | ||||
| 
 | ||||
|  | @ -572,7 +632,7 @@ def mirror(image): | |||
|     return image.transpose(Image.Transpose.FLIP_LEFT_RIGHT) | ||||
| 
 | ||||
| 
 | ||||
| def posterize(image, bits): | ||||
| def posterize(image: Image.Image, bits: int) -> Image.Image: | ||||
|     """ | ||||
|     Reduce the number of bits for each color channel. | ||||
| 
 | ||||
|  | @ -585,7 +645,7 @@ def posterize(image, bits): | |||
|     return _lut(image, lut) | ||||
| 
 | ||||
| 
 | ||||
| def solarize(image, threshold=128): | ||||
| def solarize(image: Image.Image, threshold: int = 128) -> Image.Image: | ||||
|     """ | ||||
|     Invert all pixel values above a threshold. | ||||
| 
 | ||||
|  | @ -602,7 +662,7 @@ def solarize(image, threshold=128): | |||
|     return _lut(image, lut) | ||||
| 
 | ||||
| 
 | ||||
| def exif_transpose(image, *, in_place=False): | ||||
| def exif_transpose(image: Image.Image, *, in_place: bool = False) -> Image.Image | None: | ||||
|     """ | ||||
|     If an image has an EXIF Orientation tag, other than 1, transpose the image | ||||
|     accordingly, and remove the orientation data. | ||||
|  | @ -616,7 +676,7 @@ def exif_transpose(image, *, in_place=False): | |||
|     """ | ||||
|     image.load() | ||||
|     image_exif = image.getexif() | ||||
|     orientation = image_exif.get(ExifTags.Base.Orientation) | ||||
|     orientation = image_exif.get(ExifTags.Base.Orientation, 1) | ||||
|     method = { | ||||
|         2: Image.Transpose.FLIP_LEFT_RIGHT, | ||||
|         3: Image.Transpose.ROTATE_180, | ||||
|  | @ -653,3 +713,4 @@ def exif_transpose(image, *, in_place=False): | |||
|             return transposed_image | ||||
|     elif not in_place: | ||||
|         return image.copy() | ||||
|     return None | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ | |||
| from __future__ import annotations | ||||
| 
 | ||||
| import array | ||||
| from typing import Sequence | ||||
| 
 | ||||
| from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile | ||||
| 
 | ||||
|  | @ -34,11 +35,11 @@ class ImagePalette: | |||
|         Defaults to an empty palette. | ||||
|     """ | ||||
| 
 | ||||
|     def __init__(self, mode="RGB", palette=None): | ||||
|     def __init__(self, mode: str = "RGB", palette: Sequence[int] | None = None) -> None: | ||||
|         self.mode = mode | ||||
|         self.rawmode = None  # if set, palette contains raw data | ||||
|         self.palette = palette or bytearray() | ||||
|         self.dirty = None | ||||
|         self.dirty: int | None = None | ||||
| 
 | ||||
|     @property | ||||
|     def palette(self): | ||||
|  | @ -127,7 +128,7 @@ class ImagePalette: | |||
|                 raise ValueError(msg) from e | ||||
|         return index | ||||
| 
 | ||||
|     def getcolor(self, color, image=None): | ||||
|     def getcolor(self, color, image=None) -> int: | ||||
|         """Given an rgb tuple, allocate palette entry. | ||||
| 
 | ||||
|         .. warning:: This method is experimental. | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user