Skip to content

Commit

Permalink
Merge pull request #163 from rwblair/issue155
Browse files Browse the repository at this point in the history
Generate reports at end of run_workflow.
  • Loading branch information
chrisgorgo committed Nov 3, 2016
2 parents 739a04a + c4eb15f commit 78424b0
Show file tree
Hide file tree
Showing 15 changed files with 266 additions and 82 deletions.
1 change: 1 addition & 0 deletions fmriprep/interfaces/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
from fmriprep.interfaces.bids import ReadSidecarJSON, DerivativesDataSink, BIDSDataGrabber
from fmriprep.interfaces.images import ImageDataSink
from fmriprep.interfaces.utils import FormatHMCParam, IntraModalMerge
9 changes: 5 additions & 4 deletions fmriprep/interfaces/bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@
# @Date: 2016-06-03 09:35:13
# @Last Modified by: oesteban
# @Last Modified time: 2016-10-05 15:03:25
from lockfile import LockFile
import os
import os.path as op
import pkg_resources as pkgr
import re
from shutil import copy
import simplejson as json
from shutil import copy

from nipype import logging
from nipype.interfaces.base import (traits, isdefined, TraitedSpec, BaseInterface,
BaseInterfaceInputSpec, File, InputMultiPath, OutputMultiPath)
from nipype.interfaces.base import (
traits, isdefined, TraitedSpec, BaseInterface, BaseInterfaceInputSpec,
File, InputMultiPath, OutputMultiPath
)

from fmriprep.utils.misc import collect_bids_data, make_folder

Expand Down
88 changes: 88 additions & 0 deletions fmriprep/interfaces/images.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import json
import re
import os
import os.path as op
from shutil import copy

from nipype import logging
from nipype.interfaces.base import (
traits, isdefined, TraitedSpec, BaseInterface, BaseInterfaceInputSpec,
File, InputMultiPath, OutputMultiPath, traits
)

from fmriprep.interfaces.bids import _splitext
from fmriprep.utils.misc import make_folder

class ImageDataSinkInputSpec(BaseInterfaceInputSpec):
base_directory = traits.Directory(
desc='Path to the base directory for storing data.')
in_file = traits.Str(desc='the image to be saved')
base_file = traits.Str(desc='the input func file')
overlay_file = traits.Str(desc='the input func file')
origin_file = traits.Str(desc='File from the dataset that image is primarily derived from')

class ImageDataSinkOutputSpec(TraitedSpec):
out_file = OutputMultiPath(File(exists=True, desc='written file path'))

class ImageDataSink(BaseInterface):
input_spec = ImageDataSinkInputSpec
output_spec = ImageDataSinkOutputSpec
_always_run = True

def __init__(self, **inputs):
self._results = {'out_file': []}
super(ImageDataSink, self).__init__(**inputs)

def _run_interface(self, runtime):
origin_fname, _ = _splitext(self.inputs.origin_file)

image_inputs = {}
if isdefined(self.inputs.base_file):
image_inputs['base_file'] = self.inputs.base_file
if isdefined(self.inputs.overlay_file):
image_inputs['overlay_file'] = self.inputs.overlay_file
if isdefined(self.inputs.origin_file):
image_inputs['origin_file'] = self.inputs.overlay_file

m = re.search(
'^(?P<subject_id>sub-[a-zA-Z0-9]+)(_(?P<ses_id>ses-[a-zA-Z0-9]+))?'
'(_(?P<task_id>task-[a-zA-Z0-9]+))?(_(?P<acq_id>acq-[a-zA-Z0-9]+))?'
'(_(?P<rec_id>rec-[a-zA-Z0-9]+))?(_(?P<run_id>run-[a-zA-Z0-9]+))?',
origin_fname
)

base_directory = os.getcwd()
if isdefined(self.inputs.base_directory):
base_directory = op.abspath(self.inputs.base_directory)

out_path = 'images/{subject_id}'.format(**m.groupdict())

out_path = op.join(base_directory, out_path)

make_folder(out_path)

_, out_filename = op.split(self.inputs.in_file)

# test incoming origin file for these identifiers, if they exist
# we want to fold them into out filename
group_keys = ['ses_id', 'task_id', 'acq_id', 'rec_id', 'run_id']
if [x for x in group_keys if m.groupdict().get(x)]:
out_filename, ext = _splitext(out_filename)
out_filename = '{}_{}.{}'.format(out_filename, origin_fname, ext)

out_file = op.join(out_path, out_filename)


self._results['out_file'].append(out_file)
copy(self.inputs.in_file, out_file)
json_fname, _ = _splitext(out_filename)

json_out_filename = '{}.{}'.format(json_fname, 'json')
json_out_file = op.join(out_path, json_out_filename)
with open(json_out_file, 'w') as fp:
json.dump(image_inputs, fp)

return runtime

def _list_outputs(self):
return self._results
4 changes: 4 additions & 0 deletions fmriprep/run_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from argparse import RawTextHelpFormatter
from multiprocessing import cpu_count


def main():
"""Entry point"""
from fmriprep import __version__
Expand Down Expand Up @@ -70,6 +71,7 @@ def create_workflow(opts):
import logging
from nipype import config as ncfg
from fmriprep.utils import make_folder
from fmriprep.viz.reports import run_reports
from fmriprep.workflows import base as fwb
from fmriprep.workflows.base import base_workflow_enumerator

Expand Down Expand Up @@ -143,5 +145,7 @@ def create_workflow(opts):
if opts.write_graph:
preproc_wf.write_graph()

run_reports(settings['output_dir'])

if __name__ == '__main__':
main()
3 changes: 3 additions & 0 deletions fmriprep/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@

INPUTS_SPEC = {'fieldmaps': [], 'func': [], 't1': [], 'sbref': []}

def _first(inlist):
return sorted(inlist)[0]

def make_folder(folder):
try:
os.makedirs(folder)
Expand Down
28 changes: 26 additions & 2 deletions fmriprep/viz/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,34 @@
"elements":
[
{
"name": "sbref_to_t1",
"file_pattern": "sbref_to_t1",
"name": "t1_seg_native",
"file_pattern": "t1_seg_native",
"title": "Sbref to T1",
"description": "Corrected sbref image registred into t1 space."
},
{
"name": "t1_to_mni_overlay",
"file_pattern": "t1_to_mni_overlay",
"title": "T1 to MNI",
"description": "Corrected anatomical T1 image registered into MNI space"
},
{
"name": "mean_epi_overlay",
"file_pattern": "mean_epi_overlay",
"title": "EPI",
"description": " "
},
{
"name": "corrected_EPI",
"file_pattern": "corrected_EPI",
"title": "Corrected EPI",
"description": " "
},
{
"name": "mean_epi_to_sbref",
"file_pattern": "mean_epi_to_sbref",
"title": "Mean EPI to SBRef",
"description": " "
}
]
}
Expand Down
8 changes: 6 additions & 2 deletions fmriprep/viz/report.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
{% for sub_report in sub_reports %}
<h2>{{ sub_report.name }}</h2>
{% for elem in sub_report.elements %}
{{ elem.name }}
{% if elem.files_contents %}
<h4>{{ elem.name }}<h4/>
<br>
{% for image in elem.files_contents %}
{{ image }}
{{ image.1 }}<br>
{{ image.0 }}
{% endfor %}
{% endif %}
{% endfor %}
{% endfor %}

Expand Down
28 changes: 24 additions & 4 deletions fmriprep/viz/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import json
import re
import os
from os.path import join, exists, basename, dirname

import jinja2
from pkg_resources import resource_filename as pkgrf
Expand All @@ -18,6 +17,7 @@ def __init__(self, name, file_pattern, title, description):
self.description = description
self.files = []
self.files_contents = []
self.files_subjects = []


class SubReport(object):
Expand All @@ -38,6 +38,7 @@ def generate_sub_report(self, report):
sub_report_render = sub_report_tpl.render
return sub_report_render


class Report(object):

def __init__(self, path, config, out_dir, out_filename='report.html'):
Expand All @@ -48,8 +49,11 @@ def __init__(self, path, config, out_dir, out_filename='report.html'):
self.out_filename = out_filename

def _load_config(self, config):
if isinstance(config, str):
try:
config = json.load(open(config, 'r'))
except Exception as e:
print(e)
return

for e in config['sub_reports']:
sub_report = SubReport(**e)
Expand All @@ -60,15 +64,15 @@ def _load_config(self, config):
def index(self):
for root, directories, filenames in os.walk(self.root):
for f in filenames:
f = join(root, f)
f = os.path.join(root, f)
for sub_report in self.sub_reports:
for element in sub_report.elements:
if element.file_pattern.search(f) and f.split('.')[-1] == 'svg':
element.files.append(f)
with open(f) as fp:
content = fp.read()
content = '\n'.join(content.split('\n')[1:])
element.files_contents.append(content)
element.files_contents.append((f, content))

def generate_report(self):
searchpath = pkgrf('fmriprep', '/')
Expand All @@ -81,3 +85,19 @@ def generate_report(self):
with open(os.path.join(self.out_dir, self.out_filename), 'w') as fp:
fp.write(report_render)
return report_render

def run_reports(out_dir):
path = os.path.join(out_dir, 'images/')
config = pkgrf('fmriprep', 'viz/config.json')

for root, _, _ in os.walk(path):
# relies on the fact that os.walk does not return a trailing /
dir = root.split('/')[-1]
try:
subject = re.search('^(?P<subject_id>sub-[a-zA-Z0-9]+)$', dir).group()
out_filename='{}{}'.format(subject, '.html')
report = Report(path, config, out_dir, out_filename)
report.generate_report()
except AttributeError:
continue

14 changes: 0 additions & 14 deletions fmriprep/viz/run_reports.py

This file was deleted.

42 changes: 36 additions & 6 deletions fmriprep/workflows/anatomical.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@
import pkg_resources as pkgr

from nipype.interfaces import ants
from nipype.interfaces import fsl
from nipype.interfaces import freesurfer as fs
from nipype.interfaces import fsl
from nipype.interfaces import io as nio
from nipype.interfaces import utility as niu
from nipype.pipeline import engine as pe

from niworkflows.anat.skullstrip import afni_wf as skullstrip_wf
from niworkflows.anat.mni import RobustMNINormalization
from niworkflows.anat.skullstrip import afni_wf as skullstrip_wf
from niworkflows.common import reorient as mri_reorient_wf

from fmriprep.interfaces import DerivativesDataSink, IntraModalMerge
from niworkflows.data import get_mni_template

from fmriprep.interfaces import (DerivativesDataSink, IntraModalMerge,
ImageDataSink)
from fmriprep.viz import stripped_brain_overlay, anatomical_overlay


Expand Down Expand Up @@ -111,6 +112,11 @@ def t1w_preprocessing(name='t1w_preprocessing', settings=None):
)
t1_stripped_overlay.inputs.out_file = 't1_stripped_overlay.svg'

t1_stripped_overlay_ds = pe.Node(
ImageDataSink(base_directory=settings['output_dir']),
name='T1StrippedOverlayDS'
)

# t1 segmentation mapped onto t1 before registration into mni space
t1_seg_native = pe.Node(
niu.Function(
Expand All @@ -122,6 +128,11 @@ def t1w_preprocessing(name='t1w_preprocessing', settings=None):
)
t1_seg_native.inputs.out_file = 't1_seg_native.svg'

t1_seg_native_ds = pe.Node(
ImageDataSink(base_directory=settings['output_dir']),
name='T1SegNativeDS'
)

# The T1-to-MNI will be plotted using the segmentation.
# That's why we transform it first
seg_2_mni = pe.Node(
Expand All @@ -144,6 +155,13 @@ def t1w_preprocessing(name='t1w_preprocessing', settings=None):
t1_2_mni_overlay.inputs.overlay_file = op.join(get_mni_template(),
'MNI152_T1_1mm.nii.gz')

t1_2_mni_overlay_ds = pe.Node(
ImageDataSink(base_directory=settings['output_dir']),
name='T12MNIOverlayDS'
)
t1_2_mni_overlay_ds.inputs.overlay_file = op.join(get_mni_template(),
'MNI152_T1_1mm.nii.gz')

datasink = pe.Node(
interface=nio.DataSink(
base_directory=op.join(settings['output_dir'], 'images')
Expand All @@ -155,15 +173,27 @@ def t1w_preprocessing(name='t1w_preprocessing', settings=None):
workflow.connect([
(inu_n4, t1_stripped_overlay, [('output_image', 'overlay_file')]),
(asw, t1_stripped_overlay, [('outputnode.out_mask', 'in_file')]),
(t1_stripped_overlay, datasink, [('out_file', '@t1_stripped_overlay')]),
(t1_stripped_overlay, t1_stripped_overlay_ds, [('out_file', 'in_file')]),
(inu_n4, t1_stripped_overlay_ds, [('output_image', 'base_file')]),
(asw, t1_stripped_overlay_ds, [('outputnode.out_mask', 'overlay_file')]),
(inputnode, t1_stripped_overlay_ds, [('t1w', 'origin_file')]),

(t1_seg, seg_2_mni, [('tissue_class_map', 'input_image')]),
(t1_2_mni, seg_2_mni, [('forward_transforms', 'transforms'),
('forward_invert_flags', 'invert_transform_flags')]),

(seg_2_mni, t1_2_mni_overlay, [('output_image', 'in_file')]),
(t1_2_mni_overlay, datasink, [('out_file', '@t1_2_mni_overlay')]),
(seg_2_mni, t1_2_mni_overlay_ds, [('output_image', 'base_file')]),
(t1_2_mni_overlay, t1_2_mni_overlay_ds, [('out_file', 'in_file')]),
(inputnode, t1_2_mni_overlay_ds, [('t1w', 'origin_file')]),

(t1_seg, t1_seg_native, [('tissue_class_map', 'in_file')]),
(inu_n4, t1_seg_native, [('output_image', 'overlay_file')]),
(t1_seg_native, datasink, [('out_file', '@t1_seg_native')])
(t1_seg, t1_seg_native_ds, [('tissue_class_map', 'base_file')]),
(inu_n4, t1_seg_native_ds, [('output_image', 'overlay_file')]),
(inputnode, t1_seg_native_ds, [('t1w', 'origin_file')]),
(t1_seg_native, t1_seg_native_ds, [('out_file', 'in_file')])
])

# Write corrected file in the designated output dir
Expand Down
1 change: 1 addition & 0 deletions fmriprep/workflows/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def wf_ds054_type(subject_data, settings, name='fMRI_prep'):
(bidssrc, t1w_pre, [('t1w', 'inputnode.t1w')]),
(bidssrc, fmap_est, [('fmap', 'inputnode.input_images')]),
(bidssrc, sbref_pre, [('sbref', 'inputnode.sbref')]),
(bidssrc, sbref_t1, [('sbref', 'inputnode.sbref')]),
(fmap_est, sbref_pre, [('outputnode.fmap', 'inputnode.fmap'),
('outputnode.fmap_ref', 'inputnode.fmap_ref'),
('outputnode.fmap_mask', 'inputnode.fmap_mask')]),
Expand Down

0 comments on commit 78424b0

Please sign in to comment.