ApeeScapeのブログに関する彼の最近の記事では、 熟練したデータサイエンティスト チャールズクックはについて書いた オープンソースツールを使用した科学計算 。彼のチュートリアルでは、オープンソースツールと、データを簡単に処理して結果を取得する際にツールが果たすことができる役割について重要なポイントを示しています。
しかし、これらの複素微分方程式をすべて解くとすぐに、別の問題が発生します。これらのシミュレーションから得られる膨大な量のデータをどのように理解して解釈するのでしょうか。大規模なシミュレーション内で数百万のグリッドポイントを持つデータなど、潜在的なギガバイトのデータをどのように視覚化するのでしょうか。
私の同様の問題に取り組んでいる間 修士論文 、私はVisualization Toolkit、またはVTK(データの視覚化に特化した強力なグラフィックライブラリ)と接触しました。
このチュートリアルでは、VTKとそのパイプラインアーキテクチャを簡単に紹介し、インペラーポンプでシミュレートされた流体からのデータを使用した実際の3D視覚化の例について説明します。最後に、ライブラリの長所と、遭遇した短所をリストします。
オープンソースライブラリ VTK 多くの高度な視覚化アルゴリズムを備えた堅実な処理およびレンダリングパイプラインが含まれています。ただし、時間の経過とともに画像とメッシュの処理アルゴリズムも追加されているため、その機能はそれだけではありません。歯科研究会社との現在のプロジェクトでは、メッシュベースの処理タスクにVTKを利用しています。 Qt ベースのCADのようなアプリケーション。ザ・ VTKのケーススタディ 幅広い適切なアプリケーションを示します。
VTKのアーキテクチャは、強力なパイプラインの概念を中心に展開しています。この概念の基本的な概要を次に示します。
vtkConeSource
3Dコーンを作成し、vtkSTLReader
読み取り*.stl
3Dジオメトリファイル。vtkCutter
平面などの陰関数を使用して、アルゴリズム内の前のオブジェクトの出力をカットします。 VTKに付属するすべての処理アルゴリズムはフィルターとして実装されており、自由にチェーンできます。典型的なVTKレンダリングパイプラインは、1つ以上のソースから始まり、さまざまなフィルターを使用してそれらをいくつかの出力オブジェクトに処理し、マッパーとアクターを使用して別々にレンダリングします。この概念の背後にある力は、更新メカニズムです。フィルタまたはソースの設定が変更されると、依存するすべてのフィルタ、マッパー、アクター、およびレンダリングウィンドウが自動的に更新されます。一方、パイプラインのさらに下流にあるオブジェクトがそのタスクを実行するために情報を必要とする場合、それは簡単に取得できます。
さらに、OpenGLのようなレンダリングシステムを直接扱う必要はありません。 VTKは、すべての低レベルのタスクをプラットフォームおよび(部分的に)レンダリングシステムに依存しない方法でカプセル化します。開発者ははるかに高いレベルで作業します。
IEEE Visualization Contest 2011の回転インペラポンプ内の流体の流れのデータセットを使用したデータ視覚化の例を見てみましょう。データ自体は、Charles Cookの記事で説明されているものと同様に、計算流体力学シミュレーションの結果です。
注目のポンプのzip形式のシミュレーションデータのサイズは30GBを超えています。複数のパーツと複数のタイムステップが含まれているため、サイズが大きくなります。このガイドでは、これらのタイムステップの1つで、圧縮サイズが約150MBのローター部分を試してみます。
AWSの認定を受けるにはどのくらい時間がかかりますか
私がVTKを使用するために選択した言語はC ++ですが、Tcl / Tk、Java、Pythonなどの他のいくつかの言語へのマッピングがあります。ターゲットが単一のデータセットの視覚化のみである場合、コードを記述する必要はまったくなく、代わりに利用できます。 Paraview 、VTKのほとんどの機能のグラフィカルフロントエンド。
Paraviewで1つのタイムステップを開き、ローターパーツを別のファイルに抽出することにより、上記の30GBデータセットからローターデータセットを抽出しました。これは非構造格子ファイルです。つまり、六面体、四面体などの点と3Dセルで構成される3Dボリュームです。各3Dポイントには、関連付けられた値があります。セルにも値が関連付けられている場合がありますが、この場合はそうではありません。このトレーニングでは、ポイントでの圧力と速度に焦点を当て、これらを3Dコンテキストで視覚化しようとします。
VTKをロードした場合、圧縮ファイルサイズは約150 MB、メモリ内サイズは約280MBです。ただし、VTKで処理することにより、データセットはVTKパイプライン内で複数回キャッシュされ、32ビットプログラムの2GBのメモリ制限にすぐに到達します。 VTKを使用するときにメモリを節約する方法はいくつかありますが、簡単にするために、64ビットでサンプルをコンパイルして実行します。
謝辞 :データセットは、ドイツのクラウスタール大学応用力学研究所(Dipl。Wirtsch.-Ing。AndreasLucius)の厚意により提供されています。
VTKをツールとして使用して達成するのは、下の画像に示す視覚化です。 3Dコンテキストとして、データセットのアウトラインは、部分的に透明なワイヤーフレームレンダリングを使用して表示されます。次に、データセットの左側を使用して、表面の単純な色分けを使用して圧力を表示します。 (この例では、より複雑なボリュームレンダリングをスキップします)。速度フィールドを視覚化するために、データセットの右側の部分は 合理化 、速度の大きさによって色分けされています。この視覚化の選択は技術的には理想的ではありませんが、VTKコードをできるだけ単純に保ちたいと思いました。さらに、この例が視覚化の課題の一部である理由があります。つまり、流れに多くの乱流があります。
VTKコードについて段階的に説明し、レンダリング出力が各段階でどのように見えるかを示します。完全なソースコードは、トレーニングの最後にダウンロードできます。
まず、VTKから必要なものをすべて含めて、メイン関数を開きます。
#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) {
次に、結果を表示するためにレンダラーとレンダーウィンドウを設定します。背景色とレンダリングウィンドウサイズを設定します。
// 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);
このコードを使用すると、静的レンダリングウィンドウをすでに表示できます。代わりに、vtkRenderWindowInteractor
を追加することを選択しますシーンをインタラクティブに回転、ズーム、パンするために。
// Setup the render window interactor vtkNew interact; vtkNew style; interact->SetRenderWindow(renWin.Get()); interact->SetInteractorStyle(style.Get());
これで、灰色の空のレンダリングウィンドウを示す実行例ができました。
次に、VTKに付属している多くのリーダーの1つを使用してデータセットをロードします。
// Read the file vtkSmartPointer pumpReader = vtkSmartPointer::New(); pumpReader->SetFileName('rotor.vtu');
VTKメモリ管理への短い遠足 :VTKは、参照カウントを中心とした便利な自動メモリ管理の概念を使用しています。ただし、他のほとんどの実装とは異なり、参照カウントは、スマートポインタークラスではなく、VTKオブジェクト自体に保持されます。これには、VTKオブジェクトがrawポインターとして渡された場合でも、参照カウントを増やすことができるという利点があります。マネージドVTKオブジェクトを作成する主な方法は2つあります。 vtkNew
およびvtkSmartPointer::New()
、主な違いはvtkSmartPointer
です。生のポインタT*
に暗黙的にキャスト可能であり、関数から返すことができます。 vtkNew
のインスタンスの場合.Get()
に電話する必要があります生のポインタを取得し、それをvtkSmartPointer
にラップすることによってのみ返すことができます。この例では、関数から戻ることはなく、すべてのオブジェクトが常に存在するため、デモンストレーションの目的で上記の例外を除いて、短いvtkNew
を使用します。
この時点では、ファイルから何も読み取られていません。私たちまたはチェーンのさらに下のフィルターはUpdate()
を呼び出す必要がありますファイルの読み取りが実際に行われるようにします。通常、VTKクラスに更新を自分で処理させるのが最善のアプローチです。ただし、たとえばこのデータセットの圧力範囲を取得するために、フィルターの結果に直接アクセスしたい場合があります。次に、Update()
を呼び出す必要があります手動で。 (結果はキャッシュされるため、Update()
を複数回呼び出してもパフォーマンスが低下することはありません。)
// Get the pressure range pumpReader->Update(); double pressureRange[2]; pumpReader->GetOutput()->GetPointData()->GetArray('Pressure')->GetRange(pressureRange);
次に、vtkClipDataSet
を使用して、データセットの左半分を抽出する必要があります。これを実現するために、最初にvtkPlane
を定義します。分割を定義します。次に、VTKパイプラインがどのように接続されているかを初めて確認します:successor->SetInputConnection(predecessor->GetOutputPort())
。 clipperLeft
からの更新を要求するときはいつでもこの接続により、先行するすべてのフィルターも最新の状態になります。
// 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());
最後に、最初のアクターとマッパーを作成して、左半分のワイヤーフレームレンダリングを表示します。マッパーは、フィルター同士とまったく同じ方法でフィルターに接続されていることに注意してください。ほとんどの場合、レンダラー自体がすべてのアクター、マッパー、および基になるフィルターチェーンの更新をトリガーしています。
powerpivot Excel2016とは
自明ではない唯一の行はおそらくleftWireMapper->ScalarVisibilityOff();
です。 -現在アクティブなアレイとして設定されている圧力値によるワイヤフレームの色付けを禁止します。
次のうち、欠落している要素の間違いと見なされるのはどれですか?
// 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());
この時点で、レンダリングウィンドウには最終的に何か、つまり左側のワイヤーフレームが表示されます。
(新しく作成された)vtkClipDataSet
の平面法線を切り替えることにより、同様の方法で右側のパーツのワイヤーフレームレンダリングが作成されます。反対方向に移動し、(新しく作成された)マッパーとアクターの色と不透明度をわずかに変更します。ここで、VTKパイプラインが同じ入力データセットから2つの方向(右と左)に分割されていることに注意してください。
// 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());
予想どおり、出力ウィンドウに両方のワイヤーフレームパーツが表示されます。
これで、いくつかの有用なデータを視覚化する準備が整いました。左側の部分に圧力の視覚化を追加するために、多くのことを行う必要はありません。新しいマッパーを作成し、それをclipperLeft
に接続します同様に、しかし今回は圧力配列によって着色します。ついにpressureRange
を利用するのもここです上記で導き出しました。
// 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());
出力は次の画像のようになります。中央の圧力は非常に低く、ポンプに材料を吸い込みます。その後、この材料は外部に運ばれ、急速に圧力が高まります。 (もちろん、実際の値を含むカラーマップの凡例があるはずですが、例を短くするために省略しました。)
ここで、より難しい部分が始まります。右側に速度の流線を描きたい。流線は、ソースポイントからのベクトル場内の統合によって生成されます。ベクトル場は、「速度」ベクトル配列の形式ですでにデータセットの一部になっています。したがって、ソースポイントを生成するだけで済みます。 vtkPointSource
ランダムな点の球を生成します。それらのほとんどはデータセット内に存在せず、ストリームトレーサーによって無視されるため、1500のソースポイントを生成します。
// 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);
次に、streamtracerを作成し、その入力接続を設定します。 '待つ、 複数 接続?」と言うかもしれません。はい-これは、私たちが遭遇する複数の入力を持つ最初のVTKフィルターです。ベクトル場には通常の入力接続が使用され、シードポイントにはソース接続が使用されます。 「Velocities」はclipperRight
の「アクティブな」ベクトル配列であるため、ここで明示的に指定する必要はありません。最後に、統合をシードポイントから両方向に実行するように指定し、統合方法を次のように設定します。 ルンゲクッタ-4.5 。
vtkNew tracer; tracer->SetInputConnection(clipperRight->GetOutputPort()); tracer->SetSourceConnection(pointSource->GetOutputPort()); tracer->SetIntegrationDirectionToBoth(); tracer->SetIntegratorTypeToRungeKutta45();
次の問題は、速度の大きさによって流線を着色することです。ベクトルの大きさの配列がないため、単純に大きさを計算して新しいスカラー配列にします。ご想像のとおり、このタスクにはVTKフィルターもあります:vtkArrayCalculator
。データセットを取得して変更せずに出力しますが、既存の1つ以上の配列から計算された配列を1つだけ追加します。この配列計算機は、「Velocity」ベクトルの大きさを取得して「MagVelocity」として出力するように構成します。最後に、Update()
と呼びます。新しい配列の範囲を導出するために、再度手動で。
// 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
ポリラインとvtkArrayCalculator
を直接出力しますそれらを変更せずに渡します。したがって、magCalc
の出力を表示するだけで済みます。新しいマッパーとアクターを直接使用します。
代わりに、このトレーニングでは、代わりにリボンを表示することで、出力を少し良くすることを選択します。 vtkRibbonFilter
入力のすべてのポリラインのリボンを表示する2Dセルを生成します。
// 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());
現在も欠落しており、中間レンダリングを生成するためにも実際に必要なのは、実際にシーンをレンダリングしてインタラクターを初期化するための最後の5行です。
// Render and show interactive window renWin->Render(); interact->Initialize(); interact->Start(); return 0; }
最後に、完成した視覚化に到達します。これをもう一度ここに示します。
上記の視覚化の完全なソースコードは次のとおりです。 ここに 。
この記事は、VTKフレームワークの個人的な長所と短所のリストで締めくくります。
ために : 活発な開発 :VTKは、主に研究コミュニティ内から、いくつかの貢献者から活発に開発されています。これは、いくつかの最先端のアルゴリズムが利用可能であり、多くの3D形式をインポートおよびエクスポートでき、バグが積極的に修正され、問題は通常、ディスカッション掲示板に既成の解決策があることを意味します。
と : 信頼性 :ただし、さまざまな貢献者からの多くのアルゴリズムをVTKのオープンパイプライン設計と組み合わせると、異常なフィルターの組み合わせで問題が発生する可能性があります。複雑なフィルターチェーンが目的の結果を生成しない理由を理解するために、VTKソースコードを数回調べなければなりませんでした。デバッグが可能な方法でVTKを設定することを強くお勧めします。
ために : ソフトウェアアーキテクチャ :VTKのパイプライン設計と一般的なアーキテクチャはよく考えられているようで、一緒に作業するのは楽しいことです。数行のコード行で驚くべき結果が得られます。組み込みのデータ構造は、理解と使用が簡単です。
と : マイクロアーキテクチャ :いくつかのマイクロアーキテクチャ設計の決定は私の理解を逃れます。 const-correctnessはほとんど存在せず、配列は明確な区別なしに入力と出力として渡されます。パフォーマンスを放棄し、vtkMath
に独自のラッパーを使用することで、独自のアルゴリズムでこれを軽減しました。 typedef std::array Pnt3d;
のようなカスタム3Dタイプを利用します。
ために : マイクロドキュメンテーション :すべてのクラスとフィルターのDoxygenドキュメントは広範で使用可能です。また、wikiの例とテストケースは、フィルターの使用方法を理解するのに非常に役立ちます。
と : マクロドキュメント :Web上にVTKの優れたチュートリアルと紹介がいくつかあります。しかし、私が知る限り、特定のことがどのように行われるかを説明する大きなリファレンスドキュメントはありません。あなたが何か新しいことをしたいのなら、しばらくの間それをする方法を探すことを期待してください。さらに、タスクの特定のフィルターを見つけるのは困難です。ただし、それを見つけたら、通常はDoxygenのドキュメントで十分です。 VTKフレームワークを探索する良い方法は、Paraviewをダウンロードして試すことです。
ために : 暗黙的な並列化のサポート :ソースを個別に処理できる複数の部分に分割できる場合、並列化は、単一の部分を処理する各スレッド内に個別のフィルターチェーンを作成するのと同じくらい簡単です。ほとんどの大きな視覚化の問題は通常、このカテゴリに分類されます。
と : 明示的な並列化のサポートなし :大きくて分割可能な問題に恵まれていないが、複数のコアを利用したい場合は、自分で行ってください。どのクラスがスレッドセーフであるか、または試行錯誤によって、またはソースを読み取ることによって再入可能であるかを把握する必要があります。私はかつて、Cライブラリを呼び出すために静的グローバル変数を使用するVTKフィルターへの並列化の問題を追跡しました。
ために : ビルドシステムCMake :マルチプラットフォームメタビルドシステム CMake Kitware(VTKのメーカー)によっても開発され、Kitware以外の多くのプロジェクトで使用されています。 VTKと非常にうまく統合され、複数のプラットフォーム用のビルドシステムのセットアップがはるかに簡単になります。
次のうち、IoTに関する懸念事項ではないものはどれですか?
ために : プラットフォームの独立性、ライセンス、および寿命 :VTKは、すぐに使用できるプラットフォームに依存せず、 非常に寛容なBSDスタイルのライセンス 。さらに、それを必要とする重要なプロジェクトには専門的なサポートが利用できます。 Kitwareは多くの研究機関や他の企業に支えられており、しばらくの間存在するでしょう。
全体として、VTKは、私が好きな種類の問題に最適なデータ視覚化ツールです。視覚化、メッシュ処理、画像処理、または同様のタスクを必要とするプロジェクトに遭遇した場合は、入力例を使用してParaviewを起動し、VTKがツールになり得るかどうかを評価してみてください。