为python回调函数设置argtype

为python回调函数设置argtype,python,ctypes,Python,Ctypes,我对Python非常熟悉,所以希望我能正确地表达这个问题 整个问题涉及从Python调用C例程。我可以把一些相关的问题/答案凑在一起,但我似乎无法把事情凑在一起。有两个方面:第一个是用指针调用C例程,第二个是使用回调函数 背景 Rubner提供了一个用C[]编写的土方工程距离(EMD)例程,他还提供了两个调用EMD例程的示例C程序。我正在尝试开发一个Python例程,作为调用EMD例程的example2.c的替代。(是的,我熟悉EMD的OpenCV实现。) 为方便起见,下面是我希望从python

我对Python非常熟悉,所以希望我能正确地表达这个问题

整个问题涉及从Python调用C例程。我可以把一些相关的问题/答案凑在一起,但我似乎无法把事情凑在一起。有两个方面:第一个是用指针调用C例程,第二个是使用回调函数

背景 Rubner提供了一个用C[]编写的土方工程距离(EMD)例程,他还提供了两个调用EMD例程的示例C程序。我正在尝试开发一个Python例程,作为调用EMD例程的example2.c的替代。(是的,我熟悉EMD的OpenCV实现。)

为方便起见,下面是我希望从python调用的emd.c代码的头文件:

/* DEFINITIONS */
#define MAX_SIG_SIZE   100
#define MAX_ITERATIONS 500
#define INFINITY       1e20
#define EPSILON        1e-6

/*****************************************************************************/
/* feature_t SHOULD BE MODIFIED BY THE USER TO REFLECT THE FEATURE TYPE      */
typedef int feature_t; 
/* typedef struct { int X,Y,Z; } feature_t;*/
/*typedef struct { int X; } feature_t; */
/*****************************************************************************/

typedef struct
{
  int n;                /* Number of features in the signature */
  feature_t *Features;  /* Pointer to the features vector */
  float *Weights;       /* Pointer to the weights of the features */
} signature_t;

typedef struct
{
  int from;             /* Feature number in signature 1 */
  int to;               /* Feature number in signature 2 */
  float amount;         /* Amount of flow from "from" to "to" */
} flow_t;

float emd(signature_t *Signature1, signature_t *Signature2,
      float (*func)(feature_t *, feature_t *),
      flow_t *Flow, int *FlowSize);

#endif
最后,这里是我到目前为止拼凑的python代码。我认为(但不确定)我已经正确地设置了结构。(请注意,这是Rubner emd.c代码中可能的功能结构的简化版本。我最终希望整个过程都能正常工作,但我现在开始做的很简单。)我遇到的第一个问题是调用函数的argtypes中的某个地方。我尝试了一些变体,但网络上提供的示例非常少,我遇到了麻烦

import ctypes

MAX_FEATURE_SIZE = 30
ARRAYFE = ctypes.c_int*MAX_FEATURE_SIZE
ARRAYWE= ctypes.c_float*MAX_FEATURE_SIZE 
ARRAYFL = ctypes.c_float*(2*MAX_FEATURE_SIZE-1)
flowSize = ctypes.c_int

emdlib = ctypes.CDLL('emdlib.dylib')
ctypes.CMPFUNC = ctypes.CFUNCTYPE(ctypes.c_float, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))

def py_dist_func(f1,f2):
    print "dist: ", f1, f2
    return(abs(f1-f2))

dist = ctypes.CMPFUNC(py_dist_func)

n = ctypes.c_int
flowSize = ctypes.c_int

class flow_t(ctypes.Structure):
    _fields_ = [("from", ctypes.c_int),
                ("to", ctypes.c_int),
                ("amount", ctypes.c_float)]

class signature_t(ctypes.Structure):
    _fields_ = [("N", n),("feature", ARRAYFE),
                ("weight", ARRAYWE)]

# emdlib.emd.argtypes = [ctypes.POINTER(signature_t), ctypes.POINTER(signature_t), ctypes.POINTER(ctypes.c_float), ctypes.POINTER(flow_t), ctypes.POINTER(ctypes.c_int)]

# emdlib.emd.argtypes = [ctypes.POINTER(signature_t), ctypes.POINTER(signature_t), ctypes.CMPFUNC(py_dist_func), ctypes.POINTER(flow_t), ctypes.POINTER(ctypes.c_int)]


emdlib.emd.argtypes = [ctypes.POINTER(signature_t), ctypes.POINTER(signature_t), ctypes.c_float, ctypes.POINTER(flow_t), ctypes.POINTER(ctypes.c_int)]

# emd.restype  = ctypes.c_float
emdlib.emd.restype  = flow_t

signature1=signature_t()
signature2=signature_t()
feature1 = ARRAYFE
feature2 = ARRAYFE
weight1 =ARRAYWE
weight2 = ARRAYWE

feature1 = [0,1,2,3]
feature2 = [0,3]
weight1 = [1,1,1,1]
weight2 = [1,1] 

#signature1= [4,feature1, weight1]
#signature2 = [2, feature2, weight2]
# sample: arr = (ctypes.c_int * len(pyarr))(*pyarr)

signature1.N = len(feature1)
signature1.feature = (ctypes.c_int * MAX_FEATURE_SIZE)(*feature1)
signature2.feature = (ctypes.c_int * MAX_FEATURE_SIZE)(*feature2)
signature1.weight = (ctypes.c_float * MAX_FEATURE_SIZE)(*weight1)
signature2.weight = (ctypes.c_float * MAX_FEATURE_SIZE)(*weight2)


e = emdlib.emd(ctypes.byref(signature1), ctypes.byref(signature2), dist, ctypes.POINTER(flow_t), flowSize)

print "EMD= ", e
print "flowSize", flowSize
任何关于我哪里出错的建议都将不胜感激

我肯定会遇到的第二个问题是返回指针的argtypes;如有任何建议,我们将不胜感激

提前谢谢

--------------更新(工作)代码


通过各种神秘的方法和有价值的评论,我终于得到了一段代码。正如我在评论中提到的,我不确定礼仪是什么,但我已经看到很多类似的问题,我认为发布最后一段代码会很有用。它不漂亮,如果您觉得它足够有用,可以清理它,我希望有一个链接指向更优雅的实现。

我认为您的
py\u dist\u func
不正确<代码>f1和
f2
都是
功能*
对象。因此,应使用
abs(f1.contents.value-f2.contents.value)
代替
abs(f1-f2)
。不要将
CMPFUNC
保存到ctypes模块
CMPFUNC
进入
argtypes
,而不是
c_float
restype
应该是
c_float
。正如@nymk指出的那样,您的
py_dist_func
需要取消对指针的引用。我会使用abs(f1[0]-f2[0]);这样就不需要使用
。感谢这两种方法的建议@eryksun:不确定我是否完全理解“不要将CMFUNC保存到ctypes模块”。CMPFUNC使用argtypes…“但是我已经对代码做了一些更改并编辑了这个问题。仍然没有joy:TypeError:argtypes中的项目3没有from_param方法只需在当前模块中将
CMPFUNC
设置为全局。我不知道你为什么要把它添加到ctypes模块。然后使用
CMPFUNC
作为argtypes中的函数指针类型。@eryksun谢谢,我想你认为我比实际情况更熟悉python,但这让我走上了正确的方向。我不确定礼仪是什么,但我已经发布了最终代码,因为1)完整的工作示例很难找到,2)EMD代码可能对其他人有用。
import ctypes
import math
import itertools

MAX_FEATURE_SIZE = 25

FEATURE_t = ctypes.c_int
FEATURE_ptr = ctypes.POINTER(FEATURE_t)

WEIGHT_t = ctypes.c_float
WEIGHT_ptr = ctypes.POINTER(WEIGHT_t)

COUNT_t = ctypes.c_int
COUNT_ptr = ctypes.POINTER(COUNT_t)

class FLOW_t(ctypes.Structure):
    _fields_ = [("frm", ctypes.c_int),
                ("to", ctypes.c_int),
                ("amount", ctypes.c_float)]

# Note that ctypes.POINTER is compatible with a ctypes array declared
# as TYPE * array_len.  This is equivalent to the way we can say 'char
# *foo = "ABCDEF"' in C.
class SIGNATURE_t(ctypes.Structure):
    _fields_ = [("N", COUNT_t ),
                ("feature", FEATURE_ptr),
                ("weight", WEIGHT_ptr)]

FLOW_ARRAY_t = FLOW_t * (2*MAX_FEATURE_SIZE - 1)
CMPFUNC_t = ctypes.CFUNCTYPE(ctypes.c_float, FEATURE_ptr, FEATURE_ptr)

SIGNATURE_ptr = ctypes.POINTER(SIGNATURE_t)
FLOW_ptr = ctypes.POINTER(FLOW_t)

# Convenience function - keeps us from having to remember all the types and parameters later on

def make_signature(features, weights):
    sig = SIGNATURE_t()
    sig.N = len(features)
    sig.feature = (len(features) * FEATURE_t)(*features)
    sig.weight = (len(weights) * WEIGHT_t)(*weights)
    return sig

# We want to pass into C a custom distance function from Python
def py_dist_func(f1,f2):
#   print "f1, f2: %d, %d" % ( f1[0], f2[0] )
    d= distance(f1[0],f2[0])
    return d

# set this up as a holder for distance function between any two n-D points
def distance(p0,p1):
    return(math.fabs(p0-p1))

dist_callback = CMPFUNC_t(py_dist_func)

#print "Importing emdlib"
emdlib = ctypes.CDLL('emdlib.dylib')
#print "Setting argtypes"
emdlib.emd.argtypes = [ SIGNATURE_ptr,
                        SIGNATURE_ptr,
                        CMPFUNC_t,
                        FLOW_ptr,
                        COUNT_ptr ]
#print "Setting restype"
emdlib.emd.restype  = ctypes.c_float

feature1 = [0, 1,2,3,4,5,6,7,8]
feature2 = [0, 1,2,3,4,5,6,7,8]
weight1 = [0.275,0.296,0.002,0.131,0.208,0.048,0.058,0.098,0.455]
weight2 = [0.285,0.421,0.028,0.021,0.240,0.166,0.023,0.054,0.469]

#print "Creating signatures"
signature1 = make_signature(feature1, weight1)
signature2 = make_signature(feature2, weight2)

flow_array = FLOW_ARRAY_t()
flow_size = COUNT_t()

#print "Calling EMD"
e = emdlib.emd(ctypes.byref(signature1),
               ctypes.byref(signature2),
               dist_callback,
               flow_array,
               ctypes.byref(flow_size))

print "EMD= ", e
print "Number of FlowS", flow_size.value

print "Flow"
print "from to amount"
totalFlow=0.0
for i in range(0,flow_size.value):
#   print "Flow from %d to %d amount :%f" %(flow_array[i].frm, flow_array[i].to, flow_array[i].amount)
    print "  %d  %d  %f" %(flow_array[i].frm, flow_array[i].to, flow_array[i].amount)
    totalFlow=totalFlow+flow_array[i].amount

#
# now adjust EMD to account for different signature masses and make it a metric
alpha=1.0

mass1=sum(weight1)
mass2=sum(weight2)

fList=[feature1,feature2]

max_distance= 0.0
for p0, p1 in list(itertools.product(*fList)):
#   print p0,p1, distance(p0,p1), max_distance
    max_distance = max(max_distance, distance(p0, p1))

print "\nMax distance= %f" % max_distance
print "Total Source = %f" % mass1
print "Total Demand = %f" % mass2
print "Total Flow= %f\n " % totalFlow
print "Alpha = %f\n" %alpha

# emdHat = e*totalFlow+math.sqrt((mass1-mass2)*(mass1-mass2))*alpha*max_distance
emdHat = e*totalFlow+math.fabs((mass1-mass2))*alpha*max_distance
print "Corrected Earth Movers Distance \n"
print "emdHat = %f\n" % emdHat;