Вложенные циклы с Cython для обработки изображений - PullRequest
0 голосов
/ 22 мая 2019

Я пытаюсь перебрать двумерное изображение, содержащее данные о глубине с плавающей точкой, оно имеет несколько нормальное разрешение (640, 480), но python работает слишком медленно, поэтому я пытался оптимизировать проблему с помощью используя cython.

Я пытался перенести цикл на другие функции, перемещаясь по оператору nogil, и, похоже, не сработал, после доработки проблемы я смог заставить его часть работать. Но эта последняя часть ускользает от меня безрезультатно.

Я попытался избавиться от объектов python из цикла prange(), предварительно переместив их в раздел with gil, поэтому:

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

вместо

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

но ошибка сохраняется

Мой код работает в двух частях:

  1. Метод split_data() подразделяет изображение на num квадранты, которые хранятся в трехмерном массиве bits. Они используются для облегчения разделения работы на несколько потоков / процессов. Эта часть работает хорошо.
@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, где num - это длина bits, называемая points, которая представляет собой последовательность Трехмерные координаты, представляющие действительные точки глубины. Затем он использует 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

Есть ли способ сделать это? И если да, то как мне это сделать?

1 Ответ

2 голосов
/ 22 мая 2019

Вы просто хотите выполнять итерацию по индексу, а не по итерации по итератору Python:

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

Это где-то, где лучшая практика в Python отличается от лучшей практики в Cython - Cython ускоряет итерацию только по числовым циклам,То, как вы пытаетесь это сделать, снова превращается в итератор Python, который медленнее и требует GIL.

...