指定并保存具有精确像素大小的图形

2025-01-10 08:47:00
admin
原创
124
摘要:问题描述:假设我有一张尺寸为 3841 x 7195 像素的图像。我想将图形的内容保存到磁盘,从而得到一张具有我指定的像素大小的图像。没有轴,没有标题。只有图像。我个人并不关心 DPI,因为我只想指定图像在磁盘屏幕上占用的像素大小。我读过其他 帖子,它们似乎都转换为英寸,然后以英寸为单位指定图形尺寸并以某种方...

问题描述:

假设我有一张尺寸为 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而不是使用其他图像保存库来实现此目的的方法。


解决方案 1:

Matplotlib 不直接处理像素,而是处理物理尺寸和 DPI。如果要显示具有特定像素大小的图形,则需要知道显示器的 DPI。例如,此链接将为您检测。

如果您有 3841x7195 像素的图像,您的显示器不太可能那么大,因此您将无法显示该尺寸的图形(matplotlib 要求图形适合屏幕,如果您要求的尺寸太大,它将缩小到屏幕大小)。让我们假设您想要一个 800x800 像素的图像,仅作为示例。以下是如何在我的显示器上显示 800x800 像素图像(my_dpi=96):

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

因此,您基本上只需将像素尺寸除以 DPI 即可。

如果你想保存一个特定尺寸的图形,那就另当别论了。屏幕 DPI 不再那么重要(除非你要求的图形不适合屏幕)。使用相同的 800x800 像素图形示例,我们可以使用关键字 以不同的分辨率保存它dpisavefig要以与屏幕相同的分辨率保存它,只需使用相同的 dpi:

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

要将其保存为 8000x8000 像素的图像,请使用 10 倍大的 dpi:

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 的图形来适应大多数屏幕,但保存时达到所需的分辨率。在我的系统中,这会生成一个 3840x7190 像素的 png——保存的 DPI 似乎总是比选定的值小 0.02 像素/英寸,这会对大图像尺寸产生(微小)影响。这里对此dpi=1000有更多讨论。

解决方案 2:

这对我有用,基于您的代码,生成一个具有颜色噪声和所需尺寸的 93Mb png 图像:

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 中使用 Python 2.7 库的最新 PIP 版本。

希望有帮助!

解决方案 3:

OP 希望保留 1:1 像素数据。作为一名研究科学图像的天文学家,我不能允许对图像数据进行任何插值,因为这会引入未知且不可预测的噪声或错误。例如,这是通过 pyplot.savefig() 保存的 480x480 图像的片段:
matplotlib 重新采样为大约 2x2 的像素细节,但请注意 1x2 像素的列

您可以看到大多数像素只是简单地翻倍了(因此 1x1 像素变成了 2x2),但有些列和行变成了每像素 1x2 或 2x1,这意味着原始科学数据已被改变。

正如 Alka 所暗示的,plt.imsave() 将实现 OP 所要求的。假设您将图像数据存储在图像数组 im 中,那么可以执行以下操作

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

在此示例中,文件名具有“png”扩展名(但据我所知,您仍然必须使用 format='png' 指定格式),图像数组为 arr,我们选择反转灰度“gray_r”作为颜色图。我通常会添加 vmin 和 vmax 来指定动态范围,但这些是可选的。

最终结果是与 im 数组具有完全相同像素尺寸的 png 文件。

注意:OP 没有指定轴等,而此解决方案正是这样做的。如果想要添加轴、刻度等,我首选的方法是在一个单独的图上执行此操作,使用 transparent=True (PNG 或 PDF) 保存,然后将后者叠加在图像上。这可以保证您保持原始像素完好无损。

解决方案 4:

根据 tiago 接受的响应,这里有一个小的通用函数,它将 numpy 数组导出为具有与数组相同分辨率的图像:

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

解决方案 5:

此解决方案适用于 matplotlib 版本 3.0.1、3.0.3 和 3.2.1。

def save_inp_as_output(_img, c_name, dpi=100):
    h, w, _ = _img.shape
    fig, axes = plt.subplots(figsize=(h/dpi, w/dpi))
    fig.subplots_adjust(top=1.0, bottom=0, right=1.0, left=0, hspace=0, wspace=0) 
    axes.imshow(_img)
    axes.axis('off')
    plt.savefig(c_name, dpi=dpi, format='jpeg') 

因为 subplots_adjust 设置使轴填充图形,所以您不需要指定 bbox_inches='tight',因为在这种情况下它实际上会创建空白填充。当您有多个子图时,此解决方案也有效。

解决方案 6:

matplotlib 参考中有关于如何以不同单位设置图形大小的示例。对于像素:

px = 1/plt.rcParams['figure.dpi']  # pixel in inches
plt.subplots(figsize=(600*px, 200*px))
plt.text(0.5, 0.5, '600px x 200px', **text_kwargs)
plt.show()

https://matplotlib.org/stable/gallery/subplots_axes_and_figures/figure_size_units.html#

解决方案 7:

我遇到了同样的问题。我使用 PIL Image 加载图像并转换为 numpy 数组,然后使用 matplotlib 修补矩形。这是一张 jpg 图像,所以我无法从 PIL img.info['dpi'] 获取 dpi,所以接受的解决方案对我来说不起作用。但经过一番调整后,我找到了保存与原始图像大小相同的图形的方法。

我在这里添加以下解决方案,认为它能够帮助遇到与我相同问题的人。

import matplotlib.pyplot as plt
from PIL import Image
import numpy as np

img = Image.open('my_image.jpg') #loading the image
image = np.array(img) #converting it to ndarray
dpi = plt.rcParams['figure.dpi'] #get the default dpi value
fig_size = (img.size[0]/dpi, img.size[1]/dpi) #saving the figure size
fig, ax = plt.subplots(1, figsize=fig_size) #applying figure size
#do whatver you want to do with the figure
fig.tight_layout() #just to be sure
fig.savefig('my_updated_image.jpg') #saving the image

这将以与原始图像相同的分辨率保存图像。

如果您没有使用 jupyter 笔记本,您可以按照以下方式获取 dpi。

figure = plt.figure()
dpi = figure.dpi

解决方案 8:

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)

解决方案 9:

假设您有一个大小为 (w,h) 的 2D 数据,plt.imshow()可以显示其 RGB 图像。现在您想要保存一个尺寸为 (w,h,3) 的图像文件,其中 3 是 RGB 通道。一种方法是显示数据,plt.imshow()然后通过调整 dpi 和其他显示设置来保存结果图形。另一种更直接的方法(在我看来)是自己将数据更改为 RGB 通道,然后使用 PIL 库将其保存为图像。困难的部分可能是将您的数据配置为 RGB 通道。如果您不更改中的vmaxvminnorm参数plt.imshow(),那么基本上plt.imshow()就是将您的数据规范化为颜色图的 RGB。让我们考虑最简单的方法,将您的数据分为数据的最小值和最大值之间的 3 个类(RGB):

# normalizing arr_RGB to have values between 0-255
def normRGB(a):
    mynorm = Normalize(vmin=np.min(a), vmax=np.max(a))
    return mynorm(a)

# channelizing data between 3 equally divided classes
def arrRGB(arr, original_minmax = False): # use original_minmax = True if you prefer original array's minimum and maximum
    # producing RGB variants by slicing intensity of the data into 3 equal sections
    R = np.percentile(arr,100 - 100/3)
    B = np.percentile(arr,100/3)
    arr_R = np.where(arr >= R, arr, 0)
    arr_B = np.where(arr <= B, arr, 0)
    arr_G = np.where((arr > B) & (arr < R), arr, 0)
    if original_minmax == True:
        arr = normRGB(abs(arr))  # absolute values help acknowledging negative values' intensity, exclude them if you mean otherwise
    # # otherwise run below if you prefer RGB channels' minimums and maximums
    elif original_minmax == False:
        arr_R = normRGB(abs(arr_R)) # absolute values help acknowledging negative values' intensity, exclude them if you mean otherwise
        arr_G = normRGB(abs(arr_G))
        arr_B = normRGB(abs(arr_B))
    # stacking RGB into one ndarray
    arr_RGB = np.dstack((arr_R,arr_G,arr_B))

    return arr_RGB

现在,您可以简单地将通道化和规范化函数应用于您的数据(这里是具有非常大大小的随机数据):

import matplotlib.pyplot as plt
from  matplotlib.colors import Normalize
from PIL import Image
import numpy as np

w = 7195
h = 3841

arr = numpy.random.rand(h, w)
arr_RGB = arrRGB(arr)
im = Image.fromarray((arr_RGB * 255).astype(np.uint8))
im.save("arr_RGB.png")   # > 16 MB file size

下面的小截图片段显示了生成的 .png 文件如何将原始数据维度显示为像素大小:

MS Paint 中的小片段截图显示生成的 .png 文件具有初始尺寸

解决方案 10:

这里的共识是,应该使用figsize=(width_px / dpi, height_px / dpi), dpi=dpi创建具有明确定义的大小的图形,并figure.savefig(target, dpi=dpi)生成精确为width_pxx的输出大小。 但是,matplotlib在确定输出大小时height_px似乎使用floor而不是。round

示例:使用width_px = 853和,Matplotlib 产生大小为(不是)dpi = 100.0的输出,可能是因为。852`853`(853 / 100.0) * 100.0 = 852.9999999999999

因此可能需要在像素大小上添加一个小的 epsilon:

figsize=((width_px + 1e-6) / dpi, (height_px + 1e-6) / dpi), dpi=dpi

解决方案 11:

为什么每个人都继续使用 matplotlib?

如果你的图像是一个形状为 (3841, 7195, 3) 的 numpy 数组,其数据类型为 numpy.uint8,rgb 值范围为 0 到 255,你可以简单地将此数组保存为图像,而无需使用 matplotlib:

from PIL import Image
im = Image.fromarray(A)
im.save("your_file.jpeg")

我从另一篇文章中找到了这段代码

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2577  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1553  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。其中,技术评审与决策评审是IPD流程中至关重要的环节,它们既有明显的区别,又存在紧密的协同关系。深入理解这两者的区别与协同,对于企业有效实施IPD流程,提升产品开发效率与质量具有重要意义...
IPD管理流程   26  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、ClickUp、Freshdesk、GanttPRO、Planview、Smartsheet、Asana、Nifty、HubPlanner、Teamwork。在当今快速变化的商业环境中,项目管理软件已成为企业提升效率、优化资源分配和确保项目按时交付的关键工具。然而...
项目管理系统   21  
  建设工程项目质量关乎社会公众的生命财产安全,也影响着企业的声誉和可持续发展。高质量的建设工程不仅能为使用者提供舒适、安全的环境,还能提升城市形象,推动经济的健康发展。在实际的项目操作中,诸多因素会对工程质量产生影响,从规划设计到施工建设,再到后期的验收维护,每一个环节都至关重要。因此,探寻并运用有效的方法来提升建设工程...
工程项目管理制度   18  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用