Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: interface for whitestripe normalization #3351

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion docker/generate_dockerfiles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function generate_base_dockerfile() {
--install afni ants apt-utils bzip2 convert3d file fsl-core \
fsl-mni152-templates fusefat g++ git graphviz make python ruby \
unzip xvfb git-annex-standalone liblzma-dev \
gfortran libreadline-dev libx11-dev libxt-dev libpng-dev libjpeg-dev libcairo2-dev libssl-dev libxml2-dev libudunits2-dev libgdal-dev libbz2-dev libzstd-dev liblzma-dev libpcre2-dev \
--add-to-entrypoint "source /etc/fsl/fsl.sh && source /etc/afni/afni.sh" \
--env ANTSPATH='/usr/lib/ants' \
PATH='/usr/lib/ants:$PATH' \
Expand All @@ -87,7 +88,15 @@ function generate_main_dockerfile() {
--label maintainer="The nipype developers https://github.com/nipy/nipype" \
--env MKL_NUM_THREADS=1 \
OMP_NUM_THREADS=1 \
--arg PYTHON_VERSION_MAJOR=3 PYTHON_VERSION_MINOR=8 BUILD_DATE VCS_REF VERSION \
--arg PYTHON_VERSION_MAJOR=3 PYTHON_VERSION_MINOR=8 R_VERSION_MAJOR=4 R_VERSION_MINOR=1 R_VERSION_PATCH=0 R_CONFIGURE_OPTS=CONFIGURE_OPTIONS="--with-cairo --with-jpeglib --enable-R-shlib --with-blas --with-lapack" BUILD_DATE VCS_REF VERSION \
--run 'curl -LO https://cran.rstudio.com/src/base/R-${R_VERSION_MAJOR}/R-${R_VERSION_MAJOR}.${R_VERSION_MINOR}.${R_VERSION_PATCH}.tar.gz
&& tar zxvf R-${R_VERSION_MAJOR}.${R_VERSION_MINOR}.${R_VERSION_PATCH}.tar.gz
&& rm R-${R_VERSION_MAJOR}.${R_VERSION_MINOR}.${R_VERSION_PATCH}.tar.gz
&& cd R-${R_VERSION_MAJOR}.${R_VERSION_MINOR}.${R_VERSION_PATCH}
&& ./configure ${CONFIGURE_OPTIONS}
&& make && make install && cd .. && rm -rf R-${R_VERSION_MAJOR}.${R_VERSION_MINOR}.${R_VERSION_PATCH}
&& echo '"'"'options(repos = c(CRAN = "https://cran.rstudio.com/"), download.file.method = "libcurl")'"'"' >> /usr/local/lib/R/etc/Rprofile.site
&& Rscript -e "source('"'"'https://neuroconductor.org/neurocLite.R'"'"'); neuro_install(c('"'"'WhiteStripe'"'"'));"' \
--user neuro \
--workdir /home/neuro \
--miniconda create_env=neuro \
Expand Down
17 changes: 11 additions & 6 deletions nipype/interfaces/r.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,18 @@ def _format_arg(self, name, trait_spec, value):
def _gen_r_command(self, argstr, script_lines):
"""Generates commands and, if rfile specified, writes it to disk."""
if not self.inputs.rfile:
# replace newlines with ;, strip comments
# replace newlines with ;
script = "; ".join(
[
line
for line in script_lines.split("\n")
if not line.strip().startswith("#")
]
list(
filter(
None, # drop empty lines
[
line
for line in script_lines.split("\n")
if not line.strip().startswith("#") # strip comments
],
)
)
)
# escape " and $
script = script.replace('"', '\\"')
Expand Down
39 changes: 39 additions & 0 deletions nipype/interfaces/tests/test_auto_WhiteStripe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT
from ..whitestripe import WhiteStripe


def test_WhiteStripe_inputs():
input_map = dict(
img_type=dict(
mandatory=False,
),
in_file=dict(
extensions=None,
mandatory=True,
),
indices=dict(
mandatory=False,
),
out_file=dict(
extensions=None,
usedefault=True,
),
)
inputs = WhiteStripe.input_spec()

for key, metadata in list(input_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(inputs.traits()[key], metakey) == value


def test_WhiteStripe_outputs():
output_map = dict(
out_file=dict(
extensions=None,
),
)
outputs = WhiteStripe.output_spec()

for key, metadata in list(output_map.items()):
for metakey, value in list(metadata.items()):
assert getattr(outputs.traits()[key], metakey) == value
47 changes: 47 additions & 0 deletions nipype/interfaces/tests/test_whitestripe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
import os

import pytest
import requests
from pathlib import Path
from string import Template
from nipype.interfaces import whitestripe
from nipype.interfaces.r import get_r_command


def test_whitestripe(tmpdir):
cwd = tmpdir.chdir()

Path("T1W.nii.gz").touch()

normalizer = whitestripe.WhiteStripe()
normalizer.inputs.img_type = "T1"
normalizer.inputs.in_file = "T1W.nii.gz"
normalizer.inputs.out_file = "T1W_ws.nii.gz"
tmpfile, script = normalizer._cmdline(normalizer)

expected_script = Template(
# the level of indentation needs to match what's in the whitestripe interface
"""
library(neurobase)
library(WhiteStripe)
in_file = readnii('$in_file')
ind = whitestripe(in_file, "$img_type")$$whitestripe.ind
norm = whitestripe_norm(in_file, ind)
out_file = '$out_file'
writenii(norm, out_file)
"""
).substitute(
{
"in_file": normalizer.inputs.in_file,
"out_file": normalizer.inputs.out_file,
"img_type": normalizer.inputs.img_type,
}
)
assert tmpfile is False
assert script == expected_script
os.remove(normalizer.inputs.in_file)

cwd.chdir()
130 changes: 130 additions & 0 deletions nipype/interfaces/whitestripe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
from nipype.interfaces.r import RCommand
from nipype.interfaces.base import (
TraitedSpec,
BaseInterface,
BaseInterfaceInputSpec,
File,
traits,
)
import os
import tempfile
from string import Template


class WhiteStripeInputSpec(BaseInterfaceInputSpec):
in_file = File(exists=True, mandatory=True)
out_file = File("out.nii.gz", usedefault=True)
indices = traits.Array(desc="WhiteStripe indices", mandatory=False)
img_type = traits.String(
desc="WhiteStripe image type", mandatory=False, default="T1"
)


class WhiteStripeOutputSpec(TraitedSpec):
out_file = File(exists=True)


class WhiteStripe(BaseInterface):
input_spec = WhiteStripeInputSpec
output_spec = WhiteStripeOutputSpec

def _run_interface(self, runtime):
tmpfile, script = self._cmdline(runtime)

# rfile = True will create a .R file with your script and executed.
# Alternatively
# rfile can be set to False which will cause the R code to be
# passed
# as a commandline argument to the R executable
# (without creating any files).
# This, however, is less reliable and harder to debug
# (code will be reduced to
# a single line and stripped of any comments).
rcmd = RCommand(script=script, rfile=False)
result = rcmd.run()
if tmpfile:
os.remove(tmpfile)
return result.runtime

def _list_outputs(self):
outputs = self._outputs().get()
outputs["out_file"] = os.path.abspath(self.inputs.out_file)
return outputs

def _cmdline(self, runtime):
d = dict(
in_file=self.inputs.in_file,
out_file=self.inputs.out_file,
img_type=self.inputs.img_type,
)
if len(self.inputs.indices) == 0:
tmpfile = False
script = Template(
"""
library(neurobase)
library(WhiteStripe)
in_file = readnii('$in_file')
ind = whitestripe(in_file, "$img_type")$$whitestripe.ind
norm = whitestripe_norm(in_file, ind)
out_file = '$out_file'
writenii(norm, out_file)
"""
).substitute(d)
else:
# d['indices'] = ",".join(map(str, self.inputs.indices))
tmpfile = tempfile.mkstemp()[1]
self._write_indices(tmpfile, self.inputs.indices)
d["indices"] = tmpfile
script = Template(
"""
library(neurobase)
library(WhiteStripe)
in_file = readnii('$in_file')
# ind = c($indices)
ind = as.vector(read.table("$indices")[[1]], mode='numeric')
norm = whitestripe_norm(in_file, ind)
out_file = '$out_file'
writenii(norm, out_file)
"""
).substitute(d)

return tmpfile, script

def gen_indices(self):
path = tempfile.mkstemp()[1]
d = dict(
in_file=self.inputs.in_file, out_file=path, img_type=self.inputs.img_type
)
script = Template(
"""
library(neurobase)
library(WhiteStripe)
in_file = readnii('$in_file')
t1_ind = whitestripe(in_file, "$img_type")$$whitestripe.ind
write.table(t1_ind, file = "$out_file", row.names = F, col.names = F)
"""
).substitute(d)
RCommand(script=script, rfile=False).run()
ret = self._read_indices(path)
os.remove(path)
return ret

def _read_indices(self, fn):
with open(fn) as f:
# read lines as ints
return list(map(int, f))

def _write_indices(self, fn, indices):
with open(fn, "w") as f:
for idx in indices:
f.write("{}\n".format(idx))


if __name__ == "__main__":
w = WhiteStripe()
w.inputs.img_type = "T1"
w.inputs.in_file = "T1W.nii.gz"
# w.inputs.indices = [1,2,3]
w.inputs.indices = w.gen_indices()
w.inputs.out_file = "T1W_ws.nii.gz"
w.run()