Вказання та збереження фігури з точним розміром у пікселях


152

Скажіть, у мене є зображення розміром 3841 x 7195 пікселів. Я хотів би зберегти вміст фігури на диску, в результаті чого зображення має точний розмір, який я вказую в пікселях.

Ні осі, ні заголовків. Просто зображення. Мене особисто не цікавить DPI, тому що я хочу лише вказати розмір зображення на екрані в диску в пікселях .

Я прочитав інші теми , і всі вони, здається, роблять перетворення на дюйми, а потім визначають розміри фігури в дюймах і певним чином коригують dpi. Я хотів би не стикатися з можливою втратою точності, яка може бути наслідком перетворень пікселів на дюйми.

Я спробував:

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)

без удачі (Python скаржиться, що ширина і висота повинні бути нижче 32768 (?))

З усього, що я бачив, matplotlibпотрібно вказувати розмір фігури inchesі dpi, але мене цікавлять лише пікселі, які фігура займає на диску. Як я можу це зробити?

Для уточнення: я шукаю спосіб зробити це за допомогою matplotlib, а не з іншими бібліотеками, що зберігають зображення.


За допомогою matplotlib неможливо встановити розмір фігури безпосередньо в дюймах.
tiago

Відповіді:


175

Matplotlib не працює безпосередньо з пікселями, а лише з фізичними розмірами та DPI. Якщо ви хочете відобразити фігуру з певним розміром пікселя, вам потрібно знати DPI вашого монітора. Наприклад, це посилання виявить це для вас.

Якщо у вас є зображення розміром 3841x7195 пікселів, навряд чи монітор буде настільки великим, тому ви не зможете показати фігуру такого розміру (matplotlib вимагає, щоб цифра вміщувалася на екрані, якщо ви запитаєте розмір занадто великий він зменшиться до розміру екрана). Давайте уявимо, що ви хочете зображення 800х800 пікселів лише для прикладу. Ось як показати зображення 800x800 пікселів на моніторі ( my_dpi=96):

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

Таким чином, ви в основному просто розділяєте розміри в дюймах за допомогою свого DPI.

Якщо ви хочете зберегти фігуру конкретного розміру, то це вже інша справа. Екранні DPI вже не такі важливі (якщо тільки ви не попросите фігуру, яка не вміститься на екрані). Використовуючи той самий приклад фігури розміром 800x800 пікселів, ми можемо зберегти її в різних дозволах, використовуючи dpiключове слово savefig. Щоб зберегти його в тій же роздільній здатності, що і екран, використовуйте той самий dpi:

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

Щоб зберегти його як зображення 8000x8000 пікселів, використовуйте dpi в 10 разів більше:

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

Зауважте, що налаштування DPI підтримується не всіма пакетами. Тут використовується резервний файл PNG, але розміри файлів pdf та ps реалізують розмір по-різному. Також зміна DPI та розмірів також вплине на такі речі, як розмір шрифту. Більш великий DPI зберігатиме однакові розміри шрифтів та елементів, але якщо ви хочете менших шрифтів для більшої фігури, вам потрібно збільшити фізичний розмір замість DPI.

Повертаючись до свого прикладу, якщо ви хочете зберегти зображення з розміром 3841 x 7195 пікселів, ви можете зробити наступне:

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

Зауважте, що я використав цифру dpi 100 для розміщення на більшості екранів, але збережений з, dpi=1000щоб досягти необхідної роздільної здатності. У моїй системі це створює png з 3840x7190 пікселями - схоже, що збережений DPI завжди на 0,02 пікселя / дюйм менший від вибраного значення, що матиме (малий) ефект на великих розмірах зображення. Ще трохи обговорення цього питання тут .


5
Корисно пам'ятати, що розміри моніторів (а отже, стандартні розміри вікон браузера та інтерфейсу користувача) зазвичай складають 96 dpi - кратні 96. Раптом такі цифри, як 1440 пікселів, мають сенс (15 дюймів), коли думають про це.
Danny Staple

Не вдалося цю роботу проходячи figsizeдо plt.figure. Рішення було зробити так, як пропонують інші відповіді, а після зателефонувавши без нього figsize, а потім зателефонуватиfig.set_size_inches(w,h)
Тринідад

Документи для figureта savefig.
обробляти

Посилання не показує правильне значення для Apple Thunderbolt Display.
Дмитро

Мені подобається це рішення, але я маю одне попередження. Розмір тексту масштабується обернено, як dpi. (Моя система MacBook Pro, OS X), тому для інтерактивної друку роблячи велику точку dpi (наприклад, 10 * my_dpi) скорочення тексту до майже невидимості.
Проф Хустер

16

Це працювало для мене, грунтуючись на вашому коді, генеруючи PNG-зображення 93 Мбіт із кольоровим шумом та потрібними розмірами:

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)

Я використовую останні версії PIP бібліотек Python 2.7 у Linux Mint 13.

Сподіваюся, що це допомагає!


4
Якщо встановити дуже низький dpi, це означатиме, що шрифти майже не будуть видимі, якщо явно не використовуються дуже великі розміри шрифту.
tiago

1
Напевно, краще встановити більший dpi і розділити розмір дюйма (що все одно довільно) цим dpi. Крім цього, ваша установка створює точний піксель для відтворення пікселів, дякую!
ФранцузькийХельдар

Я намагаюся використовувати це перед тим, як зберегти зображення з елементами сюжету на ньому (кола, лінії, ...). Це порушує ширину лінії, так що елементи ледь видні.
Готьє

5

Виходячи з прийнятої відповіді від tiago, ось невелика загальна функція, яка експортує масивний масив до зображення, що має таке ж дозвіл, як і масив:

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, спочатку слід знайти екран DPI, що можна зробити тут, наприклад: http://dpi.lv

Я додав додатковий аргумент resize_factу функцію, за допомогою якої ви можете експортувати зображення, наприклад, до 50% (0,5) від початкової роздільної здатності.


2

ОП хоче зберегти дані пікселів 1: 1. Як астроном, який працює з науковими образами, я не можу дозволити будь-яку інтерполяцію даних зображень, оскільки це спричинило б невідомий та непередбачуваний шум чи помилки. Наприклад, ось фрагмент зображення розміром 480x480, збереженого за допомогою pyplot.savefig (): Деталі пікселів, які matplotlib повторно виділено, становитимуть приблизно 2х2, але помічайте стовпчик 1х2 пікселів

Ви можете бачити, що більшість пікселів були просто подвоєні (тому піксель 1x1 стає 2x2), але деякі стовпці та рядки стали 1x2 або 2x1 на піксель, що означає, що вихідні наукові дані були змінені.

Як натякає Alka, plt.imsave (), який дозволить досягти того, про що вимагає ОП. Скажімо, у вас є дані зображення, що зберігаються в масиві зображень im, тоді можна зробити щось на кшталт

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

де ім'я файлу має розширення "png" у цьому прикладі (але ви все одно повинні вказати формат з format = 'png ", наскільки я можу сказати), масив зображень arr, і ми вибрали перевернуту шкалу сірого" greyr " як кольорова карта. Я зазвичай додаю vmin та vmax, щоб вказати динамічний діапазон, але вони необов'язкові.

Кінцевим результатом є файл png з точно такими ж розмірами пікселів, що і масив im.

Примітка: в ОП не вказано осей тощо. Це саме таке рішення. Якщо ви хочете додати осі, галочки тощо, моїм переважним підходом є зробити це на окремому сюжеті, економлячи прозорі = Правда (PNG або PDF), а потім накладати останні на зображення. Це гарантує, що ви зберегли оригінальні пікселі неушкодженими.


Дякую. Минуло декілька років, як я запитав це питання, але з того, що я пам’ятаю і бачу у вашій відповіді, це спрацювало б добре, коли ви намагаєтесь зберегти масив даних як зображення, але що, якщо ви хочете зберегти, це фактичне сама фігура (незалежно від її вмісту) і все ще точно контролює розміри пікселів отриманого файлу зображення?
Амеліо Васкес-Рейна

-1

plt.imsave працював на мене. Документацію ви можете знайти тут: 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)

Будь ласка, додайте зразок коду, який точно показує, який параметр ви встановлюєте та рекомендовані значення для випадку використання оригінальної публікації.
Сара Мессер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.