3D-(Voxel-/)Schnittbilder(MRT) Visualisieren (OpenGL 4.3)



  • eigentlich renderst du nicht die schichten der textur, sondern schichten in die tiefe vom view-cone. du passt dabei die indizierung der textur so an, dass die UVs inverse rotiert sind zur camera rotation. natuerlich benutzt du dafuer dann eine 3d textur, keine z * 2d texturen.



  • JangoK schrieb:

    Der zweite ist der von dot angesprochene... das Direct Volume Rendering

    Dieser beschäftigt sich damit, wie man die 2D-Schichten Visualisieren kann ohne sie vorher in ein 3D-Modell zurück zu rechnen.

    Das wird z.B. gemacht indem man die 2D-Schichten als Textur Implementiert unter Hintereinander ablegt und dann per Verfahren wie Ray-Casting auf eine 2D-Schicht abbildet. (Wie Osbios es ansprach)

    Problem, dass ich hierbei sehe ist die Rotation um genau 90°, da kommt es bei der Visualisierung zu einer Lücke, weil die 2D-Schichten keine Tiefe haben.

    3d Texturen sampelt man mit 3d-coordinaten.
    Man kann dabei einen normalen linear filter anwenden.
    Anstelle von 4 Texeln wie bei einer 2d Textur werden bei 3d Texturen 8 Texel gelesen.



  • JangoK schrieb:

    Das wird z.B. gemacht indem man die 2D-Schichten als Textur Implementiert unter Hintereinander ablegt und dann per Verfahren wie Ray-Casting auf eine 2D-Schicht abbildet. (Wie Osbios es ansprach)

    Problem, dass ich hierbei sehe ist die Rotation um genau 90°, da kommt es bei der Visualisierung zu einer Lücke, weil die 2D-Schichten keine Tiefe haben.

    In der Literatur, die ich Online gefunden und einsehen konnte habe ich dafür die Lösung noch nicht gefunden, oder einfach nicht wirklich verstanden 😉

    Was haelt Dich davon ab, entsprechende Texturen in x, y und z Richtung zu erzeugen? Du koenntest die dann gleichzeitig darstellen oder einfach immer die nehmen, die fuer den Blickwinkel am Besten passen. Zumindest wuerdest Du so diese "Luecke" ganz einfach umgehen.



  • Gregor schrieb:

    Was haelt Dich davon ab, entsprechende Texturen in x, y und z Richtung zu erzeugen? Du koenntest die dann gleichzeitig darstellen oder einfach immer die nehmen, die fuer den Blickwinkel am Besten passen. Zumindest wuerdest Du so diese "Luecke" ganz einfach umgehen.

    Nunja, einerseits ist es unnötig weil es 3D Texturen gibt.
    Andererseits ist die Schwachstelle dieser Methode der benötigte Speicher und man würde so die dreifache menge Speicher benötigen.



  • Heyho Osbios, heyho Gregor, heyho Community,

    ich hab jetzt ein wenig weiter Recherchiert und gelesen und habe mich dazu entschieden beide Vorschläge von Euch zu Implementieren und in meiner Arbeit auf zu nehmen.

    Das generelle Vorgehen bei den beiden Verfahren habe ich auch soweit verstanden.
    Lediglich zu Implementierung hätte ich noch eine Frage.

    Als Daten habe ich einen Stapel von 2D-Bildern vorliegen.
    Dieser würde beim 2D-Texture-Based-Volume-Rendering ja exakt den Textur-Daten einer Achse entsprechen.

    Wie Berechne ich mir aus diesem Datensatz sinnvolle Datensätze für die anderen beiden Achsen?

    Da steh ich grad noch was auf dem Schlauch 😉

    Wäre gut, wenn mir da jemand weiterhelfen könnte ^^

    Lieben Gruß


  • Mod

    JangoK schrieb:

    Wie Berechne ich mir aus diesem Datensatz sinnvolle Datensätze für die anderen beiden Achsen?

    auch fuer den fall dass ich mich wiederhole:

    raps schrieb:

    eigentlich renderst du nicht die schichten der textur, sondern schichten in die tiefe vom view-cone. du passt dabei die indizierung der textur so an, dass die UVWs inverse rotiert sind zur camera rotation. natuerlich benutzt du dafuer dann eine 3d textur, keine z * 2d texturen.



  • Hallo rapso,

    meine Frage setzt allerdings etwas früher an (wenn ich mich nicht Irre).

    Meinen Recherchen zufolge ist es notwendig die durch die Aufnahme vom MRT diskretisierten Daten des Objektes wieder zurück zu Rechnen, da gegebenenfalls auch Daten zwischen den Abtast-Schichten des MRT benötigt werden.
    Unabhängig von 2D oder 3D Textur.

    Methoden zur Rekonstruktion der Daten habe ich mittlerweile allerdings auch gefunden. Beschrieben und verglichen werden einige im Paper von Marschner et. al. "An Evaluation of Reconstruction Filters for Volume Rendering".

    Tut mir leid, falls die Frage unklar formuliert war.

    Lieben Gruß


  • Mod

    ich dachte dir ging es darum den existierenden scan zu visualisieren. daten zwischen den pixeln zu interpolieren klingt nach einem ganz anderem topic.

    wie waere es wenn du erst die visualisierung loest? wenn das geht, kannst du die daten immer noch erweitern. zuviel auf einmal kann am ende dazu fuehren dass nichts da ist 😉



  • Heyho rapso,

    du hast schon recht, mir geht es darum verschiedene Visualisierungs-Verfahren für MRT-Schichtdaten zu implementieren und zu vergleichen.

    In der Literatur steht allerdings beschrieben, dass zur Visualisierung mittels Direct-Volume-Rendering vorher eine Rekonstruktion und Klassifikation der Schichtdaten vorgenommen werden muss. Ansonsten kann Beispielsweise das 2D und/oder 3D-Texture-Based-Rendering garnicht vorgenommen werden, da Werte fehlen um die notwendigen Render-Schichten zu erzeugen.

    Daher muss ich scheinbar um das Problem der Visualisierung zu lösen, erst einmal das Problem der Rekonstruktion und Klassifizierung lösen. ^^



  • Du musst nicht unbedingt interpolieren.

    Wenn Du Direct Volume Rendering betreibst und dafür einen Raycasting-Algorithmus verwendest, dann ist letztendlich die Frage, wie Du den "Strahl" diskretisierst. Du kannst natürlich sagen, dass Du einen Strahl hast, der die Voxel nicht genau trifft und deshalb muss immer zwischen verschiedenen Voxeln interpoliert werden. Du könntest aber auch einen Bresenham Linienalgorithmus zur Erzeugung des Strahls verwenden. Der liefert Dir einen Strahl in Form von einer Menge diskreter Koordinaten, an denen sich jeweils ein Voxel befindet. Das Resultat davon ist möglicherweise um Nuancen schlechter als ein Algorithmus, der mit Interpolation arbeitet, dafür ist er aber auch wesentlich schneller. Du brauchst dort eben keine Interpolation und zudem läuft das Erzeugen der Linie mit Ganzzahlarithmetik.

    Ich habe vor langer Zeit auch mal etwas mit Direct Volume Rendering rumgespielt und dafür so einen Bresenham Algorithmus verwendet. Herausgekommen sind Darstellungen wie die da:

    https://i.imgur.com/i7oXNBq.png

    Ist vielleicht etwas grobpixelig. Andererseits wird das pixelige nicht direkt etwas mit dem Bresenham-Algorithmus zu tun haben.


  • Mod

    @JangoK

    mir ist nicht ganz klar was du rekonstruieren bzw klassifizieren willst. fuer die visualisierung sind eigentlich nur die schnittbilder noetig und die hast du, wie du sagtest.
    dann muss man das nur auf die GPU laden und raycastern wie z.B. bei http://demos.vicomtech.org/volren/ zu sehen (ein browser mit WebGL ist noetig).



  • @rapso

    Generell hast du recht.
    Angenommen ich möchte Raycasten kann ich das wie Gregor auch schon beschrieben hat vollkommen ohne Interpolation/Rekonstruktion machen.

    Allerdings geht es mir weniger darum irgend einen Algorithmus zu implementieren.
    Sondern mehr darum mehrere mögliche Verfahren zu beschreiben, zu testen und dann Anhand der Ergebnisse zu Vergleichen um begründen zu können warum ich mich für Verfahren XY entschieden habe.

    Zu den Verfahren gehört unter anderem auch das Raycasting, welches als Bildbasiertes Verfahren gilt.

    Allerdings gibt es im Bezug auf Direct-Volume-Rendering noch verfahren wie Splatting, welches Objektbasiert ist, oder Shear-Warp und Texture-Mapping, welche Mischformen sind.

    Bei diesen Verfahren ist dann eine Interpolation/Rekonstruktion der Daten notwendig.

    Und bei allen Verfahren ist eine Klassifikation notwendig, auch beim Raycasting.
    Denn die Definition der Objekteigenschaft (Grauwert XY ist transparent etc) ist ein Teil der Klassifikation.

    Des Weiteren gibt es auch noch das Indirect-Volume-Rendering. Wo dann Verfahren wie Marching-Cubes eine Rolle spielen um Oberflächen Approximationen zu erzeugen.

    Gruß

    Jango


  • Mod

    ich nehme an mit klassifizierung meinst du die transfer function, die wird oft nicht vor dem rendern gemacht, sondern waehrend des rendern, da grauwerte sehr viel kompakter sind und der zugriff auf das volume das performance kritischte ist (meistens). natuerlich ist das resultat etwas anders, je nachdem ob erst die transfer function oder erst die interpolation durchgefuehrt wird.

    wenn du also verfahren vergleichst, beachte die reihenfolge bei denen (vielleicht ist pre- und post- klassifizierung sogar etwas, was wert waere verglichen zu werden).

    und vergiss nicht ein paar schoene bilder davon in unseren screenshot thread zu posten 😉

    http://www.c-plusplus.net/forum/191889-160



  • Heyho, ich schon wieder 😉

    Ich hab den Theorie-Teil jetzt soweit abgeschlossen und bin gerade dabei (zu versuchen) das 3D-Based-Texture-Mapping zu implementieren.

    Allerdings habe ich keine Idee wie ich auch nur an nährend diese Blickrichtungsorientierten 2D-Schichten berechnen soll...

    Ich hab dazu viel theorie gefunden, aber kA wie ich das jetzt in die Praxis umsetzten kann. wäre nett wenn jemand mir da helfen könnte.

    Die Fragen sind also:

    Wie bestimme ich die bounding-Box um meine Textur?

    Wie berechne ich die Schichten auf die ich meine Textur ziehe aus dieser bounding-Box?

    Ich habe einen Ansatz mit Marching-Cubes gefunden.
    Den verstehe ich aber auch nicht so ganz um ihn selbst nach implementieren zu können..

    http://www.vmg.cs.bangor.ac.uk/Papers/05CGGM.pdf

    z.B. was bedeuten die Zahlen im Index bei GL_MODELVIEWMATRIX und woher kommt diese magische Tabelle die auf all meine Probleme Lösungen bietet? ^^

    Ich hoffe ihr könnt mir auch hier behilflich sein 🙂



  • Moin,

    ich beschäftige mich schon seit Jahren mit dem Thema und ich würde Dir raten das Rad nicht neu zu erfinden wenn Du nicht unbedingt musst.

    Es gibt da ein sehr gutes Toolkit namens VTK von Kiteware. Das basiert auf OpenGL ist auch noch OpenSource. Das bietet Dir alles was Du brauchst.
    Damit kannst Du Surfacerendern mit MarchingCubes, Raycasten und Schnittbilder erzeugen. Die haben sehr viele Beispiele und ich hatte damals schnell Ergebnisse.

    So kannst Du Dich auf Deine Visualisierung konzentrieren, denn die ist bei MRT Daten nicht so einfach wie beim CT mit der Hounsfield scala. Die Schnittbilder mal außen vor, musst Du bei den anderen Verfahren schon wissen was Du hervorheben möchtest(Knochen, Weichteile ...) und welche Voxel- Wertebereiche diese Strukturen in Deiner Serie haben.

    Ich denke für den Anfang werden Dir die Reslice Geschichten im VTK schon gut helfen. Und der Source Code ist recht leserlich, falls Du wissen willst wie die das machen.

    Ich hoffe es hilft Dir
    Gruß Andi



  • Heyho Andiamo,

    das Rad neu erfinden möchte ich natürlich nicht, aber auf ein fertiges Rad wie VTK zurück zu greifen ist mir leider auch nicht möglich. Was ich gerne hätte, wäre ein guter Bauplan des Rades damit ich es gut Nachbauen kann ;D.

    Es geht bei meiner Arbeit ja darum die Verfahren zu analysieren und zu implementieren. daraufhin zu vergleichen. Dafür muss ich eh wissen was im Hintergrund passiert. Und da das ganze auch noch Projekt gebunden ist kann ich ohnehin nicht auf VTK zurück greifen, sondern bin leider verpflichtet das zu nehmen, was das Projekt mir gibt.

    Und das ist nunmal OpenGL und GLFW.

    Trotzdem danke für den Tipp.

    Gruß´

    Jango



  • verstehe.
    Kannst Du mal Dein Thema konkreter beschreiben? Wofür ist das? Was für ein Projekt ist das? Welche Kriterien ziehst Du für den Vergleich der Methoden heran? Ich hatte für die Einarbeitung in diese Thematik das Buch "Visualization in Medicine" von Dirk Bartz in den Händen. Da steht doch schon alles drin. Mit Vergleich.

    Für Dein Projekt muss ich sagen, dass das was Du da vor hast extrem viel Arbeit ist. Die Verfahren sind alle an sich schon nicht trivial und dann gibt es noch Verbesserungen die die Qualität und Performance beeinflussen. Zum Beispiel denke ich da an Parallelität und verschiedenste Interpolationsmethoden. Fließt das alles auch in Deine Arbeit ein?

    Ich habe irgendwas hier gelesen, dass Deine Daten schon "vorverarbeitet" sind, dass man nur die Adern sieht. Wie wurde das gemacht? Window/Level? VOI-Lut?



  • Heyho,

    also die ganzen performance verbesserungen werde ich wohl aus zeitlichen Gründen nicht mehr mit rein nehmen können.
    Evt werd ich es erwähnen und auch erklären wie es funktioniert, aber nicht implementieren, da dazu die Zeit einfach nicht aussreicht. Der Wesentliche Vergleichspunkt ist das Visuelle Ergebnis und die Möglichkeit zur Weiterverarbeitung. Nebenkriterien sind Performance und Speicherbedarf. Evt wird für den Algorithmus, für den sich daraufhin entschieden wird auch noch die "Verbesserung" implementiert.

    Bzgl der vorverarbeitung der Daten.
    Die MRT-Daten vom Gehirn wurden in 2 schritten vor segmentiert.
    Zuerst wurde mit einem verfahren ähnlich dem von Forkert et. al. das Gehirn vor segmentiert.

    http://www.egms.de/static/de/meetings/gmds2008/08gmds245.shtml

    Daraufhin mit Hilfe von des Vesselness Filters von Frangi et. al. (Paper leider grad nicht zur Hand) eine Ader-Segmentiertung vorgenommen.

    Die Daten sollen nun visuallisiert werden.
    Da sie allerdings so gut vor segmentiert sind, sind die Materialeigenschaften eigl ziemlich unwichtig für mich.
    Das einzige, was meine Transferfunktion nach machen muss ist den Alpha-Wert setzten damit schwarze Pixel durchsichtig werden.

    Da es sich um eine Bachelor-Arbeit handelt in einer wissenschaftlichen Fakultät möchte ich halt begründen können welchen Algorithmus ich verwende. Und das geht am besten in dem ich mehrere Miteinander vergleiche und dann den für das Projekt am besten geeignete Verwende. Damit ich allerdings vernünftige Vergleichsdaten generieren kann sollte ich die natürlich auch mal Implementiert haben. Bei einer BA ist es aber auch kein Problem zu sagen, dass die Algorithmen erstmal in ihrer Grundform verglichen werden. Und somit die Auswahl auf 2 beschränkt wird. die dann im weiteren Verlauf nochmal in Ihrer "verbesserten" Form verglichen werden.
    Das kann ich ja als "Ausblick" dann einbauen und muss ich nicht zwingend jetzt machen. Ich würde gerne folgende Algorithmen Vergleichen. 3D-Texture-Based-Volume-Rendering, Volume-Ray-Casting, Splatting, Shear-Warp, und den ansatz über die ISO-Oberfläche mittels Marching-Cubes.

    Falls die Zeit überhaupt nicht reicht wird das auch noch weiter eingegrenzt werden müssen.

    Ideal wäre natürlich literatur in der die Algorithmen mit Beispielhafter Implementiert erklärt werden. Bin nämlich noch nicht so der OpenGL guru und hab da so meine Probleme mit :D.
    In der Theorie hab ich die alle verstanden und könnte die im Kolloqium auch super bildlich erklären. Beim umsetzten mit OpenGL harkt es aber noch gewaltig =(...

    Daher ja auch meine Frage nach Hilfe was das betrifft ^^

    Lieben Gruß


  • Mod

    damit es nicht daran scheitert 🙂

    JangoK schrieb:

    Wie bestimme ich die bounding-Box um meine Textur?

    die darfst du frei bestimmen, von [0|0|0] bis [1|1|1] oder [-1|-1|-1] bis [1|1|1] oder ...

    am ende hat das keine absolute bedeutung. du koenntest natuerlich, falls du das weisst, die 'real world' ausmasse in cm oder mm oder so benutzen, dann koenntest du vermessungen anbieten oder sowas. aber auch mit einem einheitswuerfel hast du die moeglichkeit, musst du lediglich umrechnen.

    Wie berechne ich die Schichten auf die ich meine Textur ziehe aus dieser bounding-Box?

    ist das nicht die theorie von der du schon alles intus hast? oder ist das problem wie du es an opengl uebergibst?

    Ich habe einen Ansatz mit Marching-Cubes gefunden.
    Den verstehe ich aber auch nicht so ganz um ihn selbst nach implementieren zu können..

    http://www.vmg.cs.bangor.ac.uk/Papers/05CGGM.pdf

    z.B. was bedeuten die Zahlen im Index bei GL_MODELVIEWMATRIX und ...

    ich nehme an du meinst "Algorithm 1", die indices sind scheinbar die elemente der modelview matrix.
    es schaut aus als ob er in dem fall einfach nur die 8 ecken des cubes berechnet, dank modelview matrix ist das entsprechend im view space.

    danach zucht er sich minZ und maxZ im view space (also vorderste kante und hinterste kante.

    dann (in 1.) prueft er ob der jeweilige vertex fuer diesen slice vor oder nach dem slice ist.

    im anschluss generiert er dann davon abhaengig ein triangle fan das den sliece abdeckt.

    woher kommt diese magische Tabelle die auf all meine Probleme Lösungen bietet? ^^

    die tabelle ist das resultat des algorithms und vermutlich nur exemplarisch da, du erstellst sie implizit mit dem code.



  • Hi rapso =),

    rapso schrieb:

    Wie berechne ich die Schichten auf die ich meine Textur ziehe aus dieser bounding-Box?

    ist das nicht die theorie von der du schon alles intus hast? oder ist das problem wie du es an opengl uebergibst?

    Das Problem ist OpenGL und ich ;D. Teilweise aber auch die Mathematik dahinter.

    Ich weiß (jetzt, danke 😉 ), ich habe einen beliebigen Würfel, welchen ich als bounding-Box verwende. Ich verstehe allerdings noch nicht, wie ich das in openGL umsetzte. Reicht es die 8 Eckpunkte zu definieren? Oder muss ich Tatsächlich 12 Triangles definieren und an OpenGL übergeben als würde ich den Würfel rendern wollen?
    Die Rotation und andere Manipulationen führe ich dann auf den Würfel aus und durch die Texturierung später wird das automatisch umgesetzt? oder muss ich irgendwas beim Volume (also meiner 3D-Textur) berücksichtigen? (Ich denke da schon etwas weiter und überlege ob ich da was beim Volume-Ray-Casting wiederverwenden kann).
    Die Bestimmung des min und max punktes sollte theoretisch ganz einfach sein.
    Die ecke mit dem kleinsten z-Wert = min, die mit dem Größten = max.
    Die Frage ist, wie kriege ich den Abstand zur Cam, also dem Viewport bestimmt?
    Ich Multipliziere den Würfel so wie ich das verstanden habe mit der Viewmatrix,
    Aber wo passiert das ganze?
    Logischerweise muss das in der Rendering-Schleife passieren, da es ja immer wieder passieren muss wenn ich mit dem Würfel interagiere.
    Aber passiert es auf der CPU oder in einem Shader?
    Wenn auf der CPU, wie komme ich an mein Würfelobjekt, alles was ich habe ist doch eine BufferID? Oder arbeite ich immer auf dem Array was ich in den Buffer stecke? Werden dann Rotationen überhaupt sinnvoll abgespeichert?
    Wenn ich das dann irgendwann mal verstanden habe, ist es wohl auch ziemlich einfach alle punkte an der stelle z=sliceXY-distance darauf zu utnersuchen ob sie auf einer Kante liegen, also einen Eckpunkt darstellen.
    Dann ist es nur noch daran die Punkte so zu ordnen, dass ich einen Triangle-Fan malen kann und die Textur drauf ziehen kann. Ich denke das sollt ich dann wieder hin kriegen.
    Alternativ kann ich dazu dann auch den Algorithmus den ich verlinkt hatte und den du mir netter Weise erklärt hast verwendet und in die Tabelle schauen, die scheinbar aus den beiden Funktionen entsteht. Dann hab ich direkt eine Anordnung die zu einem Triangle-Fan passt.
    Am ende stand noch da, dass ich das Objekt vom Viewspace wieder in den Model-Space zurück bringen muss. Meines bescheidenen Mathematischen wissens nach müssten das mit der Inversen-Matrix der Viewmatrix gehen oder?
    Wie komm ich an die ran (falls ich recht hab ^^)?

    Wie gesagt, die Theorie habe ich denk ich verstanden.
    Mit der Umsetzung komm ich allerdings nicht so klar ^^.

    Gruß

    Jango


Anmelden zum Antworten