Tam boyutta bir rakamı piksel cinsinden belirtme ve kaydetme


152

3841 x 7195 piksel boyutunda bir resmim olduğunu varsayalım. Rakamın içeriğini diske kaydetmek istiyorum, bu da piksel cinsinden belirlediğim tam boyutta bir görüntüye neden oluyor .

Eksen yok, başlık yok. Sadece görüntü. Kişisel olarak DPI'ları umursamıyorum, çünkü sadece görüntünün ekranda diskte aldığı boyutu piksel cinsinden belirtmek istiyorum .

Ben diğer konuları okudum ve hepsi inç dönüşümleri yapmak ve daha sonra şekil boyutlarını inç cinsinden belirtmek ve dpi bir şekilde ayarlamak gibi görünüyor. Piksel-inç dönüşümlerinden kaynaklanabilecek olası doğruluk kaybıyla uğraşmaktan kaçınmak istiyorum.

Ben denedim:

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)

şanssız (Python, genişlik ve yüksekliğin her birinin 32768 (?) altında olması gerektiğinden şikayet ediyor)

Gördüğüm her şeyden matplotlib, şekil boyutunun belirtilmesi gerekiyor inchesve dpiancak sadece figürün diskte aldığı piksellerle ilgileniyorum . Bunu nasıl yapabilirim?

Açıklığa kavuşturmak için: Bunu matplotlibdiğer görüntü tasarruflu kütüphanelerle değil, bununla yapmanın bir yolunu arıyorum .


Matplotlib ile şekil boyutunu doğrudan inç olarak ayarlamak mümkün değildir.
tiago

Yanıtlar:


175

Matplotlib doğrudan piksellerle değil, fiziksel boyutlar ve DPI ile çalışır. Belirli bir piksel boyutuna sahip bir şekil görüntülemek istiyorsanız, monitörünüzün DPI'sını bilmeniz gerekir. Örneğin, bu bağlantı bunu sizin için algılar.

3841x7195 piksel bir resminiz varsa, monitörünüzün bu kadar büyük olması olası değildir, bu nedenle bu boyutta bir şekil gösteremezsiniz (matplotlib, boyut istersen, ekrana sığmasını gerektirir) çok büyük ekran boyutuna küçülecektir). Sadece bir örnek olarak 800x800 piksel boyutunda bir görüntü istediğinizi düşünelim. Monitörümde 800x800 piksellik bir görüntüyü nasıl göstereceğim ( my_dpi=96):

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

Yani temel olarak boyutları inç cinsinden DPI'nıza bölebilirsiniz.

Belirli bir boyuttaki bir rakamı kaydetmek istiyorsanız, o zaman farklı bir konudur. Ekran DPI'ları artık o kadar önemli değil (ekrana sığmayan bir şekil istemiyorsanız). Aynı 800x800 piksel rakam örneğini kullanarak, dpianahtar kelimesini kullanarak farklı çözünürlüklerde kaydedebiliriz savefig. Ekranla aynı çözünürlükte kaydetmek için sadece aynı dpi'yi kullanın:

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

8000x8000 piksel görüntü olarak kaydetmek için, 10 kat daha büyük bir dpi kullanın:

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

DPI ayarının tüm arka uçlar tarafından desteklenmediğini unutmayın. Burada PNG arka ucu kullanılır, ancak pdf ve ps arka uçları boyutu farklı şekilde uygular. Ayrıca, DPI ve boyutların değiştirilmesi yazı tipi boyutu gibi şeyleri de etkiler. Daha büyük bir DPI, aynı göreceli boyutta yazı tipi ve öğeyi korur, ancak daha büyük bir şekil için daha küçük yazı tipleri istiyorsanız, DPI yerine fiziksel boyutu artırmanız gerekir.

Örneğinize geri dönersek, 3841 x 7195 piksel boyutundaki bir görüntüyü kaydetmek istiyorsanız, aşağıdakileri yapabilirsiniz:

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

Çoğu ekrana sığması için 100 rakamlı dpi kullandığımı, ancak dpi=1000gerekli çözünürlüğü elde etmek için kaydettiğimi unutmayın . Sistemimde bu, 3840x7190 pikselli bir png üretir - kaydedilen DPI, seçilen görüntüden her zaman 0.02 piksel / inç daha küçüktür, bu da büyük görüntü boyutları üzerinde (küçük) bir etkiye sahip olacaktır. Bunlardan bazıları daha tartışma burada .


5
Monitör boyutlarının (ve dolayısıyla standart tarayıcı ve ui pencere boyutlarının) normalde 96 dpi - 96'nın katları cinsinden olduğunu hatırlamakta fayda vardır. 1440 piksel gibi sayılar bu şekilde düşünüldüğünde anlamlıdır (15 inç).
Danny Staple

Geçen işine bu alınamadı figsizeiçin plt.figure. Çözüm, diğer cevapların önerdiği gibi yapmak ve olmadan çağırdıktan figsizesonrafig.set_size_inches(w,h)
Trinidad

İçin Dokümanlar figureve savefig.
sap

Bağlantı, Apple Thunderbolt Display için doğru değeri göstermiyor.
Dmitry

Bu çözümü seviyorum, ama bir uyarım var. Metin boyutu dpi olarak ters ölçekler. (Sistemim MacBook Pro, OS X), bu nedenle dpi'yi büyük yapmak (10 * my_dpi gibi) etkileşimli yazdırma için metni neredeyse görünmezliğe çeker.
Prof Huster

16

Bu benim için çalıştı, kodunuza dayanarak, renk gürültüsü ve istenen boyutlarla 93 Mb png resim oluşturma:

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)

Linux Mint 13'te Python 2.7 kitaplıklarının son PIP sürümlerini kullanıyorum.

Umarım yardımcı olur!


4
Çok düşük bir dpi ayarlamak, çok büyük font boyutları açıkça kullanılmadıkça fontların neredeyse hiç görünmeyeceği anlamına gelir.
tiago

1
Daha yüksek bir dpi ayarlamak ve inç boyutunuzu (yine de keyfi) o dpi'ye bölmek muhtemelen daha iyidir. Bunun dışında, kurulumunuz piksel üretimi için tam bir piksel üretir, teşekkürler!
FrenchKheldar

Üzerine arsa elemanları olan resimleri (daireler, çizgiler, ...) kaydetmeden önce bunu kullanmaya çalışıyorum. Bu, çizgi genişliğini bozar, böylece elemanlar zar zor görülebilir.
Gauthier

5

Tiago tarafından kabul edilen cevaba dayanarak, dizi ile aynı çözünürlüğe sahip bir görüntüye numpy dizi ihraç eden küçük bir genel fonksiyon:

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

Tiago tarafından verilen önceki yanıtta belirtildiği gibi, ilk önce ekran DPI'sinin bulunması gerekir, örneğin burada yapılabilir: http://dpi.lv

resize_factÖrneğin orijinal çözünürlüğün% 50'sine (0,5) görüntü aktarabileceğiniz ek bir argüman ekledim .


2

OP 1: 1 piksel verilerini korumak istiyor. Bilim görüntüleri ile çalışan bir gökbilimci olarak, görüntü verilerinin bilinmeyen ve öngörülemeyen gürültü veya hatalar getireceği için enterpolasyonuna izin veremem. Örneğin, pyplot.savefig () ile kaydedilen 480x480 görüntüden bir snippet: Matplotlib'in kabaca 2x2 olarak yeniden örneklendiği, ancak 1x2 piksel sütununa dikkat eden piksellerin ayrıntıları

Piksellerin çoğunun iki katına çıktığını görebilirsiniz (bu nedenle 1x1 piksel 2x2 olur), ancak bazı sütunlar ve satırlar piksel başına 1x2 veya 2x1 oldu, bu da orijinal bilim verilerinin değiştiği anlamına gelir.

Alka'nın ima ettiği gibi, OP'nin ne istediğini elde edecek olan plt.imsave (). Resim dizisinde im kayıtlı resim verileriniz olduğunu varsayalım, o zaman biri

plt.imsave(fname='my_image.png', arr=im, cmap='gray_r', format='png')

burada dosya adı bu örnekte "png" uzantısına sahiptir (ancak yine de format = 'png' biçimini yine de söyleyebildiğim kadarıyla belirtmeniz gerekir), resim dizisi arr'dir ve ters gri tonlamayı "gray_r" seçtik renk haritası olarak. Dinamik aralığı belirtmek için genellikle vmin ve vmax eklerim, ancak bunlar isteğe bağlıdır.

Sonuç, im dizisiyle tam olarak aynı piksel boyutlarına sahip bir png dosyasıdır.

Not: OP, bu çözümün tam olarak yaptığı şey hiçbir eksen vb. Belirtmedi. Biri eksen, keneler vb. Eklemek istiyorsa, benim tercih ettiğim yaklaşım bunu ayrı bir arsa üzerinde yapmak, şeffaf = True (PNG veya PDF) ile kaydetmektir, sonra ikincisini görüntünün üzerine bindirir. Bu, orijinal pikselleri olduğu gibi bırakmanızı garanti eder.


Teşekkürler. Bu Q'yu sordumdan bu yana birkaç yıl geçti, ancak cevabınızda hatırladığım ve gördüğüm kadarıyla, bir dizi veriyi görüntü olarak kaydetmeye çalıştığınızda bu iyi çalışır, ancak kaydetmek istiyorsanız gerçek şekil (içeriği ne olursa olsun) ve yine de sonuç görüntü dosyasının piksel boyutlarını tam olarak kontrol ?
Amelio Vazquez-Reina

-1

plt.imsave benim için çalıştı. Belgeleri burada bulabilirsiniz: 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)

Lütfen tam olarak hangi parametreyi ayarladığınızı ve orijinal yayının kullanım durumu için önerilen değerleri gösteren örnek kod ekleyin.
Sarah Messer
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.