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

2 投票
4 回答
1942 浏览
提问于 2025-04-28 02:46

这个问题在标题里已经说得很清楚了。

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

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

目前我在处理这些数据时需要进行三次操作,具体如下:

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

因为这个数组比较大,所以这会花费我几分之一秒的时间。
如果我能在处理数据时只进行一到两次操作,那肯定会更快。

有没有什么方法可以让我进行一种“复合”向量操作,从而减少操作的次数呢?
或者,有没有其他方法可以让我加快这个过程?

暂无标签

4 个回答

0

我使用 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

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

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],
)
2

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

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

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

5

我做了:

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

看起来更快 :)

我测了一下时间,结果在我的系统上大约快了两倍。看起来 ar = ar - 255/2. 这个操作在执行减法的同时还进行了类型转换。而且,和一个数进行除法的操作似乎没有优化:一次性进行除法,然后对数组进行多次乘法会更快。不过,额外的浮点运算可能会增加舍入误差。

正如评论中提到的,numexpr 可能是一个既快又简单的解决方案。在我的系统上,它的速度又快了两倍,主要是因为 numexpr 可以使用多个核心,而不仅仅是因为它对数组只进行了一次遍历。代码:

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

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

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

我很好奇这个方案是否还能进一步优化。

撰写回答