最近在使用Gretna對fMRI數(shù)據(jù)進(jìn)行預(yù)處理的時(shí)候,總是遇到卡死的狀態(tài),仔細(xì)研究了一下Gretna的源代碼,發(fā)現(xiàn)其Workflow部分使用了PSOM的工具包,這個(gè)問題就沒辦法調(diào)試了,于是開始思考是否能夠有其他的工具來完成同樣的東東。
通過一段時(shí)間的調(diào)研,發(fā)現(xiàn)了這個(gè)Python的框架(截止目前為止,該框架的版本為1.0.4),于是有了這篇文章。這是一個(gè)自己學(xué)習(xí)的筆記,準(zhǔn)備慢慢記錄Nipype,主要參考的是Nipype的網(wǎng)站和文檔,具體鏈接如下:
http://nipype.readthedocs.io/en/latest/quickstart.html
http://nipype.readthedocs.io/en/latest/documentation.html
Nipype是一個(gè)用Python編寫的框架,主要用于處理神經(jīng)影像學(xué)的各類數(shù)據(jù),其集成了大部分常用的神經(jīng)影像學(xué)數(shù)據(jù)處理軟件,包括ANTS, SPM, FSL, FreeSurfer, Camino, MRtrix, MNE, AFNI, Slicer等,其中我只用過SPM和FSL,用這個(gè)框架可以自定義工作流,使不同的軟件協(xié)同工作。
1. 安裝
需要說明的是,截止目前為止,Nipype在類Unix系統(tǒng)上會(huì)運(yùn)行的更穩(wěn)定一些,例如Linux、MacOS都是完美支持的,對于Windows則不完全支持(雖然能夠通過Conda和Pip安裝上,但是運(yùn)行的時(shí)候會(huì)報(bào)錯(cuò),似乎其中引用了一個(gè)getpwd的包,而這個(gè)包只支持Linux等系統(tǒng))。
Nipye的安裝主要有三種方式:
1.1 Docker
如果想要在Windows系統(tǒng)上使用Nipype,恐怕除了虛擬機(jī)意外,最簡單便捷的方式就是使用Docker了,在使用Docker時(shí),只需要從鏡像庫將其拖下來就好了,需要執(zhí)行如下命令:
docker pull nipype/nipype
至于拖下來之后怎么用,目前我還沒有研究,等有機(jī)會(huì)的看一下。
1.2 Conda
Conda的安裝也非常簡單,只需要執(zhí)行如下命令就可以了,當(dāng)然為了便于演示和操作,建議也安裝Jupyter。(我用的就是這種方法)
conda install --channel conda-forge nipype
conda install jupyter
如果要繪制圖片,切記要安裝以下兩個(gè)包,否則會(huì)報(bào)錯(cuò)的。
conda install graphviz, pydot
1.3 Pip
Pip的安裝同樣簡單(我都不好意思再打這幾個(gè)字了)
pip install nipype
如果想要安裝nipype的所有可選的特征,就需要使用如下命令(我沒有使用)
pip install nipype[all]
2.基本概念
從我目前的理解來看,Nipype應(yīng)該是一個(gè)比較面向于開發(fā)人員的框架,并不太適合普通的醫(yī)師進(jìn)行數(shù)據(jù)處理,這可能也是國內(nèi)并未普及的原因。Nipype是一個(gè)有些類似于TensorFlow這樣的先定義計(jì)算,再執(zhí)行操作的函數(shù)式編程框架,要想充分運(yùn)用Nipype,發(fā)揮它的最大功效,先要需要充分理解三個(gè)基本概念:
- Interface
- Node
- Workflow
2.1 Interface
Interface是封裝了各類腦圖像處理軟件的Python包。在使用時(shí)需要進(jìn)行如下引用。由于沒有實(shí)際使用過,暫時(shí)先用官方的例子來進(jìn)行一下演示,待日后對Interface的理解進(jìn)一步深入了,再來演示更復(fù)雜的例子。(話說,似乎軟件文檔的編寫者,對于FSL更熟悉一些,所選的例子全部都是FSL的)
from nipype.interfaces.fsl import BET
要想知道BET這個(gè)包應(yīng)該怎么用,可以運(yùn)行:
BET.help()
我自己試了一下SPM的接口,與FSL的使用方法基本一致。
import nipype.interfaces.spm as spm
spm.DicomImport.help()
運(yùn)行上述代碼之后,會(huì)看到如下結(jié)果:
Uses spm to convert DICOM files to nii or img+hdr.
Examples
--------
>>> import nipype.interfaces.spm.utils as spmu
>>> di = spmu.DicomImport()
>>> di.inputs.in_files = ['functional_1.dcm', 'functional_2.dcm']
>>> di.run() # doctest: +SKIP
Inputs::
[Mandatory]
in_files: (a list of items which are an existing file name)
dicom files to be converted
[Optional]
format: ('nii' or 'img', nipype default value: nii)
output format.
icedims: (a boolean, nipype default value: False)
If image sorting fails, one can try using the additional SIEMENS
ICEDims information to create unique filenames. Use this only if
there would be multiple volumes with exactly the same file names.
ignore_exception: (a boolean, nipype default value: False)
Print an error message instead of throwing an exception in case the
interface fails to run
matlab_cmd: (a unicode string)
matlab command to use
mfile: (a boolean, nipype default value: True)
Run m-code using m-file
output_dir: (a unicode string, nipype default value:
./converted_dicom)
output directory.
output_dir_struct: ('flat' or 'series' or 'patname' or 'patid_date'
or 'patid' or 'date_time', nipype default value: flat)
directory structure for the output.
paths: (a list of items which are a directory name)
Paths to add to matlabpath
use_mcr: (a boolean)
Run m-code using SPM MCR
use_v8struct: (a boolean, nipype default value: True)
Generate SPM8 and higher compatible jobs
Outputs::
out_files: (a list of items which are an existing file name)
converted files
References::
None
從中可知,該接口是用于將DICOM數(shù)據(jù)轉(zhuǎn)換為nii或者img+hdr,其中有一個(gè)必要的輸入?yún)?shù)in_files,有一堆可選參數(shù),例如:用format確定轉(zhuǎn)換為nii還是img。
2.2 Node
Node是一個(gè)用于執(zhí)行某一項(xiàng)功能的對象,執(zhí)行的功能可以由Interface完成,也可以由自己定義的函數(shù)完成,還可以使用外部的腳本。
一個(gè)Node通常由以下幾個(gè)部分組成:
- IN:至少一個(gè)輸入
- OUT:至少一個(gè)輸出
- name:節(jié)點(diǎn)的名稱
-
interface:執(zhí)行的單元(如函數(shù)、Interface等)
image.png
仍然使用上面的例子,制作一個(gè)執(zhí)行將DICOM轉(zhuǎn)換為Nifti任務(wù)的Node,需要執(zhí)行以下代碼:
import nipype.interfaces.spm as spm
from nipype import Node
node = Node(spm.DicomImport(), name='dcm2nii')
此時(shí),node就變成了一個(gè)能執(zhí)行Dicom2nii任務(wù)的節(jié)點(diǎn)了,執(zhí)行help命令,可以看到其參數(shù)與DicomImport的參數(shù)是完全一樣的。
除了使用Interface以外(這恐怕是最常見的應(yīng)用了),還可以自定義函數(shù),比如這樣(這個(gè)例子來源于官方的文檔):
from nipype import Node, Function
def add_two(x_input):
return x_input + 2
addtwo = Node(Function(input_names=["x_input"],
output_names=["val_output"],
function=add_two),
name='add_node')
addtwo.inputs.x_input = 4
addtwo.run()
其實(shí),前面沒有提到,單獨(dú)的Interface也可以執(zhí)行run
2.3 Workflow
從直觀上而言,Workflow可以認(rèn)為是由多個(gè)Node組成的一張有向無環(huán)圖(Directed Acyclic Graphs,DAG),其中定義了數(shù)據(jù)的走向。但是,Workflow又不是簡單的一個(gè)Node的組成,其中還執(zhí)行了Cache,也就是說如果某些步驟已經(jīng)執(zhí)行過,而參數(shù)又沒有進(jìn)行其他的調(diào)整,那么再次運(yùn)行Workflow的時(shí)候,就不會(huì)再重復(fù)執(zhí)行已經(jīng)執(zhí)行過的步驟,這一點(diǎn)比Gretna這種軟件要好太多了。
官方文檔提供了一張表格,寫明了其和Interface的主要區(qū)別:

Workflow的例子我也還沒有寫過,先用官方的例子代替一下吧,官方給出的例子仍然是FSL的:
from nipype import Node, Workflow
from nipype.interfaces import fsl
from os.path import abspath
in_file = abspath("/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz")
#先定義三個(gè)Node
skullstrip = Node(fsl.BET(in_file=in_file, mask=True), name="skullstrip")
smooth = Node(fsl.IsotropicSmooth(in_file=in_file, fwhm=4), name="smooth")
mask = Node(fsl.ApplyMask(), name="mask")
#再定義一個(gè)Workflow
wf = Workflow(name="smoothflow", base_dir="/output/working_dir")
# 需要使用connect函數(shù)將Node加入到Workflow中并進(jìn)行連接
wf.connect(skullstrip, "mask_file", mask, "mask_file")
wf.connect([(smooth, mask, [("out_file", "in_file")])])
這樣就定義完了一個(gè)Workflow,之后就可以調(diào)用run來執(zhí)行這個(gè)Workflow了。需要注意的是,connect函數(shù)有兩種形式,分別是:
connect(source, "source_output", dest, "dest_input")
connect([(source, dest, [("source_output1", "dest_input1"),
("source_output2", "dest_input2")
])
])
3. Workflow可視化
我們都知道,人對于抽象的東西并不十分敏感,其中最有名的莫過于安斯庫姆四重奏。在編程過程中也是一樣的,對于簡單的處理程序,我們很容易看出其中的問題,但是對于特別復(fù)雜的處理流程,要想一眼看出其中的問題就沒有那么容易了,為了幫助用戶看出Workflow中的各種各樣的問題,Nipype提供了一種可視化的函數(shù)作為工具——write_graph。其基本用法十分簡單:
wf.write_graph(dotfilename = 'workflow_graph.dot')
from IPython.display import Image
Image(filename="/output/working_dir/smoothflow/workflow_graph.png")
執(zhí)行上述命令后,就會(huì)生成一個(gè)不是十分漂亮的圖形:

上面的圖片只是最最基礎(chǔ)的顯示方式,write_graph共提供了五種圖片模式,分別是:
- orig 默認(rèn)的顯示方式,只會(huì)顯示最頂層的Workflow,不會(huì)顯示嵌套Workflow的內(nèi)部結(jié)構(gòu),默認(rèn)的就是這種形式
- flat 在繪圖時(shí),會(huì)展開Workflow的內(nèi)部結(jié)構(gòu)
- hierarchical 在繪圖時(shí),同樣會(huì)展開Workflow的內(nèi)部結(jié)構(gòu)。相比于flat方式,該方式會(huì)更加清楚的標(biāo)記出哪些是嵌套Workflow的內(nèi)部結(jié)構(gòu)
- colored 該方式會(huì)在hierarchical的基礎(chǔ)上添加顏色信息
- exec 這一種我也沒有理解,如果大家能夠理解的話希望能夠給與指點(diǎn),在此附上英文原文This visualization is the most different from the rest. Like the flat visualization, it depicts all individual nodes. But additionally, it drops the utility nodes from the workflow and expands workflows to depict iterables (can be seen in the detailed_graph visualization further down below).
在使用時(shí),只需要對write_graph的graph2use進(jìn)行指定即可。
下面我們用實(shí)際的圖片來演示一下幾種繪圖模式之間的區(qū)別:





使用上述方法,僅僅能夠看到節(jié)點(diǎn)之間的連接關(guān)系。事實(shí)上,在調(diào)用write_graph的時(shí)候,如果模式為 orig、flat或exec,系統(tǒng)已經(jīng)幫我們生成了更加細(xì)粒度的圖片,如果要查看的話,只要在文件名后添加detailed即可,例如在執(zhí)行如下代碼
Image(filename="graph_flat_detailed.png")
就能看到這樣的圖片

目前寫作的電腦上沒有安裝相應(yīng)的環(huán)境,因此都是從官方網(wǎng)站上抓取的圖片,代碼也沒有經(jīng)過測試。
未完待續(xù)...
