Matplotlib แปลง: ลบแกนตำนานและช่องว่างสีขาว


285

ฉันยังใหม่กับ Python และ Matplotlib ฉันต้องการใช้ colormap กับภาพและเขียนภาพผลลัพธ์โดยไม่ต้องใช้แกนป้ายชื่อเรื่องหรืออะไรก็ตามที่เพิ่มโดยอัตโนมัติโดย matplotlib นี่คือสิ่งที่ฉันทำ:

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)

มันประสบความสำเร็จในการลบแกนของรูป แต่รูปที่บันทึกนั้นแสดงการเติมเต็มสีขาวและกรอบรอบภาพที่แท้จริง ฉันจะลบพวกเขา (อย่างน้อยช่องว่างภายในสีขาว) ได้อย่างไร ขอบคุณ


2
imshowการแก้ปัญหาทั้งหมดที่อยู่ในคำถามนี้จะเพ่งความสนใจไป หากคุณมี Scatterplot แทนคำตอบต่อไปนี้อาจช่วยคุณได้: stackoverflow.com/a/40727744/4124317
ImportanceOfBeingErnest

คำตอบ:


373

ฉันคิดว่าคำสั่งaxis('off')ดูแลปัญหาอย่างใดอย่างหนึ่งอย่างรัดกุมกว่าการเปลี่ยนแต่ละแกนและเส้นขอบแยกกัน มันยังคงออกจากพื้นที่สีขาวรอบ ๆ ชายแดนอย่างไรก็ตาม เพิ่มbbox_inches='tight'ไปยังsavefigคำสั่งเกือบจะทำให้คุณมีคุณสามารถเห็นในตัวอย่างด้านล่างที่ด้านซ้ายพื้นที่สีขาวมีขนาดเล็กมาก แต่ยังคงอยู่

โปรดทราบว่า matplotlib รุ่นที่ใหม่กว่าอาจต้องใช้bbox_inches=0แทนสตริง'tight'(ผ่าน @episodeyang และ @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')

ป้อนคำอธิบายรูปภาพที่นี่


4
ตามคำแนะนำของ unutbu คุณสามารถใช้fig = plt.figure(), แล้วax = plt.axes([0,0,1,1]) plt.imshow(data,interpolation="nearest"เมื่อรวมกับplt.axis("off")มันควรจะกำจัดทุกอย่างที่อยู่ด้านข้างตัวเองหวังว่า!
PhilMacKay

5
การรวมเมธอดจากคำถาม ({fig.axes.get_xaxis (). set_visible (เท็จ) & fig.axes.get_yaxis (). set_visible (เท็จ)} กับ {plt.axis ('ปิด')}) แก้ไขปัญหาสำหรับ ผม. (ไม่มีช่องว่างอีกต่อไป) และอย่าลืมตั้ง {pad_inches} ของคุณใน savefig เป็น 0
Domagoj

3
ฉันเพิ่งพบปัญหานี้ไม่มีวิธีแก้ไขปัญหาใดในเธรดนี้และรายการที่เชื่อมโยงใช้งานได้ อย่างไรก็ตามการระบุอย่างชัดเจน bbox_inches=0ได้หลอกลวง
kadrach

1
โปรดทราบว่าหากคุณกำลัง 'พล็อต' เนื้อหาบน plt.imshow เช่นเดียวกับใน plt.plot (x, y) คุณต้องแน่ใจว่าคุณตั้งค่า xlim และ ylim เป็นขนาดของเมทริกซ์
กรุงเยรูซาเล็ม

1
มันไม่ทำงานอีกต่อไป เอาฉันชั่วโมงคิดออก ดูคำตอบด้านล่าง
episodeyang

107

ฉันได้เรียนรู้เคล็ดลับนี้จากmatehat ที่นี่ :

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')

อัตราผลตอบแทน

ป้อนคำอธิบายรูปภาพที่นี่


3
ฉันค่อนข้างมั่นใจว่าคุณมีคำตอบที่ถูกต้อง (แม้ว่าอาจมีมากกว่าหนึ่งวิธีที่จะทำ) แต่ฉันสงสัยว่าคุณสามารถอธิบายได้หรือไม่ว่าทำไมมันถึงเป็นคำตอบที่ถูกต้อง? รหัสของคุณเกี่ยวกับการลบช่องว่าง?
Yann

4
@Yann นอกเหนือจากเอกสารฉันพบว่ามีประโยชน์มากในการใส่เครื่องหมายความคิดเห็นทีละบรรทัดเพื่อดูว่าแต่ละคำสั่งมีผลอย่างไร มันเป็นวิธีเชิงประจักษ์!
unutbu

4
plt.Axes(fig, [0,0,1,1])บรรทัดที่เอาขอบสีขาวคือ สิ่งนี้บอกให้ matplotlib สร้างชุดแกนที่มุมล่างซ้าย ณ จุดที่อยู่ที่ (0%, 0%) และมีความกว้างและความสูง (100%, 100%)
PhilMacKay

นี่เป็นคำตอบที่ถูกต้องมากกว่าเพราะไม่เพียง แต่ลบพื้นที่สีขาวสำหรับภาพที่บันทึกไว้เท่านั้น แต่ยังรวมถึงภาพบนหน้าจอ
MaxNoe

ขอบคุณนี่เป็นทางออกเดียวที่ใช้ได้กับ Ubuntu :) :)
AlphaGoMK

56

วิธีที่ง่ายที่สุดที่เป็นไปได้:

ฉันเพียงรวมวิธีการที่อธิบายไว้ในคำถามและวิธีการจากคำตอบโดย 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)

หลังจากรหัสนี้ไม่มีช่องว่างและไม่มีเฟรม

ไม่มีช่องว่างแกนหรือกรอบ


3
วิธีการของคุณลบช่องว่างสีขาวรอบภาพนี้ออกไปใช้งานได้ดี แต่ฉันก็ยังไม่รู้ว่าทำไมการเพิ่มคำสั่งfig.axes.get_xaxis().set_visible(False)ในการแก้ไขปัญหา ขอบคุณ
ollydbg23

5
ใช่ถ้าคุณไม่เรียกคำสั่งนี้ช่องว่างส่วนใหญ่จะถูกลบออก อย่างไรก็ตามในกรณีของฉันยังคงมีช่องว่างระหว่างพล็อตของฉันกับขอบของรูปภาพ. png (อาจกว้าง 2px) โดยเรียกคำสั่งต่อไปนี้ฉันได้กำจัดช่องว่างที่ดื้อรั้นเหล่านั้น
Domagoj

3
แต่โปรดทราบว่าข้างต้นจะล้มเหลวในการปรากฏตัวของหลายแกนในรูปเดียวกัน (ในกรณีของฉันภาพที่ฝังอยู่ในพล็อต) วิธีแก้ไข: fig.axes[0]และโดยทั่วไปคือแกนทั้งหมดหรือแกนที่เลือกไว้
Ioannis Filippidis

29

ยังไม่มีใครพูดถึงimsaveซึ่งทำให้สิ่งนี้เป็นแบบหนึ่งเดียว:

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")

มันเก็บภาพโดยตรงตามที่เป็นอยู่คือไม่เพิ่มแกนหรือเส้นขอบ / แพ็ดดิ้ง

ป้อนคำอธิบายรูปภาพที่นี่


นี่คือสิ่งที่ฉันทำเกือบตลอดเวลา แต่มีบางครั้งที่ฉันต้องการแถบสีและในกรณีเหล่านี้ฉันไม่สามารถบันทึกฉันได้
อีเลียส

9

นอกจากนี้คุณยังสามารถระบุขอบเขตของรูปเพื่อbbox_inchesโต้แย้ง นี่จะเป็นการกำจัดแผ่นสีขาวรอบ ๆ ร่าง

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)

8

สิ่งนี้ควรลบการขยายและขอบทั้งหมด:

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)

คำตอบที่ต่ำกว่าเกณฑ์! นี่ทำให้ฉันชอบ 99% ของวิธีการที่นั่น ควรอัปเดตมากกว่าเพราะเป็นอัปเดตล่าสุด
นาธาน

นอกจากนี้ยังทำงานถ้าคุณไม่ได้มีวัตถุแกน ( ax) extent = plt.gca().get_window_extent().transformed(fig.dpi_scale_trans.inverted())ด้วย
ขนมปังปิ้ง

4

ฉันพบว่ามันเป็นเอกสารทั้งหมด ...

https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.axes.Axes.axis.html#matplotlib.axes.Axes.axis

รหัสของฉัน…. "bcK" เป็นภาพขนาด 512x512

plt.figure()
plt.imshow(bck)
plt.axis("off")   # turns off axes
plt.axis("tight")  # gets rid of white border
plt.axis("image")  # square up the image instead of filling the "figure" space
plt.show()

3

คำตอบที่ upvoted ไม่ทำงานอีกต่อไป ในการทำให้มันทำงานคุณต้องเพิ่มแกนที่ตั้งไว้ที่ [0, 0, 1, 1] หรือลบแพตช์ใต้ภาพ

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")

หรือคุณสามารถลบแพทช์ได้ คุณไม่จำเป็นต้องเพิ่มแผนย่อยเพื่อที่จะลบการแพ็ด สิ่งนี้ง่ายกว่าจากคำตอบของ 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')

นี่คือการทดสอบกับรุ่น3.0.3ที่ 2019/06/19 ภาพดูตะโกน:

ป้อนคำอธิบายรูปภาพที่นี่

pyplot.imsaveสิ่งที่ง่ายมากที่จะทำคือการใช้งาน สำหรับรายละเอียดโปรดดูที่คำตอบของผู้ร้อง


สำหรับฉันเฉพาะรุ่น Axes เท่านั้น ฉันได้รับช่องว่างภายในด้วยโปรแกรมแก้ไขปิดเวอร์ชัน ขอบคุณเลย!
save_jeff

2

ฉันชอบคำตอบของอูบุนตูแต่มันก็ไม่ได้แสดงให้เห็นอย่างชัดเจนถึงวิธีการกำหนดขนาดสำหรับภาพที่ไม่ใช่สี่เหลี่ยมจัตุรัสนอกกรอบดังนั้นฉันจึงปรับเปลี่ยนเพื่อให้ง่ายต่อการคัดลอก:

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()

การบันทึกภาพแบบไร้ขอบนั้นง่ายมากไม่ว่าคุณจะเลือก dpi เท่าไรหาก pixel_size / dpi = size ถูกเก็บไว้

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

ป้อนคำอธิบายรูปภาพที่นี่

อย่างไรก็ตามการแสดงนั้นเหมือนผี หากคุณเลือก dpi ขนาดเล็กขนาดภาพของคุณอาจใหญ่กว่าหน้าจอและรับขอบระหว่างการแสดงผล อย่างไรก็ตามสิ่งนี้ไม่ส่งผลต่อการประหยัด

ดังนั้นสำหรับ

save_image_fix_dpi(data, dpi=20)

จอแสดงผลจะมีขอบ (แต่บันทึกงาน): ป้อนคำอธิบายรูปภาพที่นี่


1

ขั้นแรกสำหรับรูปแบบภาพบางรูปแบบ (เช่นTIFF ) คุณสามารถบันทึก colormap ในส่วนหัวและผู้ดูส่วนใหญ่จะแสดงข้อมูลของคุณด้วย colormap

สำหรับการบันทึกภาพจริงmatplotlibซึ่งอาจเป็นประโยชน์ในการเพิ่มคำอธิบายประกอบหรือข้อมูลอื่น ๆ ลงในภาพฉันใช้วิธีแก้ไขปัญหาต่อไปนี้:

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)

0

ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมจากทุกคน ... ฉันมีปัญหาเดียวกันกับที่ต้องการพล็อตแค่ภาพที่ไม่มีช่องว่าง / ช่องว่างอื่น ๆ ดังนั้นฉันมีความสุขมากที่ได้ค้นหาแนวคิดของทุกคนที่นี่

นอกเหนือจากภาพที่ไม่มีช่องว่างภายในฉันยังต้องการเพิ่มหมายเหตุประกอบอื่น ๆ ได้อย่างง่ายดายนอกเหนือจากพล็อตรูปภาพที่เรียบง่าย

ดังนั้นสิ่งที่ฉันทำคือการรวมคำตอบของดาวิดกับcsnemes 'เพื่อสร้างเสื้อคลุมเรียบง่ายในเวลาที่สร้างหุ่น เมื่อคุณใช้สิ่งนั้นคุณไม่ต้องการการเปลี่ยนแปลงในภายหลังด้วย imsave () หรือสิ่งอื่นใด:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.