Можно ли вызвать C DLL с указателем на массив 3dim через Ctypes?

#python #arrays #c #pointers #ctypes

#питон #массивы #c #указатели #ctypes

Вопрос:

Вопрос
Можно ли вызвать C DLL с указателем на массив 3dim через Ctypes?

Прогресс
Во-первых, я совсем новичок в C и DLL, поэтому, возможно, я не знал / не наблюдал за чем-то тривиальным.
Я получил его для запуска с указателем на массив 1dim. Однако было бы очень приятно иметь возможность использовать массив 3dim, потому что я хочу манипулировать данными изображения RGB, для чего требуется повторение ребер.

Python-Код

 import ctypes
import ctypes.util
from numpy.ctypeslib import ndpointer
import PBMS_ImageIO
import numpy as np

#Loads a image as 3dim array and converts it to 1dim array with "size" as length
image = PBMS_ImageIO.readImage('Image.png')
size = np.shape(image)[0]*np.shape(image)[1]*np.shape(image)[2]
imageIn = np.reshape(image, (size,))

PBMS_ImageConverter = ctypes.windll.LoadLibrary(ctypes.util.find_library('./PBMS_ImageConvert.dll'))

nd_pointer = np.ctypeslib.ndpointer(dtype=np.float32, ndim=1, flags=("C"))
PBMS_ImageConverter.RGB2BW.argtypes=[nd_pointer, ctypes.c_int]
PBMS_ImageConverter.RGB2BW.restype = ndpointer(dtype=ctypes.c_float,
                                               shape=(size,))
imageOut = PBMS_ImageConverter.RGB2BW(imageIn, size)
 

C-Код

 #include <math.h>

float * RGB2BW(const float * matrixIn, int size)
{
    float * matrixOut = (float *)malloc(sizeof(float)*size);
    for(int i = 0; i < size; i  )
    {
        matrixOut[i] = matrixIn[i];
    }
    return matrixOut;
}
 

Альтернативный вариант
Если вызов DLL через Ctypes не позволяет использовать указатель 3dim, возможно ли преобразовать массив 1dim в массив 3dim внутри?

Неопределенность
Я также не уверен, как массив numpy хранится в памяти. Может быть, мои попытки передать указатель на массив 3dim не увенчались успехом, потому что базовый массив numpy на самом деле не хранится как 3dim?

Ответ №1:

Массивы Numpy по умолчанию хранятся в той же ориентации памяти, что и массивы C. На стороне Python вы можете работать с 3D-массивами, но на стороне C проще обрабатывать как 1D-массив и выполнять вычисления для доступа к строкам и столбцам.

Приведенный ниже пример требует 3D-массива numpy в качестве аргумента, но передает размеры, чтобы код C мог получить доступ к элементам. Код C изменяет массив на месте:

тест.c

 #include <stdio.h>

__declspec(dllexport)
void RGB2BW(float* matrix, int x, int y, int z)
{
    for(int i = 0; i < x;   i)
        for(int j = 0; j < y;   j)
            for(int k = 0; k < z;   k)
            {
                int index = i * y * z   j * z   k;
                printf("matrix[%d][%d][%d] = %fn",i,j,k,matrix[index]);
                matrix[index] *= 2;
            }
}
 

test.py

 import numpy as np
from ctypes import *

dll = CDLL('./test')
dll.RGB2BW.argtypes = np.ctypeslib.ndpointer(dtype=np.float32,ndim=3),c_int,c_int,c_int
dll.RGB2BW.restype = None

a = np.arange(0,2.4,.1,dtype=np.float32).reshape(2,3,4)
dll.RGB2BW(a,*a.shape)
print(a)
 

Вывод:

 matrix[0][0][0] = 0.000000
matrix[0][0][1] = 0.100000
matrix[0][0][2] = 0.200000
matrix[0][0][3] = 0.300000
matrix[0][1][0] = 0.400000
matrix[0][1][1] = 0.500000
matrix[0][1][2] = 0.600000
matrix[0][1][3] = 0.700000
matrix[0][2][0] = 0.800000
matrix[0][2][1] = 0.900000
matrix[0][2][2] = 1.000000
matrix[0][2][3] = 1.100000
matrix[1][0][0] = 1.200000
matrix[1][0][1] = 1.300000
matrix[1][0][2] = 1.400000
matrix[1][0][3] = 1.500000
matrix[1][1][0] = 1.600000
matrix[1][1][1] = 1.700000
matrix[1][1][2] = 1.800000
matrix[1][1][3] = 1.900000
matrix[1][2][0] = 2.000000
matrix[1][2][1] = 2.100000
matrix[1][2][2] = 2.200000
matrix[1][2][3] = 2.300000
[[[0.        0.2       0.4       0.6      ]
  [0.8       1.        1.2       1.4      ]
  [1.6       1.8000001 2.        2.2      ]]

 [[2.4       2.6000001 2.8       3.       ]
  [3.2       3.4       3.6000001 3.8      ]
  [4.        4.2000003 4.4       4.6      ]]]
 

Комментарии:

1. Спасибо, это объяснение было очень полезным!