3D-Image umkopieren / performant filtern
-
Heyho,
ich arbeite derzeit mit 3D-Bilddatensätzen und versuche diese für ein Projekt halbwegs performant weiter zu verarbeiten.
Um das zu erreichen, benutzte ich OpenCV.
Leider unterstützt openCV nur 2D-Datensätze, ich muss aber auch 3D-Filter umsetzten.Mein bisheriges vorgehen:
Ich habe alle meine Filter in 3 1D-Filter gesplittet.
So kann ich mit Hilfe von OpenCV sehr schnell über die X- und Y-Achse rennen.
Allerdings frisst die Z-Achse sehr viel Zeit.Daraufhin ist mir die Idee gekommen mein Array um zu drehen, so dass beispielsweise die Y-Achse zur Z-Achse wird.
Diese dann mit einem 1D-Filter zu filtern und es wieder zurück zu drehen.Ansatz im code
void copyYAxisToZAxis(vector<cv::Mat *> *src, vector<cv::Mat *> **dest, bool copyBackwards = false) { vector<cv::Mat *> *tmp; int srcDepth = (*src).size(); if(src == *dest) { tmp = new vector<cv::Mat *>(); } else if((*dest)->size() > 0) { cerr << "\nWrong usage of deviate3D(vector<cv::Mat *> *src, vector<cv::Mat *> **dest, char direction).\nDimensions must be 0.\n" << endl; return; } else { tmp = *dest; } if(srcDepth <= 0) { cerr << "\nWrong usage of copyYAxisToZAxis(vector<cv::Mat *> *src, vector<cv::Mat *> **dest).\nSrc Dimension must be greater 0.\n" << endl; return; } int srcWidth = (*src)[0]->size().width; int srcHeight = (*src)[0]->size().height; for(int destZ = 0; destZ < srcHeight; destZ ++) { tmp->push_back(new cv::Mat(srcDepth, srcWidth, CV_64F, cv::Scalar(0))); } for(int destZ = 0; destZ < srcHeight; destZ ++) { for(int destY = 0; destY < srcDepth; destY ++) { for(int destX = 0; destX < srcWidth; destX ++) { if(copyBackwards) ((*tmp)[destZ])->at<double>(destY, destX) = ((*src)[(srcDepth-destY-1)])->at<double>(destZ, destX); else ((*tmp)[destZ])->at<double>(destY, destX) = ((*src)[destY])->at<double>(srcHeight-destZ-1, destX); } } } if(src == *dest) { delete src; *dest = tmp; } } // EDIT: - Funktion so erweitert, dass sie auch Rückwärts die Achsen tauschen kann ( also auch Z zu Y)
vector<cv::Mat *> *tmp = new vector<cv::Mat *>(); double test[] = {1, 2, 3, 4}; double test2[] = {5, 6, 7, 8}; tmp->push_back(new cv::Mat(2,2, CV_64F, test)); tmp->push_back(new cv::Mat(2,2, CV_64F, test2)); cout << *((*tmp)[0]) << endl << *((*tmp)[1]) << endl; copyYAxisToZAxis(tmp, &tmp); cout << *((*tmp)[0]) << endl << *((*tmp)[1]) << endl;
Der Ansatz läuft, aber ist alles andere als schön/lesbar.
Und so performant wie erhofft ist er nun auch nicht.Wenn ich da in nem Jahr nochmal drüber guck versteh ich da nix mehr ^^.
Kennt jemand einen Weg 3D Images performant zu filtern (Beispielsweise Gauß-Filter)?
Oder kennt jemand einen Weg um 3D-Bilder performant zu "drehen" um so ein workaround zum 3D-Filtern erzeugen zu können?Ich benutzte derzeit OpenGL, glm und opencv.
Falls jemand ein kostenloses Framework kennt das mir da weiterhelfen kann und kompatibel mit OpenGL 3+ ist würde ich darauf allerdings auch noch gerne zurück greifen.Ich hoffe Ihr könnt mir helfen.
Gruß
Jango
-
bei jedem wert den du umkopieren moechtest, machst du unmengen redundanter operationen, dein inner loop ist somit ein paradebeispiel fuer optimierungspotential.
besorg dir eine ebene drueber die source und destination pointer und dann sollte dein innerloop nicht mehr sein als
for(int destX = 0; destX < srcWidth; destX++,pDst+=Pitch) *pDst=*pSrc++;
zudem, da du scheinbar c++ nutzt, uebergebe die parameter als referencen, statt pointer, dann sieht dein code schonmal leserlicher aus (auch noch in einem jahr).
-
Heyho rapso,
bin nicht sicher ob ich dir Folgen kann.
rapso schrieb:
besorg dir eine ebene drueber die source und destination pointer und dann sollte dein innerloop nicht mehr sein als
for(int destX = 0; destX < srcWidth; destX++,pDst+=Pitch) *pDst=*pSrc++;
in meiner inneren Loop berechne ich die neue Position(x,y,z) meines wertes.
Dies mache ich indem ich eine Rotation, gefolgt von einer Translation auf meinen 3D Datensatz anwende.sin(pi/2) = 1 cos(pi/2) = 0 x' 1 0 0 0 1 0 0 0 x y'= 0 1 0 z_max *0 0 -1 0 * y z' 0 0 1 0 0 1 0 0 z 1 0 0 0 1 0 0 0 1 1 transl. rotation vek => x' = x y' = z_max - z z' = y
Und das setzte ich mit der Zeile
((*tmp)[destZ])->at<double>(destY, destX) = ((*src)[destY])->at<double>(-destZ+srcDepth-1, destX);
um.
Wie ich mir die Berechnung Sparen kann, durch einfaches Pointer umhängen verstehe ich nicht ganz.
Wenn ich (falls du das meinst) anstatt die Werte zu kopieren einfach Zeiger umhänge, bekomme ich das Problem, dass ich evt mit 2 Unterschiedlichen Bildern auf ein und den selben Wert zugreife.
Beispiel:// Funktion (fiktiv) umgeschrieben auf Pointer umhängen copyYAxisToZAxis(a, &b); a[0][1][2] = 7; // b[0][27][1] ist plötzlich auch 7 bei 30 Schichten in der z-Ebene
und...
rapso schrieb:
zudem, da du scheinbar c++ nutzt, uebergebe die parameter als referencen, statt pointer, dann sieht dein code schonmal leserlicher aus (auch noch in einem jahr).
Ja, ich hatte auch schon überlegt mit ref. zu arbeiten,
allerdings sind meinen bescheidenen OOA-Wissens zu folge ref. ja konstante Pointer.
Und da ich meinen Code so halten wollte, dass ich die Funktion mit z.B.copyYAxisToZAxis(a, a);
aufrufen kann geht das doch nicht so einfach,
denn Dazu muss ich mir ja ein tmp bild anlegen, in das ich meine umgerechneten Daten ablege, dann den Speicher von a freigeben mittels delete, und anschließend den Pointer von a umhängen.
Da ref allerdings const pointer sind darf ich die ja unter keinen umständen verändern.
Also bin ich bei Zeigern geblieben.Da ich an die Funktion allerdings auch nur eine Kopie meines Zeigers übergebe, und diese nach dem Umhängen wieder wieder verworfen wird, muss ich mit Zeiger auf Zeiger auf vector arbeiten.
Jetzt muss ich die funktion halt so aufrufen
copyYAxisToZAxis(a, &a);
Berichtige mich, falls ich einen Denkfehler habe
würde gerne mit ref. arbeitenGruß
Jango
-
vielleicht solltest du es nicht als kopie, sondern als transpose sehen. als simpler linearer code waere das dann
for(int x=0;x<width;x++) { const double TempZ=pIn[0][0][x]; const double TempY=pIn[0][x][0]; pOut[0][0][x]=TempY; pOut[0][x][0]=TempZ; }
damit reduziert sich deine ganze funktion auf ein paar zeilen, die zudem auch noch sehr viel schneller laufen, keinen temporaerspeicher verbrauchen und in einem jahr noch verstaendlich sind. (+kannst references nutzen)
(und ja, du musst die anderen achsen noch dazu coden, das war nur ein beispiel)
-
rapso schrieb:
vielleicht solltest du es nicht als kopie, sondern als transpose sehen. als simpler linearer code waere das dann
for(int x=0;x<width;x++) { const double TempZ=pIn[0][0][x]; const double TempY=pIn[0][x][0]; pOut[0][0][x]=TempY; pOut[0][x][0]=TempZ; }
damit reduziert sich deine ganze funktion auf ein paar zeilen, die zudem auch noch sehr viel schneller laufen, keinen temporaerspeicher verbrauchen und in einem jahr noch verstaendlich sind. (+kannst references nutzen)
(und ja, du musst die anderen achsen noch dazu coden, das war nur ein beispiel)
Das entspricht doch grob dem was ich gemacht habe.
Ich fasse mal deinen Code ein bisschen zusammen:
for(int x=0;x<width;x++) { const double TempZ=pIn[0][0][x]; const double TempY=pIn[0][x][0]; pOut[0][0][x]=TempY; pOut[0][x][0]=TempZ; } // entspricht ohne tmp variablen for(int x=0;x<width;x++) { pOut[0][0][x]=pIn[0][x][0]; pOut[0][x][0]=pIn[0][0][x]; } //entspricht in meinem Code for(int destX = 0; destX < srcWidth; destX ++) { ((*tmp)[destZ])->at<double>(destY, destX) = ((*src)[destY])->at<double>(srcHeight-destZ-1, destX); // in schön steht da dann // pOut[z][y][x] = pIn[y][height-z-1][x] // Das "-1" bei der höhe kommt zustande, da ich ja mit Indizes arbeite, und die bei 0 anfangen zu zählen }
den Zwischenspeicher habe ich nur eingebaut, um die Funktion mit pIn = pOut auf rufen zu können.
Der Zwischenspeicher selber ist, auch wenn es der Name vermuten lässt, nicht wirklich temporär.Wenn ich die Funktion Aufrufe mit pIn != pOut komme ich am anfang in den else-Zweig (siehe kommentare).
Wenn aber pIn == pOut, komme ich am ende ins if()if(src == *dest) tmp = new vector<cv::Mat *>(); else // pIn != pOut tmp = *dest; // .... eigentliche Funktion if(src == *dest) { // Speicher von "Source" freigeben for(int i = 0; i < src->size(); i ++) { delete (*src)[i]; } delete src; // Ziel = tmp *dest = tmp; }
Du siehst also, dass in jedem Fall mein tmp eigentlich mein Ergebnis ist.
(Sollte den Namen vllt mal ändern )Wenn ich das nicht so machen würde, müsste ich dennoch den Speicher für mein Ziel außerhalb der Funktion reservieren.
Da komm ich nicht drum rum den einmal an zu legen.
Ob ichs nur außerhalb oder innerhalb mache tut der Laufzeit keinen Abbruch.Es lediglich als Transpose zu betrachten Funktioniert leider nicht.
Es ist eigentlich lediglich eine Rotation.
Da ich allerdings nicht im 3D-Rendering-Space bin, sondern tatsächlich auf dem Speicher arbeite muss ich verhindern Negative werte an zu nehmen.
Deshalb transponiere ich meine Rotierte matrix um Zmax bzw Ymax (je nachdem ob + oder - 90°) um in meinem gültigen positiven Index-Bereich zu bleiben.Gruß
-
JangoK schrieb:
Das entspricht doch grob dem was ich gemacht habe.
natuerlich macht mein code dasselbe wie deiner, funktional sollte sich nichts aendern. nur ist er schneller. er liest einen wert, schreibt einen wert, inkrementiert zwei pointer,prueft schleifenbedingung und springt (ursprungsversion ganz oben).
6instruktionen im optimalfall.deiner
((*tmp)[destZ])->at<double>(destY, destX) = ((*src)[destY])->at<double>(srcHeight-destZ-1, destX);
macht ein klein wenig mehr
(*tmp)[destZ] //zugrif auf einen coniners also lesen of pointer auf die daten und mit destZ offset berechnen und dann den pointer fuer die naechste zeile lesen : +3 (destY, destX) // berechnet vermutlich x*breit+z +2 ->at<double> // at macht einen boundscheck, liest den pointer fur die daten und indiziert darauf und liefert die referenz (addresse) fuer die zuweisungsoperation: +3 = ((*src)[destY])->at<double>(srcHeight-destZ-1, destX);//dieselben 8 operationen und dazu zwei subtraktionen, lesen des wertes und die schlussendliche zuweisung: +12
du siehst, 6 vs 20 logische operationen. auf der cpu seite passt mein code super in ein paar register und das einzig aufwendige was die cpu machen muss ist daten verschieben, das wird so schnell gehen wie dein speicher es schafft.
bei deiner implementirung reichen die register eventuel nicht aus, zu den 20operationen kommen also noch einige dazu. manche operationen (wie die multiplikation) dauern laenger und du hast abhaengigkeiten zwischen operationen (z.b. die addition nach der multiplikation) das verhindert dass die cpu die befehle pipelinen kann, womit sie also am anfang der pipeline mit der addition wartet bis am ende der pipeline die multiplikation rauskommt.
deine variante kann also 5 bis 10mal mehr zeit kosten fuer dieselbe kleine aufgabe.Ich fasse mal deinen Code ein bisschen zusammen:
for(int x=0;x<width;x++) { const double TempZ=pIn[0][0][x]; const double TempY=pIn[0][x][0]; pOut[0][0][x]=TempY; pOut[0][x][0]=TempZ; } // entspricht ohne tmp variablen for(int x=0;x<width;x++) { pOut[0][0][x]=pIn[0][x][0]; pOut[0][x][0]=pIn[0][0][x]; }
nein, damit ist es nicht mehr in-place. du hattest als anforderung gestellt dass du denselben container als ein und ausgabe nutzen willst, was logisch dann
for(int x=0;x<width;x++) { pData[0][0][x]=pData[0][x][0]; pData[0][x][0]=pData[0][0][x]; }
waere. du merkst, du schreibst pData[0][0][x], liest es dann und schreibst es zurueck. du brauchst also die temporaeren variablen...
//entspricht in meinem Code for(int destX = 0; destX < srcWidth; destX ++) { ((*tmp)[destZ])->at<double>(destY, destX) = ((*src)[destY])->at<double>(srcHeight-destZ-1, destX); // in schön steht da dann // pOut[z][y][x] = pIn[y][height-z-1][x] // Das "-1" bei der höhe kommt zustande, da ich ja mit Indizes arbeite, und die bei 0 anfangen zu zählen }
natuerlich solltest du es nicht als 3dimensionales array machen, das war ja nur ein beispiel
du traversierst im inner-loop in festen schritten, je fuer pOut und pIn, du musst also nicht im inner loop die addresse neu berechnen lassen, du kannst ausserhalb vom loop einmal den anfangspunkt fuer in und out bestimmen und die jeweilige 'steigung' ermitteln.den Zwischenspeicher habe ich nur eingebaut, um die Funktion mit pIn = pOut auf rufen zu können.
ich auch, nur besteht der 'zwischenspeicher' bei mir aus zwei kleinen variablen, TempZ und TempY
Der Zwischenspeicher selber ist, auch wenn es der Name vermuten lässt, nicht wirklich temporär.
du rufst innerhalb der funktion new und delete auf, das sieht schon sehr temporaer aus. je nach container groesse hast du eventuel mehr zeit in den system calls als in der eigentlichen transponierung.
Wenn ich die Funktion Aufrufe mit pIn != pOut komme ich am anfang in den else-Zweig (siehe kommentare).
Wenn aber pIn == pOut, komme ich am ende ins if()if(src == *dest) tmp = new vector<cv::Mat *>(); else // pIn != pOut tmp = *dest; // .... eigentliche Funktion if(src == *dest) { // Speicher von "Source" freigeben for(int i = 0; i < src->size(); i ++) { delete (*src)[i]; } delete src; // Ziel = tmp *dest = tmp; }
einer der 'spezialfaelle' die den code nicht wirklich schoen und in einem jahr leicht verstaendlich machen, oder?
Du siehst also, dass in jedem Fall mein tmp eigentlich mein Ergebnis ist.
(Sollte den Namen vllt mal ändern )minor, oder?
Es lediglich als Transpose zu betrachten Funktioniert leider nicht.
Es ist eigentlich lediglich eine Rotation.
Da ich allerdings nicht im 3D-Rendering-Space bin, sondern tatsächlich auf dem Speicher arbeite muss ich verhindern Negative werte an zu nehmen.
Deshalb transponiere ich meine Rotierte matrix um Zmax bzw Ymax (je nachdem ob + oder - 90°) um in meinem gültigen positiven Index-Bereich zu bleiben.das machst du, die frage ist aber, ist das noetig? ein filter wie z.B. guass den du oben angabst brauchst keine rotation, transpose wuerde reichen. hast du filter bei denen die richtung kritisch ist?
-
rapso schrieb:
JangoK schrieb:
Das entspricht doch grob dem was ich gemacht habe.
natuerlich macht mein code dasselbe wie deiner, funktional sollte sich nichts aendern. nur ist er schneller. er liest einen wert, schreibt einen wert, inkrementiert zwei pointer,prueft schleifenbedingung und springt (ursprungsversion ganz oben).
6instruktionen im optimalfall.deiner
((*tmp)[destZ])->at<double>(destY, destX) = ((*src)[destY])->at<double>(srcHeight-destZ-1, destX);
macht ein klein wenig mehr
(*tmp)[destZ] //zugrif auf einen coniners also lesen of pointer auf die daten und mit destZ offset berechnen und dann den pointer fuer die naechste zeile lesen : +3 (destY, destX) // berechnet vermutlich x*breit+z +2 ->at<double> // at macht einen boundscheck, liest den pointer fur die daten und indiziert darauf und liefert die referenz (addresse) fuer die zuweisungsoperation: +3 = ((*src)[destY])->at<double>(srcHeight-destZ-1, destX);//dieselben 8 operationen und dazu zwei subtraktionen, lesen des wertes und die schlussendliche zuweisung: +12
du siehst, 6 vs 20 logische operationen. [...]
Ok, got it
Glaub ich zu mindest ^^.
Das Problem:
rapso schrieb:
(destY, destX) // berechnet vermutlich x*breit+z +2
->at<double> // at macht einen boundscheck, liest den pointer fur die daten und indiziert darauf und liefert die referenz (addresse) fuer die zuweisungsoperation: +3ist Datenstruktur bedingt, da hab ich keine Möglichkeit zur Verbesserung, wenn ich nicht auf OpenCV verzichten möchte.
Warum ich das nicht möchte komme ich gleich zu. ^^rapso schrieb:
= ((*src)[destY])->at<double>(srcHeight-destZ-1, destX);//dieselben 8 operationen und dazu zwei subtraktionen, lesen des wertes und die schlussendliche zuweisung: +12
Die Subtraktionen entsprechen der translation um (srcHeight-1) Warum ich die brauche ebenfalls gleich ^^
rapso schrieb:
nein, damit ist es nicht mehr in-place. du hattest als anforderung gestellt dass du denselben container als ein und ausgabe nutzen willst, was logisch dann
for(int x=0;x<width;x++) { pData[0][0][x]=pData[0][x][0]; pData[0][x][0]=pData[0][0][x]; }
waere. du merkst, du schreibst pData[0][0][x], liest es dann und schreibst es zurueck. du brauchst also die temporaeren variablen...
Ok, macht vollkommen Sinn, denkfehler meinerseits.
Werde ich versuchen um zusetzten für den Fall pin == pout.
Für den Fall pin != pout brauch ich allerdings immernoch mein komplettes 3D-"TMP"-Array was dann allerdings den namen dest kriegen wird ^^rapso schrieb:
Wenn ich die Funktion Aufrufe mit pIn != pOut komme ich am anfang in den else-Zweig (siehe kommentare).
Wenn aber pIn == pOut, komme ich am ende ins if()if(src == *dest) tmp = new vector<cv::Mat *>(); else // pIn != pOut tmp = *dest; // .... eigentliche Funktion if(src == *dest) { // Speicher von "Source" freigeben for(int i = 0; i < src->size(); i ++) { delete (*src)[i]; } delete src; // Ziel = tmp *dest = tmp; }
einer der 'spezialfaelle' die den code nicht wirklich schoen und in einem jahr leicht verstaendlich machen, oder?
Ja, dass ist Richtig, aber das ist einer der "Spezialfälle" auf die ich nicht verzichten möchte/kann.
Muss sie nur noch schöner verpackenSonderbehandlungen für speed und lesbarkeit und vielseitige funktionalität vertragen sich scheinbar nich so gut
rapso schrieb:
Es lediglich als Transpose zu betrachten Funktioniert leider nicht.
Es ist eigentlich lediglich eine Rotation.
Da ich allerdings nicht im 3D-Rendering-Space bin, sondern tatsächlich auf dem Speicher arbeite muss ich verhindern Negative werte an zu nehmen.
Deshalb transponiere ich meine Rotierte matrix um Zmax bzw Ymax (je nachdem ob + oder - 90°) um in meinem gültigen positiven Index-Bereich zu bleiben.das machst du, die frage ist aber, ist das noetig? ein filter wie z.B. guass den du oben angabst brauchst keine rotation, transpose wuerde reichen. hast du filter bei denen die richtung kritisch ist?
Also...
Ich habe meine Matrix [512][512][40] z.B.
Diese möchte ich 3x 1D-Gauß filtern und danach noch nen paar andere Filter drüber jagen die ich auch alle auf 1D runter gebrochen habe.Mit Hilfe von OpenCV und der filter2D funktion lassen sich die Bilder sehr schnell in 1D oder in 2D filtern.
Die Filtermaske die ich übergeben ist variabel, kann also auch eine 1D maske sein.
Ich nehme an, dass hier intern im Frequenzraum gefiltert wird.Möchte ich jetzt einen meiner Filter
Nehmen wir mal als Beispiel ne ganz einfache Ableitung [ -1, 0 , 1] über die Z-Ebene jagen,
Muss ich natürlich (512*512*40) mal 3 Pixel anfassen,
Wobei das schlimme daran ist, dass die 3 Pixel auf der Z-Achse liegen,
also nicht hintereinander im Speicher, also (512*512*40*3) Seitenfehler produziert werden, die das ganze extrem langsam machen. ( Hier sollte eigentlich der Kernpunkt meiner Optimierungs-Anfrage liegen, dass frisst nämlich sehr sehr viel mehr Zeit als die 14 Operationen innerhalb der inneren for, die ich mehr mache als dein Vorschlag )Da ich mehrere Filter über mein Bild Jage, habe ich dann den Ansatz gewählt, mein Bild so zu drehen, dass ich wieder mit Filter2D arbeiten kann und mir somit das Problem des Filtern in der Z-Ebene spare.
Das Ganze hat sich auch als schneller heraus gestellt.Warum brauche ich meiner Meinung nach dafür eine Rotation und eine Translation?
Möchte ich mein Bild um die X-Achse drehen, sodass meine neue Y-Achse meine alte Z-Achse ist (damit ich gut über z filtern kann)
so muss ich folgende Rotations-Matrix anwenden:
Rx 1 0 0 0 cos(t) -sin(t) 0 sin(t) cos(t)
Mit dieser Rotations-Matrix komme ich auf eine ZielMatrix von M2[512][40][512]
Wobei sich die Indizies auf der neuen Y-Achse jetzt zwischen -39 und 0 bewegen.
Da der versuch auf einen negativen Index zu zugreifen allerdings einen Speicherzugriffsfehler wirft
muss ich dann noch um die Tiefe meines src-Arrays translatieren.
Da ich allerdings nur eine Variable habe wo die Tiefe meines Arrays als Anzahl der Elemente drin steht
und Ich Indizies betrachte komm ich auf die verschieben um (Tiefe - 1).Es kann sein, dass du vollkommen Recht hast und man das alles nur über eine Translation Abbilden kann,
allerdings habe ich dann keine Ahnung wie das Funktionieren soll ^^.Gruß
Jango
-
Nur mal so aus Interesse: Wie kommst du zu solchen Bilddaten, die in eine dreidimensionale Matrix gehören? Normalerweise sind 3D-Bilder ja einfach nur 2D-Bilder mit einem zusätzlichen Kanal, in dem die Z-Koordinate steht.
-
Dobi schrieb:
Wie kommst du zu solchen Bilddaten
http://www9.informatik.uni-erlangen.de/External/vollib/
http://www-graphics.stanford.edu/data/voldata/
http://www.ifi.uzh.ch/vmml/research/datasets.html
http://www.sci.utah.edu/cibc-software/ctdata.html
http://www.volvis.org/
http://www.cg.tuwien.ac.at/research/vis/datasets/
http://digimorph.org/
...Normalerweise
Laut wem?
sind 3D-Bilder ja einfach nur 2D-Bilder mit einem zusätzlichen Kanal, in dem die Z-Koordinate steht.
Nein, das sind normale Bilder mit Tiefeninformation, keine 3D bzw Volumetrischen Bilder.
-
OK, cool. Ich hab den Thread nicht ganz gelesen, wollte aber trotzdem sichergehen, dass du nicht versehentlich unnötig mit 3d-Matrizen rumhantierst, obwohl du eigentlich nur Kinect-Bilder verarbeiten willst oder sowas.
-
Etwas spät, aber sind die OpenCV Filter wirklich die richtigen für den Zweck? Das ganze sieht mir so aus, dass man mit OpenCl sehr viel machen könnte.
-
Marthog schrieb:
Etwas spät, aber sind die OpenCV Filter wirklich die richtigen für den Zweck? Das ganze sieht mir so aus, dass man mit OpenCl sehr viel machen könnte.
Ja ich hatte auch schon an Parallelisieren und auslagern auf die Grafikkarte gedacht,
allerdings mit Hilfe der ComputeShader (OpenGL 4.4), die macht aber im Prinzip das selbe wie OpenCL.Nur möchte mein Auftraggeber das nicht, da das wohl zu viel Zeit frisst.
(Hab auch noch keine Erfahrung damit großartig zu parallelsieren).Und einen schlechten Algorithmus auf die Grafikkarte aus zu lagern, damit er trotzdem schnell ist, finde ich auch eher suboptimal...
Ist mehr workaround als Lösung ^^Wird aber eventuell dann in einer V2 des Projektes umgesetzt.
Trotzdem danke für den Hinweis
Zu OpenCV:
Ich hatte auch schon überlegt darauf zu verzichten, und meine Datenstruktur in ein Eindimensionales-Array der Größe x*y*z um zu wandeln, in der Hoffnung, dass es schneller ist.
Allerdings hab ich nichts gefunden um die Zeiger schnell umhängen zu können, sodass mir die Z-Achse weniger Probleme macht als bisher.
Und dann müsste ich auch die Fouriertransformation usw selbst implementieren, da ist das mit OpenCV einfach mindestens genauso schnell und einfacher ^^Gruß
-
Guck Dir mal die VIGRA-Bildverarbeitungsbibliothek an. Die kann n-dimensionale Bilder verarbeiten.
-
So ein Austauschen der Achsen, wie Du es machen möchtest, sollte sich IMHO nur dann lohnen, wenn Du die entsprechende Darstellung häufig brauchst. Wie oft brauchst Du sie denn? Und: Wenn Du sie häufig brauchst, dann sollte doch der Zeitbedarf für das Erzeugen und Füllen der unterschiedlichen Arrays nicht der entscheidende Punkt sein, oder?
Etwas ähnliches wird übrigens beim Shear Warp Algorithmus gemacht, der manchmal zum Volume Rendering verwendet wird.
-
Gregor schrieb:
Guck Dir mal die VIGRA-Bildverarbeitungsbibliothek an. Die kann n-dimensionale Bilder verarbeiten.
Ich werds mir mal anschauen, danke
Gregor schrieb:
So ein Austauschen der Achsen, wie Du es machen möchtest, sollte sich IMHO nur dann lohnen, wenn Du die entsprechende Darstellung häufig brauchst. Wie oft brauchst Du sie denn? Und: Wenn Du sie häufig brauchst, dann sollte doch der Zeitbedarf für das Erzeugen und Füllen der unterschiedlichen Arrays nicht der entscheidende Punkt sein, oder?
Etwas ähnliches wird übrigens beim Shear Warp Algorithmus gemacht, der manchmal zum Volume Rendering verwendet wird.
Den Austausch der Achsen nehme ich vor, da ich mehrere Filter über mein Bild Jagen muss,
da ich im 2D-Raum sehr sehr schnell und effektiv filtern kann, aber im 3D-Raum sehr lange dafür brauche, geht es schneller, wenn ich mein 3D-Bild so drehe, dass ich normale 2D-Filter-Funktionen verwenden kann.Ich verliere beim hin und zurück drehen ungefähr so viel zeit wie bei 2x 3D-Filtern, sobald ich mehr als 2 3D-Filter benutzte ist es schneller.
Gruß
-
Marthog schrieb:
Etwas spät, aber sind die OpenCV Filter wirklich die richtigen für den Zweck? Das ganze sieht mir so aus, dass man mit OpenCl sehr viel machen könnte.
es braucht schon ein wenig koennen um eine bestehende gute lib wie openCV zu ueberbieten. Es ist nicht nur die raw compute power um die es geht, die algorithmen haben oft optimierungen die es weit schneller machen als ein simples opencl program.
und opencl optimieren bedarf auch ein wenig koennen, was opencl auf einer gpu schnell macht, kann es auf einer anderen langsam machen.
das endet schnell im second system syndrom, wenn seine laeds ein wenig ahnung haben, sagen sie 'nein' dazu