使用 NumPy 将 ubyte [0, 255] 数组转换为浮点数组 [-0.5, +0.5] 的最快方法

Posted

技术标签:

【中文标题】使用 NumPy 将 ubyte [0, 255] 数组转换为浮点数组 [-0.5, +0.5] 的最快方法【英文标题】:Fastest way to convert ubyte [0, 255] array to float array [-0.5, +0.5] with NumPy 【发布时间】:2014-12-12 23:23:29 【问题描述】:

问题在标题中,非常简单。

我有一个文件f,我正在从中读取ubyte 数组:

arr = numpy.fromfile(f, '>u1', size * rows * cols).reshape((size, rows, cols))
max_value = 0xFF  # max value of ubyte

目前我正在对数据进行 3 遍重新规范化,如下所示:

arr = images.astype(float)
arr -= max_value / 2.0
arr /= max_value

由于数组有点大,这需要明显的几分之一秒。 如果我能在 1 或 2 次遍历数据中做到这一点,那就太好了,因为我认为这样会更快。

我有什么方法可以执行“复合”向量运算来减少通过次数吗? 或者,我还有其他方法可以加快速度吗?

【问题讨论】:

为什么不arr = (images.astype(float) / max_value) - 0.5 @RicardoCárdenes:你为什么希望它更快? 没想到它会马上更快,只是想知道。无论如何,为此我可能会看看一些额外的模块,如numexpr @RicardoCárdenes:哦,我明白了。好吧,我没有这样做,因为我认为无缘无故分配额外的内存块没有任何好处,而不是就地分配,它只是从 CPU 缓存中逐出数据。这个链接看起来很有趣,谢谢。 @Alex:我使用numpy.fromfile 向您展示的代码阅读了它。是的,每一行需要几百毫秒,除以max_value 是最慢的操作。 【参考方案1】:

我做到了:

ar = ar - 255/2.
ar *= 1./255

似乎更快:)

不,我计时了,它在我的系统上大约快两倍。 ar = ar - 255/2. 似乎在进行减法和类型转换。此外,标量除法似乎没有优化:一次除法然后在数组上进行一堆乘法运算会更快。虽然额外的浮点运算可能会增加舍入误差。

正如 cmets 中所述,numexpr 可能是实现这一目标的真正快速而简单的方法。在我的系统上,它是另一个更快的因素,但主要是由于numexpr 使用多个内核,而不是它只在阵列上进行一次传递的事实。代码:

import numexpr
ar = numexpr.evaluate('(ar - 255.0/2.0) / 255.0')

【讨论】:

+1,但我必须注意它不等同。 assert 244.0 * (1.0 / 255.0) == 244.0 / 255.0 +1 表示 numexpr。如果你不介意依赖,它就是为这种情况而发明的,而且我打赌在性能方面是不可能被击败的。【参考方案2】:

这个查找表可能比重复计算要快一点:

table = numpy.linspace(-0.5, 0.5, 256)
images = numpy.memmap(f, '>u1', 'r', shape=(size, rows, cols))
arr = table[images]

在我的系统上,与您的系统相比,它节省了 10% 到 15% 的时间。

【讨论】:

在我的系统上,它的速度是我的 memmap 解决方案的两倍。【参考方案3】:

我自己找到了一个更好的解决方案(快了大约 25%):

arr = numpy.memmap(f, '>u1', 'r', shape=(size, rows, cols))
arr = arr / float(max_value)
arr -= 0.5

我很好奇它是否可以改进。

【讨论】:

为了公平比较,请务必在计时时清除文件系统缓存,否则第二次运行会比第一次快很多。【参考方案4】:

使用cython.parallel.prange 和下面的代码,大型数组的速度提高了 50%(为一维数组完成,但易于扩展);我想加速取决于 CPU 内核的数量:

pilot.pyx文件:

cimport cython
from cython.parallel import prange
import numpy as np
cimport numpy as np
from numpy cimport float64_t, uint8_t, ndarray

@cython.boundscheck(False)
@cython.wraparound(False)
def norm(np.ndarray[uint8_t, ndim=1] img):
    cdef:
        Py_ssize_t i, n = len(img)
        np.ndarray[float64_t, ndim=1] arr = np.empty(n, dtype='float64')
        float64_t * left = <float64_t *> arr.data
        uint8_t * right = <uint8_t *> img.data

    for i in prange(n, nogil=True):
        left[i] = (right[i] - 127.5) / 255.0

    return arr

setup.py 文件,用上面的代码构建一个 C 扩展模块:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

ext_module = Extension(
    'pilot',
    ['pilot.pyx'],
    extra_compile_args=['-fopenmp'],
    extra_link_args=['-fopenmp'],
)

setup(
    name = 'pilot',
    cmdclass = 'build_ext': build_ext,
    ext_modules = [ext_module],
)

【讨论】:

以上是关于使用 NumPy 将 ubyte [0, 255] 数组转换为浮点数组 [-0.5, +0.5] 的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

使用数组进行 Numpy 过滤

python opencv3 使用numpy访问图像数据

使用条件更改 numpy 数组中的每个值

重新调整 3d numpy 数组中的值

将numpy数组转换为rgb图像

numpy.ndarray 如何标准化?