66
77import functools
88from operator import itemgetter
9- from typing import List , Optional , Tuple , Union
9+ from typing import Any , List , Optional , Tuple , Union
1010
1111import numpy
12+ import numpy .typing as npt
1213import PIL
1314import PIL .Image
1415
16+ # TAGS has been in PIL since Pillow 8.2.0
17+ from PIL .ExifTags import TAGS as ExifTags
18+
1519from mathics .core .atoms import Rational
1620from mathics .core .builtin import String
1721from mathics .core .convert .python import from_python
2125from mathics .core .symbols import Symbol
2226from mathics .core .systemsymbols import SymbolRule , SymbolSimplify
2327
24- try :
25- from PIL .ExifTags import TAGS as ExifTags
26- except ImportError :
27- ExifTags = {}
28-
2928# Exif: Exchangeable image file format for digital still cameras.
3029# See http://www.exiv2.org/tags.html
3130
31+
3232# names overriding the ones given by Pillow
3333Exif_names = {
3434 37385 : "FlashInfo" ,
@@ -85,11 +85,24 @@ def convolve(in1, in2, fixed=True):
8585 return ret [tuple (slice (p , - p ) for p in excess )]
8686
8787
88+ def eval_ImageImport (path : str , evaluation : Evaluation ):
89+ """Called from ImageImport[path_String]"""
90+
91+ # PIL.Image.open can raise an error. The caller will handle that.
92+ pillow = PIL .Image .open (path )
93+
94+ pixels = numpy .asarray (pillow )
95+ is_rgb = len (pixels .shape ) >= 3 and pixels .shape [2 ] >= 3
96+ options_from_exif = extract_exif (pillow , evaluation )
97+
98+ return pillow , pixels , is_rgb , options_from_exif
99+
100+
88101def extract_exif (image , evaluation : Evaluation ) -> Optional [Expression ]:
89102 """
90- Convert Exif information from image into options
91- that can be passed to Image[].
92- Return None if there is no Exif information.
103+ Extract and convert EXIF (Exchangeable Image File Format)
104+ metadata from `image` into options that can be passed to
105+ Image[]. Return None if there is no EXIF information.
93106 """
94107 if hasattr (image , "getexif" ):
95108 # PIL seems to have a bug in getting v2_tags,
@@ -112,9 +125,11 @@ def extract_exif(image, evaluation: Evaluation) -> Optional[Expression]:
112125 if not name :
113126 continue
114127
115- # EXIF has the following types: Short, Long, Rational, Ascii, Byte
116- # (see http://www.exiv2.org/tags.html). we detect the type from the
117- # Python type Pillow gives us and do the appropriate MMA handling.
128+ # EXIF has the following types: Short, Long, Rational, Ascii
129+ # (note the capital first letter only), and Byte. See
130+ # http://www.exiv2.org/tags.html. We detect the type from
131+ # the Python type Pillow gives us and do the appropriate WMA
132+ # handling.
118133
119134 if isinstance (v , tuple ) and len (v ) == 2 : # Rational
120135 value = Rational (v [0 ], v [1 ])
@@ -124,7 +139,7 @@ def extract_exif(image, evaluation: Evaluation) -> Optional[Expression]:
124139 value = Expression (SymbolSimplify , value ).evaluate (evaluation )
125140 elif isinstance (v , bytes ): # Byte
126141 value = String (" " .join ([str (x ) for x in v ]))
127- elif isinstance (v , (int , str )): # Short, Long, ASCII
142+ elif isinstance (v , (int , str )): # Short, Long, Ascii
128143 value = from_python (v )
129144 else :
130145 continue
@@ -176,7 +191,7 @@ def image_pixels(matrix):
176191 return None
177192
178193
179- def linearize_numpy_array (a : numpy . array ) -> Tuple [numpy . array , int ]:
194+ def linearize_numpy_array (a : npt . NDArray [ Any ] ) -> Tuple [npt . NDArray [ Any ] , int ]:
180195 """
181196 Transforms a numpy array numpy array and return the array and the number
182197 of dimensions in the array
0 commit comments