Wizualizacja danych 3D z narzędziami Open Source: samouczek z wykorzystaniem VTK
Opublikowany: 2022-03-11W swoim ostatnim artykule na blogu Toptala, wykwalifikowany naukowiec zajmujący się danymi, Charles Cook, pisał o obliczeniach naukowych przy użyciu narzędzi open source. Jego samouczek zawiera ważny punkt na temat narzędzi open source i roli, jaką mogą odegrać w łatwym przetwarzaniu danych i uzyskiwaniu wyników.
Ale jak tylko rozwiążemy wszystkie te złożone równania różniczkowe, pojawia się kolejny problem. Jak rozumiemy i interpretujemy ogromne ilości danych pochodzących z tych symulacji? W jaki sposób wizualizujemy potencjalne gigabajty danych, na przykład dane z milionami punktów siatki w ramach dużej symulacji?
Podczas pracy nad podobnymi problemami do mojej pracy magisterskiej zetknąłem się z Visualization Toolkit, czyli VTK - potężną biblioteką graficzną specjalizującą się w wizualizacji danych.
W tym samouczku przedstawię szybkie wprowadzenie do VTK i jego architektury rurociągów, a następnie omówię przykład rzeczywistej wizualizacji 3D przy użyciu danych z symulowanego płynu w pompie wirnikowej. Na koniec wymienię mocne strony biblioteki, a także słabe punkty, które napotkałem.
Wizualizacja danych i rurociąg VTK
Biblioteka open source VTK zawiera solidny potok przetwarzania i renderowania z wieloma wyrafinowanymi algorytmami wizualizacji. Jednak jego możliwości nie kończą się na tym, ponieważ z biegiem czasu dodano również algorytmy przetwarzania obrazu i siatki. W moim obecnym projekcie z firmą zajmującą się badaniami stomatologicznymi wykorzystuję VTK do zadań przetwarzania opartych na siatce w aplikacji opartej na Qt, podobnej do CAD. Studia przypadków VTK pokazują szeroki zakres odpowiednich zastosowań.
Architektura VTK opiera się na potężnej koncepcji potoku. Poniżej przedstawiono podstawowy zarys tej koncepcji:
- Źródła są na samym początku potoku i tworzą „coś z niczego”. Na przykład
vtkConeSource
tworzy stożek 3D, avtkSTLReader
odczytuje pliki geometrii 3D*.stl
. - Filtry przekształcają dane wyjściowe źródeł lub innych filtrów w coś nowego. Na przykład
vtkCutter
wycina dane wyjściowe poprzedniego obiektu w algorytmach za pomocą niejawnej funkcji, np. płaszczyzny. Wszystkie algorytmy przetwarzania, które są dostarczane z VTK, są zaimplementowane jako filtry i można je dowolnie łączyć. - Programy mapujące przekształcają dane w elementy graficzne. Na przykład można ich użyć do określenia tabeli przeglądowej do kolorowania danych naukowych. Są abstrakcyjnym sposobem określenia, co ma być wyświetlane.
- Aktorzy reprezentują obiekt (geometrię i właściwości wyświetlania) w scenie. W tym miejscu określa się takie elementy, jak kolor, krycie, cieniowanie lub orientacja.
- Renderery i Windows wreszcie opisują renderowanie na ekranie w sposób niezależny od platformy.
Typowy potok renderujący VTK zaczyna się od jednego lub więcej źródeł, przetwarza je przy użyciu różnych filtrów na kilka obiektów wyjściowych, które są następnie renderowane osobno przy użyciu maperów i aktorów. Siłą tej koncepcji jest mechanizm aktualizacji. Jeśli ustawienia filtrów lub źródeł zostaną zmienione, wszystkie zależne filtry, mapery, aktorzy i okna renderowania zostaną automatycznie zaktualizowane. Z drugiej strony, jeśli obiekt w dalszej części rurociągu potrzebuje informacji, aby wykonać swoje zadania, może je łatwo uzyskać.
Ponadto nie ma potrzeby bezpośredniego zajmowania się systemami renderowania, takimi jak OpenGL. VTK hermetyzuje wszystkie zadania niskiego poziomu w sposób niezależny od platformy i (częściowo) renderowania systemu; deweloper pracuje na znacznie wyższym poziomie.
Przykład kodu z zestawem danych pompy wirnikowej
Spójrzmy na przykład wizualizacji danych przy użyciu zestawu danych przepływu płynu w pompie z wirnikiem obrotowym z IEEE Visualization Contest 2011. Same dane są wynikiem symulacji dynamiki obliczeniowej płynów, podobnej do tej opisanej w artykule Charlesa Cooka.
Spakowane dane symulacyjne prezentowanej pompy mają rozmiar ponad 30 GB. Zawiera wiele części i wiele kroków czasowych, stąd duży rozmiar. W tym przewodniku będziemy bawić się częścią wirnika jednego z tych kroków czasowych, która ma skompresowany rozmiar około 150 MB.
Moim wybranym językiem do używania VTK jest C++, ale istnieją mapowania dla kilku innych języków, takich jak Tcl/Tk, Java i Python. Jeśli celem jest tylko wizualizacja pojedynczego zestawu danych, nie trzeba w ogóle pisać kodu i zamiast tego można użyć Paraview, graficznego interfejsu dla większości funkcji VTK.
Zestaw danych i dlaczego 64-bit jest konieczny
Wyodrębniłem zestaw danych wirnika z podanego powyżej zestawu danych o pojemności 30 GB, otwierając jeden krok czasowy w Paraview i wyodrębniając część wirnika do osobnego pliku. Jest to plik siatki bez struktury, tj. objętość 3D składająca się z punktów i komórek 3D, takich jak sześcian, czworościan i tak dalej. Każdy z punktów 3D ma skojarzone wartości. Czasami komórki mają również skojarzone wartości, ale nie w tym przypadku. Ten trening skoncentruje się na ciśnieniu i prędkości w punktach i spróbuje zwizualizować je w kontekście 3D.
Rozmiar skompresowanego pliku wynosi około 150 MB, a rozmiar w pamięci około 280 MB po załadowaniu za pomocą VTK. Jednak przetwarzając go w VTK, zestaw danych jest wielokrotnie buforowany w potoku VTK i szybko osiągamy limit 2 GB pamięci dla programów 32-bitowych. Istnieją sposoby na zaoszczędzenie pamięci podczas korzystania z VTK, ale dla uproszczenia po prostu skompilujemy i uruchomimy przykład w wersji 64-bitowej.
Podziękowania : Zbiór danych został udostępniony dzięki uprzejmości Instytutu Mechaniki Stosowanej Uniwersytetu Clausthal w Niemczech (dypl. Wirtsch.-Ing. Andreas Lucius).
Cel
To, co osiągniemy, używając VTK jako narzędzia, to wizualizacja pokazana na poniższym obrazku. W kontekście 3D zarys zestawu danych jest pokazany przy użyciu częściowo przezroczystego renderowania szkieletowego. Lewa część zestawu danych jest następnie używana do wyświetlania nacisku za pomocą prostego kodowania kolorami powierzchni. (W tym przykładzie pominiemy bardziej złożone renderowanie objętości). Aby zwizualizować pole prędkości, prawa część zbioru danych jest wypełniona liniami prądu, które są oznaczone kolorami według wielkości ich prędkości. Ten wybór wizualizacji nie jest technicznie idealny, ale chciałem, aby kod VTK był jak najprostszy. Ponadto istnieje powód, dla którego ten przykład stanowi część wyzwania wizualizacyjnego, tj. dużo turbulencji w przepływie.
Krok po kroku
Omówię krok po kroku kod VTK, pokazując, jak będzie wyglądał wynik renderowania na każdym etapie. Pełny kod źródłowy można pobrać pod koniec szkolenia.
Zacznijmy od włączenia wszystkiego, czego potrzebujemy od VTK i otwórzmy główną funkcję.
#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) {
Następnie konfigurujemy renderer i okno renderowania, aby wyświetlić nasze wyniki. Ustawiamy kolor tła i rozmiar okna renderowania.
// 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);
Za pomocą tego kodu moglibyśmy już wyświetlić statyczne okno renderowania. Zamiast tego decydujemy się dodać vtkRenderWindowInteractor
, aby interaktywnie obracać, powiększać i przesuwać scenę.
// Setup the render window interactor vtkNew<vtkRenderWindowInteractor> interact; vtkNew<vtkInteractorStyleTrackballCamera> style; interact->SetRenderWindow(renWin.Get()); interact->SetInteractorStyle(style.Get());
Teraz mamy działający przykład pokazujący szare, puste okno renderowania.
Następnie ładujemy zestaw danych za pomocą jednego z wielu czytników dostarczanych z VTK.
// Read the file vtkSmartPointer<vtkXMLUnstructuredGridReader> pumpReader = vtkSmartPointer<vtkXMLUnstructuredGridReader>::New(); pumpReader->SetFileName("rotor.vtu");
Krótka wycieczka do zarządzania pamięcią VTK : VTK wykorzystuje wygodną koncepcję automatycznego zarządzania pamięcią, obracającą się wokół zliczania referencji. Jednak w odróżnieniu od większości innych implementacji, liczba odwołań jest przechowywana w samych obiektach VTK, a nie w klasie inteligentnego wskaźnika. Ma to tę zaletę, że licznik odwołań można zwiększyć, nawet jeśli obiekt VTK jest przekazywany jako surowy wskaźnik. Istnieją dwa główne sposoby tworzenia zarządzanych obiektów VTK. vtkNew<T>
i vtkSmartPointer<T>::New()
, z główną różnicą polegającą na tym, że vtkSmartPointer<T>
można niejawnie rzutować na surowy wskaźnik T*
i można go zwrócić z funkcji. W przypadku wystąpień vtkNew<T>
będziemy musieli wywołać .Get()
, aby uzyskać surowy wskaźnik i możemy go zwrócić tylko przez zawinięcie go w vtkSmartPointer
. W naszym przykładzie nigdy nie wracamy z funkcji, a wszystkie obiekty są aktywne przez cały czas, dlatego użyjemy skrótu vtkNew
, z powyższym wyjątkiem do celów demonstracyjnych.
W tym momencie nic nie zostało jeszcze odczytane z pliku. My lub filtr znajdujący się dalej w łańcuchu musielibyśmy wywołać funkcję Update()
, aby faktycznie nastąpiło odczytanie pliku. Zwykle najlepszym sposobem jest umożliwienie klasom VTK samodzielnej obsługi aktualizacji. Czasami jednak chcemy uzyskać bezpośredni dostęp do wyniku filtra, na przykład, aby uzyskać zakres ciśnień w tym zbiorze danych. Następnie musimy ręcznie wywołać Update()
. (Nie tracimy wydajności przez wielokrotne wywoływanie funkcji Update()
, ponieważ wyniki są buforowane).
// Get the pressure range pumpReader->Update(); double pressureRange[2]; pumpReader->GetOutput()->GetPointData()->GetArray("Pressure")->GetRange(pressureRange);
Następnie musimy wyodrębnić lewą połowę zestawu danych za pomocą vtkClipDataSet
. Aby to osiągnąć, najpierw definiujemy vtkPlane
, który definiuje podział. Następnie po raz pierwszy zobaczymy, jak potok VTK jest połączony ze sobą: successor->SetInputConnection(predecessor->GetOutputPort())
. Za każdym razem, gdy zażądamy aktualizacji od clipperLeft
to połączenie zapewni teraz, że wszystkie poprzednie filtry są również aktualne.
// 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());
Na koniec tworzymy naszych pierwszych aktorów i maperów, aby wyświetlić renderowanie szkieletowe lewej połowy. Zauważ, że program odwzorowujący jest połączony ze swoim filtrem dokładnie w taki sam sposób, jak filtry ze sobą. Przez większość czasu sam renderer uruchamia aktualizacje wszystkich aktorów, maperów i podstawowych łańcuchów filtrów!
Jedyna linia, która nie jest oczywista, to prawdopodobnie leftWireMapper->ScalarVisibilityOff();
- zabrania kolorowania szkieletu według wartości nacisku, które są ustawione jako aktualnie aktywna tablica.
// 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());
W tym momencie okno renderowania w końcu pokazuje coś, tj. szkielet dla lewej części.

Rendering szkieletowy dla prawej części jest tworzony w podobny sposób, przez przełączenie normalnej płaszczyzny (nowo utworzonego) vtkClipDataSet
w przeciwnym kierunku i nieznaczną zmianę koloru i krycia (nowo utworzonego) mapera i aktora. Zauważ, że tutaj nasz potok VTK dzieli się na dwa kierunki (prawy i lewy) z tego samego wejściowego zbioru danych.
// 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());
Zgodnie z oczekiwaniami okno danych wyjściowych pokazuje teraz obie części modelu krawędziowego.
Teraz jesteśmy gotowi do wizualizacji przydatnych danych! Aby dodać wizualizację ciśnienia do lewej części, nie musimy wiele robić. Tworzymy nowy mapper i również łączymy go z clipperLeft
, ale tym razem kolorujemy według tablicy ciśnienia. To również tutaj w końcu wykorzystujemy zakres pressureRange
, który wyprowadziliśmy powyżej.
// 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());
Dane wyjściowe wyglądają teraz tak, jak na poniższym obrazku. Ciśnienie w środku jest bardzo niskie, zasysając materiał do pompy. Następnie materiał ten jest transportowany na zewnątrz, szybko zyskując ciśnienie. (Oczywiście powinna istnieć legenda mapy kolorów z rzeczywistymi wartościami, ale pominąłem ją, aby przykład był krótszy).
Teraz zaczyna się trudniejsza część. Chcemy narysować linie prędkości we właściwej części. Strumienie są generowane przez integrację w polu wektorowym z punktów źródłowych. Pole wektorowe jest już częścią zestawu danych w postaci tablicy wektorów „Velocities”. Więc musimy tylko wygenerować punkty źródłowe. vtkPointSource
generuje sferę losowych punktów. Wygenerujemy 1500 punktów źródłowych, ponieważ większość z nich i tak nie będzie znajdować się w zbiorze danych i zostaną zignorowane przez śledzenie strumienia.
// 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);
Następnie tworzymy streamtracer i ustawiamy jego połączenia wejściowe. „Czekaj, wiele połączeń?”, możesz powiedzieć. Tak – to pierwszy filtr VTK z wieloma wejściami, z jakimi się spotykamy. Normalne połączenie wejściowe jest używane dla pola wektorowego, a połączenie źródłowe jest używane dla punktów siewnych. Ponieważ „Velocities” jest „aktywną” tablicą wektorów w clipperRight
, nie musimy jej tutaj wyraźnie określać. Na koniec określamy, że integracja powinna być wykonywana w obu kierunkach od punktów zarodkowych i ustawiamy metodę integracji na Runge-Kutta-4.5.
vtkNew<vtkStreamTracer> tracer; tracer->SetInputConnection(clipperRight->GetOutputPort()); tracer->SetSourceConnection(pointSource->GetOutputPort()); tracer->SetIntegrationDirectionToBoth(); tracer->SetIntegratorTypeToRungeKutta45();
Naszym następnym problemem jest pokolorowanie linii prądu według wielkości prędkości. Ponieważ nie ma tablicy dla wielkości wektorów, po prostu obliczymy wielkości w nowej tablicy skalarnej. Jak się domyślasz, do tego zadania jest również filtr VTK: vtkArrayCalculator
. Pobiera zestaw danych i wyprowadza go bez zmian, ale dodaje dokładnie jedną tablicę, która jest obliczona z jednej lub więcej istniejących. Konfigurujemy ten kalkulator tablicowy, aby pobierał wielkość wektora „Velocity” i wyprowadzał go jako „MagVelocity”. Na koniec ponownie wywołujemy Update()
ręcznie, aby uzyskać zakres nowej tablicy.
// 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
bezpośrednio wypisuje polilinie, a vtkArrayCalculator
przekazuje je bez zmian. Dlatego możemy po prostu wyświetlić dane wyjściowe magCalc
bezpośrednio za pomocą nowego mapera i aktora.
Zamiast tego w tym szkoleniu zdecydowaliśmy się na ulepszenie wyników, wyświetlając zamiast tego wstążki. vtkRibbonFilter
generuje komórki 2D, aby wyświetlić wstążki dla wszystkich polilinii danych wejściowych.
// 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());
Tym, czego teraz nadal brakuje, a które są faktycznie potrzebne do wytworzenia pośrednich renderów, jest ostatnie pięć linii, aby faktycznie wyrenderować scenę i zainicjować interaktor.
// Render and show interactive window renWin->Render(); interact->Initialize(); interact->Start(); return 0; }
W końcu dochodzimy do gotowej wizualizacji, którą jeszcze raz przedstawię tutaj:
Pełny kod źródłowy powyższej wizualizacji można znaleźć tutaj.
Dobry, zły i brzydki
Zakończę ten artykuł listą moich osobistych zalet i wad frameworka VTK.
Za : Aktywny rozwój : VTK jest aktywnie rozwijany przez kilku współpracowników, głównie ze społeczności naukowej. Oznacza to, że dostępne są najnowocześniejsze algorytmy, wiele formatów 3D można importować i eksportować, błędy są aktywnie naprawiane, a problemy zwykle mają gotowe rozwiązanie na forach dyskusyjnych.
Wada : Niezawodność : Łączenie wielu algorytmów pochodzących od różnych autorów z projektem otwartego potoku VTK może jednak prowadzić do problemów z nietypowymi kombinacjami filtrów. Kilka razy musiałem zajrzeć do kodu źródłowego VTK, aby dowiedzieć się, dlaczego mój złożony łańcuch filtrów nie daje oczekiwanych wyników. Zdecydowanie polecam skonfigurowanie VTK w sposób umożliwiający debugowanie.
Pro : Architektura oprogramowania : Projekt potoku i ogólna architektura VTK wydaje się dobrze przemyślana i praca z nią jest przyjemnością. Kilka linijek kodu może dać niesamowite rezultaty. Wbudowane struktury danych są łatwe do zrozumienia i użytkowania.
Wada : Mikroarchitektura : Niektóre decyzje dotyczące mikroarchitektury wymykają się mojemu zrozumieniu. Poprawność const prawie nie istnieje, tablice są przekazywane jako dane wejściowe i wyjściowe bez wyraźnego rozróżnienia. Złagodziłem to dla moich własnych algorytmów, rezygnując z pewnej wydajności i używając własnego wrappera dla
vtkMath
, który wykorzystuje niestandardowe typy 3D, takie jaktypedef std::array<double, 3> Pnt3d;
.Pro : Mikrodokumentacja: Dokumentacja Doxygen wszystkich klas i filtrów jest obszerna i użyteczna, przykłady i przypadki testowe na wiki są również bardzo pomocne w zrozumieniu, w jaki sposób używane są filtry.
Wada : Dokumentacja makr : Istnieje kilka dobrych samouczków i wprowadzeń do VTK w sieci. Jednak o ile wiem, nie ma dużej dokumentacji referencyjnej, która wyjaśniałaby, jak robi się konkretne rzeczy. Jeśli chcesz zrobić coś nowego, spodziewaj się, że przez jakiś czas poszukasz, jak to zrobić. Ponadto trudno jest znaleźć konkretny filtr do zadania. Kiedy już go znajdziesz, zwykle wystarczy dokumentacja Doxygena. Dobrym sposobem na poznanie frameworka VTK jest pobranie i eksperymentowanie z Paraview.
Pro : Niejawna obsługa paralelizacji : Jeśli twoje źródła można podzielić na kilka części, które mogą być przetwarzane niezależnie, równoległość jest tak prosta, jak utworzenie oddzielnego łańcucha filtrów w każdym wątku, który przetwarza pojedynczą część. Większość dużych problemów z wizualizacją zwykle należy do tej kategorii.
Wada : Brak wyraźnej obsługi paralelizacji : Jeśli nie jesteś pobłogosławiony dużymi, podzielnymi problemami, ale chcesz korzystać z wielu rdzeni, jesteś zdany na siebie. Będziesz musiał dowiedzieć się, które klasy są bezpieczne dla wątków, a nawet ponownie wprowadzane metodą prób i błędów lub poprzez odczytanie źródła. Kiedyś wyśledziłem problem z równoległością w filtrze VTK, który używał statycznej zmiennej globalnej w celu wywołania jakiejś biblioteki C.
Pro : Buildsystem CMake : Wieloplatformowy meta-system CMake jest również rozwijany przez Kitware (twórców VTK) i używany w wielu projektach poza Kitware. Bardzo dobrze integruje się z VTK i sprawia, że konfigurowanie systemu kompilacji dla wielu platform jest znacznie mniej bolesne.
Pro : Niezależność od platformy, licencja i długowieczność : VTK jest niezależną platformą po wyjęciu z pudełka i jest licencjonowana na podstawie bardzo liberalnej licencji w stylu BSD. Ponadto dostępne jest profesjonalne wsparcie dla ważnych projektów, które tego wymagają. Kitware jest wspierane przez wiele jednostek badawczych i innych firm i będzie dostępne przez jakiś czas.
Ostatnie słowo
Ogólnie rzecz biorąc, VTK jest najlepszym narzędziem do wizualizacji danych dla rodzajów problemów, które kocham. Jeśli kiedykolwiek natkniesz się na projekt, który wymaga wizualizacji, przetwarzania siatki, przetwarzania obrazu lub podobnych zadań, spróbuj uruchomić Paraview z przykładem wejściowym i oceń, czy VTK może być dla Ciebie narzędziem.