当前位置: 首页 > 面试题库 >

将数以千计的图像读取到一个大的numpy数组中的最快方法

申博厚
2023-03-14
问题内容

我试图找到最快的方法将一堆图像从目录读取到numpy数组中。我的最终目标是计算所有这些图像中像素的最大,最小和第n个百分点之类的统计信息。当所有图像中的像素位于一个大的numpy数组中时,这是直接且快速的,因为我可以使用诸如.max和的内置数组方法.min以及np.percentile函数。

以下是一些带有25张tiff图像(512x512像素)的时序示例。这些基准来自%%timit一本jupyter笔记本中的使用。差异太小,不足以对25张图像产生任何实际影响,但我打算将来阅读数千张图像。

# Imports
import os
import skimage.io as io
import numpy as np
  1. 附加到列表

    %%timeit
    

    imgs = []
    img_path = ‘/path/to/imgs/’
    for img in os.listdir(img_path):
    imgs.append(io.imread(os.path.join(img_path, img)))

    32.2 ms ± 355 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

  2. 使用字典

    %%timeit
    

    imgs = {}
    img_path = ‘/path/to/imgs/’
    for img in os.listdir(img_path):
    imgs[num] = io.imread(os.path.join(img_path, img))

    33.3 ms ± 402 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

对于上面的列表和字典方法,我尝试用相应的理解替换循环,并在时间上获得相似的结果。我还尝试了预分配字典键,但所花费的时间没有明显差异。为了将图像从列表传递到一个大数组,我将使用np.concatenate(imgs),它只需要大约1毫秒。

  1. 沿第一个维度预分配一个numpy数组

    %%timeit
    

    imgs = np.ndarray((51225,512), dtype=’uint16’)
    img_path = ‘/path/to/imgs/’
    for num, img in enumerate(os.listdir(img_path)):
    imgs[num
    512:(num+1)*512, :] = io.imread(os.path.join(img_path, img))

    33.5 ms ± 804 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

  2. 沿第三维预分配一个numpy

    %%timeit
    

    imgs = np.ndarray((512,512,25), dtype=’uint16’)
    img_path = ‘/path/to/imgs/’
    for num, img in enumerate(os.listdir(img_path)):
    imgs[:, :, num] = io.imread(os.path.join(img_path, img))

    71.2 ms ± 2.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

我最初认为numpy的预分配方法会更快,因为循环中没有动态变量扩展,但是事实并非如此。我发现最直观的方法是最后一个,其中每个图像沿数组的第三个轴占据一个单独的维度,但这也是最慢的。所花费的额外时间不是由于预分配本身而引起的,预分配本身仅花费〜1
ms。

我对此有三个问题:

  1. 为什么Numpy预分配方法不比字典和列表解决方案快?
  2. 将数千个图像读入一个大的numpy数组中,最快的方法是什么?
  3. 我可以从numpy和scikit-image之外查看,以获得更快的读取图像的模块吗?我试过了plt.imread(),但是scikit-image.io模块更快。

问题答案:

A部分:访问和分配NumPy数组

按照NumPy数组以行优先顺序存储元素的方式,每次迭代沿最后一个轴存储这些元素时,您做的正确。这些将占用连续的内存位置,因此对于访问和分配值将是最有效的。因此,像注释中提到的那样,初始化类似np.ndarray((512*25,512), dtype='uint16')np.ndarray((25,512,512), dtype='uint16')最好的初始化。

将它们编译为用于测试时序并以随机数组(而不是图像)进行馈入的函子后,

N = 512
n = 25
a = np.random.randint(0,255,(N,N))

def app1():
    imgs = np.empty((N,N,n), dtype='uint16')
    for i in range(n):
        imgs[:,:,i] = a
        # Storing along the first two axes
    return imgs

def app2():
    imgs = np.empty((N*n,N), dtype='uint16')
    for num in range(n):    
        imgs[num*N:(num+1)*N, :] = a
        # Storing along the last axis
    return imgs

def app3():
    imgs = np.empty((n,N,N), dtype='uint16')
    for num in range(n):    
        imgs[num,:,:] = a
        # Storing along the last two axes
    return imgs

def app4():
    imgs = np.empty((N,n,N), dtype='uint16')
    for num in range(n):    
        imgs[:,num,:] = a
        # Storing along the first and last axes
    return imgs

时间-

In [45]: %timeit app1()
    ...: %timeit app2()
    ...: %timeit app3()
    ...: %timeit app4()
    ...: 
10 loops, best of 3: 28.2 ms per loop
100 loops, best of 3: 2.04 ms per loop
100 loops, best of 3: 2.02 ms per loop
100 loops, best of 3: 2.36 ms per loop

这些定时确认在一开始提出的表演理论,虽然我预期的最后一个设置时序有定时的药粥之间app3app1,但也许从最后要到第一线,用于访问和分配的影响不是线性的。关于这一方面的更多研究可能会很有趣(在此处跟踪问题)。

为了示意性地说明,考虑我们正在存储图像数组,分别由x(图像1)和o(图像2)表示,我们将有:

应用1:

[[[x 0]
  [x 0]
  [x 0]
  [x 0]
  [x 0]]

 [[x 0]
  [x 0]
  [x 0]
  [x 0]
  [x 0]]

 [[x 0]
  [x 0]
  [x 0]
  [x 0]
  [x 0]]]

因此,在内存空间中,将是:[x,o,x,o,x,o..]遵循行优先顺序。

App2:

[[x x x x x]
 [x x x x x]
 [x x x x x]
 [o o o o o]
 [o o o o o]
 [o o o o o]]

因此,在内存空间中,它将为:[x,x,x,x,x,x...o,o,o,o,o..]

App3:

[[[x x x x x]
  [x x x x x]
  [x x x x x]]

 [[o o o o o]
  [o o o o o]
  [o o o o o]]]

因此,在存储空间上,它将与前一个相同。

B部分:从磁盘读取图像作为数组

现在,关于读取图像的部分,我已经看到OpenCV的imread速度要快得多。

作为测试,我从Wiki页面下载了Mona Lisa的图像,并测试了图像读取的性能-

import cv2 # OpenCV

In [521]: %timeit io.imread('monalisa.jpg')
100 loops, best of 3: 3.24 ms per loop

In [522]: %timeit cv2.imread('monalisa.jpg')
100 loops, best of 3: 2.54 ms per loop


 类似资料:
  • 问题内容: 要求: 我需要从数据中任意增加一个数组。 我可以猜测大小(大约100-200),但不能保证每次都适合该数组 一旦增长到最终大小,我就需要对其进行数值计算,因此我更希望最终使用二维numpy数组。 速度至关重要。例如,对于300个文件之一,update()方法被称为4500万次(大约需要150秒),而finalize()方法被称为500k次(总共需要106s)……总共需要250s或者。

  • 问题内容: 我知道我可以像下面这样: 但是,由于它做了完整的排序,所以它非常慢。 我想知道numpy是否提供一些可以快速完成的方法。 问题答案: 该模块具有一种快速的局部排序方法,可直接与Numpy数组配合使用:。 请注意,返回的是已排序的实际值,如果要使用已排序的值的索引(返回值),则应使用。 我已经进行了基准测试: 其中是一个随机的1,000,000个元素的数组。 时间安排如下: :每个循环2

  • 问题内容: 我正在尝试从Matplotlib图中获取一个numpy数组图像,目前正在通过保存到文件中,然后再读回文件的方式来完成此操作,但是我觉得必须有一种更好的方法。这是我现在正在做的事情: 我尝试了这个: 从我发现的示例中发现,但是它给我一个错误,指出“ FigureCanvasAgg”对象没有属性“ renderer”。 问题答案: 为了获得图形内容作为RGB像素值,需要首先绘制画布的内容。

  • 问题内容: 假设我有数组和围棋。什么是追加的所有值最快的方式来? 问题答案: Go中的数组是次要的,而 切片 则是方法。Go提供了一个内置功能来附加切片: 输出: 在Go Playground上尝试一下。 笔记: Go中的数组是固定大小的:创建数组后,就无法增加其大小,因此无法向其添加元素。如果需要,您将需要分配一个更大的新数组。大到足以容纳2个数组中的所有元素。切片更加灵活。 Go中的数组是如此

  • 问题内容: 我在AI项目上使用Redis。 这个想法是让多个环境模拟器在许多cpu内核上运行策略。模拟器将体验(状态/操作/奖励元组列表)写入Redis服务器(重播缓冲区)。然后,培训过程将经验作为数据集读取以生成新策略。将新策略部署到模拟器,删除先前运行的数据,然后继续该过程。 大部分经验都记录在“状态”中。通常将其表示为尺寸为80 x 80的大型numpy数组。模拟器会以cpu允许的最快速度生

  • 本文向大家介绍Numpy数组的保存与读取方法,包括了Numpy数组的保存与读取方法的使用技巧和注意事项,需要的朋友参考一下 1. 数组以二进制格式保存 np.save和np.load是读写磁盘数组数据的两个主要函数。默认情况下,数组以未压缩的原始二进制格式保存在扩展名为npy的文件中,以数组a为例 利用这种方法,保存文件的后缀名字一定会被置为.npy 2. 存取文本文件 使用 np.savetxt