我目前正在嘗試想辦法將多維陣列(雙精度陣列)從 C 中的共享庫回傳到 python 并使其成為 np.array。我目前的方法如下所示:
共享庫(“utils.c”)
#include <stdio.h>
void somefunction(double *inputMatrix, int d1_inputMatrix, int d2_inputMatrix, int h_inputMatrix, int w_inputMatrix, double *kernel, int d1_kernel, int d2_kernel, int h_kernel, int w_kernel, int stride) {
double result[d1_kernel][d2_kernel][d2_inputMatrix][h_inputMatrix-h_kernel 1][w_inputMatrix-w_kernel 1];
// ---some operation--
return result;
}
現在,我編譯 utils.c:cc -fPIC -shared -o utils.so utils.c
蟒蛇(“somefile.py”)
from ctypes import *
import numpy as np
so_file = "/home/benni/Coding/5.PK/Code/utils.so"
utils = CDLL(so_file)
INT = c_int64
ND_POINTER_4 = np.ctypeslib.ndpointer(dtype=np.float64, ndim=4, flags="C")
utils.convolve.argtypes = [ND_POINTER_4, INT, INT, INT, INT, ND_POINTER_4, INT, INT, INT, INT, INT]
a = ... #some np.array with 4 dimensions
b = ... #some np.array with 4 dimensions
result = utils.somefunction(a, a.shape[0], a.shape[1], a.shape[2], a.shape[3], b, b.shape[0], b.shape[1], b.shape[2], b.shape[3], 1)
現在,如何將 utils.somefunction() 的結果轉換為 np.array?我知道,為了解決我的問題,我必須指定 utils.convolve.restype。但是,如果我希望回傳型別是 np.array,我必須為 restype 輸入什么?
uj5u.com熱心網友回復:
首先,分配在堆疊上的作用域 C 陣列(如 in somefunction)絕不能由函式回傳。堆疊的空間將被其他函式重用,例如 CPython 的一個。回傳的陣列必須在堆上分配。
此外,使用 ctypes 撰寫一個處理 Numpy 陣列的函式非常麻煩。如您所見,您需要在引數中傳遞完整的形狀。但問題是您還需要在函式的引數中傳遞每個維度和每個輸入陣列的步幅,因為它們在記憶體中可能不連續(例如np.transpose更改它)。話雖如此,為了性能和完整性,我們可以假設輸入陣列是連續的。這可以通過np.ascontiguousarray. a視圖的指標b可以使用 提取numpy.ctypeslib.as_ctypes,但希望 ctype 可以自動完成。此外,回傳的陣列當前是 C 指標,而不是 Numpy 陣列。因此,您需要創建一個具有正確形狀的 Numpy 陣列并從中跨步numpy.ctypeslib.as_array. 因為呼叫者不知道生成的形狀,所以您需要使用幾個整數指標(每個維度一個)從被呼叫函式中檢索它。最后,這會導致一個非常 丑陋且 容易出錯的代碼(如果出現任何問題,它通常會默默地崩潰,更不用說如果你不注意可能的記憶體泄漏)。您可以使用Cython為您完成大部分作業。
假設你不想使用 Cython 或者你不能,這里是一個帶有 ctypes 的示例代碼:
import ctypes
import numpy as np
# Example of input
a = np.empty((16, 16, 12, 12), dtype=np.float64)
b = np.empty((8, 8, 4, 4), dtype=np.float64)
# Better than CDLL regarding the Numpy documentation.
# Here the DLL/SO file is found in:
# Windows: ".\utils.dll"
# Linux: "./libutils.so"
utils = np.ctypeslib.load_library('utils', '.')
INT = ctypes.c_int64
PINT = ctypes.POINTER(ctypes.c_int64)
PDOUBLE = ctypes.POINTER(ctypes.c_double)
ND_POINTER_4 = np.ctypeslib.ndpointer(dtype=np.float64, ndim=4, flags="C_CONTIGUOUS")
utils.somefunction.argtypes = [
ND_POINTER_4, INT, INT, INT, INT,
ND_POINTER_4, INT, INT, INT, INT,
PINT, PINT, PINT, PINT, PINT
]
utils.somefunction.restype = PDOUBLE
d1_out, d2_out, d3_out, d4_out, d5_out = INT(), INT(), INT(), INT(), INT()
p_d1_out = ctypes.pointer(d1_out)
p_d2_out = ctypes.pointer(d2_out)
p_d3_out = ctypes.pointer(d3_out)
p_d4_out = ctypes.pointer(d4_out)
p_d5_out = ctypes.pointer(d5_out)
out = utils.somefunction(a, a.shape[0], a.shape[1], a.shape[2], a.shape[3],
b, b.shape[0], b.shape[1], b.shape[2], b.shape[3],
p_d1_out, p_d2_out, p_d3_out, p_d4_out, p_d5_out)
d1_out = d1_out.value
d2_out = d2_out.value
d3_out = d3_out.value
d4_out = d4_out.value
d5_out = d5_out.value
result = np.ctypeslib.as_array(out, shape=(d1_out, d2_out, d3_out, d4_out, d5_out))
# Some operations
# WARNING:
# You should free the memory of the allocated buffer
# with `free(out)` when you are done with `result`
# since Numpy does not free it for you: it just creates
# a view and does not take the ownership.
# Note that the right libc must be used, otherwise the
# call to free will cause an undefined behaviour
# (eg. crash, error message, nothing)
這是 C 代碼(注意固定長度型別):
/* utils.c */
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
double* somefunction(
double* inputMatrix, int64_t d1_inputMatrix, int64_t d2_inputMatrix, int64_t h_inputMatrix, int64_t w_inputMatrix,
double* kernel, int64_t d1_kernel, int64_t d2_kernel, int64_t h_kernel, int64_t w_kernel,
int64_t* d1_out, int64_t* d2_out, int64_t* d3_out, int64_t* d4_out, int64_t* d5_out
)
{
*d1_out = d1_kernel;
*d2_out = d2_kernel;
*d3_out = d2_inputMatrix;
*d4_out = h_inputMatrix - h_kernel 1;
*d5_out = w_inputMatrix - w_kernel 1;
const size_t size = *d1_out * *d2_out * *d3_out * *d4_out * *d5_out;
double* result = malloc(size * sizeof(double));
if(result == NULL)
{
fprintf(stderr, "Unable to allocate an array of %d bytes", size * sizeof(double));
return NULL;
}
/* Some operation: fill `result` */
return result;
}
這是用于使用 GCC 構建庫的命令:
# On Windows
gcc utils.c -shared -o utils.dll
# On Linux
gcc utils.c -fPIC -shared -o libutils.so
欲了解更多資訊,請閱讀以下內容:
- ctype Numpy 檔案
- 如何將 NumPy 陣列與 ctypes 一起使用?
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/430179.html
上一篇:可以在定義之前宣告主函式嗎?
下一篇:如何區分C中的0和NULL
