2013-07-25 15 views
5

Sono abbastanza verde con Python quindi spero di poter formulare correttamente questa domanda.impostazione di argtype per la funzione di callback python

Il problema generale riguarda il richiamo di una routine C da Python. Riesco ad avvicinarmi un po 'mettendo insieme alcune domande/risposte SO correlate, ma non riesco a far sì che le cose vadano bene insieme. Ci sono due aspetti: il primo è chiamare la routine C con i puntatori e il secondo è l'uso di una funzione di callback.

Sfondo Rubner fornisce una routine di Motori della terra Distanza (EMD) scritto in C [EMD C code location] Egli fornisce anche due programmi di esempio C che chiamano la routine EMD. Sto cercando di sviluppare una routine Python come alternativa, ad esempio, a example2.c che chiamerà la routine EMD. (Sì, ho familiarità con l'attuazione OpenCV di EMD.)

Per comodità Questo è il file di intestazione per il codice emd.c mi piacerebbe chiamata in pitone:

/* 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 

Infine, qui sono i codici python che ho scritto insieme finora. Penso (ma non sono sicuro) che ho ottenuto il setup delle strutture correttamente. (Si noti che questa è una versione semplificata delle possibili strutture di feature nel codice emd.c di Rubner. Vorrei finire per far funzionare tutto, ma per ora sto iniziando in modo semplice.) Il primo problema che sto avendo è da qualche parte negli argtype per la funzione di chiamata. Ho provato alcune varianti, ma gli esempi disponibili sul web sono piuttosto sottili e ho colpito un muro.

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 

Qualsiasi suggerimento su dove sono andato male sarebbe molto apprezzato.

Il secondo problema che sono sicuro di eseguire è con gli argomenti per i puntatori di ritorno; qualsiasi suggerimento qui sarebbe apprezzato pure.

Grazie in anticipo.

-------------- aggiornamento (di lavoro) CODICE

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

Penso che il tuo 'py_dist_func' non sia corretto. 'f1' e' f2' sono entrambi oggetti 'feature_t *'. Quindi 'abs (f1.contents.value - f2.contents.value)' dovrebbe essere usato al posto di 'abs (f1 - f2)'. – nymk

+1

Non salvare 'CMPFUNC' nel modulo ctypes. 'CMPFUNC' va in' argtypes', non 'c_float', e' restype' dovrebbe essere 'c_float'. Anche come @nymk sottolinea che il tuo 'py_dist_func' deve dereferenziare i puntatori. Io userei 'abs (f1 [0] - f2 [0])'; in questo modo non è necessario usare 'valore'. – eryksun

+0

grazie a entrambi per i vostri suggerimenti. @eryksun: non sono sicuro di aver capito completamente 'non salvare CMFUNC nel modulo ctypes. CMPFUNC entra in argtype ... 'ma ho apportato alcune modifiche al codice e modificato la domanda. Ancora nessuna gioia: TypeError: l'elemento 3 in _argtypes_ non ha il metodo from_param – Aengus

risposta

1

attraverso vari metodi arcani ed i preziosi commenti, finalmente ho un pezzo di codice di lavoro. Come ho detto nei commenti, non sono sicuro di quale sia l'etichetta, ma ho visto abbastanza domande simili che ho pensato che sarebbe stato utile postare l'ultimo bit di codice. Non è bello, e se lo trovi abbastanza utile da ripulirlo, apprezzerei un collegamento a un'implementazione più elegante.

Problemi correlati