mcpField
M
matlab-display-volume

matlab-display-volume

github.com/matlabデータ・データベース1.2k73 回インストール1 日前に更新
リポジトリ

matlab-display-volume について

ボリュームデータ、サーフェスメッシュ、オーバーレイを対話的に表示する MATLAB スキルです。`volshow`、3D ビューアー、グラフィックスオブジェクトを使ったアプリや可視化の手順を扱います。

主な機能

  1. 01`volshow` によるボリュームデータ描画
  2. 023D サーフェスメッシュ表示
  3. 03オーバーレイと注釈の追加
  4. 04ビューアーの対話型コールバック設定
  5. 05ストリーミング表示用のデータ更新

活用シーン

  1. 01ボリューム表示アプリの作成
  2. 02医用画像ボリュームの確認
  3. 03セグメンテーションラベルの重ね合わせ
  4. 04サーフェスメッシュの可視化とアニメーション

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-display-volume

インストール後、Claude Code で/matlab-display-volumeを入力して読み込まれたか確認します。

同梱ファイルmanifest.yaml

詳細

ボリューム表示

高性能で高品質なボリューム表示には、volshow を使用します。等値面メッシュ、三角形分割、サーフェスを表示する場合は、isosurface ではなく images.ui.graphics.Surface を使用してください。あらゆるサイズのメッシュで、より高性能かつ高品質なメッシュ表示と、応答性の高いインタラクションを実現できます。

使用する場面

  • ボリューム、等値面、サーフェスメッシュを表示する GUI、アプリ、ダッシュボード、インタラクティブツールの作成をユーザーが求めている場合
  • ボリュームまたはサーフェスメッシュ上に、ROI、注釈、その他の線や図形をプロットしたい場合
  • ボリューム上にラベル付きデータやその他のオーバーレイボリュームを表示したい場合

使用しない場面

  • ユーザーが Image Processing Toolbox を持っていない場合 — isosurface + patch に切り替えます。ただし、パフォーマンス向上のため volshow を推奨してください。

医用画像ボリューム

ユーザーが Medical Imaging Toolbox を持っている場合は、medicalVolume を使って DICOM/NIfTI/NRRD ファイルを読み込み、そのオブジェクトの volshow メソッドを呼び出します。これにより、ファイルのメタデータから空間 Transformation と SpatialUnits が自動的に設定されます。このスキルのその他すべてのパターン(ビューアー、オーバーレイ、注釈、ストリーミング、アプリの構築)は引き続き適用され、異なるのは開始方法だけです。

medVol = medicalVolume("brain.nii");
obj = volshow(medVol);

主要なオブジェクト

オブジェクトコンストラクター主要なコールバック
Viewerviewer3d(parent)CameraMovedFcn、ObjectClickedFcn
Volumevolshow(data, Parent=viewer)
Surfaceimages.ui.graphics.Surface(viewer, Data=tri)
インタラクティブ注釈uidraw(parent, "shape")AnnotationMovedFcn(Viewer 上)

ユーティリティ: linkviewers(viewers) は複数の Viewer 間でカメラの動きを同期します。title(viewer, "text") は Viewer のタイトルを設定します。

ワークフロー

  1. Viewer を作成 — アプリのコンテキストでは viewer3d() または viewer3d(parent) を呼び出します
  2. ボリュームを追加 — volshow(V, Parent=viewer) を呼び出してボリュームデータを表示します
  3. (任意) サーフェスを追加 — メッシュ表示には images.ui.graphics.Surface(viewer, Data=tri) を呼び出します
  4. (任意) 注釈を追加 — インタラクティブな ROI には uidraw(volObj, "line") を呼び出します
  5. (任意) コールバックを設定 — Viewer の CameraMovedFcn、AnnotationMovedFcn、または ObjectClickedFcn を設定します
  6. データを更新 — ストリーミングには obj.Data = newV を設定します。同期待ちには waitfor(viewer,"Busy",false) を使用します

避けるべき旧式のパターン

使用しないもの代わりに使用するもの理由
isosurface + patchimages.ui.graphics.Surface(viewer, Data=tri)レンダリング、インタラクティブなパフォーマンス、デプスピーリングが向上します
clear(viewer) の後にオブジェクトを再追加オブジェクトを再利用し、Data プロパティを更新再構築のオーバーヘッドを回避できます
ボリュームストリーミングの同期に drawnow を使用waitfor(viewer,"Busy",false)ボリュームデータは非同期で読み込まれます。Surface アニメーションには引き続き drawnow が適切です
フレームごとに新しい volshow を作成参照を保持し、obj.Data = V を設定再利用により GPU の再割り当てを回避できます

パターン

標準的なボリューム表示

単純なボリューム表示では、親を指定せずに volshow を呼び出せます。すべての名前と値の引数は Volume オブジェクトのプロパティとして設定でき、Data プロパティを設定することでボリュームデータを更新できます。

obj = volshow(V);

データ型と値の範囲に基づいて DisplayRangeMode を選択します。

データの特性推奨モード
[0, 1] の範囲に正規化された single/double"data-range"(デフォルト)
uint8 または int8"data-range"(デフォルト)
最大値が 1023 の uint16(例:10 ビット CT)"10-bit"
最大値が 4095 の uint16(例:12 ビット CT/MR)"12-bit"
全範囲の uint16 または int16"16-bit"
カスタムのウィンドウ/レベルが必要DisplayRange=[low high] を指定した "manual"
% 12-bit medical volume
obj = volshow(V, DisplayRangeMode="12-bit");

% Manual window/level
obj = volshow(V, DisplayRangeMode="manual", DisplayRange=[200 1200]);

ボクセルデータの可視化方法を制御するには、RenderingStyle を設定します。詳細なガイダンスについては、以下の専用セクション レンダリングスタイル を参照してください。

カラーマップの選択 — データの内容に基づいて選択します。

データの内容推奨カラーマップ理由
CT/MR(解剖画像)gray(256)臨床での慣例であり、放射線科医になじみのある表示を維持できます
ウィンドウ/レベルを設定したCTgray(256) + DisplayRange範囲を狭めることで、対象組織を強調できます
蛍光/発光hot(256) または green(256)hot は強度のピークを強調し、green は蛍光色素に合います
一般的な科学データparula(256)(デフォルト)知覚的に均一で、見やすい配色です
複数構造/ラベル付きデータturbo(256)全スペクトルの虹色で、知覚的な順序を保ちます
符号付きデータ(例:流量、ひずみ)カスタムの発散型(青-白-赤)中点を基準に正と負を区別できます
obj = volshow(V, Colormap=gray(256));

アルファマップの選択 — アルファマップは、正規化された強度 [0, 1] を不透明度 [0, 1] に対応付ける 256×1 のベクトルです。どの構造を表示し、どの構造を透明にするかを制御します。デフォルトは三次ランプ(x.^3)で、低強度の背景を抑えながら、明るい構造を表示します。

目的アルファマップの形状生成方法
明るい構造を表示し、背景を隠す三次ランプ(デフォルト)linspace(0,1,256)'.^3
すべての強度を均等に表示する線形ランプlinspace(0,1,256)'
特定の強度帯のみを表示するガウスピークexp(-((x-center).^2)/(2*width^2))
ハードしきい値(表示/非表示の二値化)ステップ関数double((1:256)' > threshold)
明るい領域を隠し、暗い領域を表示する(例:空洞)反転ランプlinspace(1,0,256)'
カスタム:骨を表示し、軟部組織を隠す(CT)骨のHU値でのステップdouble(linspace(0,1,256)' > 0.3)
% Gaussian alphamap centered at 60% intensity (width 10%)
x = linspace(0, 1, 256)';
alpha = exp(-((x - 0.6).^2) / (2*0.1^2));
obj = volshow(V, Alphamap=alpha, Colormap=gray(256));

% Hard threshold — only show voxels above 30% of the display range
alpha = double(linspace(0, 1, 256)' > 0.3);
obj = volshow(V, Alphamap=alpha);

強度に関係なく特定の領域をマスクするなど、ボクセルごとに不透明度を制御するには、AlphaData プロパティに、Data と同じサイズで値が [0, 1] のボリュームを設定します。

obj = volshow(V, AlphaData=double(regionMask));

マスク、セマンティックセグメンテーション、その他のボリュームデータを別のボリュームに重ねて表示する場合は、Volume オブジェクトの OverlayData プロパティと、対応する名前と値の引数 OverlayColormap、OverlayAlpha、OverlayRenderingStyle、OverlayDisplayRange、OverlayDisplayRangeMode を使用して、オーバーレイの表示を調整します。これは、オーバーレイをボリュームとブレンドして Data プロパティを更新したり、Viewer に 2 つ目の Volume オブジェクトを追加したりするよりも高速です。オーバーレイ表示のデフォルトプロパティはセマンティックラベルに対応するよう調整されていますが、他の用途で連続データを扱うよう変更できます。

obj = volshow(V, OverlayData=mask);

空間参照情報がある場合は、Transformation の名前と値の引数に含めます。ボクセル間隔とボリュームサイズから imref3d を作成するか、任意のアフィン変換には affinetform3d を使用します。medicalVolume を使用する場合、volshow メソッドがファイルのメタデータから変換を自動的に設定します。

% From voxel spacing (e.g., [0.5 0.5 1.0] mm)
R = imref3d(size(V), 0.5, 0.5, 1.0);
obj = volshow(V, Transformation=R);

% From an affine matrix
tform = affinetform3d(A);
obj = volshow(V, Transformation=tform);

GPUテクスチャメモリを超える大容量ボリューム(通常は 2048³ ボクセル超)や、RAMに収まらないボリュームには、マルチレベルの blockedImage を作成して volshow に渡します。makeMultiLevel3D 関数はプログレッシブレンダリング用の解像度ピラミッドを作成します。Viewer はまず粗いレベルを読み込み、カメラが静止すると段階的に高精細化します。

bim = blockedImage(V, BlockSize=[512,512,512]);
mbim = makeMultiLevel3D(bim, Scales=[2 4 8]);
volshow(mbim);

ダウンサンプリング係数は Scales で制御します。各スケールによって、1/N の解像度のレベルが作成されます。非常に大きなボリューム(例:4096³ 以上)には、より多くのスケール(例:[2 4 8 16])を含めます。テクスチャ制限をわずかに超える程度の小さなボリュームでは、必要なレベルは少なくなります(例:[2 4])。BlockSize はGPUテクスチャブロックのサイズと同じか、その倍数にします(デフォルトには 512³ が適しています)。

サーフェスメッシュの表示

サーフェスメッシュの表示には、isosurface + patch よりも、images.ui.graphics.Surface を使用するほうが、レンダリングとインタラクティブ操作のパフォーマンスに優れています。triangulation オブジェクトを Data として、頂点ごとの RGB 色を表す N×3 行列を Color として渡します。

% tri = triangulation(...), cpoints = N-by-3 vertex colors
viewer = viewer3d(BackgroundGradient="off", BackgroundColor="white");
obj = images.ui.graphics.Surface(viewer, Data=tri, Color=cpoints);

可能な場合は、パフォーマンスを最適化するため、デフォルトの Alpha である 1.0 を使用してメッシュを表示することをおすすめします。images.ui.graphics.Surface を複数回呼び出すことでシーンに複数のオブジェクトを追加でき、オブジェクトは空間的に正しい順序でソートされ、深度ピーリングによってレンダリングされます。オブジェクトが透明な場合、必要なレンダーパスが増えるため、パフォーマンスが低下します。

サーフェスメッシュは、同じ Viewer 内でボリュームと一緒に表示できます。

viewer = viewer3d();
volObj = volshow(V, Parent=viewer);
surfObj = images.ui.graphics.Surface(viewer, Data=tri);

Surface の位置やサイズを更新する場合、新しい三角形分割で Data を再設定するより、Transformation プロパティを更新するほうがはるかに高速です。フレーム間のグラフィックスキューをフラッシュするには drawnow を使用します(これは Surface のアニメーションに適しています。waitfor が必要なのはボリュームのストリーミングのみです)。

viewer = viewer3d(BackgroundGradient="off", BackgroundColor="white");
obj = images.ui.graphics.Surface(viewer, Data=tri, Color=cpoints);

% Disable auto camera position update
viewer.CameraPositionMode = "manual";

% Animate the surface moving in the z direction
for idx = 1:100
    tform = transltform3d(0, 0, idx);
    obj.Transformation = tform;
    drawnow;
end
レンダリングスタイル

Volume オブジェクトの RenderingStyle プロパティは、ボクセルデータの可視化方法を制御します。表示する構造に応じてスタイルを選択してください。

最大強度投影(MIP) — 各視線に沿った最も明るいボクセルを画面に投影します。血管イメージング(血管造影)、蛍光顕微鏡、または明るい構造が注目対象となるデータに最適です。

obj = volshow(V, RenderingStyle="MaximumIntensityProjection");

グラデーション不透明度 — 大きく均質な領域(勾配が低い部分)を透明にしつつ、強度が急激に変化する領域(勾配が高い部分)を保持します。これにより、ボリューム内の境界やエッジを見えるようにしながら、組織の大部分を透かして見ることができます。

obj = volshow(V, RenderingStyle="GradientOpacity");

シネマティックレンダリング — グローバルイルミネーション、アンビエントオクルージョン、ソフトシャドウを用いた、最先端のフォトリアリスティックなレンダリングです。CT スキャンの骨のような固体構造を最高品質で可視化できます。計算負荷は高くなりますが、出版物に適した品質の結果が得られます。

obj = volshow(V, RenderingStyle="CinematicRendering");

その他のスタイル:

RenderingStyle使用する場面
"VolumeRendering"デフォルト — 強度に基づく伝達関数を使用した半透明のボリューム
"MinimumIntensityProjection"最も暗い構造(例:気道、空洞)の表示
"Isosurface"しきい値でのサーフェス表示(IsosurfaceValue を設定)
"SlicePlanes"ボリュームを通る直交断面の表示
"LightScattering"軟組織の外観を再現する、光散乱のシミュレーション
% Isosurface at a specific threshold (e.g., bone in CT)
obj = volshow(V, RenderingStyle="Isosurface", IsosurfaceValue=300);

% Orthogonal slice planes
obj = volshow(V, RenderingStyle="SlicePlanes");
ボリュームのストリーミング

表示を更新する際は、可能な限りオブジェクトを再利用してください。同じ種類のオブジェクトを再度追加する場合は、Viewer の clear メソッドを使用せず、必ずオブジェクトを再利用してください。volshow が返す Volume オブジェクトを保持し、その Data プロパティを更新します。ボリュームデータは非同期で読み込まれるため、レンダリングが完了する前に drawnow が戻る場合があります。データの読み込みが完了するまで待機するには、Viewer の Busy プロパティに対して waitfor を使用します。

viewer = viewer3d();
obj = volshow([], Parent=viewer);

for idx = 1:10
    V = rand([512,512,512]);
    obj.Data = V;
    waitfor(viewer, "Busy", false);
end
アニメーションの生成

アニメーションを生成したりフレームをキャプチャしたりする場合は、getframe を使用し、Volume オブジェクトの親である Viewer を第 1 引数として渡します。Viewer は保留中の更新がすべて処理されるまで待機してからキャプチャするため、各フレームが完全にレンダリングされます。

viewer = viewer3d();
obj = volshow([], Parent=viewer);

frames = cell(1, 100);
for idx = 1:100
    V = rand([512,512,512]);
    obj.Data = V;
    frames{idx} = getframe(viewer);
end
ボリュームへの注釈の追加

ボリュームにインタラクティブな注釈や少数の注釈を表示する場合は、uidraw 関数を使用して、インタラクティブな描画を開始するか、プログラムから注釈を配置します。

uidraw は、注釈が 100 個未満の場合に最適です。Position を省略すると、インタラクティブな描画が開始されます。Label を指定しない場合、ROI には標準的な測定値(例:線分の距離)が表示されます。測定値の表示に単位を含めるには、Viewer の SpatialUnits を設定します。

obj = volshow(V);
viewer = obj.Parent;
viewer.SpatialUnits = "mm";

% Interactive drawing (user places the line)
roi = uidraw(obj, "line", Color=[1,0,0]);

% Programmatic placement (no interaction required)
roi2 = uidraw(obj, "point", Position=[20,20,50], Color=[0,0,1], Label="Point of Interest");

% Multi-point polyline from a position matrix (each row is a point)
pos = [20,20,50; 50,60,75; 50,80,100];
roi3 = uidraw(obj, "polyline", Position=pos, Color=[0,1,0]);

% Make an annotation static (disable further interaction)
set(roi3, "Interactions", "none");
ユーザー操作への応答

Viewer のコールバックプロパティに関数ハンドルを追加して、ユーザー操作に応答します。CameraMovedFcn はカメラが移動した後に実行されます。AnnotationMovedFcn は、ユーザーがアノテーションをインタラクティブに移動または変形した後に実行されます。ObjectClickedFcn は、ユーザーが Viewer 内でドラッグせずにクリックしてボタンを離した後に実行されます(クリックしてドラッグすると、通常はパンなどのデフォルトの操作が開始されます)。イベントデータを使用して、どのオブジェクトがクリックされたかを判定します。

V = rand([100,100,100]);
obj = volshow(V);
viewer = obj.Parent;
% Draw a rectangle ROI interactively
roi = uidraw(obj, "point", Color=[0,1,0], Label="ROI");
% Listen for movement and display the position
viewer.AnnotationMovedFcn = @(~,evt) fprintf("ROI Position: [%.1f, %.1f, %.1f]\n", evt.Position);
アプリの構築

アプリを構築する際は、viewer3d(parent) を呼び出して uigridlayout を親とする Viewer を作成し、次にその Viewer を親として volshow を呼び出します。Volume オブジェクトは、データが空の状態([])で、構築時にすぐ作成します。これにより、後で条件分岐を使わずに済み、データが利用可能になったら obj.Data を更新するだけでよくなります。Volume の作成を遅らせるのは、アプリが根本的に異なる表示モード(例:ボリューム表示とサーフェスのみの表示)を切り替える機能をサポートする場合に限ります。

標準的なアプリの classdef(matlab-build-app スキルを参照)を使用し、createComponents に次の内容を記述します。

% Inside createComponents(app) — viewer3d parented to grid layout
app.GridLayout = uigridlayout(app.UIFigure, [1 1]);
app.GridLayout.RowHeight = {"fit"};
app.GridLayout.ColumnWidth = {"fit"};

app.Viewer = viewer3d(app.GridLayout);
title(app.Viewer, "App Display");
app.Viewer.Layout.Row = 1;
app.Viewer.Layout.Column = 1;

% Create Volume eagerly with empty data
app.Volume = volshow([], Parent=app.Viewer);

その後、app.Volume.Data = im; でデータを更新します。

ボリュームアプリへのカラーバーの追加

Viewer は標準ではカラーバーをサポートしていないため、隣接するグリッド列にカラーバー付きの非表示 uiaxes を配置します。Volume オブジェクトに合わせて軸の Colormap と CLim を設定し、その軸に対して colorbar を呼び出します。

fig = uifigure(Name="Volume with Colorbar", Position=[100 100 800 500]);
gl = uigridlayout(fig, [1 2]);
gl.ColumnWidth = {"1x", 80};

% Volume viewer in left column
viewer = viewer3d(gl);
viewer.Layout.Row = 1;
viewer.Layout.Column = 1;
title(viewer, "Colorbar with Volshow");

V = rand([64 64 64]);
vol = volshow(V, Parent=viewer, Colormap=parula(256));

% Invisible axes with colorbar in right column
ax = uiaxes(gl, Visible="off", HitTest="off", PickableParts="none");
ax.Layout.Row = 1;
ax.Layout.Column = 2;
ax.Toolbar.Visible = "off";

% Match colormap and data range to the Volume object
ax.Colormap = vol.Colormap;
ax.CLim = [min(vol.Data, [], "all"), max(vol.Data, [], "all")];
cb = colorbar(ax, Units="normalized");
複数の Viewer のリンク

linkviewers を使用して、複数の Viewer 間でカメラの動きを同期します。ユーザーがいずれかの Viewer を回転またはズームすると、リンクされたすべての Viewer が同じ状態に更新されます。リンクを解除するには "off" を渡します。

viewer1 = viewer3d();
viewer2 = viewer3d();
title(viewer1, "Original");
title(viewer2, "Segmentation");

volshow(V, Parent=viewer1);
volshow(V, Parent=viewer2, OverlayData=mask);

linkviewers([viewer1, viewer2]);

規約

  • 必ず:images.ui.graphics.Surface(viewer, ...) の最初の位置引数として Viewer を渡します。名前と値の引数として Parent=viewer を使用しないでください
  • 必ず:obj.Data を更新して Volume オブジェクトを再利用します。フレームごとにボリュームを再作成しないでください
  • 必ず:タイミングが重要な場合(スクリーンショット、フレームのキャプチャ)は、Data の設定後に waitfor(viewer, "Busy", false) を使用して同期します
  • 必ず:スクリーンショットには getframe(gcf) ではなく getframe(viewer) を使用します。Viewer は非同期処理の完了を処理します
  • 必ず:ユーザーが Medical Imaging Toolbox を使用している場合は、medicalVolume オブジェクトに対して volshow(medVol) を使用します。これにより Transformation と SpatialUnits が自動的に設定されます
  • 禁止:シーンをリセットするために clear(viewer) を呼び出さないでください。既存のオブジェクトを再利用してください
  • 禁止:Image Processing Toolbox が利用可能な場合、メッシュ表示に isosurface + patch を使用しないでください
  • 推奨:マスクやセグメンテーションには、Data にブレンドしたり 2 つ目のボリュームを追加したりするよりも、OverlayData プロパティを使用してください
  • 推奨:位置やサイズのみが変わる場合は、Data を再設定するよりも Transformation を更新してください

Copyright 2026 The MathWorks, Inc.


コメント