Both sides previous revision
Previous revision
Next revision
|
Previous revision
|
neuroimagen:xnat_pipelines [2019/10/29 01:13] daniel |
neuroimagen:xnat_pipelines [2020/08/04 10:58] (current) |
==== Estructura de un pipeline ==== | ==== Estructura de un pipeline ==== |
| |
Un //pipeline// se define en un archivo XML dentro de un subdirectorio de ''pipeline/catalog'' que se crea al instalar en //pipeline engine// (dentro de ''/nas/data/xnat''). En el subdirectorio se recomienda la siguiente estructura: | Un //pipeline// se define en un archivo XML dentro de un subdirectorio de ''pipeline/catalog'' que se crea al instalar el //pipeline engine// (dentro de ''/nas/data/xnat''). En el subdirectorio se recomienda la siguiente estructura: |
* descripción XML del //pipeline// | * descripción XML del //pipeline// |
* ''resources'': subdirectorio con definiciones XML para llamar a programas externos | * ''resources'': subdirectorio con definiciones XML para llamar a programas externos |
* ''scripts'': subdirectorio con los scripts o programas externos propios del //pipeline// | * ''scripts'': subdirectorio con los scripts o programas externos propios del //pipeline// |
| |
**Ejemplo:** el pipeline ... | |
| |
La descripción del //pipeline// se hace en un documento XML con la siguiente estructura: | La descripción del //pipeline// se hace en un documento XML con la siguiente estructura: |
| |
<code> | <code xml> |
| <?xml version="1.0" encoding="UTF-8"?> |
| |
| <Pipeline xmlns="http://nrg.wustl.edu/pipeline" |
| xmlns:xi="http://www.w3.org/2001/XInclude" |
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| xsi:schemaLocation="http://nrg.wustl.edu/pipeline ..\schema\pipeline.xsd" |
| xmlns:fileUtils="http://www.xnat.org/java/org.nrg.imagingtools.utils.FileUtils"> |
| |
| <name>Name of the Pileline</name> |
| <location>Directory</location> |
| <description>A brief description</description> |
| |
| <resourceRequirements>...</resourceRequirements> |
| |
| <!-- info about the pipeline and input parameters received from Xnat --> |
| <documentation> |
| <authors>...</authors> |
| <version>0</version> |
| <input-parameters> |
| <parameter>...</parameter> |
| ... |
| </input-parameters> |
| </documentation> |
| |
| <!-- what sort of data the pipeline applies to --> |
| <xnatInfo appliesTo="xnat:mrSessionData"/> |
| |
| <!-- directory where log output is written --> |
| <outputFileNamePrefix>...</outputFileNamePrefix> |
| |
| <!-- parameter on which to iterate steps through PIPELINE_LOOPON(iter) --> |
| <loop id="iter" xpath="..."/> |
| |
| <!-- other parameters defined in the pipeline --> |
| <parameters> |
| <parameter>...</parameter> |
| ... |
| </parameters> |
| |
| <!-- processing steps --> |
| <steps> |
| <!-- first step (workdirectory and flow control options are shown as an example) --> |
| <step id="1" description="Step 1" workdirectory="..." continueOnFailure="false" precondition="EXISTS(...)"> |
| <!-- command to be run (described as a resource XML) --> |
| <resource name="command" location="directory" > |
| <argument id="arg"> |
| <value>arg value</value> |
| </argument> |
| </resource> |
| </step> |
| ... |
| </steps> |
| |
| </Pipeline> |
| |
</code> | </code> |
| |
==== Ejemplo: modificación del //pipeline// ... ==== | ==== Ejemplo: modificación del pipeline dcm2nii/DicomToNifti_X.xml ==== |
| |
| Queremos cambiar el modo en que se llama a dcm2niix desde este pipeline, de manera que se agreguen en los nombres de los archivos NII generados el nombre del paciente (%n), el número de adquisición de la serie (%s) y la descripción o tipo de serie (%d). |
| |
==== Ejemplo: limpieza de las series en una sesión DICOM ==== | Para ello, primero definimos un //input-parameter// con la cadena de formato que se pasará a dcm2niix desde Xnat (que, en teoría, podríamos modificar al llamar al pipeline): |
| |
Aquí se describirá el //pipeline// ''CleanMRSession'' que lleva a cabo la selección de los scans de una serie DICOM de forma análoga a la descrita en la página [[neuroimagen:xnat_api|Usar la API de XNAT]]. | <code xml> |
| <input-parameters> |
| ... |
| <parameter> |
| <name>output_nifti_filename_format</name> |
| <values> |
| <csv>%n_%s_%d</csv> |
| </values> |
| <description>Filename format</description> |
| </parameter> |
| </input-parameters> |
| </code> |
| |
Este //pipeline// consta de los siguientes archivos: | Luego, modificaremos la llamada a dcm2niix en el //step// "CONVERT" seleccionando nuestro argumento output_filename_format (usando la sintaxis XPath). Aprovechamos para añadir el nuevo argumento ignore_derived, para que en el procesamiento se ignoren las imágenes derivadas, los localizers y las imágenes 2D. |
| |
* ''CleanMRSession.xml'' | <code xml> |
* ''resources/cleanMRSession.xml'' | |
* ''scripts/cleanMRSession.sh'' (script Bash) | |
| |
== CleanMRSession.xml == | <step id="CONVERT" description="Convert each DICOM series into a 4d NIFTI file" precondition="EXISTS(^concat(/Pipeline/parameters/parameter[name='niidir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^)" continueOnFailure="true"> |
| <resource name="dcm2niix" location="dcm2niix/resources"> |
| <argument id="gzip"> |
| <value>y</value><!-- compress --> |
| </argument> |
| <argument id="ignore_derived"><!-- new argument --> |
| <value>y</value> |
| </argument> |
| <argument id="output_filename_format"> |
| <value>^/Pipeline/parameters/parameter[name='output_nifti_filename_format']/values/unique/text()^</value><!-- selection from parameters --> |
| </argument> |
| <argument id="output"> |
| <value>^concat(/Pipeline/parameters/parameter[name='niidir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^</value> |
| </argument> |
| <argument id="input"> |
| <value>^concat(/Pipeline/parameters/parameter[name='rawdir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^</value> |
| </argument> |
| </resource> |
| </step> |
| |
| </code> |
| |
| Lo que sucede es que el //resource// dcm2niix/resources/dcm2niix.xml no contempla el argumento ignore_derived, así que tenemos que añadírselo: |
| |
++++ El código, completo, es así: | | |
<code xml> | <code xml> |
| <argument id="ignore_derived"> |
| <name>i</name> |
| <description>ignore derived [y|n]</description> |
| </argument> |
| </code> |
| |
| |
| ++++ El código del nuevo DicomToNifti_Y.xml queda así | |
| <file xml DicomToNifti_Y.xml> |
| <?xml version="1.0" encoding="UTF-8"?> |
| <Pipeline xmlns="http://nrg.wustl.edu/pipeline" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://nrg.wustl.edu/pipeline ..\schema\pipeline.xsd" xmlns:fileUtils="http://www.xnat.org/java/org.nrg.imagingtools.utils.FileUtils"> |
| |
| <name>DicomToNifti_Y</name> |
| |
| <location>dcm2niix</location> |
| |
| <description>Pipeline creates NIFTI files from DICOM files using dcm2niix (modified).</description> |
| |
| <documentation> |
| <authors> |
| <author> |
| <lastname>Flavin</lastname> |
| <firstname>John</firstname> |
| <contact> |
| <email>flavinj@mir.wustl.edu</email> |
| </contact> |
| </author> |
| </authors> |
| <version>20180811</version> |
| <input-parameters> |
| <parameter> |
| <name>scanids</name> |
| <values> |
| <schemalink>xnat:imageSessionData/scans/scan/ID</schemalink> |
| </values> |
| <description>Scan ids of all the scans of the session</description> |
| </parameter> |
| <parameter> |
| <name>output_nifti_filename_format</name> |
| <values> |
| <csv>%n_%s_%d</csv> |
| </values> |
| <description>Filename format</description> |
| </parameter> |
| </input-parameters> |
| </documentation> |
| |
| <outputFileNamePrefix>^concat(/Pipeline/parameters/parameter[name='logdir']/values/unique/text(),'/',/Pipeline/parameters/parameter[name='label']/values/unique/text())^</outputFileNamePrefix> |
| |
| <loop id="series" xpath="^/Pipeline/parameters/parameter[name='scanids']/values/list^"/> |
| |
| <parameters> |
| <parameter> |
| <name>workdir</name> |
| <values> |
| <unique>^concat(/Pipeline/parameters/parameter[name='builddir']/values/unique/text(),'/',/Pipeline/parameters/parameter[name='label']/values/unique/text())^</unique> |
| </values> |
| </parameter> |
| <parameter> |
| <name>logdir</name> |
| <values> |
| <unique>^concat(/Pipeline/parameters/parameter[name='workdir']/values/unique/text(),'/LOGS')^</unique> |
| </values> |
| </parameter> |
| <parameter> |
| <name>rawdir</name> |
| <values> |
| <unique>^concat(/Pipeline/parameters/parameter[name='workdir']/values/unique/text(),'/RAW')^</unique> |
| </values> |
| </parameter> |
| <parameter> |
| <name>niidir</name> |
| <values> |
| <unique>^concat(/Pipeline/parameters/parameter[name='workdir']/values/unique/text(),'/NIFTI')^</unique> |
| </values> |
| </parameter> |
| </parameters> |
| |
| <steps> |
| <step id="MKDIR_RAW" description="Create RAW folder"> |
| <resource name="mkdir" location="commandlineTools"> |
| <argument id="dirname"> |
| <value>^/Pipeline/parameters/parameter[name='rawdir']/values/unique/text()^</value> |
| </argument> |
| </resource> |
| </step> |
| |
| <step id="MKDIR_NII" description="Create NIFTI folder"> |
| <resource name="mkdir" location="commandlineTools"> |
| <argument id="dirname"> |
| <value>^/Pipeline/parameters/parameter[name='niidir']/values/unique/text()^</value> |
| </argument> |
| </resource> |
| </step> |
| |
| <step id="MKDIR_RAW_SCAN" description="Create folder for each series in RAW subfolder" workdirectory="^/Pipeline/parameters/parameter[name='rawdir']/values/unique/text()^"> |
| <resource name="mkdir" location="commandlineTools"> |
| <argument id="dirname"> |
| <value>^PIPELINE_LOOPON(series)^</value> |
| </argument> |
| </resource> |
| </step> |
| |
| <step id="GET_SCANS" description="Download scan DICOMs" workdirectory="^concat(/Pipeline/parameters/parameter[name='rawdir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^"> |
| <resource name="XnatDataClient" location="xnat_tools"> |
| <argument id="sessionId"> |
| <value>^fileUtils:getJSESSION('DUMMY')^</value> |
| </argument> |
| <argument id="absolutePath"/> |
| <argument id="batch"/> |
| <argument id="method"> |
| <value>GET</value> |
| </argument> |
| <argument id="remote"> |
| <value>^concat('"',/Pipeline/parameters/parameter[name='host']/values/unique/text(),'/data/experiments/',/Pipeline/parameters/parameter[name='id']/values/unique/text(),'/scans/',PIPELINE_LOOPON(series),'/resources/DICOM/files"')^</value> |
| </argument> |
| </resource> |
| </step> |
| <step id="RMEMPTYDIRS" description="Removes empty dirs" precondition="EXISTS(^concat(/Pipeline/parameters/parameter[name='rawdir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^)" continueOnFailure="true"> |
| <!-- rmdir ../$SERIES --> |
| <resource name="rmdir" location="commandlineTools" > |
| <!-- resource not in standard xnat pipelines catalog --> |
| <argument id="dir"> |
| <value>^concat(/Pipeline/parameters/parameter[name='rawdir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^</value> |
| </argument> |
| </resource> |
| </step> |
| |
| <step id="MKDIR_NII_SCAN" description="Create folder for each series in NIFTI subfolder" precondition="EXISTS(^concat(/Pipeline/parameters/parameter[name='rawdir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^)" workdirectory="^/Pipeline/parameters/parameter[name='niidir']/values/unique/text()^"> |
| <resource name="mkdir" location="commandlineTools"> |
| <argument id="dirname"> |
| <value>^PIPELINE_LOOPON(series)^</value> |
| </argument> |
| </resource> |
| </step> |
| <step id="CONVERT" description="Convert each DICOM series into a 4d NIFTI file" precondition="EXISTS(^concat(/Pipeline/parameters/parameter[name='niidir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^)" continueOnFailure="true"> |
| <resource name="dcm2niix" location="dcm2niix/resources"> |
| <argument id="gzip"> |
| <value>y</value> |
| </argument> |
| <argument id="ignore_derived"><!-- new argument --> |
| <value>y</value> |
| </argument> |
| <argument id="output_filename_format"> |
| <value>^/Pipeline/parameters/parameter[name='output_nifti_filename_format']/values/unique/text()^</value> |
| </argument> |
| <argument id="output"> |
| <value>^concat(/Pipeline/parameters/parameter[name='niidir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^</value> |
| </argument> |
| <argument id="input"> |
| <value>^concat(/Pipeline/parameters/parameter[name='rawdir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^</value> |
| </argument> |
| </resource> |
| </step> |
| <step id="UPLOAD" description="Upload NIFTI files" precondition="EXISTS(^concat(/Pipeline/parameters/parameter[name='niidir']/values/unique/text(),'/',PIPELINE_LOOPON(series))^)" continueOnFailure="true"> |
| <resource name="XnatDataClient" location="xnat_tools"> |
| <argument id="sessionId"> |
| <value>^fileUtils:getJSESSION('DUMMY')^</value> |
| </argument> |
| <argument id="method"> |
| <value>PUT</value> |
| </argument> |
| <argument id="remote"> |
| <value>^concat('"',/Pipeline/parameters/parameter[name='host']/values/unique/text(),'/data/experiments/',/Pipeline/parameters/parameter[name='id']/values/unique/text(),'/scans/',PIPELINE_LOOPON(series),'/resources/NIFTI/files?overwrite=true&format=NIFTI&content=NIFTI_RAW&reference=',/Pipeline/parameters/parameter[name='niidir']/values/unique/text(),'/',PIPELINE_LOOPON(series),'&event_id=',/Pipeline/parameters/parameter[name='workflowid']/values/unique/text(),'"')^</value> |
| </argument> |
| </resource> |
| </step> |
| </steps> |
| |
| </Pipeline> |
| </file> |
| ++++ |
| |
| ++++ El código de dcm2niix.xml queda así | |
| <file xml dcm2niix.xml> |
| <?xml version="1.0" encoding="UTF-8"?> |
| <Resource xmlns="http://nrg.wustl.edu/pipeline"> |
| <name>dcm2niix</name> |
| <commandPrefix>source /nas/data/xnat/pipeline/scripts/dcm2niix_setup.sh;</commandPrefix> |
| <type>Executable</type> |
| <description>Generates NIFTI files from DICOM</description> |
| <input> |
| <argument id="gzip"> |
| <name>z</name> |
| <description>gzip [y|n]</description> |
| </argument> |
| <argument id="ignore_derived"> |
| <name>i</name> |
| <description>ignore derived [y|n]</description> |
| </argument> |
| <argument id="output_filename_format"> |
| <name>f</name> |
| <description>Output filename format string. (%c=comments %f=folder name %i ID of patient %m=manufacturer %n=name of patient %p=protocol %s=series, %t=time; default 'N')</description> |
| </argument> |
| <argument id="output"> |
| <name>o</name> |
| <description>Output Folder</description> |
| </argument> |
| <argument id="input"/> |
| </input> |
| </Resource> |
| </file> |
| ++++ |
| |
| ==== Ejemplo: resource nuevo para llamar al programa tar ==== |
| |
| En algunos pipelines puede interesar comprimir varios ficheros resultantes en un archivo tar.gz, usando el muy común programa tar de UNIX. Los //pipelines// de ejemplo no incluyen este programa como uno de los //resources// en commandlineTools, pero es muy fácil de escribirlo con las opciones básicas para comprimir archivos como |
| <code bash> |
| $ tar -z -cf archivo.tar.gz fichero1.ext fichero2.ext ... |
| </code> |
| |
| ++++El código de tar.xml sería así | |
| <file xml tar.xml> |
| <?xml version="1.0" encoding="UTF-8"?> |
| <Resource xmlns="http://nrg.wustl.edu/pipeline"> |
| <name>tar</name> |
| <type>Executable</type> |
| <description>TAR a dir/file</description> |
| <estimated_time>00:00:01</estimated_time> |
| <input> |
| <argument id="compress"> |
| <name>z</name> |
| <description>apply gzip</description> |
| </argument> |
| <argument id="archive"> |
| <name>cf</name> |
| <description>Archive name</description> |
| </argument> |
| <argument id="files"> |
| <description>Dir/files to tar</description> |
| </argument> |
| </input> |
| </Resource> |
| </file> |
| ++++ |
| |
| ==== Ejemplo: pipeline nuevo para limpieza de las series en una sesión DICOM ==== |
| |
| Partiendo de un //pipeline// de ejemplo, creamos el pipeline ''CleanMRSession.xml'' que lleva a cabo la selección de los scans de una serie DICOM de forma análoga a la descrita en la página [[neuroimagen:xnat_api|Usar la API de XNAT]]. |
| |
| Este //pipeline// consta de los siguientes archivos: |
| |
| * ''CleanMRSession.xml'': la descripción de los pasos de procesamiento |
| * ''resources/cleanMRSession.xml'': el //resource// que describe cómo se llama al script Bash siguiente |
| * ''scripts/cleanMRSession.sh'': el script Bash |
| |
| |
| ++++El código de CleanMRSession.xml es así | |
| <file xml CleanMRSession.xml> |
| |
<?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> |
</Pipeline> | </Pipeline> |
| |
</code> | </file> |
++++ | ++++ |
| |
== cleanMRSession.xml (descripción de cleamMRSession.sh) == | ++++ El código de cleanMRSession.xml (descripción de cleamMRSession.sh) es así | |
| <file xml cleanMRSession.xml> |
++++ El código, completo, es así: | | |
<code xml> | |
| |
<?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> |
</Resource> | </Resource> |
| |
</code> | </file> |
++++ | ++++ |
| |
| |
== cleanMRSession.sh (script que realiza el borrado) == | ++++ El código de cleanMRSession.sh (script que realiza el borrado) es así | |
| <file bash cleanMRSession.sh> |
++++ El código, completo, es así: | | |
<code bash> | |
| |
#!/bin/bash | #!/bin/bash |
| |
#temporaries | #temporaries |
TMP_PRO=$(tempfile) | TMP_PRO=$(mktemp) |
| |
#Tipos de estudios | #Tipos de estudios |
dckey -k "AcquisitionDate" "$SERIES/$DCM" 2>&1 | grep -v Error &&\ | dckey -k "AcquisitionDate" "$SERIES/$DCM" 2>&1 | grep -v Error &&\ |
dckey -k "SeriesDescription" "$SERIES/$DCM" 2>&1 | grep -v Error | grep -v -f $TMP_PRO &&\ | dckey -k "SeriesDescription" "$SERIES/$DCM" 2>&1 | grep -v Error | grep -v -f $TMP_PRO &&\ |
/nas/data/xnat/pipeline/xnat-tools/XnatDataClient -u $USER -p $PASSWORD -r "$URL/$SERIES" -m DELETE | $HOME/pipeline/xnat-tools/XnatDataClient -u $USER -p $PASSWORD -r "$URL/$SERIES" -m DELETE |
| |
#Dejar todo limpio | #Dejar todo limpio |
rm -f $TMP_PRO | rm -f $TMP_PRO |
| |
</code> | </file> |
++++ | ++++ |
| |
| |