MRI画像とCT画像の融合
MATLAB8以降、R2012b以降からImage Processing Toolboxに2つの三次元配列データの位置を自動的にそろえる機能が整備されました。 脳外科ではよく使う機能なので試しに使ってみたのですが、初期設定のままでは残念ながら実用的ではありませんでした。 もう少しparameterのtuningが必要なんだだと思います。
Contents
MRIとCTファイルの読み込み
Image Processing ToolboxのDICOM読み込み関数を使ってDICOMのヘッダー部分とデータ部分を読み込みます。 path1とpath2はそれぞれMRIとCTのDICOMファイルがあるfolderです。
for nn=1:2 eval(sprintf('imagepath=path%d;',nn));%1 MRI 2 CT files=ls(imagepath);% ファイル数×最長ファイル名のcharの配列 if ispc % windows用 nfiles=size(files,1); else % mac/linux用 k=strfind(files,char(10)); nfiles=length(k); files=[char(10),files]; k=[1,k+1]; end; data=[]; info=[]; num=1; for n=1:nfiles if ispc loadfile=strrep(files(n,:),' ','');% 空白文字を削除 else loadfile=files((k(n)+1):(k(n+1)-1)); end; loadfile=[imagepath,loadfile]; try info{num}=dicominfo(loadfile); data(:,:,num)=int16(dicomread(loadfile));% double→int16 num=num+1; catch continue; end; end; num=num-1; SliceLocation=zeros(1,num); for n=1:num SliceLocation(n)=info{n}.SliceLocation; end; [x,y]=sort(SliceLocation);% スライス順に並び替える eval(sprintf('info%d=info(y);',nn)); eval(sprintf('data%d=data(:,:,y);',nn)); end;
画像表示
Image Processing Toolboxのmaketform関数、tformarray関数、montage関数を使って画像を表示します。 tformarray関数の出力の1,2,3軸はY軸、X軸、Z軸に対応するので[2,1,3]にしています。 montage関数はint16は使えないのでuint8に変換して表示させます。 方位角45度、天頂角90度、回転角0度の画像をmontage関数で表示してます。 輝度は側面中心点を通る線のDICOM値から脳実質に適当な値を探しました。
for nn=1:2 clear T; eval(sprintf('info=info%d;',nn)); eval(sprintf('data=data%d;',nn)); Dsize=size(data)/2; dicomvalue=squeeze(data(round(Dsize(1)),:,round(Dsize(3)))); scale=[info{1}.PixelSpacing;info{1}.SliceThickness]'; figure; plot(dicomvalue);hold on; set(gca,'xlim',[1,Dsize(2)*2]); xx=[1;Dsize(2)*2]*ones(1,2); if nn==1 clim=[5,345]; else clim=[1000,1100]; end; plot(xx,ones(2,1)*clim,'r'); old_shift=-Dsize; new_shift=round(Dsize.*scale); ang1=45;% 方位角 ang2=90;% 天頂角 ang3=0;% 回転角 ang1=ang1/180*pi; ang2=ang2/180*pi; ang3=ang3/180*pi; Phi=[cos(ang1),-sin(ang1),0;sin(ang1),cos(ang1),0;0,0,1]; Theta=[1,0,0;0,cos(ang2),-sin(ang2);0,sin(ang2),cos(ang2)]; Lambda=[cos(ang3),-sin(ang3),0;sin(ang3),cos(ang3),0;0,0,1]; T=Lambda*Theta*Phi*diag(scale); T1=[eye(4,3),[new_shift';1]]; T2=[[T,zeros(3,1)];[0,0,0,1]]; T3=[eye(4,3),[old_shift';1]]; TT=T1*T2*T3; T=maketform('affine',TT'); resample=makeresampler({'linear','linear','linear'},'fill'); tic; D=tformarray(data,T,resample,[1,2,3],[2,1,3],new_shift*2,[],0); toc clear DD; DD(:,:,1,:)=D; DD=(DD-clim(1))/(clim(2)-clim(1))*255; DD(DD<0)=0;DD(DD>255)=255; DD=uint8(DD); index=round(linspace(1,size(D,3),14)); index([1,end])=[]; figure;montage(DD,'indices',index); end;
経過時間は 7.016832 秒です。 警告: イメージが大きすぎて画面に収まりません。67% で表示します。 経過時間は 7.014341 秒です。 警告: イメージが大きすぎて画面に収まりません。67% で表示します。
RIPEデータの形式に変換
上記のデータでMATLABのImage Processing Toolboxの例「マルチモーダルな3次元医療画像のレジストレーション」のコードを実行します。 The Restrospective Image Registration Evaluation (RIRE)形式に変換します。 helperVolumeRegistration関数は対話方式で回転ができ、両軸は同期して動きます。
for nn=1:2 eval(sprintf('info=info%d;',nn)); eval(sprintf('data=data%d;',nn)); R.Modality=info{1}.Modality; R.SliceThickness=info{1}.SliceThickness; R.Rows=info{1}.Rows; R.Columns=info{1}.Columns; R.PixelSize=info{1}.PixelSpacing'; R.Slices=size(data,3); if nn==1; fixedHeader=R; fixedVolume=single(data); else movingHeader=R; movingVolume=single(data); end; end; helperVolumeRegistration(fixedVolume,movingVolume);
imshowpair関数でMRIとCTを比較
imshowpair関数を使ってMRIとCTの上下中間の面で比較しました。
centerFixed =size(fixedVolume)/2;
centerMoving=size(movingVolume)/2;
centerFixed =round(centerFixed);
centerMoving=round(centerMoving);
figure;title('位置合わせ前のMRIとCT');
imshowpair(movingVolume(:,:,centerMoving(3)), fixedVolume(:,:,centerFixed(3)));
imregister関数を用いて位置合わせの
Image Processing Toolboxの以下の特殊な仕組みを使います。 1+1の進化の最適化構成のためのregistration.optimizer.OnePlusOneEvolutionaryクラス、 相対輝度が同じである一致ピクセル数を最大にするためのregistration.metric.MattesMutualInformationクラス、 ワールド座標用のimref3dクラスです。 尚、「マルチモーダルな3次元医療画像のレジストレーション」では
警告: 最適化が分岐処理されたため、登録に失敗しました。OnePlusOneEvolutionaryOptimizer 最適子の InitialRadius プロパティを減らしてください。
と表示され
optimizer.InitialRadius=6e-3;
としないと、ちゃんと位置合わせできませんでした。
[optimizer,metric]=imregconfig('multimodal'); % 次の2つのコード実行と等価 % optimizer=registration.optimizer.OnePlusOneEvolutionary(); % metric =registration.metric.MattesMutualInformation(); Rfixed =imref3d(size(fixedVolume),fixedHeader.PixelSize(2),fixedHeader.PixelSize(1),fixedHeader.SliceThickness); Rmoving=imref3d(size(movingVolume),movingHeader.PixelSize(2),movingHeader.PixelSize(1),movingHeader.SliceThickness); tic; movingRegisteredVolume=imregister(movingVolume,Rmoving,fixedVolume,Rfixed,'rigid',optimizer, metric); toc; figure; subplot(121); imshowpair(movingRegisteredVolume(:,:,centerFixed(3)),fixedVolume(:,:,centerFixed(3))); title('位置合わせ後のMRIとCT(水平断)'); subplot(122); imshowpair(squeeze(movingRegisteredVolume(:,centerFixed(2),:)),squeeze(fixedVolume(:,centerFixed(2),:))); title('位置合わせ後のMRIとCT(矢状断)'); helperVolumeRegistration(fixedVolume,movingRegisteredVolume);
経過時間は 36.106146 秒です。
imregtform関数とimwarp関数を用いて位置合わせ
imregtform関数は幾何学的変換を推定し、affine3dクラスを出力します。 transformPointsForward関数はaffine3dクラスの変換行列を用いて座標を変換します。 worldToSubscript関数はワールド座標から行と列の数値ベクトルへ変換します。 imregisterよりも位置合わせの精度がよくなるのではと期待したのですが、あんまり変化ありませんでした。 もう少しtuningが要るみたいです。
tic; geomtform=imregtform(movingVolume,Rmoving,fixedVolume,Rfixed,'rigid',optimizer,metric) toc; centerXWorld=mean(Rmoving.XWorldLimits); centerYWorld=mean(Rmoving.YWorldLimits); centerZWorld=mean(Rmoving.ZWorldLimits); [xWorld,yWorld,zWorld]=transformPointsForward(geomtform,centerXWorld,centerYWorld,centerZWorld); [r,c,p]=worldToSubscript(Rfixed,xWorld,yWorld,zWorld); tic; movingRegisteredVolume2=imwarp(movingVolume,Rmoving,geomtform,'bicubic','OutputView',Rfixed); toc; figure; subplot(121);imshowpair(movingRegisteredVolume2(:,:,centerFixed(3)), fixedVolume(:,:,centerFixed(3))); title('新・位置合わせ後のMRIとCT(水平断)'); subplot(122);imshowpair(squeeze(movingRegisteredVolume2(:,centerFixed(2),:)),squeeze(fixedVolume(:,centerFixed(2),:))); title('位置合わせ後のMRIとCT(矢状断)');
geomtform = affine3d のプロパティ: T: [4x4 double] Dimensionality: 3 経過時間は 37.126211 秒です。 経過時間は 2.577665 秒です。