Oma hiljutises artiklis ApeeScape'i ajaveebis vilunud andmeteadlane Charles Cook kirjutas teaduslik arvutus avatud lähtekoodiga tööriistadega . Tema õpetuses on oluline punkt avatud lähtekoodiga tööriistade ja nende rolli kohta andmete hõlpsas töötlemises ja tulemuste hankimises.
Kuid niipea, kui oleme kõik need keerukad diferentsiaalvõrrandid lahendanud, kerkib üles veel üks probleem. Kuidas mõista ja tõlgendada tohutut andmemahtu, mis nendest simulatsioonidest välja tuleb? Kuidas visualiseerida suure simulatsiooni käigus potentsiaalseid gigabaiti andmeid, näiteks miljoneid võrgupunkte sisaldavaid andmeid?
Minu töö ajal sarnaste probleemidega minu jaoks Magistritöö , Puutusin kokku Visualization Toolkitiga ehk VTK - võimsa graafikakoguga, mis on spetsialiseerunud andmete visualiseerimisele.
Selles õpetuses tutvustan kiiresti VTK-d ja selle torujuhtme arhitektuuri ning jätkan arutelu reaalse 3D visualiseerimise näite kohta, kasutades tiiviku pumba simuleeritud vedeliku andmeid. Lõpuks loetlen nii raamatukogu tugevad küljed kui ka nõrgad kohad, millega kokku puutusin.
Avatud lähtekoodiga teek VTK sisaldab tugevat töötlemis- ja renderdustorustikku koos paljude keerukate visualiseerimisalgoritmidega. Selle võimalused ei piirdu sellega, sest aja jooksul on lisatud ka piltide ja võrkude töötlemise algoritme. Oma praeguses hambaraviettevõtte projektis kasutan VTK-d võrgupõhiste töötlemisülesannete täitmiseks a piires Qt -põhine, CAD-laadne rakendus. The VTK juhtumianalüüsid näidata sobivate rakenduste laia valikut.
VTK arhitektuur keerleb võimsa torujuhtme kontseptsiooni ümber. Selle kontseptsiooni põhijooned on toodud siin:
vtkConeSource
loob 3D-koonuse ja vtkSTLReader
loeb *.stl
3D-geomeetriafailid.vtkCutter
lõikab algoritmides eelmise objekti väljundi, kasutades kaudset funktsiooni, näiteks tasapinda. Kõik VTK-ga kaasas olevad töötlusalgoritmid on rakendatud filtritena ja neid saab vabalt kokku aheldada.Tüüpiline VTK renderdustorustik algab ühest või mitmest allikast, töötleb need mitmesuguste filtrite abil mitmeks väljundobjektiks, mis seejärel renderdatakse kaardistajate ja näitlejate abil eraldi. Selle kontseptsiooni taga on uuendusmehhanism. Filtrite või allikate sätete muutmisel värskendatakse automaatselt kõiki sõltuvaid filtreid, kaardistajaid, näitlejaid ja renderdamisaknaid. Kui seevastu torujuhtme allpool asuv objekt vajab oma ülesannete täitmiseks teavet, võib ta selle hõlpsasti kätte saada.
Lisaks pole vaja otseselt tegeleda selliste renderdussüsteemidega nagu OpenGL. VTK hõlmab kõiki madalama taseme ülesandeid platvormilt ja (osaliselt) renderdussüsteemist sõltumatult; arendaja töötab palju kõrgemal tasemel.
Vaatame andmete visualiseerimise näidet, kasutades IEEE Visualization Contest 2011. aasta pöörleva tiiviku pumba vedeliku voolu andmekogumit. Andmed ise on arvutusliku vedeliku dünaamika simulatsiooni tulemus, umbes nagu Charles Cooki artiklis kirjeldatud.
Esiletõstetud pumba lukustatud simulatsiooniandmed on üle 30 GB. See sisaldab mitut osa ja mitu ajalist sammu, seega suur suurus. Selles juhendis mängime ringi ühe neist ajatelgedest koosneva rootori osaga, mille tihendatud maht on umbes 150 MB.
Interneti-ettevõtete väärtus põhineb eelkõige
Minu valitud keel VTK kasutamiseks on C ++, kuid on olemas kaardistamised mitmetele teistele keeltele nagu Tcl / Tk, Java ja Python. Kui sihtmärk on vaid ühe andmekogumi visualiseerimine, ei pea kood üldse kirjutama ja saab selle asemel kasutada Paraview , graafiline kasutajaliides enamiku VTK funktsioonide jaoks.
Eemaldasin rootori andmekogumi ülaltoodud 30 GB andmekogumist, avades Paraview'is ühe ajavõtte ja eraldades rootori osa eraldi faili. See on struktureerimata ruudustikfail, st 3D-maht, mis koosneb punktidest ja 3D-lahtritest, nagu heksaheedrid, tetraeedrid jne. Igal 3D-punktil on seotud väärtused. Mõnikord on lahtritel ka seotud väärtused, kuid mitte sel juhul. See koolitus keskendub rõhule ja kiirusele punktides ning püüab neid 3D-kontekstis visualiseerida.
Pakitud faili suurus on VTK-ga laadimisel umbes 150 MB ja mälusisene suurus on umbes 280 MB. Kuid VTK-s töötlemisel salvestatakse andmekogum VTK-ga mitu korda vahemällu ja jõuame kiiresti 32-bitiste programmide 2 GB mälupiiranguni. VTK kasutamisel on mälu kokkuhoiu võimalusi, kuid lihtsuse huvides koostame ja käitame näite lihtsalt 64-bitisena.
Tänusõnad : Andmekogum on kättesaadav Saksamaa Clausthali ülikooli rakendusmehaanika instituudi nõusolekul (dipl. Wirtsch.-Ing. Andreas Lucius).
VTK tööriistana kasutamisel saavutame alloleval pildil kujutatud visualiseerimise. 3D-kontekstina kuvatakse andmekogumi kontuur osaliselt läbipaistva traatvõrgu renderduse abil. Andmekogumi vasakut osa kasutatakse seejärel rõhu kuvamiseks, kasutades pindade lihtsat värvikodeerimist. (Jätame selle näite jaoks keerulisema helitugevuse renderdamise vahele). Kiirusevälja visualiseerimiseks täidetakse andmekogumi parem osa sujuvamaks , mis on värviga kodeeritud nende kiiruse suuruse järgi. See visualiseerimise valik pole tehniliselt ideaalne, kuid tahtsin hoida VTK koodi võimalikult lihtsana. Lisaks on sellel näitel põhjust osaleda visualiseerimise väljakutses, s.o palju turbulentse voos.
Arutan samm-sammult VTK koodi, näidates, kuidas renderdusväljund igas etapis välja näeks. Täieliku lähtekoodi saab koolituse lõpus alla laadida.
kuidas pääseda Bloombergi terminali
Alustame kõigest vajalikust VTK-st ja avame põhifunktsiooni.
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int main(int argc, char** argv) {
Järgmisena seadistame renderdaja ja renderdamisakna, et tulemusi kuvada. Me määrame taustavärvi ja renderdamisakna suuruse.
// Setup the renderer vtkNew renderer; renderer->SetBackground(0.9, 0.9, 0.9); // Setup the render window vtkNew renWin; renWin->AddRenderer(renderer.Get()); renWin->SetSize(500, 500);
Selle koodiga saaksime juba staatilise renderdamise akna kuvada. Selle asemel valime vtkRenderWindowInteractor
stseeni interaktiivseks pööramiseks, suumimiseks ja panoraamimiseks.
// Setup the render window interactor vtkNew interact; vtkNew style; interact->SetRenderWindow(renWin.Get()); interact->SetInteractorStyle(style.Get());
Nüüd on meil toimiv näide, mis näitab halli tühja renderdamisakent.
Järgmisena laadime andmekogumi, kasutades ühte paljudest VTK-ga kaasas olevatest lugejatest.
// Read the file vtkSmartPointer pumpReader = vtkSmartPointer::New(); pumpReader->SetFileName('rotor.vtu');
Lühike ekskursioon VTK mäluhaldusse : VTK kasutab mugavat automaatset mäluhalduse kontseptsiooni, mis pöörleb ümber võrdlusloenduse. Erinevalt enamikust teistest rakendustest hoitakse viitenumbreid nutikate kursorite klassi asemel VTK objektides endis. Selle eeliseks on see, et viidete arvu saab suurendada, isegi kui VTK-objekt antakse toore osutina edasi. Hallatavate VTK objektide loomiseks on kaks peamist viisi. vtkNew
ja vtkSmartPointer::New()
, peamine erinevus seisneb selles, et a vtkSmartPointer
on kaudsele osutile kaudselt ülekantav T*
ja selle saab funktsioonilt tagastada. vtkNew
peame helistama .Get()
toore kursori saamiseks ja selle saame tagastada ainult siis, kui pakkida see vtkSmartPointer
Meie näites ei naase me kunagi funktsioonidest ja kõik objektid elavad kogu aeg, seetõttu kasutame lühist vtkNew
, ainult ülaltoodud erandiga tutvustamiseks.
Siiani pole failist veel midagi loetud. Meie või mõni ahelas olev filter peaks helistama Update()
et faili lugemine tegelikult juhtuks. Tavaliselt on parim viis lasta VTK klassidel värskendustega ise hakkama saada. Mõnikord soovime aga filtri tulemusele otse juurde pääseda, näiteks saada selles andmekogumis rõhuvahemik. Siis peame helistama Update()
käsitsi. (Me ei kaota jõudlust, kui helistame Update()
mitu korda, kuna tulemused on vahemällu salvestatud.)
// Get the pressure range pumpReader->Update(); double pressureRange[2]; pumpReader->GetOutput()->GetPointData()->GetArray('Pressure')->GetRange(pressureRange);
Järgmisena peame eraldama andmekogumi vasakpoolne osa, kasutades vtkClipDataSet
Selle saavutamiseks määratleme kõigepealt a vtkPlane
see määratleb jagunemise. Seejärel näeme esimest korda, kuidas VTK torujuhe on ühendatud: successor->SetInputConnection(predecessor->GetOutputPort())
Alati, kui me soovime värskendust clipperLeft
see ühendus tagab nüüd, et ka kõik eelnevad filtrid on ajakohased.
// Clip the left part from the input vtkNew planeLeft; planeLeft->SetOrigin(0.0, 0.0, 0.0); planeLeft->SetNormal(-1.0, 0.0, 0.0); vtkNew clipperLeft; clipperLeft->SetInputConnection(pumpReader->GetOutputPort()); clipperLeft->SetClipFunction(planeLeft.Get());
Lõpuks loome oma esimesed näitlejad ja kaardistajad, et kuvada vasakpoolse traadi raami renderdust. Pange tähele, et kaardistaja on oma filtriga ühendatud täpselt samamoodi nagu filtrid üksteisega. Enamasti käivitab renderdaja ise kõigi osalejate, kaardistajate ja nende aluseks olevate filtriahelate värskendused!
Ainus rida, mis ei ole iseenesestmõistetav, on tõenäoliselt leftWireMapper->ScalarVisibilityOff();
- see keelab traadiraami värvimise rõhuväärtuste abil, mis on seatud praegu aktiivseks massiiviks.
// Create the wireframe representation for the left part vtkNew leftWireMapper; leftWireMapper->SetInputConnection(clipperLeft->GetOutputPort()); leftWireMapper->ScalarVisibilityOff(); vtkNew 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());
Siinkohal näitab renderdamisaken lõpuks midagi, s.t vasakpoolse osa traatraami.
Parema osa traatvõrgu renderdamine luuakse sarnaselt, vahetades (äsjaloodud) vtkClipDataSet
vastupidises suunas ning (vastloodud) kaardistaja ja näitleja värvi ja läbipaistmatust veidi muutes. Pange tähele, et siin jaguneb meie VTK torujuhe samast sisendandmekogumist kahte suunda (paremale ja vasakule).
// Clip the right part from the input vtkNew planeRight; planeRight->SetOrigin(0.0, 0.0, 0.0); planeRight->SetNormal(1.0, 0.0, 0.0); vtkNew clipperRight; clipperRight->SetInputConnection(pumpReader->GetOutputPort()); clipperRight->SetClipFunction(planeRight.Get()); // Create the wireframe representation for the right part vtkNew rightWireMapper; rightWireMapper->SetInputConnection(clipperRight->GetOutputPort()); rightWireMapper->ScalarVisibilityOff(); vtkNew 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());
Väljundaknas kuvatakse ootuspäraselt nüüd mõlemad traatvõrgu osad.
iganädalase rahavoo prognoosi mall
Nüüd oleme valmis kasulikke andmeid visualiseerima! Vasakule osale rõhu visualiseerimise lisamiseks pole meil vaja palju teha. Loome uue kaardistaja ja ühendame selle clipperLeft
-ga samuti, kuid seekord värvime rõhumassi järgi. Siin on ka see, et lõpuks kasutame pressureRange
oleme tuletanud eespool.
// Create the pressure representation for the left part vtkNew pressureColorMapper; pressureColorMapper->SetInputConnection(clipperLeft->GetOutputPort()); pressureColorMapper->SelectColorArray('Pressure'); pressureColorMapper->SetScalarRange(pressureRange); vtkNew pressureColorActor; pressureColorActor->SetMapper(pressureColorMapper.Get()); pressureColorActor->GetProperty()->SetOpacity(0.5); renderer->AddActor(pressureColorActor.Get());
Väljund näeb nüüd välja nagu allpool näidatud pilt. Rõhk keskel on väga madal, imedes materjali pumba sisse. Seejärel transporditakse see materjal väljapoole, saavutades kiiresti surve. (Muidugi peaks olema värvikaardi legend tegelike väärtustega, kuid jätsin selle näite lühemaks jätmiseks välja.)
Nüüd algab keerulisem osa. Parempoolsesse ossa tahame joonistada kiiruse sujuvusi. Voolujooned genereeritakse lähtekohtadest vektorvälja integreerimise teel. Vektorväli on juba osa andmekogumist vektorimassiivi 'Kiirused' kujul. Seega peame genereerima ainult lähtekohad. vtkPointSource
genereerib juhuslike punktide sfääri. Genereerime 1500 lähtekohta, sest enamik neist ei asu mingil juhul andmekogumis ja voo jälgija ignoreerib neid.
// Create the source points for the streamlines vtkNew pointSource; pointSource->SetCenter(0.0, 0.0, 0.015); pointSource->SetRadius(0.2); pointSource->SetDistributionToUniform(); pointSource->SetNumberOfPoints(1500);
Järgmisena loome voo jälgija ja määrame selle sisendühendused. 'Oota, mitmekordne ühendused? ”, võite öelda. Jah - see on esimene mitme sisendiga VTK filter, millega kokku puutume. Vektorvälja jaoks kasutatakse tavalist sisendühendust ja lähtepunktide jaoks lähteallika ühendust. Kuna „Velocities“ on clipperRight
-i aktiivne vektorimassiiv, ei pea me seda siin otseselt määrama. Lõpuks täpsustame, et integreerimine peaks toimuma alguspunktidest mõlemas suunas, ja määrame integreerimismeetodi Runge-Kutta-4.5 .
vtkNew tracer; tracer->SetInputConnection(clipperRight->GetOutputPort()); tracer->SetSourceConnection(pointSource->GetOutputPort()); tracer->SetIntegrationDirectionToBoth(); tracer->SetIntegratorTypeToRungeKutta45();
Meie järgmine probleem on voolujoonte värvimine kiiruse suuruse järgi. Kuna vektorite suuruste jaoks pole massiivi, arvutame suurused lihtsalt uueks skalaarmassiiviks. Nagu arvasite, on ka selle ülesande jaoks VTK filter: vtkArrayCalculator
. See võtab andmekogumi ja väljastab selle muutmata kujul, kuid lisab täpselt ühe massiivi, mis arvutatakse ühest või mitmest olemasolevast. Konfigureerime selle massiivkalkulaatori nii, et see võtaks vektori „Kiirus” suuruse ja väljastaks selle kui „MagVelocity”. Lõpuks helistame Update()
uue massiivi ulatuse tuletamiseks uuesti käsitsi.
// Compute the velocity magnitudes and create the ribbons vtkNew 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
väljastab otsejooned ja vtkArrayCalculator
edastab need muutmata kujul. Seetõttu saaksime lihtsalt kuvada magCalc
väljundi otse uue kaardistaja ja näitleja abil.
Selle koolituse asemel otsustame muuta väljundi natuke ilusamaks, kuvades selle asemel paelu. vtkRibbonFilter
genereerib 2D lahtrid kõigi sisendjoonte lindide kuvamiseks.
// Create and render the ribbons vtkNew ribbonFilter; ribbonFilter->SetInputConnection(magCalc->GetOutputPort()); ribbonFilter->SetWidth(0.0005); vtkNew streamlineMapper; streamlineMapper->SetInputConnection(ribbonFilter->GetOutputPort()); streamlineMapper->SelectColorArray('MagVelocity'); streamlineMapper->SetScalarRange(magVelocityRange); vtkNew streamlineActor; streamlineActor->SetMapper(streamlineMapper.Get()); renderer->AddActor(streamlineActor.Get());
See, mis praegu veel puudu on ja mida on tegelikult vaja ka vaherenderduste tootmiseks, on viis viimast rida stseeni renderdamiseks ja interaktori initsialiseerimiseks.
// Render and show interactive window renWin->Render(); interact->Initialize(); interact->Start(); return 0; }
Lõpuks jõuame valmis visualiseeringuni, mida ma siin veel kord esitlen:
Ülaltoodud visualiseerimise täieliku lähtekoodi leiate siin .
Lõpetan selle artikli loetelu oma VTK raamistiku isiklikest plussidest ja miinustest.
Sest : Aktiivne areng : VTK on aktiivselt välja töötatud mitmete kaastöötajate poolt, peamiselt teadlaskonnast. See tähendab, et mõned tipptasemel algoritmid on saadaval, paljusid 3D-vorminguid saab importida ja eksportida, vead on aktiivselt fikseeritud ning probleemidel on arutelupaneelides tavaliselt valmis lahendus.
Koos : Usaldusväärsus : Paljude algoritmide ühendamine erinevatelt kaasautoritelt VTK avatud torujuhtme kujundusega võib aga põhjustada probleeme ebatavaliste filtrikombinatsioonidega. Olen pidanud paar korda VTK lähtekoodi minema, et aru saada, miks minu keeruline filtriahel ei anna soovitud tulemusi. Soovitaksin tungivalt VTK seadistada viisil, mis võimaldab silumist.
Sest : Tarkvaraarhitektuur : VTK torujuhtme disain ja üldine arhitektuur näib olevat läbimõeldud ja nendega on rõõm töötada. Mõni koodirida võib anda hämmastavaid tulemusi. Sisseehitatud andmekonstruktsioone on lihtne mõista ja kasutada.
Koos : Mikroarhitektuur : Mõni mikroarhitektuurne disainiotsus pääseb minu arusaamist mööda. Konst-korrektsust peaaegu ei eksisteeri, massiivid edastatakse sisendite ja väljunditena ilma selge eristamiseta. Ma leevendasin seda omaenda algoritmide jaoks, loobudes mõnest jõudlusest ja kasutades oma pakendit vtkMath
mis kasutab kohandatud 3D-tüüpe nagu typedef std::array Pnt3d;
.
Sest : Mikrodokumentatsioon : Kõigi klasside ja filtrite Doxygen dokumentatsioon on ulatuslik ja kasutatav, ka wiki näited ja testijuhud on suureks abiks filtrite kasutamise mõistmisel.
millist programmeerimiskeelt robootika kasutab
Koos : Makrodokumentatsioon : Veebis on mitu head õpetust ja tutvustusi VTK-le. Minu teada pole aga suurt viitdokumentatsiooni, mis selgitaks, kuidas konkreetseid asju tehakse. Kui soovite midagi uut teha, oodake mõnda aega, kuidas seda teha. Lisaks on ülesande jaoks raske leida konkreetset filtrit. Kui olete selle siiski leidnud, piisab tavaliselt Doxygeni dokumentatsioonist. Hea viis VTK raamistiku uurimiseks on Paraview'i allalaadimine ja sellega katsetamine.
Sest : Kaudne paralleelsuse tugi : Kui teie allikaid saab jagada mitmeks osaks, mida saab iseseisvalt töödelda, on paralleelimine sama lihtne kui eraldi filtriahela loomine igas lõimes, mis töötleb ühte osa. Enamik suuri visualiseerimisprobleeme kuulub tavaliselt sellesse kategooriasse.
Koos : Pole selget paralleelsuse tuge : Kui teid pole õnnistatud suurte, jagatavate probleemidega, kuid soovite kasutada mitut südamikku, olete omaette. Peate välja selgitama, millised klassid on niidikindlad, või proovige uuesti ja proovige uuesti läbi, lugedes allikat. Kord leidsin paralleelsusprobleemi VTK-filtrile, mis kasutas staatilist globaalset muutujat, et helistada mõnele C-teegile.
Sest : Ehitussüsteem CMake : Mitme platvormiga metaehitussüsteem CMake on välja töötanud ka Kitware (VTK tegijad) ja seda kasutatakse paljudes projektides väljaspool Kitware'i. See integreerub VTK-ga väga kenasti ja muudab mitme platvormi jaoks ehitussüsteemi seadistamise palju vähem valusaks.
Sest : Platvormi sõltumatus, litsents ja pikaealisus : VTK on platvormist sõltumatu karbist ja litsentsitud a-ga väga lubav BSD-stiilis litsents . Lisaks on professionaalne tugi neile olulistele projektidele, mis seda vajavad. Kitware'i toetavad paljud uurimisüksused ja muud ettevõtted ning see on mõnda aega olemas.
Üldiselt on VTK parim andmete visualiseerimise tööriist mulle meeldivate probleemide jaoks. Kui olete kunagi kohanud projekti, mis nõuab visualiseerimist, võrgusilma töötlemist, pilditöötlust või muid sarnaseid ülesandeid, proovige Paraview käivitada sisendnäitega ja hinnake, kas VTK võiks teie jaoks tööriist olla.