@@ -1431,7 +1431,7 @@ def read_png(*args, **kwargs):
14311431
14321432
14331433def imsave (fname , arr , vmin = None , vmax = None , cmap = None , format = None ,
1434- origin = None , dpi = 100 ):
1434+ origin = None , dpi = 100 , * , metadata = None , pil_kwargs = None ):
14351435 """
14361436 Save an array as an image file.
14371437
@@ -1464,6 +1464,17 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
14641464 dpi : int
14651465 The DPI to store in the metadata of the file. This does not affect the
14661466 resolution of the output image.
1467+ metadata : dict, optional
1468+ Metadata in the image file. The supported keys depend on the output
1469+ format, see the documentation of the respective backends for more
1470+ information.
1471+ pil_kwargs : dict, optional
1472+ If set to a non-None value, always use Pillow to save the figure
1473+ (regardless of the output format), and pass these keyword arguments to
1474+ `PIL.Image.save`.
1475+
1476+ If the 'pnginfo' key is present, it completely overrides
1477+ *metadata*, including the default 'Software' key.
14671478 """
14681479 from matplotlib .figure import Figure
14691480 from matplotlib import _png
@@ -1474,10 +1485,14 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
14741485 else rcParams ["savefig.format" ]).lower ()
14751486 if format in ["pdf" , "ps" , "eps" , "svg" ]:
14761487 # Vector formats that are not handled by PIL.
1488+ if pil_kwargs is not None :
1489+ raise ValueError (
1490+ f"Cannot use 'pil_kwargs' when saving to { format } " )
14771491 fig = Figure (dpi = dpi , frameon = False )
14781492 fig .figimage (arr , cmap = cmap , vmin = vmin , vmax = vmax , origin = origin ,
14791493 resize = True )
1480- fig .savefig (fname , dpi = dpi , format = format , transparent = True )
1494+ fig .savefig (fname , dpi = dpi , format = format , transparent = True ,
1495+ metadata = metadata )
14811496 else :
14821497 # Don't bother creating an image; this avoids rounding errors on the
14831498 # size when dividing and then multiplying by dpi.
@@ -1488,17 +1503,28 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
14881503 if origin == "lower" :
14891504 arr = arr [::- 1 ]
14901505 rgba = sm .to_rgba (arr , bytes = True )
1491- if format == "png" :
1492- _png .write_png (rgba , fname , dpi = dpi )
1506+ if format == "png" and pil_kwargs is None :
1507+ _png .write_png (rgba , fname , dpi = dpi , metadata = metadata )
14931508 else :
14941509 try :
14951510 from PIL import Image
1511+ from PIL .PngImagePlugin import PngInfo
14961512 except ImportError as exc :
1497- raise ImportError (
1498- f"Saving to { format } requires Pillow" ) from exc
1513+ if pil_kwargs is not None :
1514+ raise ImportError ("Setting 'pil_kwargs' requires Pillow" )
1515+ else :
1516+ raise ImportError (f"Saving to { format } requires Pillow" )
1517+ if pil_kwargs is None :
1518+ pil_kwargs = {}
14991519 pil_shape = (rgba .shape [1 ], rgba .shape [0 ])
15001520 image = Image .frombuffer (
15011521 "RGBA" , pil_shape , rgba , "raw" , "RGBA" , 0 , 1 )
1522+ if format == "png" and metadata is not None :
1523+ # cf. backend_agg's print_png.
1524+ pnginfo = PngInfo ()
1525+ for k , v in metadata .items ():
1526+ pnginfo .add_text (k , v )
1527+ pil_kwargs ["pnginfo" ] = pnginfo
15021528 if format in ["jpg" , "jpeg" ]:
15031529 format = "jpeg" # Pillow doesn't recognize "jpg".
15041530 color = tuple (
@@ -1507,7 +1533,9 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
15071533 background = Image .new ("RGB" , pil_shape , color )
15081534 background .paste (image , image )
15091535 image = background
1510- image .save (fname , format = format , dpi = (dpi , dpi ))
1536+ pil_kwargs .setdefault ("format" , format )
1537+ pil_kwargs .setdefault ("dpi" , (dpi , dpi ))
1538+ image .save (fname , ** pil_kwargs )
15111539
15121540
15131541def pil_to_array (pilImage ):
0 commit comments