Подтвердить что ты не робот

Вычисление с полным разрешением с помощью matplotlib.pyplot, imshow() и savefig()?

У меня есть массив среднего размера (например, 1500x3000), который я хочу построить в масштабе, поскольку это изображение. Однако вертикальные и горизонтальные шкалы очень разные. Для упрощения предположим, что есть один метр/ряд и 10/столбец. Затем график должен создать изображение, которое является c. 1500x30000. Я использую величину kwarg для шкал и аспект = 1, чтобы избежать деформации. Либо используя окна построения (QT4) и imshow(), либо используя savefig(), мне никогда не удалось создать изображение в масштабе и при полном разрешении.

Я рассмотрел многие предлагаемые решения, указанные в здесь, здесь, или здесь и там или там в случае, если это была ошибка. Я изменил свой matplotlibrc и поместил его в ~/.config/matplotlib, чтобы попробовать форсировать мои параметры display/savefig, но безрезультатно. Я также пробовал с pcolormesh(), но безуспешно. Я использую python 2.7 и matplotlib 1.3 из репо Ubuntu 14.04 и QT4Agg в качестве бэкэнд. Я тоже пробовал TkAgg, но он медленный и дает те же результаты. У меня создалось впечатление, что по оси x разрешение правильное, но оно, безусловно, понижается по вертикали. Вот фрагмент кода, который должен имитировать мою проблему.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

R, C = 1500, 3000
DATA = np.random.random((R, C))
DATA[::2, :] *= -1  # make every other line negative
Yi, Xi = 1, 10 # increment
CMP = 'seismic'
ImageFormat ='pdf'
Name = 'Image'


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0
EXTENT = [0, Xi*C, 0 ,Yi*R]
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True)

for i in range(1,4):
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True)
    Fig.suptitle(Name+str(i)+'00DPI')
    ax = Fig.add_subplot(1, 1, 1)
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres')
    ax.set_ylabel('metres')
    Fig.savefig(Name+str(i)+'00DPI.'+ImageFormat,  format = ImageFormat, dpi = Fig.dpi)
plt.close()
В imshow() интерполяция = 'none' или 'ближайшее' или 'bilinear' не изменяет разрешение по какой-либо причине, хотя я думаю, что это должно быть по крайней мере в окне Qt4, если я показываю() вместо savefig(). Обратите внимание, что разрешение одинаково на рисунках, сохраненных в настройках plt.figure(dpi =).

Я не знаю и на пределе своего понимания того, как все работает с этой системой. Любая помощь очень приветствуется.

Спасибо заранее.

4b9b3361

Ответ 1

Запуск вашего примера, все выглядит хорошо в matplotlib после масштабирования: независимо от разрешения, результаты одинаковы, и я вижу один пиксель на единицу оси. Кроме того, при работе с меньшими массивами, файлы PDF (или другие форматы) работают хорошо.

Это мое объяснение: , когда вы устанавливаете фигуру dpi, вы устанавливаете dpi всего изображения (а не только области данных). В моей системе это приводит к тому, что область графика занимает около 20% всей фигуры. Если вы установите 300 dpi и 10 в высоту, вы получите для вертикальной оси данных в общей сложности 300x10x0,2 = 600 пикселей, которых недостаточно, чтобы представить 1500 точек, это объясняет мне, почему вывод должен быть повторно сэмплирован. Примечание. что уменьшение ширины иногда происходит случайно, потому что оно изменяет долю фигуры, занятую графиком данных.

Затем вам нужно увеличить dpi, а также установить интерполяцию = 'none' (не имеет значения, идеально ли настроено разрешение, но имеет значение, если оно достаточно близко). Также вы можете отрегулировать положение и размер сюжета, чтобы взять большую часть фигуры, но вернувшись к оптимальным настройкам разрешения, в идеале вы хотите, чтобы на оси было несколько пикселей, что кратно вашим точкам данных, в противном случае некоторые должна произойти некоторая интерполяция (подумайте, как вы можете построить две точки на трех пикселях или наоборот).

Я не знаю, лучший ли способ для этого: в matplotlib могут быть более подходящие методы и свойства, но я бы попробовал что-то вроде этого для вычисления оптимального dpi:

vsize=ax.get_position().size[1]  #fraction of figure occupied by axes
axesdpi= int((Fig.get_size_inches()[1]*vsize)/R)  #(or Yi*R according to what you want to do)

Затем ваш код (сводится к первому циклу) становится:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

R, C = 1500, 3000
DATA = np.random.random((R, C))
DATA[::2, :] *= -1  # make every other line negative
Yi, Xi = 1, 10 # increment
CMP = 'seismic'
ImageFormat ='pdf'
Name = 'Image'


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0
EXTENT = [0, Xi*C, 0 ,Yi*R]
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True)

for i in (1,):
    print i 
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True)
    Fig.suptitle(Name+str(i)+'00DPI')
    ax = Fig.add_subplot(1, 1, 1)
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres')
    ax.set_ylabel('metres')
    vsize=ax.get_position().size[1]  #fraction of figure occupied by axes
    axesdpi= int((Fig.get_size_inches()[1]*vsize)/R)  #(or Yi*R according to what you want to do)
    Fig.savefig(Name+str(axesdpi)+'DPI.'+ImageFormat,  format = ImageFormat, dpi = axesdpi)
    #plt.close()

Это разумно для меня.

Ответ 2

Во-первых, когда вы сохраняете как .pdf, вы неявно используете бэкэнд pdf, даже если вы можете указать другие бэкэнды в своих опциях. Это означает, что ваше изображение сохраняется в векторном формате, и поэтому dpi не имеет смысла. В любом разрешении, если я загружаю ваш PDF в приличном средстве просмотра (я использовал inkscape, другие доступны), вы можете четко видеть полосы - мне действительно было легче наблюдать, если вы установите каждую вторую строку на ноль. Все созданные PDF файлы содержат полную информацию для воспроизведения полос и, следовательно, практически идентичны. Когда вы укажете figsize=(45, 10), все созданные PDF файлы предложили размер дисплея 45 дюймов x 10 дюймов.

Если я укажу png как тип изображения, я вижу разницу в размере файла на основе параметра dpi, который, я думаю, является тем, что вы ожидаете. Если вы посмотрите на изображение с разрешением 100 точек на дюйм, оно имеет 4500000, то изображение с разрешением 200 точек на дюйм имеет 18000000 пикселей (в 4 раза больше), а изображение с разрешением 300 точек на дюйм - 40500000 (в 9 раз больше). Вы заметите, что 4500000 == 1500 x 3000, т.е. один пиксель на члена вашего исходного массива. Из этого следует, что более крупные настройки dpi не наделяют вас дальнейшим определением - вместо этого ваши полосы имеют ширину 2 или 3 пикселя вместо 1.

Я думаю, что вы хотите сделать, чтобы построить каждый столбец 10 раз, так что вы получите изображение размером 1500 х 30000 пикселей. Чтобы сделать это, используя весь свой собственный код, вы можете использовать np.repeat, чтобы сделать что-то вроде следующего:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

R, C = 1500, 3000
DATA = np.random.random((R, C))
DATA[::2, :] = 0  # make every other line plain white
Yi, Xi = 1, 10 # increment
DATA = np.repeat(DATA, Xi, axis=1)
DATA = np.repeat(DATA, Yi)

CMP = 'seismic'
ImageFormat ='pdf'
Name = 'Image'


DataRange = (np.absolute(DATA)).max() # I want my data centred on 0
EXTENT = [0, Xi*C, 0 ,Yi*R]
NORM = matplotlib.colors.Normalize(vmin =-DataRange, vmax= DataRange, clip =True)

for i in range(1,4):
    Fig=plt.figure(figsize=(45, 10), dpi = 100*i, tight_layout=True)
    Fig.suptitle(Name+str(i)+'00DPI')
    ax = Fig.add_subplot(1, 1, 1)
    Plot = ax.imshow(DATA, cmap=plt.get_cmap(CMP), norm = NORM, extent = EXTENT, aspect = 1, interpolation='none') 
    ax.set_xlabel('metres')
    ax.set_ylabel('metres')
    Fig.savefig(Name+str(i)+'00DPI.'+ImageFormat,  format = ImageFormat, dpi = Fig.dpi)
plt.close()

Предостережение: Это решение с интенсивной памятью - там могут быть лучшие способы. Если вам не нужен вывод векторной графики pdf, вы можете изменить свою переменную ImageFormat на png


Мне кажется, что другая вещь, о которой вы можете подумать, - это дать картине соответствующее соотношение сторон (то есть в 20 раз больше, чем оно высокое). Это вы уже делаете. Итак, если вы посмотрите на каждое представление пикселя в pdf, они прямоугольные (в 10 раз шире, чем они высоки), а не квадратные.