Chỉ định và lưu một hình với kích thước chính xác bằng pixel


152

Nói rằng tôi có một hình ảnh kích thước 3841 x 7195 pixel. Tôi muốn lưu nội dung của hình vào đĩa, dẫn đến hình ảnh có kích thước chính xác tôi chỉ định bằng pixel.

Không trục, không tiêu đề. Chỉ là hình ảnh. Cá nhân tôi không quan tâm đến DPI, vì tôi chỉ muốn chỉ định kích thước hình ảnh trong màn hình tính bằng pixel .

Tôi đã đọc các chủ đề khác và tất cả chúng dường như thực hiện chuyển đổi sang inch và sau đó chỉ định kích thước của hình theo inch và điều chỉnh dpi theo một cách nào đó. Tôi muốn tránh phải đối phó với khả năng mất độ chính xác có thể xảy ra do chuyển đổi từ pixel sang inch.

Tôi đã thử với:

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)

không có may mắn (Python phàn nàn rằng mỗi chiều rộng và chiều cao phải dưới 32768 (?))

Từ tất cả mọi thứ tôi đã thấy, matplotlibyêu cầu kích thước hình được chỉ định trong inchesdpi, nhưng tôi chỉ quan tâm đến các pixel mà hình ảnh có trong đĩa. Tôi có thể làm cái này như thế nào?

Để làm rõ: Tôi đang tìm cách để làm điều này với matplotlib, chứ không phải với các thư viện lưu ảnh khác.


Với matplotlib, không thể đặt kích thước hình trực tiếp bằng inch.
tiago

Câu trả lời:


175

Matplotlib không hoạt động với pixel trực tiếp mà thay vào đó là kích thước vật lý và DPI. Nếu bạn muốn hiển thị một con số với kích thước pixel nhất định, bạn cần biết DPI của màn hình. Ví dụ liên kết này sẽ phát hiện ra điều đó cho bạn.

Nếu bạn có hình ảnh 3841x7195 pixel thì không chắc là màn hình của bạn có kích thước lớn đến vậy, vì vậy bạn sẽ không thể hiển thị hình có kích thước đó (matplotlib yêu cầu con số phải vừa với màn hình, nếu bạn yêu cầu kích thước quá lớn nó sẽ thu nhỏ kích thước màn hình). Hãy tưởng tượng bạn muốn một hình ảnh 800x800 pixel chỉ là một ví dụ. Đây là cách hiển thị hình ảnh 800x800 pixel trong màn hình của tôi ( my_dpi=96):

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

Vì vậy, về cơ bản bạn chỉ cần chia kích thước theo inch cho DPI của bạn.

Nếu bạn muốn lưu một con số của một kích thước cụ thể, thì đó là một vấn đề khác. DPI màn hình không còn quá quan trọng nữa (trừ khi bạn yêu cầu một con số không phù hợp với màn hình). Sử dụng cùng một ví dụ về con số 800x800 pixel, chúng ta có thể lưu nó ở các độ phân giải khác nhau bằng cách sử dụng dpitừ khóa của savefig. Để lưu nó ở cùng độ phân giải như màn hình, chỉ cần sử dụng cùng một dpi:

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

Để lưu nó dưới dạng hình ảnh 8000x8000 pixel, hãy sử dụng dpi lớn hơn 10 lần:

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

Lưu ý rằng cài đặt DPI không được hỗ trợ bởi tất cả các phụ trợ. Ở đây, phụ trợ PNG được sử dụng, nhưng phụ trợ pdf và ps sẽ thực hiện kích thước khác nhau. Ngoài ra, thay đổi DPI và kích thước cũng sẽ ảnh hưởng đến những thứ như phông chữ. DPI lớn hơn sẽ giữ nguyên kích thước tương đối của phông chữ và thành phần, nhưng nếu bạn muốn phông chữ nhỏ hơn cho hình lớn hơn, bạn cần tăng kích thước vật lý thay vì DPI.

Quay trở lại ví dụ của bạn, nếu bạn muốn lưu hình ảnh với 3841 x 7195 pixel, bạn có thể làm như sau:

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

Lưu ý rằng tôi đã sử dụng con số dpi là 100 để phù hợp với hầu hết các màn hình, nhưng được lưu với dpi=1000để đạt được độ phân giải cần thiết. Trong hệ thống của tôi, điều này tạo ra một png với 3840x7190 pixel - có vẻ như DPI được lưu luôn nhỏ hơn 0,02 pixel / inch so với giá trị được chọn, sẽ có hiệu ứng (nhỏ) trên kích thước hình ảnh lớn. Một số thảo luận thêm về điều này ở đây .


5
Thật thuận tiện để nhớ rằng kích thước màn hình (và do đó là kích thước cửa sổ trình duyệt và ui tiêu chuẩn) thường là theo 96 dpi - bội số của 96. Các số đột ngột như 1440 pixel có ý nghĩa (15 inch) khi nghĩ như thế này.
Daniel Staple

Không thể làm điều này để làm việc chuyển figsizeđến plt.figure. Giải pháp là làm như những câu trả lời khác gợi ý và sau khi gọi mà không có figsize , sau đó gọifig.set_size_inches(w,h)
Trinidad

Tài liệu cho figuresavefig.
xử lý

Liên kết không hiển thị giá trị chính xác cho Apple Thunderbolt Display.
Dmitry

Tôi thích giải pháp này, nhưng tôi có một cảnh báo. Các kích thước văn bản quy mô nghịch như dpi. (Hệ thống của tôi là MacBook Pro, OS X), vì vậy để in tương tác làm cho dpi thành lớn (như 10 * my_dpi) thu nhỏ văn bản đến gần tàng hình.
Giáo sư Huster

16

Điều này làm việc cho tôi, dựa trên mã của bạn, tạo ra hình ảnh png 93Mb với nhiễu màu và kích thước mong muốn:

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)

Tôi đang sử dụng các phiên bản PIP cuối cùng của thư viện Python 2.7 trong Linux Mint 13.

Mong rằng sẽ giúp!


4
Đặt dpi rất thấp sẽ có nghĩa là phông chữ sẽ khó nhìn thấy, trừ khi kích thước phông chữ rất lớn được sử dụng rõ ràng.
tiago

1
Có lẽ tốt hơn để đặt một dpi cao hơn và chia kích thước inch của bạn (dù sao cũng tùy ý) cho dpi đó. Ngoài ra, thiết lập của bạn không tạo ra một pixel chính xác để tái tạo pixel, cảm ơn!
FrenchKainedar

Tôi đang cố gắng sử dụng điều này trước khi lưu hình ảnh với các yếu tố cốt truyện trên đó (vòng tròn, đường kẻ, ...). Điều này làm xáo trộn đường truyền, do đó các yếu tố hầu như không nhìn thấy được.
Gauthier

5

Dựa trên phản hồi được chấp nhận bởi tiago, đây là một hàm chung nhỏ xuất một mảng numpy sang một hình ảnh có cùng độ phân giải như mảng:

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

Như đã nói trong bài trả lời trước của tiago, DPI màn hình cần được tìm thấy trước, có thể được thực hiện ở đây chẳng hạn: http://dpi.lv

Tôi đã thêm một đối số bổ sung resize_facttrong chức năng mà bạn có thể xuất hình ảnh thành 50% (0,5) độ phân giải gốc, chẳng hạn.


-1

plt.imsave làm việc cho tôi. Bạn có thể tìm tài liệu ở đây: 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)

Vui lòng thêm mã mẫu hiển thị chính xác tham số nào bạn đang đặt và các giá trị được đề xuất cho trường hợp sử dụng của bài đăng gốc.
Sarah Messer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.