Python封装函数后返回Darknet IMAGE的float指针异常致段错误
问题根本原因
- 首先澄清你观察到的
LP_c_float对象地址变化不是真正的问题:ctypes每次访问IMAGE结构体的POINTER(c_float)类型的data字段时,都会生成一个新的Python代理对象来包装底层的C指针值,所以代理对象的内存地址每次打印都会不同,但它指向的底层数据地址是固定的,可以用hex(cast(C_IMAGE.data, c_void_p).value)验证。 - 真正触发段错误的核心原因是numpy数组的生命周期管理问题:
- 你在函数内生成的
flattened_image是通过transpose、flatten、astype、除法操作得到的全新numpy数组(不是原输入数组的视图,是独立的内存副本),属于函数内的局部变量。 - 你通过
data_as拿到的C指针,本质上直接指向这个numpy数组的内部数据缓冲区。 - 当函数执行完成返回后,
flattened_image离开了作用域,如果没有其他引用持有它,Python的垃圾回收机制就会销毁这个数组,释放它占用的内存,导致IMAGE.data持有的指针变成野指针,后续访问自然触发段错误。 - 行内写法能正常运行,就是因为
flattened_image变量还在全局/外层作用域中,没有被回收,指针指向的内存仍然有效。
可行解决方案
核心思路是延长承载数据的numpy数组的生命周期,让它和返回的IMAGE对象绑定,只要IMAGE对象不被销毁,数组就不会被回收:
修改你的封装函数,把flattened_image作为属性挂载到返回的C_IMAGE_frame对象上即可:
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 = ctypes.POINTER(ctypes.c_float) c_float_p_frame = flattened_image.ctypes.data_as(c_float_p) C_IMAGE_frame = dn.make_image(w, h, c) C_IMAGE_frame.data = c_float_p_frame # 新增:把numpy数组绑定到IMAGE对象上,延长生命周期 C_IMAGE_frame._data_holder = flattened_image return C_IMAGE_frame
最小复现代码的修改方式完全一致:
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._data_holder = flattened_image return C_IMAGE_frame
修改后只要你持有返回的C_IMAGE对象,_data_holder属性就会一直引用flattened_image数组,不会被垃圾回收,指针就一直有效,且不会产生额外内存泄漏,C_IMAGE对象被回收时绑定的数组也会同步被回收。
内容的提问来源于stack exchange,提问作者ljbkusters
相关产品推荐
相关产品推荐

