Menentukan dan menyimpan angka dengan ukuran tepat dalam piksel

152

Katakanlah saya memiliki gambar ukuran 3841 x 7195 piksel. Saya ingin menyimpan konten gambar ke disk, menghasilkan gambar dengan ukuran tepat yang saya tentukan dalam piksel.

Tanpa sumbu, tanpa judul. Hanya gambar. Saya pribadi tidak peduli dengan DPI, karena saya hanya ingin menentukan ukuran gambar di layar dalam disk dalam piksel .

Saya telah membaca utas lainnya , dan mereka semua tampaknya melakukan konversi ke inci dan kemudian menentukan dimensi gambar dalam inci dan menyesuaikan dpi dalam beberapa cara. Saya ingin menghindari berurusan dengan kemungkinan hilangnya keakuratan yang dapat dihasilkan dari konversi piksel ke inci.

Saya sudah mencoba:

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)

tanpa keberuntungan (Python mengeluh bahwa lebar dan tinggi masing-masing harus di bawah 32.768 (?))

Dari semua yang saya lihat, matplotlibmembutuhkan ukuran gambar yang ditentukan inchesdan dpi, tetapi saya hanya tertarik pada piksel yang diambil oleh gambar. Bagaimana saya bisa melakukan ini?

Untuk memperjelas: Saya mencari cara untuk melakukan ini dengan matplotlib, dan tidak dengan perpustakaan hemat gambar lainnya.

Amelio Vazquez-Reina
sumber
Dengan matplotlib, tidak mungkin untuk mengatur ukuran gambar langsung dalam inci.
tiago

Jawaban:

175

Matplotlib tidak bekerja dengan piksel secara langsung, melainkan ukuran fisik dan DPI. Jika Anda ingin menampilkan gambar dengan ukuran piksel tertentu, Anda perlu mengetahui DPI monitor Anda. Misalnya tautan ini akan mendeteksi itu untuk Anda.

Jika Anda memiliki gambar berukuran 3841x7195 piksel, kemungkinan Anda tidak akan memantau sebesar itu, sehingga Anda tidak akan dapat menampilkan gambar sebesar itu (matplotlib mengharuskan gambar tersebut agar pas di layar, jika Anda meminta ukurannya. terlalu besar akan menyusut ke ukuran layar). Bayangkan Anda menginginkan gambar 800x800 piksel hanya sebagai contoh. Berikut cara menampilkan gambar 800x800 piksel di monitor saya ( my_dpi=96):

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

Jadi pada dasarnya Anda hanya membagi dimensi dalam inci dengan DPI Anda.

Jika Anda ingin menyimpan angka dengan ukuran tertentu, maka itu adalah masalah yang berbeda. DPI layar tidak begitu penting lagi (kecuali jika Anda meminta angka yang tidak cocok di layar). Menggunakan contoh yang sama dari angka 800x800 piksel, kita dapat menyimpannya dalam resolusi yang berbeda menggunakan dpikata kunci dari savefig. Untuk menyimpannya dalam resolusi yang sama dengan layar gunakan dpi yang sama:

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

Untuk menyimpannya sebagai gambar 8000x8000 piksel, gunakan dpi 10 kali lebih besar:

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

Perhatikan bahwa pengaturan DPI tidak didukung oleh semua backend. Di sini, backend PNG digunakan, tetapi backend pdf dan ps akan menerapkan ukuran berbeda. Juga, mengubah DPI dan ukuran juga akan mempengaruhi hal-hal seperti ukuran font. DPI yang lebih besar akan menjaga ukuran relatif font dan elemen yang sama, tetapi jika Anda menginginkan font yang lebih kecil untuk gambar yang lebih besar, Anda perlu meningkatkan ukuran fisik alih-alih DPI.

Kembali ke contoh Anda, jika Anda ingin menyimpan gambar dengan 3841 x 7195 piksel, Anda dapat melakukan hal berikut:

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

Perhatikan bahwa saya menggunakan angka dpi 100 agar muat di sebagian besar layar, tetapi disimpan dengan dpi=1000untuk mencapai resolusi yang diperlukan. Dalam sistem saya ini menghasilkan png dengan 3840x7190 piksel - tampaknya DPI yang disimpan selalu lebih kecil 0,02 piksel / inci dari nilai yang dipilih, yang akan memiliki efek (kecil) pada ukuran gambar besar. Lebih banyak diskusi tentang ini di sini .

tiago
sumber
5
Sangat mudah untuk mengingat bahwa ukuran monitor (dan karenanya ukuran standar browser dan jendela ui) biasanya dalam hal 96 dpi - kelipatan dari 96. Tiba-tiba angka seperti 1440 piksel bermakna (15 inci) ketika dipikirkan seperti ini.
Danny Staple
Tidak bisa mendapatkan ini untuk bekerja lewat figsizeuntuk plt.figure. Solusinya adalah melakukan seperti yang disarankan jawaban lain dan setelah memanggilnya tanpa figsize , maka panggilanfig.set_size_inches(w,h)
Trinidad
Documents untuk figuredan savefig.
tangani
Tautan tidak menunjukkan nilai yang benar untuk Layar Apple Thunderbolt.
Dmitry
Saya suka solusi ini, tetapi saya punya satu peringatan. The ukuran teks skala terbalik sebagai dpi. (Sistem saya adalah MacBook Pro, OS X), jadi untuk pencetakan interaktif membuat dpi menjadi besar (seperti 10 * my_dpi) menyusutkan teks menjadi hampir tidak terlihat.
Prof Huster
16

Ini bekerja untuk saya, berdasarkan kode Anda, menghasilkan gambar png 93Mb dengan noise warna dan dimensi yang diinginkan:

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)

Saya menggunakan versi PIP terakhir dari pustaka Python 2.7 di Linux Mint 13.

Semoga itu bisa membantu!

heltonbiker
sumber
4
Menetapkan dpi yang sangat rendah akan berarti bahwa font akan sulit terlihat, kecuali ukuran font yang sangat besar digunakan secara eksplisit.
tiago
1
Mungkin lebih baik untuk mengatur dpi yang lebih tinggi dan membagi ukuran inci Anda (yang sewenang-wenang) oleh dpi itu. Selain itu, pengaturan Anda memang menghasilkan piksel yang tepat untuk reproduksi piksel, terima kasih!
FrenchKheldar
Saya mencoba menggunakan ini sebelum menyimpan gambar dengan elemen plot di atasnya (lingkaran, garis, ...). Ini mengganggu linewidth, sehingga elemen-elemennya hampir tidak terlihat.
Gauthier
5

Berdasarkan respons yang diterima oleh tiago, berikut adalah fungsi generik kecil yang mengekspor array numpy ke gambar yang memiliki resolusi yang sama dengan array:

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

Seperti yang dikatakan dalam balasan sebelumnya oleh tiago, layar DPI perlu ditemukan terlebih dahulu, yang dapat dilakukan di sini misalnya: http://dpi.lv

Saya telah menambahkan argumen tambahan resize_factdalam fungsi yang Anda dapat mengekspor gambar ke 50% (0,5) dari resolusi asli, misalnya.

Cyril
sumber
-1

plt.imsave bekerja untuk saya. Anda dapat menemukan dokumentasinya di sini: 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
sumber
Harap tambahkan kode sampel yang menunjukkan dengan tepat parameter yang Anda tetapkan dan nilai yang direkomendasikan untuk kasus penggunaan pos asli.
Sarah Messer