将图保存到 numpy 数组

2025-02-18 09:24:00
admin
原创
41
摘要:问题描述:在 Python 和 Matplotlib 中,可以轻松地将绘图显示为弹出窗口或将绘图保存为 PNG 文件。我怎样才能将绘图保存为 RGB 格式的 numpy 数组?解决方案 1:当您需要对已保存的图进行像素到像素的比较时,这对于单元测试等来说是一个方便的技巧。一种方法是使用fig.canvas.t...

问题描述:

在 Python 和 Matplotlib 中,可以轻松地将绘图显示为弹出窗口或将绘图保存为 PNG 文件。我怎样才能将绘图保存为 RGB 格式的 numpy 数组?


解决方案 1:

当您需要对已保存的图进行像素到像素的比较时,这对于单元测试等来说是一个方便的技巧。

一种方法是使用fig.canvas.tostring_rgb,然后numpy.fromstring使用适当的 dtype。还有其他方法,但这是我倾向于使用的方法。

例如

import matplotlib.pyplot as plt
import numpy as np

# Make a random plot...
fig = plt.figure()
fig.add_subplot(111)

# If we haven't already shown or saved the plot, then we need to
# draw the figure first...
fig.canvas.draw()

# Now we can save it to a numpy array.
data = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
data = data.reshape(fig.canvas.get_width_height()[::-1] + (3,))

解决方案 2:

@JUN_NETWORKS 的答案有一个更简单的选项。除了将图形保存为png,还可以使用其他格式,例如raw或 ,rgba并跳过cv2解码步骤。

换句话说,实际的 plot 到 numpy 的转换归结为:

io_buf = io.BytesIO()
fig.savefig(io_buf, format='raw', dpi=DPI)
io_buf.seek(0)
img_arr = np.reshape(np.frombuffer(io_buf.getvalue(), dtype=np.uint8),
                     newshape=(int(fig.bbox.bounds[3]), int(fig.bbox.bounds[2]), -1))
io_buf.close()

希望这会有所帮助。

解决方案 3:

有些人提出了这样的方法

np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')

当然,此代码有效。但是,输出的 numpy 数组图像分辨率太低。

我的提案代码是这样的。

import io
import cv2
import numpy as np
import matplotlib.pyplot as plt

# plot sin wave
fig = plt.figure()
ax = fig.add_subplot(111)

x = np.linspace(-np.pi, np.pi)

ax.set_xlim(-np.pi, np.pi)
ax.set_xlabel("x")
ax.set_ylabel("y")

ax.plot(x, np.sin(x), label="sin")

ax.legend()
ax.set_title("sin(x)")


# define a function which returns an image as numpy array from figure
def get_img_from_fig(fig, dpi=180):
    buf = io.BytesIO()
    fig.savefig(buf, format="png", dpi=dpi)
    buf.seek(0)
    img_arr = np.frombuffer(buf.getvalue(), dtype=np.uint8)
    buf.close()
    img = cv2.imdecode(img_arr, 1)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    return img

# you can get a high-resolution image as numpy array!!
plot_img_np = get_img_from_fig(fig)

此代码运行良好。

如果在 dpi 参数上设置较大的数字,则可以将高分辨率图像作为 numpy 数组获取。

解决方案 4:

是时候对您的解决方案进行基准测试了。

import io
import matplotlib
matplotlib.use('agg')  # turn off interactive backend
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()
ax.plot(range(10))


def plot1():
    fig.canvas.draw()
    data = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
    w, h = fig.canvas.get_width_height()
    im = data.reshape((int(h), int(w), -1))


def plot2():
    with io.BytesIO() as buff:
        fig.savefig(buff, format='png')
        buff.seek(0)
        im = plt.imread(buff)


def plot3():
    with io.BytesIO() as buff:
        fig.savefig(buff, format='raw')
        buff.seek(0)
        data = np.frombuffer(buff.getvalue(), dtype=np.uint8)
    w, h = fig.canvas.get_width_height()
    im = data.reshape((int(h), int(w), -1))
>>> %timeit plot1()
34 ms ± 4.16 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit plot2()
50.2 ms ± 234 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> %timeit plot3()
16.4 ms ± 36 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

在这种情况下,IO 原始缓冲区是将 matplotlib 图形转换为 numpy 数组的最快方法。

补充说明:

  • 如果您无权访问该图,您可以随时从轴中提取它:

fig = ax.figure

  • 如果你需要格式中的数组channel x height x width,请执行

im = im.transpose((2, 0, 1))

解决方案 5:

如果有人想要一个即插即用的解决方案,而不需要修改任何先前的代码(获取对 pyplot 图的引用和所有内容),下面的方法对我有用。只需在所有pyplot语句之后(即之前)添加这个即可pyplot.show()

canvas = pyplot.gca().figure.canvas
canvas.draw()
data = numpy.frombuffer(canvas.tostring_rgb(), dtype=numpy.uint8)
image = data.reshape(canvas.get_width_height()[::-1] + (3,))

解决方案 6:

MoviePy使将图形转换为 numpy 数组变得非常简单。它有一个内置函数,称为mplfig_to_npimage()。您可以像这样使用它:

from moviepy.video.io.bindings import mplfig_to_npimage
import matplotlib.pyplot as plt

fig = plt.figure()  # make a figure
numpy_fig = mplfig_to_npimage(fig)  # convert it to a numpy array

解决方案 7:

正如 Joe Kington 指出的那样,一种方法是在画布上绘图,将画布转换为字节字符串,然后将其重新塑造为正确的形状。

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

plt.switch_backend('Agg')


def canvas2rgb_array(canvas):
    """Adapted from: https://stackoverflow.com/a/21940031/959926"""
    canvas.draw()
    buf = np.frombuffer(canvas.tostring_rgb(), dtype=np.uint8)
    ncols, nrows = canvas.get_width_height()
    scale = round(math.sqrt(buf.size / 3 / nrows / ncols))
    return buf.reshape(scale * nrows, scale * ncols, 3)


# Make a simple plot to test with
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2 * np.pi * t)
fig, ax = plt.subplots()
ax.plot(t, s)

# Extract the plot as an array
plt_array = canvas2rgb_array(fig.canvas)
print(plt_array.shape)

但是,当canvas.get_width_height()返回显示坐标中的宽度和高度时,有时会出现缩放问题,该答案已解决该问题。

解决方案 8:

Jonan Gueorguiev 的答案的清理版本:

with io.BytesIO() as io_buf:
  fig.savefig(io_buf, format='raw', dpi=dpi)
  image = np.frombuffer(io_buf.getvalue(), np.uint8).reshape(
      int(fig.bbox.bounds[3]), int(fig.bbox.bounds[2]), -1)

解决方案 9:

import numpy as np 
import cv2
import time
import justpyplot as jplt

xs, ys = [], []
while(cv2.waitKey(1) != 27):
    xt = time.perf_counter() - t0
    yx = np.sin(xt)
    xs.append(xt)
    ys.append(yx)
    
    frame = np.full((500,470,3), (255,255,255), dtype=np.uint8)
    
    vals = np.array(ys)

    plotted_in_array = jplt.just_plot(frame, vals,title="sin() from Clock")
    
    cv2.imshow('np array plot', plotted_in_array)

所有 matplotlib 方法的问题是,即使您执行 plt.ioff() 或返回图形,matplotlib 仍可以渲染和显示图,即使您成功了,而它在不同的平台上的行为却不同(因为 matplotlib 会根据操作系统将其委托给后端),但获取绘制的 numpy 数组的性能会受到影响。我测量了所有之前建议的 matplotlib 方法,它花费的时间以毫秒为单位,通常是几十毫秒,有时甚至更多毫秒。

我找不到一个简单的库来做这件事,不得不自己写一个。在完全矢量化的 numpy(不是单个循环)中绘制 numpy 的所有部分,例如散点、连接、轴、网格,包括点的大小和厚度,它在几微秒内完成

https://github.com/bedbad/justpyplot

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1325  
  IPD(Integrated Product Development)流程作为一种先进的产品开发管理模式,在众多企业中得到了广泛应用。它涵盖了从产品概念产生到产品退市的整个生命周期,通过整合跨部门团队、优化流程等方式,显著提升产品开发的效率和质量,进而为项目的成功奠定坚实基础。深入探究IPD流程的五个阶段与项目成功之间...
IPD流程分为几个阶段   4  
  华为作为全球知名的科技企业,其成功背后的管理体系备受关注。IPD(集成产品开发)流程作为华为核心的产品开发管理模式,其中的创新管理与实践更是蕴含着丰富的经验和深刻的智慧,对众多企业具有重要的借鉴意义。IPD流程的核心架构IPD流程旨在打破部门墙,实现跨部门的高效协作,将产品开发视为一个整体的流程。它涵盖了从市场需求分析...
华为IPD是什么   3  
  IPD(Integrated Product Development)研发管理体系作为一种先进的产品开发模式,在众多企业的发展历程中发挥了至关重要的作用。它不仅仅是一套流程,更是一种理念,一种能够全方位提升企业竞争力,推动企业持续发展的有效工具。深入探究IPD研发管理体系如何助力企业持续发展,对于众多渴望在市场中立足并...
IPD管理流程   3  
  IPD(Integrated Product Development)流程管理旨在通过整合产品开发流程、团队和资源,实现产品的快速、高质量交付。在这一过程中,有效降低成本是企业提升竞争力的关键。通过优化IPD流程管理中的各个环节,可以在不牺牲产品质量和性能的前提下,实现成本的显著降低,为企业创造更大的价值。优化产品规划...
IPD流程分为几个阶段   4  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用