2013-07-25 39 views
5

我對Python很綠,所以希望我能正確地解釋這個問題。設置argtype for python回調函數

整體問題涉及從Python調用C例程。我可以通過拼湊一些相關的SO問題/答案來得到相當接近的結果,但我似乎無法讓事情相處得很好。有兩個方面:第一個是用指針調用C例程,第二個是使用回調函數。

背景 Rubner提供C語言編寫[EMD C code location]他還提供了調用EMD程序,有兩個例子C程序的推土機距離(EMD)例程。我正在嘗試開發一個Python例程來替代例如example2.c,它將調用EMD例程。 (是的,我熟悉的OpenCV實現EMD的。)

爲了方便起見,這裏是爲emd.c代碼,我想從Python中調用的頭文件:

/* 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;任何建議在這裏也將不勝感激。

在此先感謝。

--------------更新(工作)CODE

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; 
+0

我認爲你的'py_dist_func'不正確。 'f1'和'f2'都是'feature_t *'對象。因此應該使用'abs(f1.contents.value - f2.contents.value)'來代替'abs(f1 - f2)'。 – nymk

+1

不要將'CMPFUNC'保存到ctypes模塊。 'CMPFUNC'進入'argtypes',而不是'c_float','restype'應該是'c_float'。同樣@nymk指出你的'py_dist_func'需要取消引用指針。我會使用'abs(f1 [0] - f2 [0])';這種方式不需要使用'value'。 – eryksun

+0

感謝您的建議。 @eryksun:不知道我完全理解'不要將CMFUNC保存到ctypes模塊。 CMPFUNC進入argtypes ...'但我對代碼進行了一些更改並編輯了問題。仍然沒有喜悅:TypeError:_argtypes_中的第3項沒有from_param方法 – Aengus

回答

1

通過各種神祕的方法和寶貴意見,我終於得到了工作的一段代碼。正如我在評論中提到的那樣,我不確定禮儀是什麼,但我看到了很多類似的問題,我認爲發佈最後一段代碼會很有用。這不太好,如果你發現它足夠清理它,我會很感激鏈接到一個更優雅的實現。