"Fossies" - the Fresh Open Source Software Archive

Member "scikit-image-0.19.3/skimage/io/_plugins/pil_plugin.py" (12 Jun 2022, 8313 Bytes) of package /linux/misc/scikit-image-0.19.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "pil_plugin.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.19.2_vs_0.19.3.

    1 __all__ = ['imread', 'imsave']
    2 
    3 import numpy as np
    4 from packaging import version
    5 from PIL import Image, __version__ as pil_version
    6 
    7 from ...util import img_as_ubyte, img_as_uint
    8 
    9 # Check CVE-2021-27921 and others
   10 if version.parse(pil_version) < version.parse('8.1.2'):
   11     from warnings import warn
   12     warn('Your installed pillow version is < 8.1.2. '
   13          'Several security issues (CVE-2021-27921, '
   14          'CVE-2021-25290, CVE-2021-25291, CVE-2021-25293, '
   15          'and more) have been fixed in pillow 8.1.2 or higher. '
   16          'We recommend to upgrade this library.',
   17          stacklevel=2)
   18 
   19 
   20 def imread(fname, dtype=None, img_num=None, **kwargs):
   21     """Load an image from file.
   22 
   23     Parameters
   24     ----------
   25     fname : str or file
   26         File name or file-like-object.
   27     dtype : numpy dtype object or string specifier
   28         Specifies data type of array elements.
   29     img_num : int, optional
   30         Specifies which image to read in a file with multiple images
   31         (zero-indexed).
   32     kwargs : keyword pairs, optional
   33         Addition keyword arguments to pass through.
   34 
   35     Notes
   36     -----
   37     Files are read using the Python Imaging Library.
   38     See PIL docs [1]_ for a list of supported formats.
   39 
   40     References
   41     ----------
   42     .. [1] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html
   43     """
   44     if isinstance(fname, str):
   45         with open(fname, 'rb') as f:
   46             im = Image.open(f)
   47             return pil_to_ndarray(im, dtype=dtype, img_num=img_num)
   48     else:
   49         im = Image.open(fname)
   50         return pil_to_ndarray(im, dtype=dtype, img_num=img_num)
   51 
   52 
   53 def pil_to_ndarray(image, dtype=None, img_num=None):
   54     """Import a PIL Image object to an ndarray, in memory.
   55 
   56     Parameters
   57     ----------
   58     Refer to ``imread``.
   59 
   60     """
   61     try:
   62         # this will raise an IOError if the file is not readable
   63         image.getdata()[0]
   64     except IOError as e:
   65         site = "http://pillow.readthedocs.org/en/latest/installation.html#external-libraries"
   66         pillow_error_message = str(e)
   67         error_message = ('Could not load "%s" \n'
   68                          'Reason: "%s"\n'
   69                          'Please see documentation at: %s'
   70                          % (image.filename, pillow_error_message, site))
   71         raise ValueError(error_message)
   72     frames = []
   73     grayscale = None
   74     i = 0
   75     while 1:
   76         try:
   77             image.seek(i)
   78         except EOFError:
   79             break
   80 
   81         frame = image
   82 
   83         if img_num is not None and img_num != i:
   84             image.getdata()[0]
   85             i += 1
   86             continue
   87 
   88         if image.format == 'PNG' and image.mode == 'I' and dtype is None:
   89             dtype = 'uint16'
   90 
   91         if image.mode == 'P':
   92             if grayscale is None:
   93                 grayscale = _palette_is_grayscale(image)
   94 
   95             if grayscale:
   96                 frame = image.convert('L')
   97             else:
   98                 if image.format == 'PNG' and 'transparency' in image.info:
   99                     frame = image.convert('RGBA')
  100                 else:
  101                     frame = image.convert('RGB')
  102 
  103         elif image.mode == '1':
  104             frame = image.convert('L')
  105 
  106         elif 'A' in image.mode:
  107             frame = image.convert('RGBA')
  108 
  109         elif image.mode == 'CMYK':
  110             frame = image.convert('RGB')
  111 
  112         if image.mode.startswith('I;16'):
  113             shape = image.size
  114             dtype = '>u2' if image.mode.endswith('B') else '<u2'
  115             if 'S' in image.mode:
  116                 dtype = dtype.replace('u', 'i')
  117             frame = np.fromstring(frame.tobytes(), dtype)
  118             frame.shape = shape[::-1]
  119 
  120         else:
  121             frame = np.array(frame, dtype=dtype)
  122 
  123         frames.append(frame)
  124         i += 1
  125 
  126         if img_num is not None:
  127             break
  128 
  129     if hasattr(image, 'fp') and image.fp:
  130         image.fp.close()
  131 
  132     if img_num is None and len(frames) > 1:
  133         return np.array(frames)
  134     elif frames:
  135         return frames[0]
  136     elif img_num:
  137         raise IndexError('Could not find image  #%s' % img_num)
  138 
  139 
  140 def _palette_is_grayscale(pil_image):
  141     """Return True if PIL image in palette mode is grayscale.
  142 
  143     Parameters
  144     ----------
  145     pil_image : PIL image
  146         PIL Image that is in Palette mode.
  147 
  148     Returns
  149     -------
  150     is_grayscale : bool
  151         True if all colors in image palette are gray.
  152     """
  153     if pil_image.mode != 'P':
  154         raise ValueError('pil_image.mode must be equal to "P".')
  155     # get palette as an array with R, G, B columns
  156     palette = np.asarray(pil_image.getpalette()).reshape((-1, 3))
  157     # Not all palette colors are used; unused colors have junk values.
  158     start, stop = pil_image.getextrema()
  159     valid_palette = palette[start:stop + 1]
  160     # Image is grayscale if channel differences (R - G and G - B)
  161     # are all zero.
  162     return np.allclose(np.diff(valid_palette), 0)
  163 
  164 
  165 def ndarray_to_pil(arr, format_str=None):
  166     """Export an ndarray to a PIL object.
  167 
  168     Parameters
  169     ----------
  170     Refer to ``imsave``.
  171 
  172     """
  173     if arr.ndim == 3:
  174         arr = img_as_ubyte(arr)
  175         mode = {3: 'RGB', 4: 'RGBA'}[arr.shape[2]]
  176 
  177     elif format_str in ['png', 'PNG']:
  178         mode = 'I;16'
  179         mode_base = 'I'
  180 
  181         if arr.dtype.kind == 'f':
  182             arr = img_as_uint(arr)
  183 
  184         elif arr.max() < 256 and arr.min() >= 0:
  185             arr = arr.astype(np.uint8)
  186             mode = mode_base = 'L'
  187 
  188         else:
  189             arr = img_as_uint(arr)
  190 
  191     else:
  192         arr = img_as_ubyte(arr)
  193         mode = 'L'
  194         mode_base = 'L'
  195 
  196     try:
  197         array_buffer = arr.tobytes()
  198     except AttributeError:
  199         array_buffer = arr.tostring()  # Numpy < 1.9
  200 
  201     if arr.ndim == 2:
  202         im = Image.new(mode_base, arr.T.shape)
  203         try:
  204             im.frombytes(array_buffer, 'raw', mode)
  205         except AttributeError:
  206             im.fromstring(array_buffer, 'raw', mode)  # PIL 1.1.7
  207     else:
  208         image_shape = (arr.shape[1], arr.shape[0])
  209         try:
  210             im = Image.frombytes(mode, image_shape, array_buffer)
  211         except AttributeError:
  212             im = Image.fromstring(mode, image_shape, array_buffer)  # PIL 1.1.7
  213     return im
  214 
  215 
  216 def imsave(fname, arr, format_str=None, **kwargs):
  217     """Save an image to disk.
  218 
  219     Parameters
  220     ----------
  221     fname : str or file-like object
  222         Name of destination file.
  223     arr : ndarray of uint8 or float
  224         Array (image) to save.  Arrays of data-type uint8 should have
  225         values in [0, 255], whereas floating-point arrays must be
  226         in [0, 1].
  227     format_str: str
  228         Format to save as, this is defaulted to PNG if using a file-like
  229         object; this will be derived from the extension if fname is a string
  230     kwargs: dict
  231         Keyword arguments to the Pillow save function (or tifffile save
  232         function, for Tiff files). These are format dependent. For example,
  233         Pillow's JPEG save function supports an integer ``quality`` argument
  234         with values in [1, 95], while TIFFFile supports a ``compress``
  235         integer argument with values in [0, 9].
  236 
  237     Notes
  238     -----
  239     Use the Python Imaging Library.
  240     See PIL docs [1]_ for a list of other supported formats.
  241     All images besides single channel PNGs are converted using `img_as_uint8`.
  242     Single Channel PNGs have the following behavior:
  243     - Integer values in [0, 255] and Boolean types -> img_as_uint8
  244     - Floating point and other integers -> img_as_uint16
  245 
  246     References
  247     ----------
  248     .. [1] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html
  249     """
  250     # default to PNG if file-like object
  251     if not isinstance(fname, str) and format_str is None:
  252         format_str = "PNG"
  253     # Check for png in filename
  254     if (isinstance(fname, str)
  255             and fname.lower().endswith(".png")):
  256         format_str = "PNG"
  257 
  258     arr = np.asanyarray(arr)
  259 
  260     if arr.dtype.kind == 'b':
  261         arr = arr.astype(np.uint8)
  262 
  263     if arr.ndim not in (2, 3):
  264         raise ValueError("Invalid shape for image array: %s" % (arr.shape, ))
  265 
  266     if arr.ndim == 3:
  267         if arr.shape[2] not in (3, 4):
  268             raise ValueError("Invalid number of channels in image array.")
  269 
  270     img = ndarray_to_pil(arr, format_str=format_str)
  271     img.save(fname, format=format_str, **kwargs)