WICHTIG: Der Betrieb von goMatlab.de wird privat finanziert fortgesetzt. - Mehr Infos...

Mein MATLAB Forum - goMatlab.de

Mein MATLAB Forum

 
Gast > Registrieren       Autologin?   

Partner:




Forum
      Option
[Erweitert]
  • Diese Seite per Mail weiterempfehlen
     


Gehe zu:  
Neues Thema eröffnen Neue Antwort erstellen

MEX-Funktion auf logical Matrix anwenden

 

robertradar
Forum-Fortgeschrittener

Forum-Fortgeschrittener


Beiträge: 57
Anmeldedatum: 17.10.16
Wohnort: ---
Version: ---
     Beitrag Verfasst am: 17.10.2016, 16:40     Titel: MEX-Funktion auf logical Matrix anwenden
  Antworten mit Zitat      
Guten Tag,

ich kämpfe aktuell mit einem Problem, das ich mir noch nicht erklären kann.
Und zwar habe ich eine Matlab-Funktion, die bestimmte Werte einer Bool-Matrix "g" von 0 auf 1 setzt, mit dem aktuellen Matlab-C-Compiler (Microsoft Visual C++ 2015 Professional) in eine Mex-Funktion überführt, der ein der Matlab-Funktion gleichwertiges C-File zugrunde liegt.

g wird in meinen Experimenten von der MEX-Funktion stets unverändert ausgegeben. Eine gleichwertige Matlab-Funktion hingegen beschreibt g wie gewünscht.

Ich vermute ein Problem bei der Vewendung von logical-Variablen, konkret die Zeile mit
ptr_g[pos] = 1;
.
Ich habe mittlerweile entdeckt, dass es reibungslos funktioniert, wenn g ein Double-Array ist. Bei allen davon abweichenden Datentypen erhalte ich bei einmaliger Ausfühung nicht den erwarteten Rückgabewert g bzw. bei Ausführung des gesamten Programmes (Funktion wird mehrere 100x aufgerufen) einen Matlab Systemfehler.

Code:
% Matlab-Funktion
function [ g ] = calc_triangle( g, v )
%Algorithmus teilt Dreieck in zwei Teildreiecke:
%eines mit unterer Kante parallel zur x-Achse (bottom-flat),
%eines mit oberer Kante parallel zur x-Achse (top-flat)
 
v1 = v(1,:);
v2 = v(2,:);
v3 = v(3,:);
 
%es gilt: v1(2) <= v2(2) <= v3(2)
if v2(2) == v3(2) %keine Teilung nötig: Dreieck ist bereits bottom-flat
    g = mex_fillBottomFlatTriangle(g, v1, v2, v3);
else
    if v1(2) == v2(2) %keine Teilung nötig: Dreieck ist bereits top-flat
        g = fillTopFlatTriangle(g, v1, v2, v3);
    else %allgemeiner Fall: Dreieck in bottom-flat und top-flat aufteilen
        v4 = [floor(v1(1) + ((v2(2) - v1(2)) / (v3(2) - v1(2))) * (v3(1) - v1(1))), v2(2)];
        g = mex_fillBottomFlatTriangle(g, v1, v2, v4);
        g = fillTopFlatTriangle(g, v2, v4, v3);
    end
end
end
 



Code:
% MEX-File
#include <math.h>
#include <matrix.h>
#include <mex.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
   //declare variables
   mxArray *g;
   mxArray *v1;
   mxArray *v2;
   mxArray *v3;
   mxArray *output;
   mxArray *g_new;
   double *ptr_g;
   double *ptr_v1;
   double *ptr_v2;
   double *ptr_v3;
   double invslope1, invslope2;
   double curx1, curx2;
   int curx_min, curx_max;
   const int *dim_array;
   int x_size, y_size;
   int scanlineY;
   int v_min;         // ist das eine ganze Zahl?!
   int min_1, min_2, i, pos;
   int curx1_floor, curx2_floor;

   //associate inputs
   g = mxDuplicateArray(prhs[0]);
   v1 = mxDuplicateArray(prhs[1]);
   v2 = mxDuplicateArray(prhs[2]);
   v3 = mxDuplicateArray(prhs[3]);

   //get pointer
   ptr_g = mxGetPr(g);
   ptr_v1 = mxGetPr(v1);
   ptr_v2 = mxGetPr(v2);
   ptr_v3 = mxGetPr(v3);
   
   invslope1 = (ptr_v2[0] - ptr_v1[0]) / (ptr_v2[1] - ptr_v1[1]);
   invslope2 = (ptr_v3[0] - ptr_v1[0]) / (ptr_v3[1] - ptr_v1[1]);

   curx1 = ptr_v1[0];
   curx2 = ptr_v1[0];

   dim_array = mxGetDimensions(g);
   x_size = dim_array[0];
   y_size = dim_array[1];

   v_min = ((ptr_v2[1] < y_size) ? (ptr_v2[1]) : (y_size));  
   for (scanlineY = (int) ptr_v1[1]; scanlineY <= v_min; ++scanlineY) {
      if (scanlineY >= 1) {
         if ((curx1 < 1 && curx2 < 1) || (curx1 > x_size && curx2 > x_size)) {
            continue;
         }
         curx1_floor = floor(curx1);
         min_1 = ((curx1_floor < x_size) ? (curx1_floor) : (x_size));
         curx_min = ((1 > min_1) ? (1) : (min_1));

         curx2_floor = floor(curx2);
         min_2 = ((curx2_floor < x_size) ? (curx2_floor) : (x_size));
         curx_max = ((1 > min_2) ? (1) : (min_2));

         if (curx1 > curx2) {
            for (i = curx_max; i <= curx_min; i++) {
               pos = (scanlineY - 1)*x_size + i - 1;
               ptr_g[pos] = 1;
            }
         }
         else {
            for (i = curx_min; i <= curx_max; i++) {

               pos = (scanlineY - 1)*x_size + i - 1;
               ptr_g[pos] = 1;
            }
         }
      }
      curx1 = curx1 + invslope1;
      curx2 = curx2 + invslope2;
   }
   g_new = g;
   output = plhs[0] = g_new;

}
 
Private Nachricht senden Benutzer-Profile anzeigen


Jan S
Moderator

Moderator


Beiträge: 11.057
Anmeldedatum: 08.07.10
Wohnort: Heidelberg
Version: 2009a, 2016b
     Beitrag Verfasst am: 18.10.2016, 10:18     Titel: Re: MEX-Funktion auf logical Matrix anwenden
  Antworten mit Zitat      
Hallo robertradar,

C ist eine sehr empfindliche Sprache. Hier muss man wirklich ganz genau wissen, was man tut.

Code:
mxArray *g;
mxArray *g_new;
double *ptr_g;

% Jetzt ist ptr_g ein Pointer auf ein DOUBLE Array. Der Input ist aber kein DOUBLE!
% ptr_g[1] greift dann z.B. auf [ptr_g + 8] zu, nicht auf [ptr_g + 1]

g = mxDuplicateArray(prhs[0]);

% Jetzt werden die Inputs dupliziert, was zeitraubend und überflüssig ist:
v1 = mxDuplicateArray(prhs[1]);
v2 = mxDuplicateArray(prhs[2]);
v3 = mxDuplicateArray(prhs[3]);
% Greife besser auf die Daten der Originale zurück, ansonsten wird es sich kaum lohnen
% eine C-Mex-Funktion zu verwenden.

//get pointer
ptr_g = mxGetPr(g);
% !!!! Das ist ein Fehler, wenn der 1. Input kein DOUBLE ist!
% Besser:
mxLogical *ptr_g;
ptr_g = (mxLogical *) mxGetData(prhs[0]);


Zur Sicherheit sollte man also unbedingt die Typen der Inputs testen, z.B. mit "mxIsLogical()". Wenn man einen Scalar von den Inputs ausliest, ist mxGetScalar geeignet, weil es auch Integer-Werte umwandelt.
Beim Indizieren von Arrays ist es in C notwendig, dass der Typ des Pointers zum Inhalt der Daten passt.

Gruß, Jan
Private Nachricht senden Benutzer-Profile anzeigen
 
robertradar
Themenstarter

Forum-Fortgeschrittener

Forum-Fortgeschrittener


Beiträge: 57
Anmeldedatum: 17.10.16
Wohnort: ---
Version: ---
     Beitrag Verfasst am: 18.10.2016, 15:11     Titel:
  Antworten mit Zitat      
Videln Dank für deine kompetente Antwort! Ich werde es demnächst umsetzen.
Private Nachricht senden Benutzer-Profile anzeigen
 
robertradar
Themenstarter

Forum-Fortgeschrittener

Forum-Fortgeschrittener


Beiträge: 57
Anmeldedatum: 17.10.16
Wohnort: ---
Version: ---
     Beitrag Verfasst am: 16.11.2016, 15:08     Titel:
  Antworten mit Zitat      
Hallo Jan,
ich habe deine vorherige Antwort versucht zu implementieren.
Hier mal der aktuelle relevante Code:

Code:

#include <math.h>
#include <matrix.h>
#include <mex.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mxArray *g;
mxLogical *ptr_g;

g = mxDuplicateArray(prhs[0]);
% ich habe es nicht geschafft, das zu umgehen. Du hattest vorgeschlagen „ptr_g = (mxLogical *) mxGetData(prhs[0]);“. Mit diesem Befehl wurde aber in Matlab beim Aufruf der Funktion „g_new = mex_funktion(g, andere variablen)“ komischerweise g verändert (und nicht g_new). Wie kann ich dieses Problem lösen?

ptr_g = (mxLogical *) mxGetData(g);

% Im weiteren Verlauf des Codes möchte ich nun Einträge der Matrix g mit Einsen befüllen, die anfangs alle mit einer 0 belegt waren (daher reicht ja der Datentyp logical). Das mache ich auf folgende Art:

ptr_g[pos] = 1;

% Am Ende möchte ich nun das veränderte mxArray g wieder ausgeben:

mxSetData(g, ptr_g);
plhs[0] = g;


 


Das funktioniert jetzt auch eigentlich. Der Aufruf „g_new = mex_funktion(g, andere variablen) beschreibt g_new wie beabsichtigt und der Datentyp ist wie erhofft logical.

Allerdings wird diese Mex-Funktion sehr häufig aufgerufen und nach einigen Durchläufen nimmt der freie physikalische Speicher des PCs stark ab und Matlab wird extrem langsam.
Ich befürchte also, dass im C-Code eine Art Speicherfreigabe am Ende der Funktion notwendig ist (z.B. mxFree oder mxDestroyArray). Ich habe bisher jedoch keine Lösung dieses Problems gefunden.

Hast du einen Rat?

Vielen Dank im Voraus.
Private Nachricht senden Benutzer-Profile anzeigen
 
Jan S
Moderator

Moderator


Beiträge: 11.057
Anmeldedatum: 08.07.10
Wohnort: Heidelberg
Version: 2009a, 2016b
     Beitrag Verfasst am: 16.11.2016, 15:55     Titel:
  Antworten mit Zitat      
Hallo robertradar,
Code:
% Am Ende möchte ich nun das veränderte mxArray g wieder ausgeben:
% Nein, kein mxSetData! Diese Zeile weglassen:
mxSetData(g, ptr_g);

Mit mxSetData weißt Du dem Array einen Speicherblock zum Speichern der Elemente zu. Der vorher existierende Block wird dabei nicht freigegeben (siehe Dokumentation vom mxSetData). Nun ist g_ptr aber der Speicherblock, der ja bereits vorher im Array verwendet wurde. Also eigentlich wurde der Pointer mit sich selbst überschrieben. Ich hätte erwartet, dass Matlab das später automatisch freigeben kann. Trotzdem: mxSetData ist etwas für Leute, die sehr genau wissen, was sie tun und für den normalen Gebrauch nie erforderlich. Lasse es mal weg und berichte, ob das Problem gelöst ist.

Gruß, Jan

Nachtrag: Der Code lässt sich etwas vereinfachen:
Code:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mxLogical *ptr_g;
plhs[0] = mxDuplicateArray(prhs[0]);
ptr_g = (mxLogical *) mxGetData(plhs[0]);

ptr_g[pos] = 1;

% Das reicht und man vermeidet Verwirrung mit den "g"s
}

Das Input zu duplizieren kostet allerdings ein wenig mehr Zeit als ein neues mit mxCreateNumericMatrix zu erstellen. Die Werte des Input-Arrays dürfen dann nur ausgelesen werden! Geschrieben wird dann nur ins Output-Array, Du bräuchtest also "*ptr_g_in" und einen "*ptr_g_out", die auf 2 verschiedene Arrays zeigen. Direkt ins Input-Array zu schreiben ist eine tückische Sache, die man unbedingt vermeiden sollte, da Matlab's Copy-on-Write Methode das ausschließt:
Code:
a = rand(1, 1e6);
b = a;

Wenn man jetzt a und b an ein Mex-Script gibt und die Pointer zu den Daten bekommt, stellt man fest, dass beide auf den selben Speicherbereich zeigen. Wenn Du dann im Mex-File den Wert von a änderst, wird auch b geändert! So kann man sich leicht Bugs einhandeln, die man kaum noch nachvollziehen kann. Deshalb darf man grundsätzlich niemals in einem Mex-File in einen der Inputs etwas hineinschreiben. (Es sei denn, man kennt die undokumentierten Methoden, die das wieder erlauben und den Code viel effizienter machen...)

Zuletzt bearbeitet von Jan S am 16.11.2016, 16:09, insgesamt einmal bearbeitet
Private Nachricht senden Benutzer-Profile anzeigen
 
Harald
Forum-Meister

Forum-Meister


Beiträge: 24.501
Anmeldedatum: 26.03.09
Wohnort: Nähe München
Version: ab 2017b
     Beitrag Verfasst am: 16.11.2016, 15:56     Titel:
  Antworten mit Zitat      
Hallo,

wie hast du denn versucht, die genannten Funktionen einzusetzen?

Wäre es eine Alternative, die MEX-Function mit der MATLAB Coder App ( coder )zu generieren? Dann solltest du dich nicht mit dem Code abmühen müssen.

Grüße,
Harald
Private Nachricht senden Benutzer-Profile anzeigen
 
robertradar
Themenstarter

Forum-Fortgeschrittener

Forum-Fortgeschrittener


Beiträge: 57
Anmeldedatum: 17.10.16
Wohnort: ---
Version: ---
     Beitrag Verfasst am: 23.11.2016, 12:02     Titel:
  Antworten mit Zitat      
Hallo zusammen,
@ Jan,

vielen Dank für deine ausführliche und sehr zielführende Hilfe! Beide Vorschläge funktionieren super, ich habe mich für Deinen Nachtrag entschieden, da man sich dadurch – wie Du bereits geschrieben hast – die Variable „g“ spart.
Außerdem erklärt Dein Beispiel, wieso in meiner ursprünglichen Implementierung in Matlab bei „g_new = mex_function(g, andere variablen)“ g und nicht g_new verändert wurde.

@ Harald,
ich habe die Funktionen mit meinem C Halbwissen in Microsoft Visual Studio selber geschrieben und dann aus Matlab heraus kompiliert um insgesamt Zeit zu sparen.
Die Matalb Coder App hatte ich bisher noch gar nicht in Betracht gezogen, vielen Dank für diesen Hinweis!
Private Nachricht senden Benutzer-Profile anzeigen
 
Neues Thema eröffnen Neue Antwort erstellen



Einstellungen und Berechtigungen
Beiträge der letzten Zeit anzeigen:

Du kannst Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.
Du kannst Dateien in diesem Forum posten
Du kannst Dateien in diesem Forum herunterladen
.





 Impressum  | Nutzungsbedingungen  | Datenschutz | FAQ | goMatlab RSS Button RSS

Hosted by:


Copyright © 2007 - 2025 goMatlab.de | Dies ist keine offizielle Website der Firma The Mathworks

MATLAB, Simulink, Stateflow, Handle Graphics, Real-Time Workshop, SimBiology, SimHydraulics, SimEvents, and xPC TargetBox are registered trademarks and The MathWorks, the L-shaped membrane logo, and Embedded MATLAB are trademarks of The MathWorks, Inc.