volshow関数を用いたVolume Rendering
Contents
dicomreadVolume関数でCTのdicom fileの読み込み
R2017bからimage processing toolboxにdicomreadVolumeという関数が用意されました。 今までdicomreadとdicominfoを組み合わせてDICOMファイルを1枚1枚読んで位置関係をチェックしながら 三次元配列を作成していたのですが、これで便利になりました。
CTfolder='C:\Users\hashi\OneDrive\ドキュメント\MATLAB\CT';% CT DICOMのあるフォルダ CTfolder='E:\MATLAB\CT'; [V,spatial,dim]=dicomreadVolume(CTfolder); size(V) V=squeeze(V);% Vは4次元配列で1の次元を削除
ans = 512 512 1 125
volshow関数でvolume rendering表示
R2018bからimage processing toolboxにvolume renderingの関数volshowが用意されました。使ってみます。 どうもaxes上に表示されるのではなくuipanel上に表示されるのでdaspectやview関数は使えません。
figure; map=gray(256);% colormapは256固定! scale=[spatial.PixelSpacings(1,:),spatial.PatientPositions(2,3)-spatial.PatientPositions(1,3)]; volshow(V,'Colormap',map,'BackGroundColor',[1,1,1]); daspect(1./scale); % 反映しません! view([0,90]);% 反映しません! figure; hvol=volshow(V,'Colormap',map,'BackGroundColor',[1,1,1]); hvol %これを操作すればよい daspect(1./scale); % 反映しません! set(hvol,'ScaleFactors',scale,'CameraViewAngle',5);
hvol =
volshow のプロパティ:
Parent: [1×1 Panel]
Alphamap: [256×1 double]
Colormap: [256×3 double]
Lighting: 1
IsosurfaceColor: [1 0 0]
Isovalue: 0.5000
Renderer: 'VolumeRendering'
CameraPosition: [4 4 2.5000]
CameraUpVector: [0 0 1]
CameraTarget: [0 0 0]
CameraViewAngle: 15
BackgroundColor: [1 1 1]
ScaleFactors: [1 1 1]
InteractionsEnabled: 1
volshow関数でintensityを変化させてみる
さらにvolshowはgraphic objectではないのでfindobj関数が使えず勝手が悪いです。 parent objectがfigureかuipanelしかありません。 1つのfigureに複数のvolshowを表示させるにはuipanelを並べるしかないようです。 intensityは・・・・よくわかりません。volumeViewer関数を使えばもっとお気楽に楽しめます。 alphamapはどうも三次元配列の最小値~最大値を256等分し、その透過度を設定しているようです。 なお、lightingがあると判断しにくいので初期設定のtrueからfalseに変更してます。
close all; figure;histogram(V(:));% ヒストグラムをチェックして再表示 [min(V(:)),max(V(:))] figure;imagesc(squeeze(V(:,:,30)));colormap(jet(256));colorbar;% 軟部組織は1000前後、骨は2500くらい W=V; W(W<0)=0; vmax=2047; W(W>vmax)=vmax; queryPoints=linspace(0,vmax,256);% 256固定 hf=figure; alpha=[0,0,1,1]; for n=1:4 switch n case 1;intensity=[0,100,vmax-100,vmax]; pos=[0, 0.5,0.5,0.5];% 全体像 case 2;intensity=[0,600,1000,vmax]; pos=[0.5,0.5,0.5,0.5];% 頭皮 case 3;intensity=[0,1000,1500,vmax]; pos=[0, 0, 0.5,0.5];% 骨+軟部組織 case 4;intensity=[0,1200,2000,vmax]; pos=[0.5,0, 0.5,0.5];% 骨 end alphamap=interp1(intensity,alpha,queryPoints)';% 列ベクトル label=sprintf('intensity=[%d %d %d %d]',intensity); hu=uipanel(hf,'position',pos,'title',label);% subplotは使えません! hvol(n)=volshow(W,'parent',hu,'Colormap',map,'alphamap',alphamap,'backgroundcolor',[1,1,1]); end set(hvol,'ScaleFactors',scale,'CameraPosition',[1,-0.8,0.2],'CameraViewAngle',20,'Lighting',false); get(hvol(4),'parent') % uipanelがPanelとして表示される get(hu,'children') % uipanelからはhvolはchildrenとして認識されない!
ans =
1×2 の int16 行ベクトル
-2000 3071
ans =
Panel (intensity=[0 1200 2000 2047]) のプロパティ:
Title: 'intensity=[0 1200 2000 2047]'
BackgroundColor: [0.9400 0.9400 0.9400]
Position: [0.5000 0 0.5000 0.5000]
Units: 'normalized'
GET を使用してすべてのプロパティを表示
ans =
0×0 の空の GraphicsPlaceholder 配列。
volshow関数でintensityとcolormapを変化させてみる
colormapもalphamapと三次元配列の最小値~最大値を256等分して計算しているようです。 alphamapとcolormapそろえると輝度のメリハリがはっきりします
close all; W=V; W(W<0)=0; vmax=2047; W(W>vmax)=vmax; queryPoints=linspace(0,vmax,256);% 256固定 hf=figure; alpha=[0,0,1,1]; for n=1:4 switch n case 1;intensity=[0,100,vmax-100,vmax]; pos=[0, 0.5,0.5,0.5];% 全体像 case 2;intensity=[0,600,1000,vmax]; pos=[0.5,0.5,0.5,0.5];% 頭皮 case 3;intensity=[0,1000,1500,vmax]; pos=[0, 0, 0.5,0.5];% 骨+軟部組織 case 4;intensity=[0,1200,2000,vmax]; pos=[0.5,0, 0.5,0.5];% 骨 end alphamap=interp1(intensity,alpha,queryPoints)';% 列ベクトル colmap=alphamap*ones(1,3); label=sprintf('intensity=[%d %d %d %d]',intensity); hu=uipanel(hf,'position',pos,'title',label);% subplotは使えません! hvol(n)=volshow(W,'parent',hu,'Colormap',map,'Alphamap',alphamap,'Colormap',colmap); end set(hvol,'ScaleFactors',scale,'CameraPosition',[1,-0.8,0.2],'CameraViewAngle',20,... 'backgroundcolor',[1,1,1],'Lighting',false);
volshow関数で三次元配列の方を変化させてみる
intensityを変化させるのは慣れてないので面倒です。 そこで三次元配列の値の方を変化させることとします。 alphamapとcolormapをそろえたものと比較して細かいゴミが映ってしまいました。
close all; hf=figure; alphamap=linspace(0,1,256)';% 列ベクトル for n=1:4 switch n case 1;ww=[0,2047]; pos=[0, 0.5,0.5,0.5];% 全体像 case 2;ww=[600,1000]; pos=[0.5,0.5,0.5,0.5];% 頭皮 case 3;ww=[1000,1500]; pos=[0, 0, 0.5,0.5];% 骨+軟部組織 case 4;ww=[1200,2047]; pos=[0.5,0, 0.5,0.5];% 骨 end W=V; W(W<ww(1))=ww(1); W(W>ww(2))=ww(2); label=sprintf('window window=[%0.0f %0.0f]',ww); hu=uipanel(hf,'position',pos,'title',label);% subplotは使えません! hvol(n)=volshow(W,'parent',hu,'Colormap',map,'alphamap',alphamap,'backgroundcolor',[1,1,1]); end set(hvol,'ScaleFactors',scale,'CameraPosition',[1,-0.8,0.2],'CameraViewAngle',20,'Lighting',false);
volumeViewer関数を使ってvolume rendering表示
R2017aからimage processing toolboxにvolumeViewer関数が用意されました。 対話形式で使いやすいと思いますが、コマンドラインではいじれないです。 alphamapとかは右のレンダリングエディタで対話形式で調整するしかありません。 なお何故かパブリッシュ機能でHTMLで書き出すと複数のvolumeViewerの絵が出力されてしまいました。 bugでしょうな。
close all; volumeViewer(V,'ScaleFactors',scale);
volshow関数でisosurface画像
volshow関数はvolume rendering以外の画像も作成できます。 volshow関数のrendererをisosurfaceにしてみます。 isovalueは三次元配列の最小値と最大値を0と1にして、0~1の間のどの値か指定する必要があります。 Lighting機能はtrueでもfalseでも変わりありません。 volumeViewer関数とは違って光源は上から下を照らす方向以外の設定はできないようでした。 下からのぞくと真っ黒け。 たぶん<br> なおisosurface画像ではalphamapやらcolormapは計算に関与してません。
volumeViewer close; hf=figure; vmin=double(min(V(:)));%Vはint16 doubleに変換しないとエラー vmax=double(max(V(:))); for n=1:4 switch n case 1 value=100; isosurfacecolor=[1,1,1]; pos=[0, 0.5,0.5,0.5]; case 2 value=500; isosurfacecolor=[1,0.9,0.8]; pos=[0.5,0.5,0.5,0.5]; case 3 value=800; isosurfacecolor=[0.8,0.7,0.5]; pos=[0, 0, 0.5,0.5]; case 4 value=1500; isosurfacecolor=[0.7,0.9,1]; pos=[0.5,0, 0.5,0.5]; end isovalue=(value-vmin)/(vmax-vmin); label=sprintf('isosurface=%0.0f',value); hu=uipanel(hf,'position',pos,'title',label); hvol(n)=volshow(V,'parent',hu,'isosurfacecolor',isosurfacecolor,... 'isovalue',isovalue,'renderer','Isosurface','Lighting',false); end set(hvol,'ScaleFactors',scale,'CameraPosition',[1,-0.8,0.2],'CameraViewAngle',20,... 'backgroundcolor',[1,1,1],'Lighting',false);
isosurface関数でisosurface画像作成
今まではisosurface関数を使ってisosurface画像を作成してました。めちゃめちゃ時間がかかってました。 volumeViewer関数の出力と同じような絵になりました。 個人的にはpatch objectとしていろいろ操作できるのでこちらの方が好きかな。volshow関数が熟練されることを期待してます。
close all; hf=figure; for n=1:4 switch n case 1 value=100; isosurfacecolor=[1,1,1]*0.9;%白だとなんも見えん pos=[0, 0.5,0.5,0.5]; case 2 value=500; isosurfacecolor=[1,0.9,0.8]; pos=[0.5,0.5,0.5,0.5]; case 3 value=800; isosurfacecolor=[0.8,0.7,0.5]; pos=[0, 0, 0.5,0.5]; case 4 value=1500; isosurfacecolor=[0.7,0.9,1]; pos=[0.5,0, 0.5,0.5]; end tic; fv=isosurface(V,value); toc; label=sprintf('isosurface=%0.0f',value); hu=uipanel(hf,'position',pos,'title',label); axes(hu); p=patch(fv); p.EdgeColor='none'; p.FaceColor=isosurfacecolor; daspect(1./scale); view([60,15]);% 適当です! axis tight; camlight; lighting gouraud; rotate3d on; end
経過時間は 57.440662 秒です。 経過時間は 20.222368 秒です。 経過時間は 16.716812 秒です。 経過時間は 44.575032 秒です。
volshow関数で最大強度投影
volshow関数のrendererをMaximumIntensityProjectionとします。最大強度投影法です。 rendererがdefaultのVolumeRenderingの時と同じく三次元配列の最大値と最小値を256分割して 256色で表示します。左上で2つの白い部分は骨が固い蝸牛や前庭の骨の部分です。 ここはCT値が高いですが、右上とか見てると白い部分がない! 最大強度投影になってないです。 頭の端から端まで計算するのではなく途中で止まっている感じ。bugなんだろうか?<br> なお、RendererがVolumeRenderingの時と異なり、colormap=alphamap*[1,1,1]とすると真っ白けになってしまうので、 alphamapとcolormapはそろえてません。
close all; W=V; vmin=-0; W(W<vmin)=vmin; vmax=3000; W(W>vmax)=vmax; queryPoints=linspace(vmin,vmax,256);% 256固定 hf=figure; alpha=[0,0,1,1]; for n=1:4 switch n case 1;intensity=[vmin,100,vmax-100,vmax]; pos=[0, 0.5,0.5,0.5];% 全体像 case 2;intensity=[vmin,600,1000,vmax]; pos=[0.5,0.5,0.5,0.5];% 頭皮 case 3;intensity=[vmin,1000,1500,vmax]; pos=[0, 0, 0.5,0.5];% 骨+軟部組織 case 4;intensity=[vmin,1200,2000,vmax]; pos=[0.5,0, 0.5,0.5];% 骨 end alphamap=interp1(intensity,alpha,queryPoints)';% 列ベクトル label=sprintf('intensity=[%d %d %d %d]',intensity); hu=uipanel(hf,'position',pos,'title',label);% hvol(n)=volshow(W,'parent',hu,'Colormap',map,'alphamap',alphamap,'renderer','MaximumIntensityProjection'); end set(hvol,'ScaleFactors',scale,'CameraPosition',[1,-0.8,0.2],'CameraViewAngle',20,... 'backgroundcolor',[0,0,0],'Lighting',false);% Lightingは関係ないみたい