2017-06-04 140 views
1

C函數(以下代碼中的image_data變量)返回指向類型爲POINTER(c_ubyte)的緩衝區的指針。我希望這些數據由Python管理,因此我想將它複製到bytearray。這裏的函數調用Python`ctypes` - 如何將C函數返回的緩衝區複製到字節數組中

image_data = stb_image.stbi_load(filename_cstr, byref(width), 
           byref(height), byref(num_channels), 
           c_int(expected_num_channels)) 

我們得到了只有電話後知道圖像的widthheight,所以不能預先分配bytearray

我會用

array_type = c.c_ubyte * (num_channels.value * width.value * height.value) 

image_data_bytearray = bytearray(cast(image_data, array_type)) 

但類型cast到必須是一個指針,而不是數組,所以我得到一個錯誤。

TypeError: cast() argument 2 must be a pointer type, not c_ubyte_Array_262144 

我該怎麼辦?

+1

你會發現這個有用:https://stackoverflow.com/questions/4355524/getting-data-from-ctypes-array-into-numpy - NumPy的是一個很好的圖書館數據處理的類型。 –

+1

您可以有一個指向數組的指針,例如'ptype = POINTER(c_ubyte *(num_channels.value * width.value * height.value))'。通過使用'from_buffer'複製源指針的地址來獲取實例,然後解引用以獲取可傳遞給'bytearray'的數組,例如'image_data_bytearray = bytearray(ptype.from_buffer(image_data)[0])''。 – eryksun

回答

0

好的,閱讀評論中鏈接到的問題的答案(謝謝,@「John Zwinck」和@「eryksun」),有兩種存儲數據的方式,可以是bytearraynumpy.array。在所有這些片段,image_dataPOINTER(c_ubyte)型的,我們有array_type定義爲 -

array_type = c_ubyte * num_channels * width * height 

我們可以先創建循環的ByteArray上,並設置字節

arr_bytes = bytearray(array_size) 
for i in range(array_size): 
    arr_bytes[i] = image_data[i] 

-

或者更好的方法是使用from_address創建一個C數組實例,然後用它初始化一個bytearray -

image_data_carray = array_type.from_address(addressof(image_data.contents)) 

# Copy into bytearray 
image_data_bytearray = bytearray(image_data_carray) 

和寫入圖像(沒有問這個問題,只是分享的完整性),我們可以得到指針這樣的字節組數據,並給它stbi_write_png

image_data_carray = array_type.from_buffer(image_data_bytearray) 
image_data = cast(image_data_carray, POINTER(c_ubyte)) 

中 -

做的numpy基於方式是回答在鏈接的問題

address = addressof(image_data.contents) 
image_data = np.ctypeslib.as_array(array_type.from_address(address)) 

僅此一項但只有PO整數到由C函數返回的內存。我們可以通過創建一個numpy的數組作爲

image_data_numpy = np.array(image_data_numpy) 

複製要確認我曾經做過一個assert all(arr_np == arr_bytes)那裏。並且arr_np.dtypeuint8

和寫入圖像期間,我們可以得到一個指向numpy的陣列的數據是這樣

image_data = image_data_numpy.ctypes.data_as(POINTER(c_ubyte)) 
0

你的變量ARRAY_TYPE甚至不應該這樣叫,因爲它其實不是一個初始化數組c也不任何類型的類型,但爲做數組初始化準備的Python對象。 那麼,初始化數組也不應該這樣調用。 :d

你應該在做有的等效:

unsigned char array[channels*width*height]; 

在C.然後陣列是一個指向N *類型無符號字符指向陣列的第一個字節。 (index 0) cast()應該得到一個指針來查看數據的類型。這樣做:

array = (c.c_ubyte*(channels*width*height))() 

應該做的伎倆。但是你不需要額外分配的內存。所以你可以按照評論中的建議創建一個指針。

但我建議你使用:

image_data = bytearray(c.string_at(image_data)) 

它應該工作,假設,當然,這返回的圖像是空值終止。那麼,這也意味着使用簽名的字符,但它不一定是。 如果您編寫了C部分,只需將一個額外的字節分配給將包含聲明/轉換爲包含無符號字符並將最後一項設置爲0的映像的內存。 然後讓算法像以前一樣工作。如果你不終止它,你仍然會得到整個圖像string_at(),但會有3個字節或更多的內存泄漏。非常不受歡迎。

我在C模塊中使用了這個技巧進行色彩空間轉換。它的工作速度非常快,因爲沒有循環,沒有任何額外的。 string_at()只是拉入緩衝區並在其周圍創建Python字符串包裝。 然後,您可以使用numpy.fromstring(...)或array.array(「B」,image_data)或使用像上面的bytearray()等

否則,我剛纔看到你的答案。你也可以這樣寫,但我認爲我的詭計更好(當然,如果你可以改變C代碼)。

P.S.哎呦!我剛剛在doc字符串中看到string_at()可以具有可選的參數大小。也許使用它會完全忽略終止,並且不會有任何泄漏。我現在在問自己,爲什麼我沒有在我的項目中使用它,卻與空終止相混淆。 也許出於懶惰。使用大小不應該要求對C代碼進行任何修改。因此,這將是:

image_data = bytearray(c.string_at(image_data, channels*width*height)) 
相關問題