1. 目的
2. 準備
2.1. MEDI toolboxにパスを通す
2.2. QSM解析実行のコード準備
2.3. Write_DICOM.mを修正
2.4. QSM_3Dを集める
3. 実行
3.1. 作業フォルダ(analyzeフォルダ)へ移動
3.2. runme.mを実行
3.3. Outputファイルの確認
4. 複数の被験者データをまとめて処理したい場合
4.1. run_sequential.m
1. 目的
Cornell大学が開発したQSM software (MEDI)を使って解析
(Cornell MRI Research Lab. QSMより、URL: http://pre.weill.cornell.edu/mri/pages/qsm.html)
2. 準備
####MEDI toolboxのダウンロード
こちらからMEDI_toolboxをダウンロード
MEDI_toolboxの中身はこのような感じ。
2.1. MEDI toolboxにパスを通す
MATLABを起動し、パスの設定からサブフォルダも追加をクリックしfunctionsフォルダを選択した後、保存。
2.2. QSM解析実行のコード準備
QSM解析を実行するためのコードは以下。
以下をrunme.m(※任意の名前でOK)で保存し、作業フォルダとなるanalyzeフォルダ(※任意の名前でOK)に保存する。
clear all;clc;close all; % Set path % MEDI_set_path % STEP 1: Import data [iField,voxel_size,matrix_size,CF,delta_TE,TE,B0_dir,files]=Read_DICOM('AXL_QSM'); %%%% Generate the Magnitude image %%%% iMag = sqrt(sum(abs(iField).^2,4)); %%%%% provide a Mask here if possible %%%%%% if (~exist('Mask','var')) % Mask = genMask(iField, voxel_size); Mask = BET(iMag,matrix_size,voxel_size); end %%%%% provide a noise_level here if possible %%%%%% if (~exist('noise_level','var')) noise_level = calfieldnoise(iField, Mask); end %%%%% normalize signal intensity by noise to get SNR %%% iField = iField/noise_level; % STEP 2a: Field Map Estimation %%%%%Estimate the frequency offset in each of the voxel using a %%%%%complex fitting %%%% [iFreq_raw N_std] = Fit_ppm_complex(iField); % STEP 2b: Spatial phase unwrapping %%%% iFreq = unwrapPhase(iMag, iFreq_raw, matrix_size); % STEP 2c: Background Field Removal %%%% Background field removal %%%% [RDF shim] = PDF(iFreq, N_std, Mask,matrix_size,voxel_size, B0_dir); % CSF Mask for zero referencing R2s=arlo(TE,abs(iField)); Mask_CSF = extract_CSF(R2s, Mask, voxel_size); % STEP 3: Dipole Inversion save RDF.mat RDF iFreq iFreq_raw iMag N_std Mask matrix_size... voxel_size delta_TE CF B0_dir Mask_CSF; %%%% run MEDI %%%%% QSM = MEDI_L1('lambda',1000, 'smv', 5, 'merit'); %%% save to DICOM % ignore warnings... Write_DICOM(QSM,files,'QSM')
2.3. Write_DICOM.mを修正
解析後のQSMの保存には、Write_DICOM.mを使用する。(runme.mを実行すれば自動で実行される。)
修正前のファイルでは、dicomwriteでエラーが発生する。
そのため、MEDI_toolbox/functions/Write_DICOM.mを以下のコードにそっくりそのまま書き換える。
やっていることは、dicomwriteのCreateModeをCopyからCreateへチェンジ。
function Write_DICOM(M,files,outdir,opts) %WRITE_DICOM Summary of this function goes here % Detailed explanation goes here defopts.SeriesDescription = 'QSM'; defopts.SeriesInstanceUID = []; defopts.SeriesNumber = []; defopts.Convert = @(x) convert(x); defopts.Window = 0.500; defopts.Level = 0; defopts.flag3D = 0; % defopts.EchoNumber = []; % defopts.EchoTime = 0.0; if nargin<4; opts=struct; end deffields=fieldnames(defopts); for i=1:length(deffields) if ~isfield(opts, deffields{i}) opts.(deffields{i})=defopts.(deffields{i}); end end if isfield(files,'M') filenames=files.M; elseif isfield(files,'R') filenames=files.R; elseif isfield(files, 'info3D') opts.flag3D=1; else error('No filenames (M nor R) found.'); end flag_signed=min(M(:))<0; if ~opts.flag3D if size(M,3) ~= size(filenames,1) error([num2str(size(filenames,1)) ' filenames given for ' num2str(size(M,3)) ' slices.']); end end if isempty(opts.SeriesInstanceUID) opts.SeriesInstanceUID=dicomuid; end progress=''; mkdir(outdir); warning('off','images:dicom_add_attr:invalidAttribChar'); load_flag=1; insert_flag=~opts.flag3D; for slice=1:size(M,3) if load_flag if opts.flag3D filename=files.info3D.filename; else filename=filenames{slice,end}; end info = dicominfo(filename); info.SeriesDescription = opts.SeriesDescription; info.SeriesInstanceUID = opts.SeriesInstanceUID; if isempty(opts.SeriesNumber) opts.SeriesNumber=info.SeriesNumber*100; end info.SeriesNumber = opts.SeriesNumber; info.SOPInstanceUID = dicomuid; info.InstanceNumber = slice; if opts.flag3D load_flag=0; end end if opts.flag3D item=files.info3D.slice2index{slice}; % info.PerFrameFunctionalGroupsSequence.(item).PlanePositionSequence.Item_1.ImagePositionPatient; info.PerFrameFunctionalGroupsSequence.(item).Private_2005_140f.Item_1.SOPInstanceUID = dicomuid; end im = M(:,:,slice); if (isfield(info, 'SmallestImagePixelValue')) info.SmallestImagePixelValue=opts.Convert(min(im(:))); end if (isfield(info, 'LargestImagePixelValue')) info.LargestImagePixelValue=opts.Convert(max(im(:))); end if (isfield(info, 'RescaleIntercept')) info.RescaleIntercept=0; end if (isfield(info, 'RescaleSlope')) info.RescaleSlope=1; end info.WindowCenter=opts.Convert(opts.Level); info.WindowWidth=opts.Convert(opts.Window); % if opts.flag3D % info.PerFrameFunctionalGroupsSequence.Item_1.PixelValueTransformationSequence.Item_1.RescaleIntercept=0; % info.PerFrameFunctionalGroupsSequence.Item_1.Private_2005_140f.Item_1.RescaleIntercept=0; % info.PerFrameFunctionalGroupsSequence.Item_1.PixelValueTransformationSequence.Item_1.RescaleSlope=1; % info.PerFrameFunctionalGroupsSequence.Item_1.Private_2005_140f.Item_1.RescaleSlope=1; % info.PerFrameFunctionalGroupsSequence.Item_1.FrameVOILUTSequence.Item_1.WindowCenter=opts.Convert(opts.Level); % info.PerFrameFunctionalGroupsSequence.Item_1.FrameVOILUTSequence.Item_1.WindowWidth=opts.Convert(opts.Window); % end info.SamplesPerPixel=1; info.BitsAllocated=16; info.BitsStored=16; info.HighBit=15; info.PixelRepresentation=flag_signed; if size(M,3)==slice insert_flag=1; end if insert_flag outfile=fullfile(outdir,sprintf('IM%05d.dcm', slice)); print_progress(outfile); if opts.flag3D f=fieldnames(info.PerFrameFunctionalGroupsSequence); f=setdiff(f,files.info3D.slice2index,'stable'); for i=1:length(f) info.PerFrameFunctionalGroupsSequence=rmfield(info.PerFrameFunctionalGroupsSequence, f{i}); end for i=1:length(files.info3D.slice2index) item=files.info3D.slice2index{i}; info.PerFrameFunctionalGroupsSequence.(item).Private_2005_140f.Item_1.InstanceNumber=1; info.PerFrameFunctionalGroupsSequence.(item).PixelValueTransformationSequence.Item_1.RescaleIntercept=0; info.PerFrameFunctionalGroupsSequence.(item).Private_2005_140f.Item_1.RescaleIntercept=0; info.PerFrameFunctionalGroupsSequence.(item).PixelValueTransformationSequence.Item_1.RescaleSlope=1; info.PerFrameFunctionalGroupsSequence.(item).Private_2005_140f.Item_1.RescaleSlope=1; info.PerFrameFunctionalGroupsSequence.(item).FrameVOILUTSequence.Item_1.WindowCenter=opts.Convert(opts.Level); info.PerFrameFunctionalGroupsSequence.(item).FrameVOILUTSequence.Item_1.WindowWidth=opts.Convert(opts.Window); end info.NumberOfFrames=length(files.info3D.slice2index); sz=size(M); M=reshape(opts.Convert(M), sz(1), sz(2), 1, []); M=permute(M, [2 1 3 4]); if isfield(files, 'slices_added') if files.slices_added warning('Removing empty slice at bottom of volume'); M=M(:,:,1:end-1); end end %dicomwrite(M,outfile, ... % 'CreateMode', 'copy', 'WritePrivate', true, info); dicomwrite(M,outfile, ... 'WritePrivate', true, info); else if isfield(files, 'slices_added') if files.slices_added warning('Removing empty slice at bottom of volume'); M=M(:,:,1:end-1); end end %dicomwrite(opts.Convert(M(:,:,slice))',outfile, ... % 'CreateMode', 'copy', 'WritePrivate', true, info); % dicomwrite(opts.Convert(M(:,:,slice))',outfile, ... 'WritePrivate', true, info); end end end fprintf('\n'); function print_progress(arg) num=length(progress); num=num-numel(regexp(progress, '\\\\')); for ii=1:num; fprintf('\b'); end progress=['Writing file ' arg]; progress=regexprep(progress,'\','\\\\'); fprintf(progress); end function y=convert(x) if flag_signed y=int16(x*1000); else y=uint16(x*1000); end end end
2.4. QSM_3Dを集める
各フォルダには、強度画像と位相画像が入っている。
TEを7回変えて撮像している。
国際脳QSMの撮像は、1回撮像に128 slicesなので強度画像と位相画像はそれぞれ、896枚(=128 slices ×7 phase)
(Cornell MRI Research Lab. QSMより、URL: http://pre.weill.cornell.edu/mri/pages/qsm.html)
QSM_3Dにある強度画像と位相画像をanalyzeフォルダのrawdata(※必ずrawdata)にまとめて保存。
3. 実行
3.1. 作業フォルダ(analyzeフォルダ)へ移動
MATLABを起動し、analyzeフォルダへ移動。
3.2. runme.mを実行
runme.mをMATLABへDrag & Dropし、QSM解析を実行。
3.3. Outputファイルの確認
runme.mを実行後、
- QSM
- results
フォルダが生成される。
QSMフォルダにQSM画像がDICOM形式で保存される。
(Cornell MRI Research Lab. QSMより、URL: http://pre.weill.cornell.edu/mri/pages/qsm.html)
4. 複数の被験者データをまとめて処理したい場合
被験者ごとにフォルダを作成し一つのフォルダにまとめます。
さらに、後で紹介するまとめてMEDIを実行するスクリプトも入れておきます。
各被験者フォルダには強度画像と位相画像のDICOMがまとめて入ったrawdataフォルダがあります。
以下のスクリプトを実行すると、すべての被験者のQSM mapが計算できます。
4.1. run_sequential.m
clear all;clc;close all; % change direct to study folder selpath = uigetdir('Select the folder including all subject'); cd(selpath); % list folder folderlist = dir(selpath); folderlist = folderlist(~ismember({folderlist.name}, {'.', '..'})); folderlist = folderlist([folderlist.isdir]); % run MEDI for i = 1:length(folderlist) disp(['processing ' folderlist(i).name ' ...']) main(folderlist(i).name, selpath); end % define function of MEDI processings function main(folder, basepath) % initialize clearvars -except selpath folderlist folder basepath i % move to subject folder cd(folder) % STEP 1: Import data [iField,voxel_size,matrix_size,CF,delta_TE,TE,B0_dir,files]=Read_DICOM('rawdata'); %%%% Generate the Magnitude image %%%% iMag = sqrt(sum(abs(iField).^2,4)); %%%%% provide a Mask here if possible %%%%%% if (~exist('Mask','var')) % Mask = genMask(iField, voxel_size); Mask = BET(iMag,matrix_size,voxel_size); end %%%%% provide a noise_level here if possible %%%%%% if (~exist('noise_level','var')) noise_level = calfieldnoise(iField, Mask); end %%%%% normalize signal intensity by noise to get SNR %%% iField = iField/noise_level; % STEP 2a: Field Map Estimation %%%%%Estimate the frequency offset in each of the voxel using a %%%%%complex fitting %%%% [iFreq_raw N_std] = Fit_ppm_complex(iField); % STEP 2b: Spatial phase unwrapping %%%% iFreq = unwrapPhase(iMag, iFreq_raw, matrix_size); % STEP 2c: Background Field Removal %%%% Background field removal %%%% [RDF shim] = PDF(iFreq, N_std, Mask,matrix_size,voxel_size, B0_dir); % CSF Mask for zero referencing R2s=arlo(TE,abs(iField)); Mask_CSF = extract_CSF(R2s, Mask, voxel_size); % STEP 3: Dipole Inversion save RDF.mat RDF iFreq iFreq_raw iMag N_std Mask matrix_size... voxel_size delta_TE CF B0_dir Mask_CSF; %%%% run MEDI %%%%% QSM = MEDI_L1('lambda',1000, 'smv', 5, 'merit'); %%% save to DICOM % ignore warnings... Write_DICOM(QSM,files,'QSM') % back to study folder cd(basepath) end