MEX mit komplexen Zahlen
-
Wusste nicht recht, wie ich dieses Thema nennen sollte; vielleicht findet sich trotzdem wer, der mir helfen kann.
Ich habe in MATLAB zwei ziemlich große Matrizen (konkret: 16000 x 4096 Elemente) mit komplexen(!) Float-Werten. Diese möchte ich nun mit Hilfe der MEX-Schnittstelle (falls nicht bekannt, siehe z.B. http://cnx.org/content/m12348/latest/) an ein C-Programm übergeben, um diverse schleifenintensive Berechnungen durchzuführen. Die Rückgabe soll eine ebenso große Matrix mit reellen(!) Float-Werten sein.
Nun ist es bei MEX ja so, dass man die Eingabematrizen in Form eines Zeigerarrays bekommt (prhs[]). Mit prhs[0] kommt man also an den Zeiger für die erste Matrix. Weiterhin gibt es dann den Befehl mxGetData(prhs[0]), der ebenfalls wieder einen Zeiger zurückgibt, und zwar auf die reellwertigen Bestandteile der Matrix. mxGetImagData(prhs[0]) greift analog auf die Imaginärwerte zu.
Um mit komplexen Zahlen unter Visual Studio 2005 zurecht zu kommen, ohne C++ zu verwenden (Visual Studio ist ja leider nicht C99, sondern "nur" C90 oder C95, weiß ich grad nicht), hab ich mir einen eigenen Header complex.h definiert, der in etwa wie folgt aussieht:
/* complex.h for complex number arithmetics */ #ifndef _INC_MATH #define _INC_MATH #include <math.h> #endif /*Declaration of complex numbers with float precision*/ typedef struct { float real; float imag; } complex_float; float cfabs(complex_float c) { float b; b = sqrtf(powf(c.real,2)+powf(c.imag,2)); return b; } complex_float cfconj(complex_float c) { //... } complex_float cfmult(complex_float c1, complex_float c2) { //... } complex_float cfadd(complex_float c1, complex_float c2) { //... }
Nun zum Kern meiner Frage:
Wie kriege ich nun aus den Zeigern auf meine beiden beiden Eingabematrizen (prhs[0] bzw. prhs[1]) die Werte so ausgelesen, dass ich meinen Header darauf anwenden kann?Die Vorgehensweise
complex_float *S1, *S2; S1.real = mxGetData(prhs[0]); S1.imag = mxGetImagData(prhs[0]); S2.real = mxGetData(prhs[1]); S2.imag = mxGetImagData(prhs[1]);
funktioniert jedenfalls nicht.
Ich hab hin und wieder noch meine Probleme mit dem Aufeinandertreffen von Zeigern, Arrays und Strukturen, darin könnte das Problem wohl am ehesten liegen, denke ich.
Was mir natürlich auch aufgefallen ist, ist die Tatsache, dass meine Header-Funktionen mit konkreten Struktur-Objekten arbeiten, während im MEX-File hauptsächlich mit Zeigern gearbeitet wird - was auf jeden Fall notwendig ist, wenn man den Speicherbedarf von zwei komplexwertigen 16000x4096-Float-Matrizen bedenkt. Andauerndes rumkopieren von Variablen ist da natürlich auch auf 64-Bit-Systemen nichts.Würde mich über jedweden Input freuen, sollte mein Problem mangels konkretem Code nicht nachvollziehbar sein, kann ich natürlich noch weiter ins Detail gehen.
-
Vielleicht noch als Nachtrag:
Kern des Programms sind zwei verschachtelte for-Schleifen, die die Spalten und Zeilen der beiden Matrizen simultan durchlaufen und für jedes Pixel mathematische Operationen (Betrag einer komplexen Zahl, Summe zweier komplexer Zahlen, komplex Konjugiertes einer komplexen Zahl etc.) durchführen.
Dass man mittels "*" auf den Wert einer "gezeigerten" Variable zugreifen kann (was dann ja als Eingabeparameter für meine Header-Funktionen dienen könnte), ist mir bewusst. Jedenfalls übergebe ich nur einzelne Elemente des komplexen Arrays, sprich skalare Werte, was etwas von der Komplexität des Problems nimmt.
-
Ich glaube, das hier: http://www.c-plusplus.net/forum/viewtopic-var-t-is-256264.html kommt einer Lösung meines Problems schon recht nahe.
In der Zwischenzeit hab ich das Ganze allerdings schon per n00b-style gelöst: Real- und Imaginärteile jeder Matrix werden jeweils einfach in einem eigenen Array abgespeichert - und die Formeln für's Rechnen mit komplexen Zahlen werden einfach explizit verwendet. Nicht schön, aber es funktioniert...
-
Ok, letzter Versuch: Ich hab das Ganze nun so umgeschrieben, dass Zeiger auf Struktur-Arrays und der im ersten Beitrag dargestellte complex.h-Header verwendet werden.
Minimalbeispiel:
void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) { /*Declaration of needed pointers and variables*/ complex_float *c1, *c2; int rows1, rows2, cols1, cols2; rows1 = (int)mxGetM(prhs[0]); cols1 = (int)mxGetN(prhs[0]); rows2 = (int)mxGetM(prhs[1]); cols2 = (int)mxGetN(prhs[1]); c1 = malloc((rows1*cols1)*sizeof(complex_float)); c2 = malloc((rows2*cols2)*sizeof(complex_float)); /*Receiving complex image data from input argument pointers*/ c1.real = mxGetData(prhs[0]); // real data from input matrix 1 c1.imag = mxGetImagData(prhs[0]); // imaginary data from input matrix 1 c2.real = mxGetData(prhs[1]); // real data from input matrix 2 c2.imag = mxGetImagData(prhs[1]); // imaginary data from input matrix 2 return; }
Später greife ich dann im Rahmen einer verschachtelten Schleife auf c1 und c2 (zum Beispiel wie folgt) zu:
powf(cfabs(c1[(rows1*(j+c))+(i+r)]),2)
Beim kompilieren gibt mir Visual Studio 2005 den Fehler C2231 aus und empfiehlt, statt z.B. c1.real c1->real zu verwenden. Allerdings komme ich auch damit nicht weiter.
Das Ganze paart sich dann noch mit dem Fehler C2440, dass "void*" nicht in "float" konvertiert werden kann...
Any help highly appreciated!
-
MSCHMITT schrieb:
Beim kompilieren gibt mir Visual Studio 2005 den Fehler C2231 aus und empfiehlt, statt z.B. c1.real c1->real zu verwenden. Allerdings komme ich auch damit nicht weiter.
womit es auch recht hat, weil du ja struct-pointer hast. da du's scheinbar als array benutzt, wäre zugriff aufs erste element: c1[0].real bzw c1[0].imag, aufs zweite c1[1].real usw. und da du es offensichtlich noch als 2d-array benutzt, muss der index (in den eckigen klammern) berechnet werden, z.B. c1[x*colsize+y].real, usw. (x,y sind dabei die koordinaten, colsize ist die konstante spaltengrösse).
MSCHMITT schrieb:
Das Ganze paart sich dann noch mit dem Fehler C2440, dass "void*" nicht in "float" konvertiert werden kann...
wahrscheinlich musste 'float*' nehmen? ich kenne mich mit matlab nicht aus, aber warts mal ab, hier hängen einige rum, die matlab benutzen (oder frag mal in einem matlab-forum, sowas gibts bestimmt auch).
-
Danke für die Antwort, auch wenn ich aus der leider noch nicht ganz schlau werde, hehe.
Vielleicht zur Erläuterung nochmal die auf Strukturen verzichtende Alternativ-Implementierung, die funktioniert (das Äußere, Weggelassene entspricht dem Source oben):
float *c1_real, *c1_imag, *c2_real, *c2_imag; c1_real = mxGetData(prhs[0]); c1_imag = mxGetImagData(prhs[0]); c2_real = mxGetData(prhs[1]); c2_imag = mxGetImagData(prhs[1]);
Und da greif ich dann auf die 4 Arrays ganz normal zu:
c1_real[(rows1*j+i)]
Ganz langer Rede, ganz kurzer Sinn: Ich würde das Ganze einfach gerne etwas eleganter lösen, ohne, dass ich vier einzelne Arrays benutze, sondern eben nur zwei für die Strukturvariablen vom im ersten Post vorgestellten Typ complex_float.
-
MSCHMITT schrieb:
Danke für die Antwort, auch wenn ich aus der leider noch nicht ganz schlau werde, hehe.
Vielleicht zur Erläuterung nochmal die auf Strukturen verzichtende Alternativ-Implementierung, die funktioniert (das Äußere, Weggelassene entspricht dem Source oben):
float *c1_real, *c1_imag, *c2_real, *c2_imag; c1_real = mxGetData(prhs[0]); c1_imag = mxGetImagData(prhs[0]); c2_real = mxGetData(prhs[1]); c2_imag = mxGetImagData(prhs[1]);
Und da greif ich dann auf die 4 Arrays ganz normal zu:
c1_real[(rows1*j+i)]
jo, und mit structs sollte es dann so aussehen:
complex_float eine_struct; ... eine_struct.real = *mxGetData(...); eine_struct.imag = *mxGetImagData(...);
bzw, wenn du ein array aus structs hast (oder einen pointer, der auf ein array zeigt)
complex_float *viele_structs = malloc(...); ... // erstes element viele_structs[0].real = *mxGetData(...); viele_structs[0].imag = *mxGetImagData(...); // zweites element viele_structs[1].real = *mxGetData(...); viele_structs[1].imag = *mxGetImagData(...); // usw.
-
Aber wenn ich's ohne structs mache, muss ich ja auch nicht
c1_real[0] = ...; c1_real[1] = ...;
machen?! Ich steh aufm Schlauch.
-
MSCHMITT schrieb:
Aber wenn ich's ohne structs mache, muss ich ja auch nicht
c1_real[0] = ...; c1_real[1] = ...;
machen?! Ich steh aufm Schlauch
wenn es sich um arrays (oder pointer auf aufeinanderfolgende elemente) handelt, musste diese eckigen klammern mit dem index darin '[...]' verwenden, um einzelne elemente anzusprechen. was ist denn daran so schlimm?
-
Ich sehe nicht wirklich den Unterschied zwischen
float *c1_real; c1_real = mxGetData(prhs[0]);
und
complex_float *c1; /*c1 = malloc((rows1*cols1)*sizeof(complex_float)); (das evtl. dazu)*/ c1.real = mxGetData(prhs[0]);
...
EDIT: ersteres funktioniert, zweiteres eben nicht.
ZWEITES EDIT: Und dass ich im ersteren Fall mittels c1_real[xyz] dereferenziere, ist mir klar.Bei Fall Zwei müsste es dann c1[xyz].real sein, oder?
-
MSCHMITT schrieb:
Ich sehe nicht wirklich den Unterschied zwischen
float *c1_real; c1_real = mxGetData(prhs[0]);
und
complex_float *c1; /*c1 = malloc((rows1*cols1)*sizeof(complex_float)); (das evtl. dazu)*/ c1.real = mxGetData(prhs[0]);
...
naja, dieses 'mxGetData' liefert (nach deinem ersten code) einen float-pointer zurück, wenn du den einem 'struct complex_float-pointer' zuweist, ist das nicht richtig (falscher typ, compiler sollte meckern, ausser mxGetData gibt einen void* raus). sowas etwa wäre ok (verlängerte variante, ist vielleicht besser verständlich):
complex_float *c1; float *cl_real; ... // erstmal speicher besorgen für ein paar complex_float structs cl = malloc(...); ... // dann den pointer auf einen real-wert von dieser mx-blahblubb funktion holen cl_real = mxGetData(prhs[0]); ... // und dann den real-wert selber in das erste element des 'dynamischen struct arrays' eintragen // das sternchen bedeutet: hole das, worauf der pointer zeigt cl[0].real = *cl_real;
^^prinzipiell geht das so.