Usuwanie spacji wokół zapisanego obrazu w matplotlib

172

Muszę zrobić zdjęcie i zapisać je po jakimś procesie. Rysunek wygląda dobrze, gdy go wyświetlam, ale po zapisaniu rysunku wokół zapisanego obrazu mam trochę pustej przestrzeni. Wypróbowałem 'tight'opcję savefigmetody, też nie działała. Kod:

  import matplotlib.image as mpimg
  import matplotlib.pyplot as plt

  fig = plt.figure(1)
  img = mpimg.imread(path)
  plt.imshow(img)
  ax=fig.add_subplot(1,1,1)

  extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
  plt.savefig('1.png', bbox_inches=extent)

  plt.axis('off') 
  plt.show()

Próbuję narysować podstawowy wykres za pomocą NetworkX na rysunku i zapisać go. Zdałem sobie sprawę, że bez wykresu to działa, ale po dodaniu wykresu wokół zapisanego obrazu pojawia się biała przestrzeń;

import matplotlib.image as mpimg
import matplotlib.pyplot as plt
import networkx as nx

G = nx.Graph()
G.add_node(1)
G.add_node(2)
G.add_node(3)
G.add_edge(1,3)
G.add_edge(1,2)
pos = {1:[100,120], 2:[200,300], 3:[50,75]}

fig = plt.figure(1)
img = mpimg.imread("C:\\images\\1.jpg")
plt.imshow(img)
ax=fig.add_subplot(1,1,1)

nx.draw(G, pos=pos)

extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig('1.png', bbox_inches = extent)

plt.axis('off') 
plt.show()
Ahmet Tuğrul Bayrak
źródło
To, co naprawdę zadziałało, to przycięcie pliku PDF za pomocą innego narzędzia, takiego jak pdfcrop.
toliveira

Odpowiedzi:

170

Nie mogę twierdzić, że wiem dokładnie, dlaczego i jak działa moje „rozwiązanie”, ale to właśnie musiałem zrobić, gdy chciałem nakreślić zarys kilku sekcji płata - bez białych marginesów - do pliku PDF. (Zauważ, że użyłem matplotlib wewnątrz notebooka IPython z flagą -pylab.)

plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, 
            hspace = 0, wspace = 0)
plt.margins(0,0)
plt.gca().xaxis.set_major_locator(plt.NullLocator())
plt.gca().yaxis.set_major_locator(plt.NullLocator())
plt.savefig("filename.pdf", bbox_inches = 'tight',
    pad_inches = 0)

Próbowałem dezaktywować różne części tego, ale zawsze prowadzi to gdzieś do białego marginesu. Możesz nawet zmodyfikować to, aby zapobiec goleniu linii tłuszczu w pobliżu granic sylwetki z powodu braku marginesów.

Johannes S.
źródło
6
Wreszcie coś, co działa, bardzo dziękuję! Nawiasem mówiąc, w moim przypadku set_major_locatorpotrzebne były tylko dwie linie .
Florian Brucker
6
Spędziłem ostatnią godzinę próbując różnych rzeczy i nie mogłem pozbyć się białej ramki o wielkości 1 piksela. To była jedyna rzecz, która zadziałała - konkretnie, o pad_inches=0której inne odpowiedzi nie wspominają.
AnnanFay
1
set_major_locatorbył dla mnie kluczowy.
kmac
16
pad_inchespomogło mi.
Myles Baker
5
matplotlib.ticker.NullLocator ()
Joop,
200

Można usunąć białą przestrzeń dopełnienie przez ustawienie bbox_inches="tight"w savefig:

plt.savefig("test.png",bbox_inches='tight')

Będziesz musiał wstawić argument bbox_inchesjako ciąg, być może dlatego wcześniej nie zadziałał.


Możliwe duplikaty:

Wykresy Matplotlib: usuwanie osi, legend i spacji

Jak ustawić marginesy dla figury matplotlib?

Zmniejsz lewy i prawy margines na wykresie matplotlib

Haczykowaty
źródło
1
Jeśli masz wiele działek podrzędnych i chcesz zapisać każdy z nich, możesz użyć tego fig.savefig()również z . ( plt.savefig()nie zadziała w tym przypadku.)
Abhranil Das
30
To nie do końca w porządku. Kiedy używasz tej bbox_inchesopcji, jest inna wartość domyślna, która pozostawia trochę miejsca. Jeśli naprawdę chcesz się wszystkiego pozbyć, musisz również użyć pad_inches=0.0. Oczywiście takie ciasne obicie często odcina, np. Wykładniki ...
Mike
5
Aby usunąć również czarną krawędź, może być konieczne ustawieniepad_inches=-0.1
lenhhoxung
10
To po prostu nie działa, nadal pojawiają się spacje wokół figury. Ustawienie opcji przezroczystej (jak wspomniano w niektórych odpowiedziach) też nie pomaga, spacja nadal istnieje, jest tylko przezroczysta.
BjornW,
1
@piperchester to dobre pytanie, ale prawdopodobnie powinno zostać zadane jako nowe pytanie, aby nie zgubiło się w komentarzach. Powinieneś jednak połączyć nowe pytanie ze starym!
Hooked
21

Po wypróbowaniu powyższych odpowiedzi bez powodzenia (i mnóstwa innych postów na stosie), co w końcu zadziałało dla mnie, było po prostu

plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, 
            hspace = 0, wspace = 0)
plt.margins(0,0)
plt.savefig("myfig.pdf")

Co ważne, nie obejmuje to pola bbox ani argumentów wypełniających.

SuaveSouris
źródło
4
To powinna być akceptowana odpowiedź. Dodatkowo nie musisz nawet dzwonić set_axis_off. Nie ma to wpływu na zapisany obraz, ponieważ po subplots_adjustosi leży poza zasięgiem figury, a kura i tak nie zostanie zapisana. W notatnikach Jupyter musisz jednak jawnie wyłączyć oś, ponieważ wbudowane zaplecze zastępuje te ustawienia.
MaxPowers
20

Znalazłem coś od Arvinda Pereiry ( http://robotics.usc.edu/~ampereir/wordpress/?p=626 ) i wydawało się, że działa dla mnie:

plt.savefig(filename, transparent = True, bbox_inches = 'tight', pad_inches = 0)
unclerico
źródło
7
transparent=Truesprawi, że będzie się wydawało, że nie ma problemu, ale po prostu ukryje białą przestrzeń, wymiary obrazu nie będą w porządku.
Vlady Veselinov
Dzięki za wzmiankę pad_inches! Żałuję, że nie wiedziałem o tej opcji wcześniej!
ingomueller.net
1
Działa to w przypadku większości wykresów, ale to usunęło prawą granicę dla mojej matrycy zamieszania. Wystarczy dodać małe dopełnieniepad_inches=.25
YTZ
14

Poniższa funkcja zawiera powyższą odpowiedź Johanna. Przetestowałem to z wieloma osiami plt.figurei plt.subplots()z wieloma osiami i działa dobrze.

def save(filepath, fig=None):
    '''Save the current image with no whitespace
    Example filepath: "myfig.png" or r"C:\myfig.pdf" 
    '''
    import matplotlib.pyplot as plt
    if not fig:
        fig = plt.gcf()

    plt.subplots_adjust(0,0,1,1,0,0)
    for ax in fig.axes:
        ax.axis('off')
        ax.margins(0,0)
        ax.xaxis.set_major_locator(plt.NullLocator())
        ax.yaxis.set_major_locator(plt.NullLocator())
    fig.savefig(filepath, pad_inches = 0, bbox_inches='tight')
TomNorway
źródło
Działał jak urok. Poprzednia odpowiedź dotyczyła niektórych wymaganych poleceń w moim eksporcie.
Kevin S
11

Zauważyłem, że poniższe kody działają idealnie do tego zadania.

fig = plt.figure(figsize=[6,6])
ax = fig.add_subplot(111)
ax.imshow(data)
ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)
ax.set_frame_on(False)
plt.savefig('data.png', dpi=400, bbox_inches='tight',pad_inches=0)
Richard Yu Liu
źródło
2
Ogólnie odpowiedzi są znacznie bardziej pomocne, jeśli zawierają wyjaśnienie, do czego służy kod i dlaczego to rozwiązuje problem bez wprowadzania innych.
Tim Diekmann
7

Śledziłem tę sekwencję i zadziałało to jak urok.

plt.axis("off")
fig=plt.imshow(image array,interpolation='nearest')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
plt.savefig('destination_path.pdf',
    bbox_inches='tight', pad_inches=0, format='pdf', dpi=1200)
Chan
źródło
1
Właściwie stwierdziłem, że ta odpowiedź jest łatwa i wygodniejsza w użyciu.
sikisis
1
Ten pracował dla mnie; przyjęta odpowiedź nie.
Joe
3

Dla każdego, kto chce pracować w pikselach, a nie w calach, to zadziała.

Plus to, co zwykle będziesz potrzebować

from matplotlib.transforms import Bbox

Następnie możesz użyć następujących:

my_dpi = 100 # Good default - doesn't really matter

# Size of output in pixels
h = 224
w = 224

fig, ax = plt.subplots(1, figsize=(w/my_dpi, h/my_dpi), dpi=my_dpi)

ax.set_position([0, 0, 1, 1]) # Critical!

# Do some stuff
ax.imshow(img)
ax.imshow(heatmap) # 4-channel RGBA
ax.plot([50, 100, 150], [50, 100, 150], color="red")

ax.axis("off")

fig.savefig("saved_img.png",
            bbox_inches=Bbox([[0, 0], [w/my_dpi, h/my_dpi]]),
            dpi=my_dpi)

wprowadź opis obrazu tutaj

Simon Thomas
źródło
Nie musisz określać dpi, możesz fig.dpizamiast tego użyć domyślnej
intsco
1

O wiele prostsze podejście, które znalazłem, polega na użyciu plt.imsave:

    import matplotlib.pyplot as plt
    arr = plt.imread(path)
    plt.imsave('test.png', arr)
Parth92
źródło
Niedoceniona odpowiedź. Pomogło mi to po długich poszukiwaniach, jak zachować rozdzielczość i usunąć spacje za pomocą plt.savefig().
nihal111,
Działa to tylko w przypadku, gdy chcesz zapisać tablicę (!) Jako obraz. Nie pozwala to na zapisanie dowolnej figury.
MaxPowers
Co masz na myśli mówiąc o dowolnym obrazie? Czy obraz nie jest tablicą wartości?
Parth92,
0

Możesz tego spróbować. To rozwiązało mój problem.

import matplotlib.image as mpimg
img = mpimg.imread("src.png")
mpimg.imsave("out.png", img, cmap=cmap)
user1410665
źródło
-1

Jeśli chcesz wyświetlić to, co ma zostać zapisane, polecam użycie plt.tight_layouttransformacji, która jest w rzeczywistości lepsza, ponieważ nie powoduje niepotrzebnego przycinania podczas używaniaplt.savefig

import matplotlib as plt    
plt.plot([1,2,3], [1,2,3])
plt.tight_layout(pad=0)
plt.savefig('plot.png')
Keto
źródło
-4

Działa to dla mnie, zapisując tablicę numpy wykreśloną za pomocą imshow do pliku

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(10,10))
plt.imshow(img) # your image here
plt.axis("off")
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, 
        hspace = 0, wspace = 0)
plt.savefig("example2.png", box_inches='tight', dpi=100)
plt.show()
Alejandro Sazo
źródło