Wykresy Matplotlib: usuwanie osi, legend i białych znaków

285

Jestem nowy w Pythonie i Matplotlib, chciałbym po prostu zastosować mapę kolorów do obrazu i napisać obraz wynikowy, bez używania osi, etykiet, tytułów lub czegokolwiek zwykle automatycznie dodawanego przez matplotlib. Oto co zrobiłem:

def make_image(inputname,outputname):
    data = mpimg.imread(inputname)[:,:,0]
    fig = plt.imshow(data)
    fig.set_cmap('hot')
    fig.axes.get_xaxis().set_visible(False)
    fig.axes.get_yaxis().set_visible(False)
    plt.savefig(outputname)

Z powodzeniem usuwa oś figury, ale zapisana figura przedstawia białe wypełnienie i ramkę wokół rzeczywistego obrazu. Jak mogę je usunąć (przynajmniej białe wypełnienie)? Dzięki

sunmat
źródło
2
Wszystkie rozwiązania tego pytania są skoncentrowane imshow. Jeśli zamiast tego masz wykres rozrzutu, może Ci pomóc następująca odpowiedź: stackoverflow.com/a/40727744/4124317
ImportanceOfBeingErnest

Odpowiedzi:

373

Myślę, że polecenie axis('off')zajmuje się jednym z problemów bardziej zwięźle niż osobna zmiana każdej osi i granicy. Jednak nadal pozostawia biały obszar wokół granicy. Dodanie bbox_inches='tight'do savefigpolecenia prawie Cię tam prowadzi, możesz zobaczyć w poniższym przykładzie, że biała przestrzeń po lewej jest znacznie mniejsza, ale wciąż obecna.

Zauważ, że nowsze wersje matplotlib mogą wymagać bbox_inches=0zamiast ciągu znaków 'tight'(przez @episodeyang i @kadrach)

from numpy import random
import matplotlib.pyplot as plt

data = random.random((5,5))
img = plt.imshow(data, interpolation='nearest')
img.set_cmap('hot')
plt.axis('off')
plt.savefig("test.png", bbox_inches='tight')

wprowadź opis zdjęcia tutaj

Haczykowaty
źródło
4
Następujących sugestii unutbu użytkownika, można użyć fig = plt.figure(), ax = plt.axes([0,0,1,1]), a następnie plt.imshow(data,interpolation="nearest". W połączeniu z plt.axis("off")tym powinien pozbyć się wszystkiego oprócz samego obrazu, miejmy nadzieję!
PhilMacKay
5
Połączenie metod z pytania ({fig.axes.get_xaxis (). Set_visible (False) & fig.axes.get_yaxis (). Set_visible (False)} z {plt.axis ('off')}) rozwiązało problem mnie. (Nie ma już białych znaków). I nie zapomnij ustawić {pad_inches} w savefig na 0.
Domagoj,
3
Właśnie natknąłem się na ten problem, żadne z rozwiązań w tym wątku i powiązane nie działały. Jednak wyraźnie sprecyzowanie bbox_inches=0załatwiło sprawę.
kadrach
1
Zauważ, że jeśli „kreślisz” rzeczy na plt.imshow, jak w plt.plot (x, y), musisz upewnić się, że ustawiłeś xlim i ylim na rozmiar matrycy.
Mathusalem
1
to już nie działa. Zajęło mi godzinę, żeby to rozgryźć. Zobacz odpowiedź poniżej.
odcinek
107

Nauczyłem się tej sztuczki od matehat, tutaj :

import matplotlib.pyplot as plt
import numpy as np

def make_image(data, outputname, size=(1, 1), dpi=80):
    fig = plt.figure()
    fig.set_size_inches(size)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.set_axis_off()
    fig.add_axes(ax)
    plt.set_cmap('hot')
    ax.imshow(data, aspect='equal')
    plt.savefig(outputname, dpi=dpi)

# data = mpimg.imread(inputname)[:,:,0]
data = np.arange(1,10).reshape((3, 3))

make_image(data, '/tmp/out.png')

daje

wprowadź opis zdjęcia tutaj

unutbu
źródło
3
Jestem prawie pewien, że masz poprawną odpowiedź (choć prawdopodobnie jest na to więcej niż jeden sposób), ale zastanawiam się, czy możesz wyjaśnić, dlaczego jest to poprawna odpowiedź? Co z twoim kodem usuwa białe znaki?
Yann
4
@ Yann, oprócz dokumentacji, uważam, że bardzo pomocne jest komentowanie jednej linii na raz, aby zobaczyć, jaki efekt ma każde polecenie. To sposób empiryczny!
unutbu
4
Linia, która usuwa białą ramkę, to plt.Axes(fig, [0,0,1,1]). Mówi to matplotlibowi, aby utworzył zestaw osi z lewym dolnym rogiem w punkcie znajdującym się w (0%, 0%) oraz o szerokości i wysokości (100%, 100%).
PhilMacKay
Jest to bardziej poprawna odpowiedź, ponieważ nie tylko usuwa białe miejsca dla zapisanego obrazu, ale także dla wydruków na ekranie.
MaxNoe
Dzięki, to
JEDYNE
56

Możliwe najprostsze rozwiązanie:

Po prostu połączyłem metodę opisaną w pytaniu i metodę z odpowiedzi Hooked.

fig = plt.imshow(my_data)
plt.axis('off')
fig.axes.get_xaxis().set_visible(False)
fig.axes.get_yaxis().set_visible(False)
plt.savefig('pict.png', bbox_inches='tight', pad_inches = 0)

Po tym kodzie nie ma białych znaków ani ramki.

Bez białych znaków, osi lub ramki

Domagoj
źródło
3
Twoja metoda usunęła wszystkie białe spacje wokół tego obrazu, dobra robota, ale wciąż nie wiem, dlaczego dodanie polecenia fig.axes.get_xaxis().set_visible(False)rozwiązało problem. Dzięki
ollydbg23,
5
Tak, jeśli nie wywołasz tych poleceń, większość białych znaków zostanie usunięta. Jednak w moim przypadku między moją fabułą a krawędzią obrazu .png wciąż były odstępy (być może 2px szerokości). Przez wywołanie następujących poleceń pozbyłem się tych upartych przestrzeni.
Domagoj
3
Zauważ jednak, że powyższe nie powiedzie się w obecności wielu osi na tej samej figurze (w moim przypadku obraz osadzony na wykresie). Rozwiązanie: fig.axes[0]i ogólnie wszystkie lub wybrane osie.
Ioannis Filippidis
29

Nikt imsavejeszcze o tym nie wspominał , co sprawia, że ​​jest to jeden liniowiec:

import matplotlib.pyplot as plt
import numpy as np

data = np.arange(10000).reshape((100, 100))
plt.imsave("/tmp/foo.png", data, format="png", cmap="hot")

Bezpośrednio przechowuje obraz takim, jaki jest, tzn. Nie dodaje żadnych osi ani obramowania / wypełnienia.

wprowadź opis zdjęcia tutaj

luator
źródło
Tak robię przez większość czasu, ale zdarzają się sytuacje, gdy potrzebuję paska kolorów, aw takich przypadkach imsave nie może mnie uratować.
Elias
9

Możesz także określić zakres liczby dla bbox_inchesargumentu. Pozbyłoby się to białej wyściółki wokół postaci.

def make_image(inputname,outputname):
    data = mpimg.imread(inputname)[:,:,0]
    fig = plt.imshow(data)
    fig.set_cmap('hot')
    ax = fig.gca()
    ax.set_axis_off()
    ax.autoscale(False)
    extent = ax.get_window_extent().transformed(plt.gcf().dpi_scale_trans.inverted())
    plt.savefig(outputname, bbox_inches=extent)
gypet
źródło
8

To powinno usunąć wszystkie wypełnienia i obramowania:

from matplotlib import pyplot as plt

fig = plt.figure()
fig.patch.set_visible(False)

ax = fig.add_subplot(111)

plt.axis('off')
plt.imshow(data)

extent = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
plt.savefig("../images/test.png", bbox_inches=extent)
Vlady Veselinov
źródło
Niedoceniana odpowiedź! To zapewniło mi 99% drogi. Powinien być bardziej pozytywnie oceniany, ponieważ jest najbardziej aktualny
Nathan
Działa również, jeśli nie masz obiektu Axes ( ax) z extent = plt.gca().get_window_extent().transformed(fig.dpi_scale_trans.inverted()).
Toast
3

Pozytywna odpowiedź już nie działa. Aby to zadziałało, musisz ręcznie dodać zestaw osi do [0, 0, 1, 1] lub usunąć łatkę pod rysunkiem.

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(5, 5), dpi=20)
ax = plt.Axes(fig, [0., 0., 1., 1.])
fig.add_axes(ax)
plt.imshow([[0, 1], [0.5, 0]], interpolation="nearest")
plt.axis('off')                                # same as: ax.set_axis_off()

plt.savefig("test.png")

Możesz też po prostu usunąć łatkę. Nie trzeba dodawać wątku podrzędnego, aby usunąć wypełnienia. Upraszcza to poniższa odpowiedź Vlady

fig = plt.figure(figsize=(5, 5))
fig.patch.set_visible(False)                   # turn off the patch

plt.imshow([[0, 1], [0.5, 0]], interpolation="nearest")
plt.axis('off')

plt.savefig("test.png", cmap='hot')

Zostało to przetestowane w wersji 3.0.32019/06/19. Zdjęcie patrz poniżej:

wprowadź opis zdjęcia tutaj

Znacznie prostszą rzeczą jest użycie pyplot.imsave. Aby uzyskać szczegółowe informacje, patrz odpowiedź luatora poniżej

Episodeyang
źródło
dla mnie działa tylko wersja Axes. dostaję padding z wyłączoną wersją łatki. i tak dzięki!
save_jeff
2

Podobała mi się odpowiedź ubuntu , ale nie pokazywała wyraźnie, jak ustawić rozmiar niekwadratowych obrazów od razu po wyjęciu z pudełka, więc zmodyfikowałem go, aby łatwo skopiować i wkleić:

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

def save_image_fix_dpi(data, dpi=100):
    shape=np.shape(data)[0:2][::-1]
    size = [float(i)/dpi for i in shape]

    fig = plt.figure()
    fig.set_size_inches(size)
    ax = plt.Axes(fig,[0,0,1,1])
    ax.set_axis_off()
    fig.add_axes(ax)
    ax.imshow(data)
    fig.savefig('out.png', dpi=dpi)
    plt.show()

Zapisywanie obrazów bez obramowania jest łatwe bez względu na wybraną rozdzielczość, jeśli zachowany jest rozmiar piksela / dpi = rozmiar.

data = mpimg.imread('test.png')
save_image_fix_dpi(data, dpi=100)

wprowadź opis zdjęcia tutaj

Jednak wyświetlanie jest upiorne. Jeśli wybierzesz małą rozdzielczość, rozmiar obrazu może być większy niż ekran, a ramka będzie wyświetlana podczas wyświetlania. Niemniej jednak nie wpływa to na oszczędzanie.

Więc dla

save_image_fix_dpi(data, dpi=20)

Wyświetlacz zostaje otoczony (ale zapisywanie działa): wprowadź opis zdjęcia tutaj

csnemes
źródło
1

Po pierwsze, w przypadku niektórych formatów obrazów (np. TIFF ) można faktycznie zapisać mapę kolorów w nagłówku, a większość przeglądających wyświetli dane za pomocą mapy kolorów.

Do zapisania rzeczywistego matplotlibobrazu, który może być przydatny do dodawania adnotacji lub innych danych do obrazów, zastosowałem następujące rozwiązanie:

fig, ax = plt.subplots(figsize=inches)
ax.matshow(data)  # or you can use also imshow
# add annotations or anything else
# The code below essentially moves your plot so that the upper
# left hand corner coincides with the upper left hand corner
# of the artist
fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0)
# now generate a Bbox instance that is the same size as your
# single axis size (this bbox will only encompass your figure)
bbox = matplotlib.transforms.Bbox(((0, 0), inches))
# now you can save only the part of the figure with data
fig.savefig(savename, bbox_inches=bbox, **kwargs)
David Hoffman
źródło
0

Dzięki za niesamowite odpowiedzi od wszystkich ... Miałem dokładnie ten sam problem z chęcią wykreślenia obrazu bez dodatkowej wypełnienia / miejsca itp., Więc byłem bardzo szczęśliwy, że znalazłem tutaj pomysły wszystkich.

Oprócz obrazu bez wypełnienia, chciałem również móc łatwo dodawać adnotacje itp., Poza prostą fabułą.

Skończyło się na tym, że połączyłem odpowiedź Davida z csnemes ', by stworzyć proste opakowanie w momencie tworzenia postaci. Kiedy tego użyjesz, nie potrzebujesz później żadnych zmian za pomocą imsave () lub czegokolwiek innego:

def get_img_figure(image, dpi):
    """
    Create a matplotlib (figure,axes) for an image (numpy array) setup so that
        a) axes will span the entire figure (when saved no whitespace)
        b) when saved the figure will have the same x/y resolution as the array, 
           with the dpi value you pass in.

    Arguments:
        image -- numpy 2d array
        dpi -- dpi value that the figure should use

    Returns: (figure, ax) tuple from plt.subplots
    """

    # get required figure size in inches (reversed row/column order)
    inches = image.shape[1]/dpi, image.shape[0]/dpi

    # make figure with that size and a single axes
    fig, ax = plt.subplots(figsize=inches, dpi=dpi)

    # move axes to span entire figure area
    fig.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0)

    return fig, ax
Richard
źródło