从python中的函数返回后,指向浮点数的C类型指针发生变化

流星雨

我目前正在研究一个 Darknet/YOLO 项目,该项目使用 python 中的 opencv 从实时流接收的图像中检测对象。为了检测对象,opencv 图像,它只是一个具有形状的 numpy 数组,(height, width, color_channels)必须转换为 Darknet(用 c 编写)可以读取的格式(在 Darknet 中定义的具有 *float 类型的数据属性的 IMAGE 类)。为此,我在 python 中编写了以下代码:

    h, w, c = input_frame.shape
    # create a flattened image and normalize by devision by 255.
    # NOTE transpose(2, 0, 1) permutes the axes from 0,1,2 to 2,0,1 (clockwise cycle)
    flattened_image = input_frame.transpose(2, 0, 1).flatten().astype(np.float32)/255.
    # create a C type pointer
    c_float_p = ctypes.POINTER(ctypes.c_float) # define LP_c_float type
    c_float_p_frame = flattened_image.ctypes.data_as(c_float_p) # cast to LP_c_float
    # create empty C_IMAGE type object and then set data to c_float_p_frame
    C_IMAGE_frame = dn.make_image(w, h, c)
    C_IMAGE_frame.data = c_float_p_frame

(请注意,这dn是暗网接口,并在上方某处导入,但这不是问题,因此不是很重要)

然后将 C_IMAGE_frame 对象传递给网络。重要提示:此代码有效然而,这里有一个问题,如果我将完全相同的代码打包到一个函数中,我会在图像传递到暗网后收到访问冲突错误(即段错误)。我最初在测试脚本中内联编写了这段代码并且一切正常,所以当我开始清理我的代码时,我将上面的代码块打包到以下函数中:

def np_image_to_c_IMAGE(input_frame):
    """
    parameters
    ==========
    input_frame: ndarray            (opencv image)

    returns
    ==========
    C_IMAGE_frame: C IMAGE object   (implimented in darknet)
    converts a numpy image (w x h x c dim ndarray) to a C type IMAGE
    defined in darknet. Returns a pointer.
    """
    h, w, c = input_frame.shape
    # create a flattened image and normalize by devision by 255.
    # NOTE transpose(2, 0, 1) permutes the axes from 0,1,2 to 2,0,1 (clockwise cycle)
    flattened_image = input_frame.transpose(2, 0, 1).flatten().astype(np.float32)/255.
    # create a C type pointer
    c_float_p = ctypes.POINTER(ctypes.c_float) # define LP_c_float type
    c_float_p_frame = flattened_image.ctypes.data_as(c_float_p) # cast to LP_c_float
    # create empty C_IMAGE type object and then set data to c_float_p_frame
    C_IMAGE_frame = dn.make_image(w, h, c)
    C_IMAGE_frame.data = c_float_p_frame
    return C_IMAGE_frame

我最初很困惑为什么我的代码会创建段错误,但是我运行了一些调试测试并发现了以下问题:在C_IMAGE_frame.data[0]函数中访问(即只是读出第一个值)时,我得到了一个浮点数,就像人们期望的那样,但是如果我在像这样返回 C_IMAGE_frame 后做同样的事情:

#opencv get image and other code...
C_IMAGE = np_image_to_C_IMAGE(opencv_image)
print(C_IMAGE.data[0])

python 引发段错误。我检查了所有指针是否都正确“返回”,我看到发生了一些指针重新分配魔术。

def np_image_to_C_IMAGE(input_frame):
    # rest of function...
    print(C_IMAGE_frame)  # output: <lib.darknet.IMAGE object at 0x0000021F24F6EDC0>
    print(C_IMAGE_frame.data) # output: <lib.darknet.LP_c_float object at 0x0000021F24F6EBC0>
    print(C_IMAGE_frame.data[0]) # output: 0.0
    return C_IMAGE_frame

# after C_IMAGE is returned in script
C_IMAGE = np_image_to_C_IMAGE(opencv_image)
print(C_IMAGE)  # output: <lib.darknet.IMAGE object at 0x0000021F24F6EDC0>
print(C_IMAGE.data) # output: <lib.darknet.LP_c_float object at 0x0000021F24F6BAC0>
print(C_IMAGE.data[0] # raises Segmentation fault

请注意,data指针0x0000021F24F6EBC0更改为0x0000021F24F6BAC0so当然会出现段错误,但为什么会发生这种情况?我怎样才能避免这种情况?这只是一些内部的python技巧还是其他什么?我的意思是,如果我在 python 中返回一些东西,我希望它是我传递给的确切对象return,但也许 python ctypes 破坏了某些东西或者有一些需要解决方法的有趣的实现?

现在我将代码内联粘贴到我的分析脚本中,所以我的脚本又可以工作了,但我对为什么会发生这种情况以及如何解决它非常感兴趣。

编辑我添加了一个最小的可重现示例:

from ctypes import *
import numpy as np

class IMAGE(Structure):
    _fields_ = [("w", c_int),
                ("h", c_int),
                ("c", c_int),
                ("data", POINTER(c_float))]

img = np.zeros((1080, 1920, 3)) # h, w, c array = opencv image analogon

def np_image_to_c_IMAGE(input_frame):
    h, w, c = input_frame.shape
    flattened_image = input_frame.transpose(2, 0, 1).flatten().astype(np.float32)/255.
    c_float_p = POINTER(c_float) # define LP_c_float type
    c_float_p_frame = flattened_image.ctypes.data_as(c_float_p) # cast to LP_c_float
    C_IMAGE_frame = IMAGE(w, h, c, c_float_p_frame)
    print(C_IMAGE_frame)
    print(C_IMAGE_frame.data)
    return C_IMAGE_frame

C_IMAGE = np_image_to_c_IMAGE(img)

print(C_IMAGE)
print(C_IMAGE.data)

输出:

# within function
<__main__.IMAGE object at 0x7fc7f618ff40>
<__main__.LP_c_float object at 0x7fc7f49b1040>
# after return
<__main__.IMAGE object at 0x7fc7f618ff40>
<__main__.LP_c_float object at 0x7fc800777f40>
马克·托洛宁

存储数据指针IMAGE不会保留对图像数据的引用。一旦flattened_imagec_float_p_frame去的范围出的数据被释放。在图像中存储一个额外的引用以防止数据被释放:

from ctypes import *
import numpy as np

class IMAGE(Structure):
    _fields_ = [("w", c_int),
                ("h", c_int),
                ("c", c_int),
                ("data", POINTER(c_float))]

img = np.zeros((1080, 1920, 3))

def np_image_to_c_IMAGE(input_frame):
    h, w, c = input_frame.shape
    flattened_image = input_frame.transpose(2, 0, 1).flatten().astype(np.float32)/255.
    c_float_p = POINTER(c_float)
    c_float_p_frame = flattened_image.ctypes.data_as(c_float_p)
    C_IMAGE_frame = IMAGE(w,h,c,c_float_p_frame)
    C_IMAGE_frame.ref = c_float_p_frame     # extra reference to data stored
    print(C_IMAGE_frame)
    print(C_IMAGE_frame.data)
    print(cast(C_IMAGE_frame.data,c_void_p))  # the pointer value
    print(C_IMAGE_frame.data.contents)  # data valid
    return C_IMAGE_frame

C_IMAGE = np_image_to_c_IMAGE(img)
print(C_IMAGE)
print(C_IMAGE.data)
print(cast(C_IMAGE.data,c_void_p)) # pointer is the same, but contents freed if no ref.
print(C_IMAGE.data.contents)  # crashes here if extra reference not kept.

输出(注意存储的实际指针值是相同的,但如果该C_IMAGE_frame.ref行被注释掉,最终打印将崩溃):

<__main__.IMAGE object at 0x000001A8B8B5CBC0>
<__main__.LP_c_float object ddat 0x000001A8B8B5CC40>
c_void_p(1824215363648)
c_float(0.0)
<__main__.IMAGE object at 0x000001A8B8B5CBC0>
<__main__.LP_c_float object at 0x000001A8B8B5CC40>
c_void_p(1824215363648)
c_float(0.0)

不是很优雅,我不知道为什么存储c_float_p_frameinIMAGE.data不足以保持引用,但是将它存储在IMAGE.ref没有深入研究ctypes.

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

从函数返回后指针发生变化

从函数返回后,什么会导致 C++ 对象的类型信息发生变化?

从 C 中的函数返回后数组元素发生变化

从方法传出后返回指针值发生变化

传递给c ++中的函数后,整数变量的值会发生变化

python中的函数调用后变量发生变化

当函数中的值发生变化时,指针不反映变化

从函数返回时,为什么矢量内部的指针会发生变化?

退出 if 语句时内存中的指针发生变化

传递到函数后列表值发生变化

从Laravel 5.2中的Eloquent模型检索后,变量类型发生变化

main() 中的数组在函数中发生变化

类型为union(也包含一个结构)的指针,指向浮点数C的数组

为什么表达式类型在C ++中的版本之间会发生变化?

在C#中运行程序后,当函数参数的值发生变化时,可以再次调用同一个函数吗?

oracle中的表发生变化

将对象指针添加到向量后值发生变化

调用构造函数后,为什么initializer_list中的vector <int>的值会发生变化?

是什么导致该指针的值从简单的函数调用中发生变化?

容器道具在儿童状态变化后发生变化

调用此函数后输入函数参数发生变化

为什么将排序列表转换为Python中的集合后元素顺序会发生变化?

指针属性在重新检查时发生变化

Racket中C浮点数的正确指针类型转换是什么?

函数结束后字符串的值发生变化

为什么在运行“插入”函数后我的数字变量会发生变化?

移动到下一个函数后我的变量发生变化

如何将 void 指针转换为要在 C 中的函数中使用的浮点数?

python中的浮点数会发生什么?