3D-Datenvisualisierung mit Open-Source-Tools: Ein Tutorial mit VTK

Veröffentlicht: 2022-03-11

In seinem jüngsten Artikel im Blog von Toptal schrieb der erfahrene Datenwissenschaftler Charles Cook über wissenschaftliches Rechnen mit Open-Source-Tools. Sein Tutorial macht einen wichtigen Punkt über Open-Source-Tools und die Rolle, die sie bei der einfachen Verarbeitung von Daten und der Gewinnung von Ergebnissen spielen können.

Aber sobald wir all diese komplexen Differentialgleichungen gelöst haben, taucht ein anderes Problem auf. Wie verstehen und interpretieren wir die riesigen Datenmengen aus diesen Simulationen? Wie visualisieren wir potenzielle Gigabyte an Daten, beispielsweise Daten mit Millionen von Gitterpunkten innerhalb einer großen Simulation?

Ein Datenvisualisierungstraining für Data Scientists, die sich für 3D-Datenvisualisierungstools interessieren.

Während meiner Arbeit an ähnlichen Problemen für meine Masterarbeit kam ich in Kontakt mit dem Visualization Toolkit, kurz VTK - einer leistungsstarken Grafikbibliothek, die auf Datenvisualisierung spezialisiert ist.

In diesem Tutorial werde ich eine kurze Einführung in VTK und seine Pipeline-Architektur geben und anschließend ein reales 3D-Visualisierungsbeispiel diskutieren, das Daten aus einer simulierten Flüssigkeit in einer Impellerpumpe verwendet. Abschließend werde ich die Stärken der Bibliothek sowie die Schwachstellen auflisten, auf die ich gestoßen bin.

Datenvisualisierung und die VTK-Pipeline

Die Open-Source-Bibliothek VTK enthält eine solide Verarbeitungs- und Rendering-Pipeline mit vielen ausgefeilten Visualisierungsalgorithmen. Seine Fähigkeiten hören hier jedoch nicht auf, da im Laufe der Zeit auch Bild- und Mesh-Verarbeitungsalgorithmen hinzugefügt wurden. In meinem aktuellen Projekt mit einem Dentalforschungsunternehmen verwende ich VTK für Mesh-basierte Verarbeitungsaufgaben innerhalb einer Qt-basierten, CAD-ähnlichen Anwendung. Die VTK-Fallstudien zeigen die Bandbreite geeigneter Anwendungen.

Die Architektur von VTK dreht sich um ein leistungsstarkes Pipeline-Konzept. Die Grundzüge dieses Konzepts sind hier dargestellt:

So sieht die VTK-Datenvisualisierungspipeline aus.

  • Quellen stehen ganz am Anfang der Pipeline und schaffen „etwas aus dem Nichts“. Beispielsweise erstellt ein vtkConeSource einen 3D-Kegel und ein vtkSTLReader liest *.stl 3D-Geometriedateien.
  • Filter wandeln die Ausgabe von Quellen oder anderen Filtern in etwas Neues um. Zum Beispiel schneidet ein vtkCutter die Ausgabe des vorherigen Objekts in den Algorithmen unter Verwendung einer impliziten Funktion, zB einer Ebene. Alle mit VTK gelieferten Verarbeitungsalgorithmen sind als Filter implementiert und können frei miteinander verkettet werden.
  • Mapper wandeln Daten in grafische Grundelemente um. Beispielsweise können sie verwendet werden, um eine Nachschlagetabelle zum Einfärben wissenschaftlicher Daten anzugeben. Sie sind eine abstrakte Methode, um anzugeben, was angezeigt werden soll.
  • Akteure repräsentieren ein Objekt (Geometrie plus Anzeigeeigenschaften) innerhalb der Szene. Hier werden Dinge wie Farbe, Deckkraft, Schattierung oder Ausrichtung festgelegt.
  • Renderer & Windows beschreiben endlich plattformunabhängig das Rendering auf dem Bildschirm.

Eine typische VTK-Rendering-Pipeline beginnt mit einer oder mehreren Quellen, verarbeitet sie mit verschiedenen Filtern zu mehreren Ausgabeobjekten, die dann separat mit Mappern und Akteuren gerendert werden. Die Kraft hinter diesem Konzept ist der Update-Mechanismus. Wenn Einstellungen von Filtern oder Quellen geändert werden, werden alle abhängigen Filter, Mapper, Akteure und Renderfenster automatisch aktualisiert. Wenn andererseits ein Objekt weiter unten in der Pipeline Informationen benötigt, um seine Aufgaben auszuführen, kann es diese leicht erhalten.

Außerdem muss man sich nicht direkt mit Rendering-Systemen wie OpenGL auseinandersetzen. VTK kapselt alle Low-Level-Aufgaben in einer plattform- und (teilweise) Rendering-systemunabhängigen Weise; der Entwickler arbeitet auf einem viel höheren Niveau.

Codebeispiel mit einem Rotorpumpendatensatz

Sehen wir uns ein Beispiel für die Datenvisualisierung an, das einen Datensatz der Flüssigkeitsströmung in einer rotierenden Impellerpumpe aus dem IEEE Visualization Contest 2011 verwendet. Die Daten selbst sind das Ergebnis einer numerischen Strömungssimulation, ähnlich der in Charles Cooks Artikel beschriebenen.

Die gezippten Simulationsdaten der vorgestellten Pumpe sind über 30 GB groß. Es enthält mehrere Teile und mehrere Zeitschritte, daher die große Größe. In dieser Anleitung spielen wir mit dem Rotorteil eines dieser Zeitschritte herum, der eine komprimierte Größe von etwa 150 MB hat.

Meine bevorzugte Sprache für die Verwendung von VTK ist C++, aber es gibt Mappings für mehrere andere Sprachen wie Tcl/Tk, Java und Python. Wenn das Ziel nur die Visualisierung eines einzelnen Datensatzes ist, muss man überhaupt keinen Code schreiben und kann stattdessen Paraview verwenden, ein grafisches Front-End für die meisten Funktionen von VTK.

Der Datensatz und warum 64-Bit erforderlich ist

Ich habe den Rotordatensatz aus dem oben bereitgestellten 30-GB-Datensatz extrahiert, indem ich einen Zeitschritt in Paraview geöffnet und den Rotorteil in eine separate Datei extrahiert habe. Es ist eine unstrukturierte Gitterdatei, dh ein 3D-Volumen, das aus Punkten und 3D-Zellen besteht, wie Hexaeder, Tetraeder und so weiter. Jeder der 3D-Punkte hat zugeordnete Werte. Manchmal haben die Zellen auch zugeordnete Werte, aber nicht in diesem Fall. Dieses Training wird sich auf Druck und Geschwindigkeit an den Punkten konzentrieren und versuchen, diese in ihrem 3D-Kontext zu visualisieren.

Die komprimierte Dateigröße beträgt etwa 150 MB und die In-Memory-Größe etwa 280 MB, wenn sie mit VTK geladen wird. Durch die Verarbeitung in VTK wird der Datensatz jedoch mehrfach innerhalb der VTK-Pipeline zwischengespeichert und wir erreichen schnell die 2-GB-Speichergrenze für 32-Bit-Programme. Es gibt Möglichkeiten, bei der Verwendung von VTK Speicher zu sparen, aber um es einfach zu halten, werden wir das Beispiel einfach in 64-Bit kompilieren und ausführen.

Danksagung : Der Datensatz wird mit freundlicher Genehmigung des Instituts für Angewandte Mechanik, Universität Clausthal, Deutschland (Dipl. Wirtsch.-Ing. Andreas Lucius) zur Verfügung gestellt.

Das Ziel

Was wir mit VTK als Werkzeug erreichen werden, ist die im Bild unten gezeigte Visualisierung. Als 3D-Kontext wird der Umriss des Datensatzes mit einem teilweise transparenten Drahtgitter-Rendering angezeigt. Der linke Teil des Datensatzes dient dann zur Darstellung des Drucks durch einfache Farbkodierung der Flächen. (Wir überspringen das komplexere Volume-Rendering für dieses Beispiel). Um das Geschwindigkeitsfeld zu visualisieren, ist der rechte Teil des Datensatzes mit Stromlinien gefüllt, die nach der Größe ihrer Geschwindigkeit farblich gekennzeichnet sind. Diese Visualisierungsauswahl ist technisch nicht ideal, aber ich wollte den VTK-Code so einfach wie möglich halten. Darüber hinaus gibt es einen Grund für dieses Beispiel, Teil einer Visualisierungsherausforderung zu sein, dh viele Turbulenzen in der Strömung.

Dies ist die resultierende 3D-Datenvisualisierung aus unserem Beispiel-VTK-Tutorial.

Schritt für Schritt

Ich werde den VTK-Code Schritt für Schritt besprechen und zeigen, wie die Rendering-Ausgabe in jeder Phase aussehen würde. Der vollständige Quellcode kann am Ende der Schulung heruntergeladen werden.

Beginnen wir damit, alles, was wir von VTK benötigen, aufzunehmen und die Hauptfunktion zu öffnen.

 #include <vtkActor.h> #include <vtkArrayCalculator.h> #include <vtkCamera.h> #include <vtkClipDataSet.h> #include <vtkCutter.h> #include <vtkDataSetMapper.h> #include <vtkInteractorStyleTrackballCamera.h> #include <vtkLookupTable.h> #include <vtkNew.h> #include <vtkPlane.h> #include <vtkPointData.h> #include <vtkPointSource.h> #include <vtkPolyDataMapper.h> #include <vtkProperty.h> #include <vtkRenderer.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkRibbonFilter.h> #include <vtkStreamTracer.h> #include <vtkSmartPointer.h> #include <vtkUnstructuredGrid.h> #include <vtkXMLUnstructuredGridReader.h> int main(int argc, char** argv) {

Als nächstes richten wir den Renderer und das Renderfenster ein, um unsere Ergebnisse anzuzeigen. Wir legen die Hintergrundfarbe und die Renderfenstergröße fest.

 // Setup the renderer vtkNew<vtkRenderer> renderer; renderer->SetBackground(0.9, 0.9, 0.9); // Setup the render window vtkNew<vtkRenderWindow> renWin; renWin->AddRenderer(renderer.Get()); renWin->SetSize(500, 500);

Mit diesem Code konnten wir bereits ein statisches Renderfenster anzeigen. Stattdessen entscheiden wir uns dafür, einen vtkRenderWindowInteractor hinzuzufügen, um die Szene interaktiv zu drehen, zu zoomen und zu schwenken.

 // Setup the render window interactor vtkNew<vtkRenderWindowInteractor> interact; vtkNew<vtkInteractorStyleTrackballCamera> style; interact->SetRenderWindow(renWin.Get()); interact->SetInteractorStyle(style.Get());

Jetzt haben wir ein laufendes Beispiel, das ein graues, leeres Renderfenster zeigt.

Als nächstes laden wir den Datensatz mit einem der vielen Reader, die mit VTK geliefert werden.

 // Read the file vtkSmartPointer<vtkXMLUnstructuredGridReader> pumpReader = vtkSmartPointer<vtkXMLUnstructuredGridReader>::New(); pumpReader->SetFileName("rotor.vtu");

Kurzer Ausflug in die VTK-Speicherverwaltung : VTK verwendet ein bequemes automatisches Speicherverwaltungskonzept, das sich um das Zählen von Referenzen dreht. Anders als bei den meisten anderen Implementierungen wird die Referenzzählung jedoch in den VTK-Objekten selbst gehalten, anstatt in der Smart-Pointer-Klasse. Dies hat den Vorteil, dass der Referenzzähler erhöht werden kann, selbst wenn das VTK-Objekt als Rohzeiger herumgereicht wird. Es gibt zwei Hauptmethoden zum Erstellen verwalteter VTK-Objekte. vtkNew<T> und vtkSmartPointer<T>::New() , wobei der Hauptunterschied darin besteht, dass ein vtkSmartPointer<T> implizit in den Rohzeiger T* umgewandelt werden kann und von einer Funktion zurückgegeben werden kann. Für Instanzen von vtkNew<T> müssen wir .Get() aufrufen, um einen rohen Zeiger zu erhalten, und wir können ihn nur zurückgeben, indem wir ihn in einen vtkSmartPointer . In unserem Beispiel kehren wir nie von Funktionen zurück und alle Objekte leben die ganze Zeit, daher verwenden wir die vtkNew , mit nur der obigen Ausnahme zu Demonstrationszwecken.

Zu diesem Zeitpunkt wurde noch nichts aus der Datei gelesen. Wir oder ein Filter weiter unten in der Kette müssten Update() aufrufen, damit die Datei tatsächlich gelesen wird. Es ist normalerweise der beste Ansatz, die VTK-Klassen die Aktualisierungen selbst durchführen zu lassen. Manchmal möchten wir jedoch direkt auf das Ergebnis eines Filters zugreifen, um beispielsweise den Druckbereich in diesem Datensatz abzurufen. Dann müssen wir Update() manuell aufrufen. (Wir verlieren keine Leistung, wenn wir Update() mehrmals aufrufen, da die Ergebnisse zwischengespeichert werden.)

 // Get the pressure range pumpReader->Update(); double pressureRange[2]; pumpReader->GetOutput()->GetPointData()->GetArray("Pressure")->GetRange(pressureRange);

Als nächstes müssen wir die linke Hälfte des Datensatzes mit vtkClipDataSet . Um dies zu erreichen, definieren wir zuerst eine vtkPlane , die die Teilung definiert. Dann sehen wir zum ersten Mal, wie die VTK-Pipeline miteinander verbunden ist: successor->SetInputConnection(predecessor->GetOutputPort()) . Wann immer wir ein Update von clipperLeft , stellt diese Verbindung nun sicher, dass alle vorherigen Filter ebenfalls auf dem neuesten Stand sind.

 // Clip the left part from the input vtkNew<vtkPlane> planeLeft; planeLeft->SetOrigin(0.0, 0.0, 0.0); planeLeft->SetNormal(-1.0, 0.0, 0.0); vtkNew<vtkClipDataSet> clipperLeft; clipperLeft->SetInputConnection(pumpReader->GetOutputPort()); clipperLeft->SetClipFunction(planeLeft.Get());

Schließlich erstellen wir unsere ersten Akteure und Mapper, um das Drahtgitter-Rendering der linken Hälfte anzuzeigen. Beachten Sie, dass der Mapper genauso mit seinem Filter verbunden ist wie die Filter untereinander. Meistens löst der Renderer selbst die Aktualisierungen aller Akteure, Mapper und der zugrunde liegenden Filterketten aus!

Die einzige Zeile, die nicht selbsterklärend ist, ist wahrscheinlich leftWireMapper->ScalarVisibilityOff(); - es verbietet das Einfärben des Wireframes durch Druckwerte, die als aktuell aktives Array gesetzt sind.

 // Create the wireframe representation for the left part vtkNew<vtkDataSetMapper> leftWireMapper; leftWireMapper->SetInputConnection(clipperLeft->GetOutputPort()); leftWireMapper->ScalarVisibilityOff(); vtkNew<vtkActor> leftWireActor; leftWireActor->SetMapper(leftWireMapper.Get()); leftWireActor->GetProperty()->SetRepresentationToWireframe(); leftWireActor->GetProperty()->SetColor(0.8, 0.8, 0.8); leftWireActor->GetProperty()->SetLineWidth(0.5); leftWireActor->GetProperty()->SetOpacity(0.8); renderer->AddActor(leftWireActor.Get());

An diesem Punkt zeigt das Renderfenster endlich etwas, dh das Drahtmodell für den linken Teil.

Dies ist auch ein resultierendes Beispiel einer 3D-Datenvisualisierung aus dem VTK-Tool.

Das Wireframe-Rendering für den rechten Teil wird auf ähnliche Weise erstellt, indem die Ebenennormale eines (neu erstellten) vtkClipDataSet in die entgegengesetzte Richtung geschaltet und die Farbe und Deckkraft des (neu erstellten) Mappers und Akteurs geringfügig geändert werden. Beachten Sie, dass sich hier unsere VTK-Pipeline aus demselben Eingabedatensatz in zwei Richtungen (rechts und links) aufteilt.

 // Clip the right part from the input vtkNew<vtkPlane> planeRight; planeRight->SetOrigin(0.0, 0.0, 0.0); planeRight->SetNormal(1.0, 0.0, 0.0); vtkNew<vtkClipDataSet> clipperRight; clipperRight->SetInputConnection(pumpReader->GetOutputPort()); clipperRight->SetClipFunction(planeRight.Get()); // Create the wireframe representation for the right part vtkNew<vtkDataSetMapper> rightWireMapper; rightWireMapper->SetInputConnection(clipperRight->GetOutputPort()); rightWireMapper->ScalarVisibilityOff(); vtkNew<vtkActor> rightWireActor; rightWireActor->SetMapper(rightWireMapper.Get()); rightWireActor->GetProperty()->SetRepresentationToWireframe(); rightWireActor->GetProperty()->SetColor(0.2, 0.2, 0.2); rightWireActor->GetProperty()->SetLineWidth(0.5); rightWireActor->GetProperty()->SetOpacity(0.1); renderer->AddActor(rightWireActor.Get());

Das Ausgabefenster zeigt nun wie erwartet beide Drahtmodellteile.

Das Ausgabefenster der Datenvisualisierung zeigt jetzt beide Drahtmodellteile gemäß dem VTK-Beispiel.

Jetzt sind wir bereit, einige nützliche Daten zu visualisieren! Um die Druckvisualisierung zum linken Teil hinzuzufügen, müssen wir nicht viel tun. Wir erstellen einen neuen Mapper und verbinden ihn auch mit clipperLeft , aber dieses Mal färben wir nach dem Druckarray. Hier nutzen wir schließlich auch den oben hergeleiteten pressureRange .

 // Create the pressure representation for the left part vtkNew<vtkDataSetMapper> pressureColorMapper; pressureColorMapper->SetInputConnection(clipperLeft->GetOutputPort()); pressureColorMapper->SelectColorArray("Pressure"); pressureColorMapper->SetScalarRange(pressureRange); vtkNew<vtkActor> pressureColorActor; pressureColorActor->SetMapper(pressureColorMapper.Get()); pressureColorActor->GetProperty()->SetOpacity(0.5); renderer->AddActor(pressureColorActor.Get());

Die Ausgabe sieht nun wie das unten gezeigte Bild aus. Der Druck in der Mitte ist sehr niedrig und saugt Material in die Pumpe. Dann wird dieses Material nach außen transportiert und gewinnt schnell an Druck. (Natürlich sollte es eine Farbkartenlegende mit den tatsächlichen Werten geben, aber ich habe sie weggelassen, um das Beispiel kürzer zu halten.)

Wenn dem Datenvisualisierungsbeispiel Farbe hinzugefügt wird, beginnen wir wirklich zu sehen, wie die Pumpe funktioniert.

Jetzt beginnt der kniffligere Teil. Wir wollen im rechten Teil Geschwindigkeitsstromlinien zeichnen. Stromlinien werden durch Integration innerhalb eines Vektorfeldes aus Quellpunkten erzeugt. Das Vektorfeld ist in Form des Vektorarrays „Velocities“ bereits Bestandteil des Datensatzes. Wir müssen also nur die Quellpunkte generieren. vtkPointSource generiert eine Kugel aus zufälligen Punkten. Wir werden 1500 Quellpunkte generieren, da die meisten davon sowieso nicht im Datensatz liegen und vom Stream-Tracer ignoriert werden.

 // Create the source points for the streamlines vtkNew<vtkPointSource> pointSource; pointSource->SetCenter(0.0, 0.0, 0.015); pointSource->SetRadius(0.2); pointSource->SetDistributionToUniform(); pointSource->SetNumberOfPoints(1500);

Als nächstes erstellen wir den Streamtracer und setzen seine Eingangsverbindungen. „Warte, mehrere Verbindungen?“, könnten Sie sagen. Ja – dies ist der erste VTK-Filter mit mehreren Eingängen, auf den wir stoßen. Die normale Eingangsverbindung wird für das Vektorfeld verwendet, und die Quellenverbindung wird für die Saatpunkte verwendet. Da „Velocities“ das „aktive“ Vektor-Array in clipperRight ist, müssen wir es hier nicht explizit angeben. Schließlich spezifizieren wir, dass die Integration von den Saatpunkten in beide Richtungen durchgeführt werden soll, und setzen die Integrationsmethode auf Runge-Kutta-4.5.

 vtkNew<vtkStreamTracer> tracer; tracer->SetInputConnection(clipperRight->GetOutputPort()); tracer->SetSourceConnection(pointSource->GetOutputPort()); tracer->SetIntegrationDirectionToBoth(); tracer->SetIntegratorTypeToRungeKutta45();

Unser nächstes Problem besteht darin, die Stromlinien nach Geschwindigkeitsgröße einzufärben. Da es kein Array für die Beträge der Vektoren gibt, berechnen wir die Beträge einfach in ein neues skalares Array. Wie Sie erraten haben, gibt es auch für diese Aufgabe einen VTK-Filter: vtkArrayCalculator . Es nimmt einen Datensatz und gibt ihn unverändert aus, fügt aber genau ein Array hinzu, das aus einem oder mehreren der vorhandenen berechnet wird. Wir konfigurieren diesen Array-Rechner so, dass er die Größe des „Velocity“-Vektors nimmt und als „MagVelocity“ ausgibt. Abschließend rufen wir Update() noch einmal manuell auf, um den Bereich des neuen Arrays abzuleiten.

 // Compute the velocity magnitudes and create the ribbons vtkNew<vtkArrayCalculator> magCalc; magCalc->SetInputConnection(tracer->GetOutputPort()); magCalc->AddVectorArrayName("Velocity"); magCalc->SetResultArrayName("MagVelocity"); magCalc->SetFunction("mag(Velocity)"); magCalc->Update(); double magVelocityRange[2]; magCalc->GetOutput()->GetPointData()->GetArray("MagVelocity")->GetRange(magVelocityRange);

vtkStreamTracer gibt Polylinien direkt aus und vtkArrayCalculator gibt sie unverändert weiter. Daher könnten wir die Ausgabe von magCalc direkt mit einem neuen Mapper und Akteur anzeigen.

Stattdessen entscheiden wir uns in diesem Training dafür, die Ausgabe etwas schöner zu gestalten, indem wir stattdessen Bänder anzeigen. vtkRibbonFilter generiert 2D-Zellen, um Bänder für alle Polylinien seiner Eingabe anzuzeigen.

 // Create and render the ribbons vtkNew<vtkRibbonFilter> ribbonFilter; ribbonFilter->SetInputConnection(magCalc->GetOutputPort()); ribbonFilter->SetWidth(0.0005); vtkNew<vtkPolyDataMapper> streamlineMapper; streamlineMapper->SetInputConnection(ribbonFilter->GetOutputPort()); streamlineMapper->SelectColorArray("MagVelocity"); streamlineMapper->SetScalarRange(magVelocityRange); vtkNew<vtkActor> streamlineActor; streamlineActor->SetMapper(streamlineMapper.Get()); renderer->AddActor(streamlineActor.Get());

Was jetzt noch fehlt und eigentlich auch für die Erstellung der Zwischenrenderings benötigt wird, sind die letzten fünf Zeilen, um die Szene tatsächlich zu rendern und den Interaktor zu initialisieren.

 // Render and show interactive window renWin->Render(); interact->Initialize(); interact->Start(); return 0; }

Schließlich kommen wir zur fertigen Visualisierung, die ich hier noch einmal vorstelle:

Die VTK-Trainingsübung ergibt dieses vollständige Visualisierungsbeispiel.

Den vollständigen Quellcode für die obige Visualisierung finden Sie hier.

Der gute der böse und der Hässliche

Ich werde diesen Artikel mit einer Liste meiner persönlichen Vor- und Nachteile des VTK-Frameworks schließen.

  • Pro : Aktive Entwicklung : VTK wird von mehreren Mitwirkenden, hauptsächlich aus der Forschungsgemeinschaft, aktiv weiterentwickelt. Das bedeutet, dass einige hochmoderne Algorithmen verfügbar sind, viele 3D-Formate importiert und exportiert werden können, Fehler aktiv behoben werden und Probleme normalerweise eine fertige Lösung in den Diskussionsforen haben.

  • Nachteil : Zuverlässigkeit : Die Kopplung vieler Algorithmen von verschiedenen Mitwirkenden mit dem offenen Pipeline-Design von VTK kann jedoch zu Problemen mit ungewöhnlichen Filterkombinationen führen. Ich musste einige Male in den VTK-Quellcode einsteigen, um herauszufinden, warum meine komplexe Filterkette nicht die gewünschten Ergebnisse liefert. Ich würde dringend empfehlen, VTK so einzurichten, dass Debugging möglich ist.

  • Pro : Softwarearchitektur : Das Pipeline-Design und die allgemeine Architektur von VTK scheinen gut durchdacht zu sein und es macht Spaß, damit zu arbeiten. Ein paar Codezeilen können zu erstaunlichen Ergebnissen führen. Die integrierten Datenstrukturen sind einfach zu verstehen und zu verwenden.

  • Nachteil : Mikroarchitektur : Einige Designentscheidungen für Mikroarchitekturen entziehen sich meinem Verständnis. Konstantenkorrektheit ist fast nicht vorhanden, Arrays werden ohne klare Unterscheidung als Ein- und Ausgänge herumgereicht. Ich habe dies für meine eigenen Algorithmen gemildert, indem ich etwas Leistung aufgegeben und meinen eigenen Wrapper für vtkMath verwendet habe, der benutzerdefinierte 3D-Typen wie typedef std::array<double, 3> Pnt3d; .

  • Pro : Mikrodokumentation : Die Doxygen-Dokumentation aller Klassen und Filter ist umfangreich und brauchbar, die Beispiele und Testfälle im Wiki sind auch eine große Hilfe, um zu verstehen, wie Filter verwendet werden.

  • Nachteil: Makrodokumentation : Es gibt mehrere gute Tutorials und Einführungen in VTK im Web. Soweit ich weiß, gibt es jedoch keine große Referenzdokumentation, die erklärt, wie bestimmte Dinge gemacht werden. Wenn Sie etwas Neues tun möchten, müssen Sie einige Zeit danach suchen, wie es geht. Außerdem ist es schwierig, den spezifischen Filter für eine Aufgabe zu finden. Sobald Sie es jedoch gefunden haben, wird die Doxygen-Dokumentation normalerweise ausreichen. Eine gute Möglichkeit, das VTK-Framework zu erkunden, besteht darin, Paraview herunterzuladen und damit zu experimentieren.

  • Pro : Implizite Parallelisierungsunterstützung : Wenn Ihre Quellen in mehrere Teile aufgeteilt werden können, die unabhängig verarbeitet werden können, ist die Parallelisierung so einfach wie das Erstellen einer separaten Filterkette in jedem Thread, der einen einzelnen Teil verarbeitet. Die meisten großen Visualisierungsprobleme fallen normalerweise in diese Kategorie.

  • Nachteil : Keine explizite Parallelisierungsunterstützung : Wenn Sie nicht mit großen, teilbaren Problemen gesegnet sind, aber mehrere Kerne verwenden möchten, sind Sie auf sich allein gestellt. Sie müssen durch Trial-and-Error oder durch Lesen der Quelle herausfinden, welche Klassen Thread-sicher oder sogar wiedereintrittsfähig sind. Ich habe einmal ein Parallelisierungsproblem bei einem VTK-Filter aufgespürt, der eine statische globale Variable verwendet, um eine C-Bibliothek aufzurufen.

  • Pro : Buildsystem CMake : Das plattformübergreifende Meta-Build-System CMake wird ebenfalls von Kitware (den Machern von VTK) entwickelt und in vielen Projekten außerhalb von Kitware verwendet. Es lässt sich sehr gut in VTK integrieren und macht die Einrichtung eines Build-Systems für mehrere Plattformen viel weniger schmerzhaft.

  • Pro : Plattformunabhängigkeit, Lizenz und Langlebigkeit : VTK ist von Haus aus plattformunabhängig und wird unter einer sehr freizügigen Lizenz im BSD-Stil lizenziert. Darüber hinaus steht professionelle Unterstützung für wichtige Projekte zur Verfügung, die dies erfordern. Kitware wird von vielen Forschungseinrichtungen und anderen Unternehmen unterstützt und wird es noch einige Zeit geben.

Letztes Wort

Insgesamt ist VTK das beste Datenvisualisierungstool für die Art von Problemen, die ich liebe. Wenn Sie jemals auf ein Projekt stoßen, das Visualisierung, Netzverarbeitung, Bildverarbeitung oder ähnliche Aufgaben erfordert, versuchen Sie, Paraview mit einem Eingabebeispiel zu starten, und prüfen Sie, ob VTK das Tool für Sie sein könnte.