Kompilierung meines C++/MEX-Files funktioniert nicht - "Segmentation Violation"
-
Hallo zusammen! Hoffe, das Thema ist im richtigen Subforum gelandet...
Ich bin derzeit damit beschäftigt, eine Anwendung aus dem Bereich der Bildverarbeitung in MATLAB zu implementieren. Dabei möchte ich eine Funktion wegen ihrer Schleifenlastigkeit (zwei verschachtelte for-Schleifen mit einmal ca. 5000 und einmal ca. 2500 Durchläufen - MATLAB braucht dafür ca. 3,5 Stunden!) in C++ schreiben und als MEX-File in MATLAB einbinden. Als Compiler benute ich den von Visual Studio 2005, System ist Windows XP - und das Kompilieren läuft ohne Probleme durch.
Wenn die Funktion jedoch von MATLAB aus aufrufen will, bekomme ich immer eine "Segmentation Violation", und im Debugger wird mir eine "Access Violation" ausgegeben.
Hier mal der Code:
#include "matrix.h" #include "mex.h" #include "math.h" #include <exception> /* #pragma comment(lib, "libmx.lib") #pragma comment(lib, "libmat.lib") WOFÜR IST DAS HIER GUT? #pragma comment(lib, "libmex.lib") */ using namespace std; int round(double value); int round(double value) { return (int)floor( value + 0.5 ); } void mexFunction(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[]) { mxArray *mIr; mIr = prhs[0]; double *Ir; Ir = mxGetPr(mIr); mxArray *mIt; mIt = prhs[1]; double *It; It = mxGetPr(mIt); mxArray *mparam; mparam = prhs[2]; double *param; param = mxGetPr(mparam); int rowsR = (int)mxGetM(mIr); int colsR = (int)mxGetN(mIr); int rowsT = (int)mxGetM(mIt); int colsT = (int)mxGetN(mIt); double *ITrans; plhs[0] = mxCreateDoubleMatrix(rowsR,colsR,mxREAL); // ITrans ITrans = mxGetPr(plhs[0]); /* Verschiedene Berechnungen */ int rn, cn; for(int j = 0; j < colsR; j++) { for(int i = 0; i < rowsR; i++) { /* Aus i und j werden rn und cn berechnet */ rn = round(r); cn = round(c); if (rn < 0 || rn > rowsT-1 || cn < 0 || cn > colsT-1) continue; else { GW = It[(rowsT*cn)+rn]; ITrans[(rowsR*j)+i] = GW; } } } }
Ich habe den Eindruck, dass sich diese "unhandled exception" in dem ELSE-Block zum Schluss abspielt.
Muss dazu sagen, dass das meine ersten Gehversuche mit C++ sind, ich bin also um jeden Hinweis dankbar!
-
MSCHMITT schrieb:
Dabei möchte ich eine Funktion wegen ihrer Schleifenlastigkeit (zwei verschachtelte for-Schleifen mit einmal ca. 5000 und einmal ca. 2500 Durchläufen - MATLAB braucht dafür ca. 3,5 Stunden!) in C++ schreiben und als MEX-File in MATLAB einbinden.
Hast du mal versucht das ganze zuerst in Matlab zu optimieren? Schleifenkonstrukte sind in Matlab meist arschlahm, und meist gibt es andere (deutlich) effizientere Wege.
Beispiel:
tic; i = 0; for t = 0:.01:1000 i = i+1; z(i) = sin(t); end toc; tic; t = 0:.01:1000; y = sin(t); toc;
gibt aus:
Elapsed time is 61.269362 seconds. Elapsed time is 0.026488 seconds.
Ansonsten noch ein Link: http://www.math.ucla.edu/~getreuer/matopt.pdf
-
Ja, das mit der Vektorisierung von MATLAB-Schleifen zur Geschwindigkeitsoptimierung ist mir schon klar. Im vorliegenden Fall ist es aber leider nicht möglich, da ich eben eine Bild-Matrix Pixel für Pixel abschreite, rückwärts affin-transformiere, mit den gewonnenen Koordinaten (eventuell) ein Pixel in einem zu transformierenden Bild anspreche, dort die Grauwert-Intensitär abgreife und diese dann an die Stelle des ursprünglichen Ausgangspixels schreibe.
Dafür ist mir auch nach langem Überlegen und nach Rücksprache mit Kollegen kein vektorisierter Weg eingefallen.
-
Vorneweg: Du kannst auch mex-Files debuggen. In der Hilfe steht, wie das bei dir genau gehen sollte. Dann kannst Du den Fehler schneller finden.
Sonst:
-
Parameterchecks einbauen, ob wirklich alles korrekt übergeben wurde (gerade bei Matlab blickt nämlich nach kurzer Zeit niemand mehr so konkret durch, welche Form welche Matrizen haben, aber in C ist das essentiell).
-
r und c kann ich in deinem Code nirgends finden. Geht das wirklich durch den Compiler? Der Kommentar dazu macht recht wenig Sinn. Und GW?
-
Bisschen schwierig zu lesen, mit den vielen Variablen, die teilweise alle prinzipbedingt nur einmal benutzt werden.
-
Was macht Ir? Wird das irgendwo gereferenziert? Oder brauchst DU nur die Größe?
-
Wenn i Zeilenindex und j Spaltenindex: x(i,j) wird in C dann zu x[i*col_num + j]. Bin mir nicht sicher, ob das bei dir stimmig ist.
-
Wenn Du ein kurzes, funktionierendes Beispiel der Matlab-Aufrufzeile bastelst, das funktioniert (also den Fehler produziert), dann guck ich es mir mal näher an, so macht das keinen Spaß.
-
-
Danke für das Interesse an meinem Problem! Hatte gedacht, dass es besser ankommt, wenn ich den Quellcode auf das Kernproblem reduziere, und hab deshalb verschiedene (natürlich für den Algorithmus an sich essentielle!) Programmteile weggelassen und das nur durch Kommentare gekennzeichnet.
Hier jetzt mal der vollständige Quellcode und alles, was man sonst noch so braucht:
#include "matrix.h" #include "mex.h" #include "math.h" #include <exception> // #pragma comment(lib, "libmx.lib") // #pragma comment(lib, "libmat.lib") // #pragma comment(lib, "libmex.lib") using namespace std; // void mexFunction(int nlhs, mxArray* plhs[],int nrhs, mxArray* prhs[]); int round(double value); int round(double value) { return (int)floor( value + 0.5 ); } void mexFunction(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[]) { mxArray *mIr; mIr = prhs[0]; double *Ir; Ir = mxGetPr(mIr); mxArray *mIt; mIt = prhs[1]; double *It; It = mxGetPr(mIt); mxArray *mparam; mparam = prhs[2]; double *param; param = mxGetPr(mparam); int rowsR = (int)mxGetM(mIr); int colsR = (int)mxGetN(mIr); int rowsT = (int)mxGetM(mIt); int colsT = (int)mxGetN(mIt); double *ITrans; plhs[0] = mxCreateDoubleMatrix(rowsR,colsR,mxREAL); // ITrans ITrans = mxGetPr(plhs[0]); double pixel_size = 0.19; double x0, y0, mx, my, alpha, beta, X0, Y0, xr, yr, xt, yt, r, c, GW; x0 = param[0]; y0 = param[1]; mx = param[2]; my = param[3]; alpha = param[4]; beta = param[5]; X0 = -cos(beta)*x0/(mx*cos(alpha-beta)) - sin(beta)*y0/(mx*cos(alpha-beta)); Y0 = sin(alpha)*x0/(my*cos(alpha-beta)) - cos(alpha)*y0/(my*cos(alpha-beta)); int rn, cn; for(int j = 0; j < colsR; j++) { for(int i = 0; i < rowsR; i++) { double i0 = (rowsR-1)/2; double j0 = (colsR-1)/2; double r0 = (rowsT-1)/2; double c0 = (colsT-1)/2; xr = (j - j0) * pixel_size; yr = -(i - i0) * pixel_size; xt = X0 + cos(beta)/(mx*cos(alpha-beta))*xr + sin(beta)/(mx*cos(alpha-beta))*yr; yt = Y0 + cos(alpha)/(my*cos(alpha-beta))*yr - sin(alpha)/(my*cos(alpha-beta))*xr; c = c0 + xt/pixel_size; r = r0 - yt/pixel_size; rn = round(r); cn = round(c); if (rn < 0 || rn > rowsT-1 || cn < 0 || cn > colsT-1) continue; else { GW = It[(rowsT*cn)+rn]; ITrans[(rowsR*j)+i] = GW; } } } }
GW ist natürlich überflüssig, das ist mir klar, ich hab das Ganze nur beim Debuggen übersichtlicher haben wollen. Hab zum Debuggen einfach Visual Studio an den MATLAB-Prozess angehängt.
Aufgerufen wird das Programm nun wie folgt:
[TransformiertesBild] = trans_affine(ReferenzBild, ZuRegistrierendesBild, Parameter);
Dabei sind ReferenzBild und ZuRegistrierendesBild jeweils 2D-Matrizen mit circa 5000 x 2500 Elementen. Der Einfachheit halber hat, bei diesem ersten Gehversuch, TransformiertesBild die gleiche Größe wie ReferenzBild. Ziel ist ja, am Ende beide Bilder übereinander ("koregistriert") darstellen zu können.
Parameter ist ein 6x1 oder 1x6 Vektor, in dem die sechs Parameter einer Affintransformation (2 Translationen x0, y0, 2 Maßstäbe mx, my und zwei Rotationen α, β) gespeichert sind.
Ach ja, und rows_ bzw. cols_ stehen bei mir immer für die Anzahl der Zeilen (also quasi "Höhe"), bzw. Anzahl der Spalten (also quasi "Breite") einer Matrix.
Vielen Dank für die Hilfe!!
-
Muss die mexFunction nicht
extern "C"
deklariert werden?
-
Das bedeutet? Wie gesagt, ich bin blutiger Anfänger im Bereich C++ und hauptsächlich mit MATLAB vertraut. Bin deshalb für jedwede Anregung diesen Beispielcode betreffend dankbar, auch wenn's nicht direkt was mit der Frage zu tun hat. Aber lieber von Anfang an C++ ordentlich verwenden.
-
(zwei verschachtelte for-Schleifen mit einmal ca. 5000 und einmal ca. 2500 Durchläufen - MATLAB braucht dafür ca. 3,5 Stunden!)
Ich habe mir nicht den ganzen Thread durchgelesen. Hast du schon mal probiert in Matlab deinen Speicher/Array/2DArray als ganzes anzufordern? Das macht man, indem man seine Matrix/Array mit z.B. zero( Groesse ) vorinitialisiert. 3.5h sind arg lang fuer 12.5 Mio Iterationen.
-
Das mache ich grundsätzlich! Bei mir wächst kein Array in einer Schleife!
Außerdem ist die Idee ja sogar, das später mal für das komplette zu registrierende Bild zu machen. Momentan wird ja nur der Teil transformiert, der dann auch über dem Referenzbild liegt, der Rest wird einfach ignoriert (und damit quasi weggeschnitten).
Das hatte ich dann auch mal probiert, aber dann abgebrochen, als ich festgestellt habe, dass das sogar etwa 10 Stunden gedauert hätte...
-
MSCHMITT schrieb:
Aufgerufen wird das Programm nun wie folgt:
[TransformiertesBild] = trans_affine(ReferenzBild, ZuRegistrierendesBild, Parameter);
Dafür hätte ich eben gerne Daten gehabt, damit ich das mal ausprobieren kann. Crasht dein Programm auch für irgendeine kleine Datenmenge?
ich hab das programm mal so umgeschrieben, dass es durch den mitgebrachten lcc-c-compiler geht (alle variablen am anfang deklarieren und exception und namespace raus) und dann konnte ich den fehler mit zwei minuten aufwand nicht reproduzieren.
dafür hat die vmware meinen tastaturtreiber geschrottet. hmm.
-
Meine konkreten Daten kann ich leider nicht rausrücken, weil die unter Verschluss stehen.
Parameter könnte z.B. so aussehen:
Parameter = [5, 10, 0.95, 1.0, pi/2, pi/2];
Und die beiden Eingangsmatrizen können theoretisch beliebige Matrizen sein (Werte sind ja für Code-Funktionalität egal, werden ja nur umgesetzt), zum Beispiel 300 x 300 oder so.
-
Habs grade mal ausprobiert, mit zwei 300 x 300 Matrizen und den oben angegebenen Parametern. Da läufts tatsächlich durch. Das könnte die Fehlerquelle für erfahrene C++-User deutlich eingrenzen, oder?
-
Naja, deine Fehlermeldung liest sich nach "über Arraygrenze rausgeschrieben", aber das schließt deine if-Bedingung eigentlich aus, soweit ich das sehe (iTrans ist RowsR*colsR breit und It ist rowsT*colsT groß). Danach vermute ich eine Division durch 0 bei dem cos-Termen, aber das sollte eigentlich ne andere Fehlermeldung geben. Und danach dann die ganzen Matlab-Geschichten: die Typen stimmen teilweise nicht ganz rowsR usw. sollten keine ints sondern mwSize oder sowas sein. Vielleicht wird eine der Größen irgendwann negativ, aber da müssten deine Matrixen schon größer als 5000x5000 werden, schätz ich.
Und als letzte Ursache ein "Problem im System". Ist das Visual Studio korrekt für Matlab konfiguriert? Wie gesagt, ich hab nur den LCC zur Hand, kann das also schlecht testen. Vielleicht mag Matlab zB. den C++-Overhead nicht besonders. Muß der vielleicht irgendwie C-kompatibel linken? (Ich hab auch nicht die aktuelle Matlab-Version, sondern irgendwas altes)
Das sind ein paar Sachen, über die ich mir Gedanken machen würde und die man systematisch ausschließen nacheinander kann. Wenn das nicht hilft, tja, http://www.mathworks.com/support/tech-notes/1600/1605.html#gen_debugging
-
Danke nochmal für die Hilfe!
Den von dir geposteten Link habe ich natürlich schon vor meinem Post hier gelesen, will ja den Leuten nicht grundlos ihre Zeit stehlen.
Hab den Code jetzt mal soweit abgeändert, wie du das vorher beschrieben hast, damit ich das auch mit dem LCC kompilieren kann:
#include "matrix.h" #include "mex.h" #include "math.h" int round(double value); int round(double value) { return (int)floor( value + 0.5 ); } void mexFunction(int nlhs, mxArray *plhs[], int nrhs, mxArray *prhs[]) { mxArray *mIr, *mIt, *mparam; double *Ir, *It, *param, *ITrans; int rowsR, colsR, rowsT, colsT, rn, cn; double pixel_size, x0, y0, mx, my, alpha, beta, X0, Y0, xr, yr, xt, yt, r, c, i0, j0, r0, c0;
Sprich: namespace raus, exception raus, Variablendeklaration komplett am Anfang (außer in den for-Schleifen).
Trotzdem haut der mir jetzt immer noch ne Menge Fehlermeldungen raus:
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 13 redeclaration of
mexFunction' previously declared at D:\\MATLAB~1\\extern\\include\\mex.h 135 Error E:\\Studium\\FERNERKUNDUNG\\Diplomarbeit\\Matlab\\trans\_affine\\trans\_affine\\trans_affine.cpp: 48 syntax error; found \
int' expecting `;'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 48 syntax error; found `int' expecting `;'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 48 syntax error; found `int' expecting `)'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 48 skippingint' Error E:\\Studium\\FERNERKUNDUNG\\Diplomarbeit\\Matlab\\trans\_affine\\trans\_affine\\trans_affine.cpp: 48 undeclared identifier
j'
Warning E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 48 Statement has no effect
Warning E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 48 unreachable code
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 48 syntax error; found)' expecting
;'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 48 illegal statement termination
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 48 skipping)' Error E:\\Studium\\FERNERKUNDUNG\\Diplomarbeit\\Matlab\\trans\_affine\\trans\_affine\\trans_affine.cpp: 50 syntax error; found \
int' expecting `;'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 50 syntax error; found `int' expecting `;'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 50 syntax error; found `int' expecting `)'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 50 skippingint' Error E:\\Studium\\FERNERKUNDUNG\\Diplomarbeit\\Matlab\\trans\_affine\\trans\_affine\\trans_affine.cpp: 50 undeclared identifier
i'
Warning E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 50 Statement has no effect
Warning E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 50 unreachable code
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 50 syntax error; found)' expecting
;'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 50 illegal statement termination
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 50 skipping `)'
Error E:\Studium\FERNERKUNDUNG\Diplomarbeit\Matlab\trans_affine\trans_affine\trans_affine.cpp: 71 illegal continue statement
18 errors, 4 warningsWas stört ihn denn da jetzt noch?! Vor allem komisch das mit der Redeclaration?!
-
Hmm, i und j kann man in C nicht direkt in der Schleife anlegen, sondern müssen auch an den Blockanfang. i0 und j0 und so hättest Du in der Schleife stehen lassen können, Du kannst in C -- wenigstens der alten, hier geforderten Version -- immer nur am Blockanfang, also nach einer '{' Variablen anlegen.
-
Ja, das ist mir mittlerweile auch klar geworden. Nachdem ich das bereinigt habe, sind alle Fehlermeldungen weggefallen - bis auf die erste:
...\trans_affine.c: 13 redeclaration of `mexFunction' previously declared at D:\MATLAB~1\extern\include\mex.h 135
Und die verstehe ich beim allerbesten Willen nicht.
EDIT: Hab mir natürlich mex.h mal zu Gemüte geführt, aber ich wüsste jetzt nicht, wo ich in Zeile 13 mexFunction redeklariere?!
-
Mach mal aus 'mxArray *prhs[]' ein 'const mxArray *prhs[]'.
-
Ändert leider nichts.
-
Das ist seltsam, weil ich bekomm (mit R2007a) ohne const genau deinen Fehler. Und mit dem korrekten const vor dem letzten Parameter gehts durch.
-
Hatte das .c-File im MATLAB-Editor geöffnet und verändert, da hat er gebugt und die Änderung nicht abgespeichert. Extern konnte ich's dann ändern und jetzt funktioniert's!
Ganz herzlichen Dank für die Hilfe, das war mein Einstieg ins C-/MEX-Programmieren.
Jetzt muss ich nur noch dafür sorgen, dass er auch bei großen Matrizen nicht mehr aussteigt (was er derzeit noch tut).