Comparaison de différentes approches
Voici une comparaison rapide de certaines des approches que j'ai essayées avec des images montrant ce qu'elles donnent.
Exemple de base sans essayer de définir les dimensions de l'image
Juste pour avoir un point de comparaison :
base.py
#!/usr/bin/env python3
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
fig, ax = plt.subplots()
print('fig.dpi = {}'.format(fig.dpi))
print('fig.get_size_inches() = ' + str(fig.get_size_inches())
t = np.arange(-10., 10., 1.)
plt.plot(t, t, '.')
plt.plot(t, t**2, '.')
ax.text(0., 60., 'Hello', fontdict=dict(size=25))
plt.savefig('base.png', format='png')
courir :
./base.py
identify base.png
sorties :
fig.dpi = 100.0
fig.get_size_inches() = [6.4 4.8]
base.png PNG 640x480 640x480+0+0 8-bit sRGB 13064B 0.000u 0:00.000
Ma meilleure approche jusqu'à présent : plt.savefig(dpi=h/fig.get_size_inches()[1]
contrôle en hauteur
Je pense que c'est ce que je choisirai la plupart du temps, car c'est simple et c'est une balance :
get_size.py
#!/usr/bin/env python3
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
height = int(sys.argv[1])
fig, ax = plt.subplots()
t = np.arange(-10., 10., 1.)
plt.plot(t, t, '.')
plt.plot(t, t**2, '.')
ax.text(0., 60., 'Hello', fontdict=dict(size=25))
plt.savefig(
'get_size.png',
format='png',
dpi=height/fig.get_size_inches()[1]
)
courir :
./get_size.py 431
sorties :
get_size.png PNG 574x431 574x431+0+0 8-bit sRGB 10058B 0.000u 0:00.000
y
./get_size.py 1293
sorties :
main.png PNG 1724x1293 1724x1293+0+0 8-bit sRGB 46709B 0.000u 0:00.000
J'ai tendance à ne définir que la hauteur car je suis généralement plus préoccupé par l'espace vertical que l'image va occuper au milieu de mon texte.
plt.savefig(bbox_inches='tight'
modifie la taille de l'image
Je trouve toujours qu'il y a trop d'espace blanc autour des images, et j'ai eu tendance à ajouter des bbox_inches='tight'
de : Suppression de l'espace blanc autour d'une image enregistrée dans matplotlib
Cependant, cela fonctionne en recadrant l'image, et vous n'obtiendrez pas les tailles souhaitées avec cette méthode.
Au contraire, cette autre approche proposée dans la même question semble bien fonctionner :
plt.tight_layout(pad=1)
plt.savefig(...
ce qui donne la hauteur exacte souhaitée pour une hauteur égale à 431 :
Hauteur fixe, set_aspect
, largeur automatiquement dimensionnée et marges réduites
Ermmm, set_aspect
gâche les choses à nouveau et empêche plt.tight_layout
de supprimer réellement les marges...
Demandé à : Comment obtenir une hauteur fixe en pixels, un rapport d'aspect x/y des données fixe et supprimer automatiquement la marge horizontale d'espace blanc dans Matplotlib ?
plt.savefig(dpi=h/fig.get_size_inches()[1]
+ contrôle de la largeur
Si vous avez vraiment besoin d'une largeur spécifique en plus de la hauteur, cela semble fonctionner correctement :
width.py
#!/usr/bin/env python3
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
h = int(sys.argv[1])
w = int(sys.argv[2])
fig, ax = plt.subplots()
wi, hi = fig.get_size_inches()
fig.set_size_inches(hi*(w/h), hi)
t = np.arange(-10., 10., 1.)
plt.plot(t, t, '.')
plt.plot(t, t**2, '.')
ax.text(0., 60., 'Hello', fontdict=dict(size=25))
plt.savefig(
'width.png',
format='png',
dpi=h/hi
)
courir :
./width.py 431 869
sortie :
width.png PNG 869x431 869x431+0+0 8-bit sRGB 10965B 0.000u 0:00.000
et pour une petite largeur :
./width.py 431 869
sortie :
width.png PNG 211x431 211x431+0+0 8-bit sRGB 6949B 0.000u 0:00.000
Il semble donc que la mise à l'échelle des polices soit correcte, mais les étiquettes sont coupées dans les très petites largeurs, par exemple dans le cas de l'image suivante 100
en haut à gauche.
J'ai réussi à les contourner avec Suppression de l'espace blanc autour d'une image enregistrée dans matplotlib
plt.tight_layout(pad=1)
ce qui donne :
width.png PNG 211x431 211x431+0+0 8-bit sRGB 7134B 0.000u 0:00.000
De ce fait, nous voyons aussi que tight_layout
supprime une grande partie de l'espace vide en haut de l'image, donc je l'utilise généralement toujours.
Hauteur de la base magique fixe, dpi
en fig.set_size_inches
y plt.savefig(dpi=
mise à l'échelle
Je pense que cela équivaut à l'approche mentionnée à l'adresse suivante : https://stackoverflow.com/a/13714720/895245
magic.py
#!/usr/bin/env python3
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
magic_height = 300
w = int(sys.argv[1])
h = int(sys.argv[2])
dpi = 80
fig, ax = plt.subplots(dpi=dpi)
fig.set_size_inches(magic_height*w/(h*dpi), magic_height/dpi)
t = np.arange(-10., 10., 1.)
plt.plot(t, t, '.')
plt.plot(t, t**2, '.')
ax.text(0., 60., 'Hello', fontdict=dict(size=25))
plt.savefig(
'magic.png',
format='png',
dpi=h/magic_height*dpi,
)
courir :
./magic.py 431 231
sorties :
magic.png PNG 431x231 431x231+0+0 8-bit sRGB 7923B 0.000u 0:00.000
Et pour voir si ça se passe bien :
./magic.py 1291 693
sorties :
magic.png PNG 1291x693 1291x693+0+0 8-bit sRGB 25013B 0.000u 0:00.000
Nous constatons donc que cette approche donne également de bons résultats. Le seul problème que j'ai avec elle est que vous devez définir que magic_height
ou équivalent.
DPI fixe + set_size_inches
Cette approche a donné une taille de pixel légèrement erronée, et il est difficile de mettre tout à l'échelle de manière transparente.
set_size_inches.py
#!/usr/bin/env python3
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
w = int(sys.argv[1])
h = int(sys.argv[2])
fig, ax = plt.subplots()
fig.set_size_inches(w/fig.dpi, h/fig.dpi)
t = np.arange(-10., 10., 1.)
plt.plot(t, t, '.')
plt.plot(t, t**2, '.')
ax.text(
0,
60.,
'Hello',
# Keep font size fixed independently of DPI.
# https://stackoverflow.com/questions/39395616/matplotlib-change-figsize-but-keep-fontsize-constant
fontdict=dict(size=10*h/fig.dpi),
)
plt.savefig(
'set_size_inches.png',
format='png',
)
courir :
./set_size_inches.py 431 231
sorties :
set_size_inches.png PNG 430x231 430x231+0+0 8-bit sRGB 8078B 0.000u 0:00.000
donc la hauteur est légèrement décalée, et l'image :
La taille des pixels est également correcte si je la multiplie par trois :
./set_size_inches.py 1291 693
sorties :
set_size_inches.png PNG 1291x693 1291x693+0+0 8-bit sRGB 19798B 0.000u 0:00.000
Nous comprenons cependant que pour que cette approche soit bien adaptée, il faut que chaque paramètre dépendant du DPI soit proportionnel à la taille en pouces.
Dans l'exemple précédent, nous avons uniquement rendu le texte "Hello" proportionnel, et il a conservé sa hauteur entre 60 et 80 comme prévu. Mais tout ce pour quoi nous n'avons pas fait cela, semble minuscule, y compris :
- largeur de ligne des axes
- étiquettes à cocher
- marqueurs de points
SVG
Je n'ai pas trouvé comment le définir pour les images SVG, mes approches ne fonctionnaient que pour les PNG par exemple :
get_size_svg.py
#!/usr/bin/env python3
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
height = int(sys.argv[1])
fig, ax = plt.subplots()
t = np.arange(-10., 10., 1.)
plt.plot(t, t, '.')
plt.plot(t, t**2, '.')
ax.text(0., 60., 'Hello', fontdict=dict(size=25))
plt.savefig(
'get_size_svg.svg',
format='svg',
dpi=height/fig.get_size_inches()[1]
)
courir :
./get_size_svg.py 431
et la sortie générée contient :
<svg height="345.6pt" version="1.1" viewBox="0 0 460.8 345.6" width="460.8pt"
et identifier dit :
get_size_svg.svg SVG 614x461 614x461+0+0 8-bit sRGB 17094B 0.000u 0:00.000
et si je l'ouvre dans Chromium 86, les outils de débogage du navigateur, le survol de l'image par la souris, confirment que la hauteur est de 460,79.
Mais bien sûr, puisque SVG est un format vectoriel, tout devrait en théorie être mis à l'échelle, vous pouvez donc simplement le convertir dans n'importe quel format de taille fixe sans perte de résolution, par exemple :
inkscape -h 431 get_size_svg.svg -b FFF -e get_size_svg.png
donne la hauteur exacte :
TODO régénérer l'image, j'ai raté le téléchargement d'une manière ou d'une autre.
J'utilise Inkscape au lieu de Imagemagick. convert
ici parce que vous avez besoin de jouer avec -density
également pour obtenir des redimensionnements SVG nets avec ImageMagick :
Et la mise <img height=""
sur le HTML devrait également fonctionner pour le navigateur.
Testé sur matplotlib==3.2.2.