用于图像处理的 cython 嵌套循环

Nested loops with cython for image processing

我正在尝试迭代包含浮点深度数据的 2D 图像,它的分辨率有点正常 (640, 480),但是 python 太慢了,所以我一直尝试使用 cython 优化问题。

我试图将循环移动到其他函数,围绕 nogil 语句移动,似乎没有用,在重新解决问题后,我能够让它的一部分工作。但是这最后一部分是无济于事的。

我试图通过事先将它们移动到 with gil 部分来从 prange() 循环中删除 python 对象,因此:

cdef int[:] w_list = array.array(range(0, w_inc, interpolation))

而不是

for r in range(0, w_inc, interpolation):

但错误仍然存​​在

我的代码分为两部分:

  1. split_data() 方法将图像细分为 num 个象限,这些象限存储在 3D 数组 bits 中。这些用于将工作拆分为多个 thread/processes 更容易。这部分工作正常。
@cython.cdivision(True)
@cython.boundscheck(False)
cpdef split_data(double[:, :] frame, int h, int w, int num):
    cdef double[:, :, :] bits = np.zeros(shape=(num, h // num, w // num), dtype=float)
    cdef int c_count = os.cpu_count()
    cdef int i, j, k

    for i in prange(num, nogil=True, num_threads=c_count):
        for j in prange(h // num):
            for k in prange(w // num):
                bits[i, j, k] = frame[i * (h // num) + j, i * (w // num) + k]

    return bits
  1. scatter_data() 方法从前面的函数中获取 bits 数组,然后创建另一个长度为 num 的 3D 数组,其中 num 是 [=23] 的长度=],称为 points,这是表示有效深度点的一系列 3D 坐标。然后它使用 prange() 从每个 bits 中提取有效的深度数据并将它们存储到 points
@cython.cdivision(True)
@cython.boundscheck(False)
cpdef scatter_data(double[:, :] depths, object validator=None,
                         int h=-1, int w=-1, int interpolation=1):

    # Handles if h or w is -1 (default)
    if h < 0 or w < 0:
        h = depths.shape[0] if h < 0 else h
        w = depths.shape[1] if w < 0 else w

    cdef int max_num = w * h
    cdef int c_count = os.cpu_count()
    cdef int h_inc = h // c_count, w_inc = w // c_count

    cdef double[:, :, :] points = np.zeros(shape=(c_count, max_num, 3), dtype=float)

    cdef double[:, :, :] bits = split_data(depths, h, w, c_count)

    cdef int count = 0
    cdef int i, r, c

    cdef int[:] w_list = array.array(range(0, w_inc, interpolation))
    cdef int[:] h_list = array.array(range(0, h_inc, interpolation))

    for i in prange(c_count, nogil=True, num_threads=c_count):
        count = 0
        for r in w_list:
            for c in h_list:
                if depths[c, r] != 0:
                    points[i, count, 0] = w - r
                    points[i, count, 1] = c
                    points[i, count, 2] = depths[c, r]
                    count = count + 1

    points = points[:count]

    return points

为了完整性 3. 这是我的导入语句

import cython
from cython.parallel import prange
from cpython cimport array
import array
cimport numpy as np
import numpy as np
import os

编译代码时,我不断收到类似以下内容的错误消息:

Error compiling Cython file:
------------------------------------------------------------
...
    cdef int[:] w_list = array.array(range(0, w_inc, interpolation))
    cdef int[:] h_list = array.array(range(0, h_inc, interpolation))

    for i in prange(c_count, nogil=True, num_threads=c_count):
        count = 0
        for r in w_list:
                ^
------------------------------------------------------------

data_util/cy_scatter.pyx:70:17: Iterating over Python object not allowed without gil

Error compiling Cython file:
------------------------------------------------------------
...
    cdef int[:] w_list = array.array(range(0, w_inc, interpolation))
    cdef int[:] h_list = array.array(range(0, h_inc, interpolation))

    for i in prange(c_count, nogil=True, num_threads=c_count):
        count = 0
        for r in w_list:
                ^
------------------------------------------------------------

data_util/cy_scatter.pyx:70:17: Coercion from Python not allowed without the GIL

Error compiling Cython file:
------------------------------------------------------------
...
    cdef int[:] w_list = array.array(range(0, w_inc, interpolation))
    cdef int[:] h_list = array.array(range(0, h_inc, interpolation))

    for i in prange(c_count, nogil=True, num_threads=c_count):
        count = 0
        for r in w_list:
                ^
------------------------------------------------------------

data_util/cy_scatter.pyx:70:17: Converting to Python object not allowed without gil

有办法吗?如果是这样,我该怎么做?

您只想按索引进行迭代,而不是通过 Python 迭代器进行迭代:

for ri in range(w_list.shape[0]):
    r = w_list[ri]

这是 Python 中的最佳实践与 Cython 中的最佳实践不同的地方 - Cython 仅加速数字循环的迭代。您尝试这样做的方式将退回到成为一个 Python 迭代器,它既慢又需要 GIL。