@@ -1431,7 +1431,7 @@ def read_png(*args, **kwargs):
1431
1431
1432
1432
1433
1433
def 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 ):
1435
1435
"""
1436
1436
Save an array as an image file.
1437
1437
@@ -1464,6 +1464,17 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
1464
1464
dpi : int
1465
1465
The DPI to store in the metadata of the file. This does not affect the
1466
1466
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.
1467
1478
"""
1468
1479
from matplotlib .figure import Figure
1469
1480
from matplotlib import _png
@@ -1474,10 +1485,14 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
1474
1485
else rcParams ["savefig.format" ]).lower ()
1475
1486
if format in ["pdf" , "ps" , "eps" , "svg" ]:
1476
1487
# 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 } " )
1477
1491
fig = Figure (dpi = dpi , frameon = False )
1478
1492
fig .figimage (arr , cmap = cmap , vmin = vmin , vmax = vmax , origin = origin ,
1479
1493
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 )
1481
1496
else :
1482
1497
# Don't bother creating an image; this avoids rounding errors on the
1483
1498
# size when dividing and then multiplying by dpi.
@@ -1488,17 +1503,28 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
1488
1503
if origin == "lower" :
1489
1504
arr = arr [::- 1 ]
1490
1505
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 )
1493
1508
else :
1494
1509
try :
1495
1510
from PIL import Image
1511
+ from PIL .PngImagePlugin import PngInfo
1496
1512
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 = {}
1499
1519
pil_shape = (rgba .shape [1 ], rgba .shape [0 ])
1500
1520
image = Image .frombuffer (
1501
1521
"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
1502
1528
if format in ["jpg" , "jpeg" ]:
1503
1529
format = "jpeg" # Pillow doesn't recognize "jpg".
1504
1530
color = tuple (
@@ -1507,7 +1533,9 @@ def imsave(fname, arr, vmin=None, vmax=None, cmap=None, format=None,
1507
1533
background = Image .new ("RGB" , pil_shape , color )
1508
1534
background .paste (image , image )
1509
1535
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 )
1511
1539
1512
1540
1513
1541
def pil_to_array (pilImage ):
0 commit comments