web-dev-qa-db-ja.com

ctypes配列からnumpyにデータを取得する

Python(via ctypes)でラップされたCライブラリを使用して一連の計算を実行しています。実行のさまざまな段階で、Pythonにデータを取得したいと思います。 numpy配列。

私が使用しているラッピングは、配列データに対して2つの異なるタイプの戻り値を実行します(これは特に興味深いものです)。

  • ctypes Arraytype(x)を実行すると(xはctypes配列です)、返りに_<class 'module_name.wrapper_class_name.c_double_Array_12000'>_を取得します。このデータはドキュメントの内部データのコピーであり、numpy配列に簡単に取得できます。

    _>>> np.ctypeslib.as_array(x)
    _

これは、データの1D numpy配列を返します。

  • ctypeデータへのポインタ:この場合、ライブラリのドキュメントから、保存および使用されているデータへのポインタを取得していることを理解しています直接ライブラリに。ホエー私はtype(y)(yはポインターです)を実行します_<class 'module_name.wrapper_class_name.LP_c_double'>_を取得します。この場合でも、_y[0][2]_のようなデータを介してインデックスを作成することはできますが、非常に扱いにくいものを介してnumpyに取得することしかできませんでした。

    _>>> np.frombuffer(np.core.multiarray.int_asbuffer(
        ctypes.addressof(y.contents), array_length*np.dtype(float).itemsize))
    _

私はこれを古いnumpyメーリングリスト Travis Oliphantからのスレッド で見つけましたが、numpyのドキュメントでは見つかりませんでした。このアプローチの代わりに私が上記のように試すと、次のようになります:

_>>> np.ctypeslib.as_array(y)
...
...  BUNCH OF STACK INFORMATION
...
AttributeError: 'LP_c_double' object has no attribute '__array_interface__'
_

これは_np.frombuffer_が最善の方法ですか、これを行う唯一の方法ですか?私は他の提案を受け入れていますが、このデータで使用したいnumpy機能に依存する他の後処理コードがたくさんあるので、numpyを使用したいと思います

31
dtlussier

CtypesポインターオブジェクトからNumPy配列を作成することは問題のある操作です。ポインタが指しているメモリを実際に誰が所有しているかは不明です。いつ再び解放されますか?どのくらいの期間有効ですか?可能な限り、私はこの種の構造を避けようとしました。 Pythonコードで配列を作成してC関数に渡す方が、Python非対応のC関数によって割り当てられたメモリを使用するよりもはるかに簡単で安全です。後者を実行すると、ある程度、高水準言語がメモリ管理を処理する利点があります。

誰かがメモリを処理することを本当に確信している場合は、Python "バッファプロトコル"を公開するオブジェクトを作成し、このバッファオブジェクトを使用してNumPy配列を作成することができます。ドキュメントに記載されていないint_asbuffer()関数を使用して、投稿にバッファオブジェクトを作成します。

_buffer = numpy.core.multiarray.int_asbuffer(
    ctypes.addressof(y.contents), 8*array_length)
_

np.dtype(float).itemsizeを_8_に置き換えたことに注意してください。どのプラットフォームでも常に8です。)バッファオブジェクトを作成する別の方法は、PyBuffer_FromMemory()関数をPython ctypesを介したC API:

_buffer_from_memory = ctypes.pythonapi.PyBuffer_FromMemory
buffer_from_memory.restype = ctypes.py_object
buffer = buffer_from_memory(y, 8*array_length)
_

これらの両方の方法で、bufferからNumPy配列を作成できます

_a = numpy.frombuffer(buffer, float)
_

frombufferに2番目のパラメーターの代わりに.astype()を使用する理由が実際にはわかりません。さらに、なぜ_np.int_を使用するのか不思議です。 doubles。)

こんなに簡単にならないかなと思いますが、それほど悪くはないですね。醜い詳細をすべてラッパー関数に埋め込むことができ、もう心配する必要はありません。

26
Sven Marnach

Python 3.でこれらのいずれも機能しませんでした。python 2および3でctypesポインタをnumpy ndarrayに変換するための一般的な解決策として、これが機能することがわかりました(読み取り専用バッファーを取得する):

def make_nd_array(c_pointer, shape, dtype=np.float64, order='C', own_data=True):
    arr_size = np.prod(shape[:]) * np.dtype(dtype).itemsize 
    if sys.version_info.major >= 3:
        buf_from_mem = ctypes.pythonapi.PyMemoryView_FromMemory
        buf_from_mem.restype = ctypes.py_object
        buf_from_mem.argtypes = (ctypes.c_void_p, ctypes.c_int, ctypes.c_int)
        buffer = buf_from_mem(c_pointer, arr_size, 0x100)
    else:
        buf_from_mem = ctypes.pythonapi.PyBuffer_FromMemory
        buf_from_mem.restype = ctypes.py_object
        buffer = buf_from_mem(c_pointer, arr_size)
    arr = np.ndarray(Tuple(shape[:]), dtype, buffer, order=order)
    if own_data and not arr.flags.owndata:
        return arr.copy()
    else:
        return arr
10
wordy

別の可能性(最初の回答が書かれたときに利用可能なものよりも新しいバージョンのライブラリが必要になる場合があります-ctypes 1.1.0およびnumpy 1.5.0b2)は、ポインターから配列への変換です。

np.ctypeslib.as_array(
    (ctypes.c_double * array_length).from_address(ctypes.addressof(y.contents)))

これはまだ共有所有権のセマンティクスを持っているように思われるので、おそらく最終的に基礎となるバッファーを解放することを確認する必要があります。

9
seeker

Pythonで配列を作成することに問題がない場合は、次の2D配列の例がpython3で機能します。

import numpy as np
import ctypes

OutType = (ctypes.c_float * 4) * 6
out = OutType()
YourCfunction = ctypes.CDLL('./yourlib.so').voidreturningfunctionwithweirdname
YourCfunction.argtypes = [ctypes.POINTER(ctypes.c_float)]*3, ctypes.POINTER(ctypes.c_float)]*5, OutType]
YourCfunction(input1, input2, out)
out = np.array(out) # convert it to numpy

print(out)

numpyおよびctypesのバージョンは1.11.1および1.1.0です

0
Ilya Prokin

np.ctypeslib.as_array 必要なのはここだけです。

配列から:

 c_arr = (c_float * 8)()
 np.ctypeslib.as_array(c_arr)

ポインターから

 c_arr = (c_float * 8)()
 ptr = ctypes.pointer(c_arr[0])
 np.ctypeslib.as_array(ptr, shape=(8,))
0
Eric