Angeben und Speichern einer Figur mit exakter Größe in Pixel

151

Angenommen, ich habe ein Bild mit einer Größe von 3841 x 7195 Pixel. Ich möchte den Inhalt der Figur auf der Festplatte speichern, um ein Bild mit der genauen Größe zu erhalten, die ich in Pixel angegeben habe.

Keine Achse, keine Titel. Nur das Bild. Ich persönlich interessiere mich nicht für DPIs, da ich nur die Größe des Bilds auf dem Bildschirm auf der Festplatte in Pixel angeben möchte .

Ich habe andere Threads gelesen , und alle scheinen Konvertierungen in Zoll vorzunehmen und dann die Abmessungen der Figur in Zoll anzugeben und die dpis auf irgendeine Weise anzupassen. Ich möchte vermeiden, mich mit dem potenziellen Genauigkeitsverlust zu befassen, der durch Konvertierungen von Pixel in Zoll entstehen kann.

Ich habe versucht mit:

w = 7195
h = 3841
fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig(some_path, dpi=1)

ohne Glück (Python beschwert sich, dass Breite und Höhe jeweils unter 32768 (?) liegen müssen)

Nach allem, was ich gesehen habe, matplotlibmuss die Figurengröße in inchesund angegeben werden dpi, aber ich interessiere mich nur für die Pixel, die die Figur auf der Festplatte aufnimmt. Wie kann ich das machen?

Zur Verdeutlichung: Ich suche nach einer Möglichkeit, dies mit matplotlibund nicht mit anderen bildspeichernden Bibliotheken zu tun .

Amelio Vazquez-Reina
quelle
Mit matplotlib ist es nicht möglich, die Figurengröße direkt in Zoll einzustellen.
Tiago

Antworten:

175

Matplotlib funktioniert nicht direkt mit Pixeln, sondern mit physischen Größen und DPI. Wenn Sie eine Figur mit einer bestimmten Pixelgröße anzeigen möchten, müssen Sie die DPI Ihres Monitors kennen. Zum Beispiel erkennt dieser Link das für Sie.

Wenn Sie ein Bild mit einer Größe von 3841 x 7195 Pixel haben, ist es unwahrscheinlich, dass Ihr Monitor so groß ist, sodass Sie keine Figur dieser Größe anzeigen können (matplotlib erfordert, dass die Figur in den Bildschirm passt, wenn Sie nach einer Größe fragen zu groß wird es auf die Bildschirmgröße schrumpfen). Stellen Sie sich vor, Sie möchten nur als Beispiel ein Bild mit 800 x 800 Pixel. So zeigen Sie ein 800 x 800 Pixel großes Bild auf meinem Monitor an ( my_dpi=96):

plt.figure(figsize=(800/my_dpi, 800/my_dpi), dpi=my_dpi)

Sie teilen also einfach die Abmessungen in Zoll durch Ihre DPI.

Wenn Sie eine Figur einer bestimmten Größe speichern möchten, ist dies eine andere Sache. Bildschirm-DPIs sind nicht mehr so ​​wichtig (es sei denn, Sie fragen nach einer Zahl, die nicht in den Bildschirm passt). Anhand des gleichen Beispiels für die 800 x 800 Pixel große Figur können wir sie mit dem dpiSchlüsselwort von in verschiedenen Auflösungen speichern savefig. Um es in der gleichen Auflösung wie der Bildschirm zu speichern, verwenden Sie einfach die gleiche Auflösung:

plt.savefig('my_fig.png', dpi=my_dpi)

Verwenden Sie eine 10-mal größere Auflösung, um sie als 8000 x 8000 Pixel großes Bild zu speichern:

plt.savefig('my_fig.png', dpi=my_dpi * 10)

Beachten Sie, dass die Einstellung der DPI nicht von allen Backends unterstützt wird. Hier wird das PNG-Backend verwendet, aber das PDF- und das ps-Backend implementieren die Größe unterschiedlich. Das Ändern der DPI und der Größe wirkt sich auch auf die Schriftgröße aus. Eine größere DPI behält die gleiche relative Größe von Schriftarten und Elementen bei. Wenn Sie jedoch kleinere Schriftarten für eine größere Figur wünschen, müssen Sie die physische Größe anstelle der DPI erhöhen.

Zurück zu Ihrem Beispiel: Wenn Sie ein Bild mit 3841 x 7195 Pixel speichern möchten, können Sie Folgendes tun:

plt.figure(figsize=(3.841, 7.195), dpi=100)
( your code ...)
plt.savefig('myfig.png', dpi=1000)

Beachten Sie, dass ich die Zahl dpi von 100 verwendet habe, um in die meisten Bildschirme zu passen, aber mit gespeichert habe dpi=1000, um die erforderliche Auflösung zu erreichen. In meinem System erzeugt dies ein PNG mit 3840 x 7190 Pixel - es scheint, dass die gespeicherte DPI immer 0,02 Pixel / Zoll kleiner als der ausgewählte Wert ist, was sich auf große Bildgrößen (klein) auswirkt. Weitere Diskussion hier .

tiago
quelle
5
Es ist praktisch, sich daran zu erinnern, dass Monitorgrößen (und damit Standardgrößen für Browser- und UI-Fenster) normalerweise 96 dpi betragen - Vielfache von 96. Plötzlich sind Zahlen wie 1440 Pixel sinnvoll (15 Zoll), wenn man so denkt.
Danny Staple
Könnte das nicht an der Arbeit vorbei figsizezu plt.figure. Die Lösung bestand darin, zu tun, was die anderen Antworten vermuten lassen, und nach einem Anruf ohne figsize - dann anzurufenfig.set_size_inches(w,h)
Trinidad
Dokumente für figureund savefig.
behandeln
Der Link zeigt nicht den korrekten Wert für Apple Thunderbolt Display an.
Dmitry
Ich mag diese Lösung, aber ich habe eine Warnung. Die Textgröße skaliert umgekehrt als dpi. (Mein System ist MacBook Pro, OS X). Wenn Sie also interaktiv drucken und die dpi zu groß machen (z. B. 10 * my_dpi), wird der Text nahezu unsichtbar.
Prof Huster
16

Dies funktionierte für mich basierend auf Ihrem Code und erzeugte ein 93-MB-PNG-Bild mit Farbrauschen und den gewünschten Abmessungen:

import matplotlib.pyplot as plt
import numpy

w = 7195
h = 3841

im_np = numpy.random.rand(h, w)

fig = plt.figure(frameon=False)
fig.set_size_inches(w,h)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
fig.add_axes(ax)
ax.imshow(im_np, aspect='normal')
fig.savefig('figure.png', dpi=1)

Ich verwende die letzten PIP-Versionen der Python 2.7-Bibliotheken in Linux Mint 13.

Hoffentlich hilft das!

Heltonbiker
quelle
4
Das Einstellen einer sehr niedrigen dpi bedeutet, dass Schriftarten kaum sichtbar sind, es sei denn, sehr große Schriftgrößen werden explizit verwendet.
Tiago
1
Es ist wahrscheinlich besser, eine höhere dpi einzustellen und die Zollgröße (die ohnehin willkürlich ist) durch diese dpi zu teilen. Abgesehen davon erzeugt Ihr Setup ein genaues Pixel für die Pixelwiedergabe, danke!
FrenchKheldar
Ich versuche dies zu verwenden, bevor ich Bilder mit Plotelementen (Kreise, Linien, ...) speichere. Dies stört die Linienbreite, so dass die Elemente kaum sichtbar sind.
Gauthier
5

Basierend auf der akzeptierten Antwort von tiago ist hier eine kleine generische Funktion, die ein numpy-Array in ein Bild mit der gleichen Auflösung wie das Array exportiert:

import matplotlib.pyplot as plt
import numpy as np

def export_figure_matplotlib(arr, f_name, dpi=200, resize_fact=1, plt_show=False):
    """
    Export array as figure in original resolution
    :param arr: array of image to save in original resolution
    :param f_name: name of file where to save figure
    :param resize_fact: resize facter wrt shape of arr, in (0, np.infty)
    :param dpi: dpi of your screen
    :param plt_show: show plot or not
    """
    fig = plt.figure(frameon=False)
    fig.set_size_inches(arr.shape[1]/dpi, arr.shape[0]/dpi)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(arr)
    plt.savefig(f_name, dpi=(dpi * resize_fact))
    if plt_show:
        plt.show()
    else:
        plt.close()

Wie in der vorherigen Antwort von tiago erwähnt, muss zuerst die Bildschirm-DPI gefunden werden. Dies kann beispielsweise hier erfolgen: http://dpi.lv

Ich habe resize_factder Funktion ein zusätzliches Argument hinzugefügt , mit dem Sie das Bild beispielsweise in 50% (0,5) der ursprünglichen Auflösung exportieren können.

Cyril
quelle
-1

plt.imsave hat für mich gearbeitet. Die Dokumentation finden Sie hier: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.imsave.html

#file_path = directory address where the image will be stored along with file name and extension
#array = variable where the image is stored. I think for the original post this variable is im_np
plt.imsave(file_path, array)
Alka
quelle
Bitte fügen Sie einen Beispielcode hinzu, der genau angibt, welchen Parameter Sie einstellen, und welche Werte für den Anwendungsfall des ursprünglichen Beitrags empfohlen werden.
Sarah Messer