diff --git a/docker/generate_dockerfiles.sh b/docker/generate_dockerfiles.sh index e8af4d0042..9b570c23f3 100755 --- a/docker/generate_dockerfiles.sh +++ b/docker/generate_dockerfiles.sh @@ -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' \ @@ -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 \ diff --git a/nipype/interfaces/r.py b/nipype/interfaces/r.py index a586de183c..e15a613f53 100644 --- a/nipype/interfaces/r.py +++ b/nipype/interfaces/r.py @@ -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('"', '\\"') diff --git a/nipype/interfaces/tests/test_auto_WhiteStripe.py b/nipype/interfaces/tests/test_auto_WhiteStripe.py new file mode 100644 index 0000000000..4eb473d4bc --- /dev/null +++ b/nipype/interfaces/tests/test_auto_WhiteStripe.py @@ -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 diff --git a/nipype/interfaces/tests/test_whitestripe.py b/nipype/interfaces/tests/test_whitestripe.py new file mode 100644 index 0000000000..ff109d79da --- /dev/null +++ b/nipype/interfaces/tests/test_whitestripe.py @@ -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() diff --git a/nipype/interfaces/whitestripe.py b/nipype/interfaces/whitestripe.py new file mode 100644 index 0000000000..c8b77e9610 --- /dev/null +++ b/nipype/interfaces/whitestripe.py @@ -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()