Commit 88e62f4c authored by William Clarke's avatar William Clarke
Browse files

Merge branch 'bf/win_script_support' into 'dev/windows_support'

Sort out path handling for WSL wrappers.

See merge request !51
parents 167a166d b51e7594
Pipeline #14553 passed with stages
in 16 minutes and 7 seconds
......@@ -5,6 +5,7 @@ This document contains the FSL-MRS release history in reverse chronological orde
- Updated setup script to allow command line scripts to run on MS Windows.
- Unde the hood changes to call all FSL cmd line scripts through fslpy runfsl method.
- Updated install instructions for Windows.
- Added the fsl_mrs_verify script which can be run to verify correct function of FSL-MRS.
1.1.12 (Wednesday 20th April)
-----------------------------
......
......@@ -58,6 +58,13 @@ Alternatively, as of V1.1.13 of FSL-MRS the python-only FSL-MRS package can be r
1. Enable WSL and install FSL into WSL as described in the `FSL install instructions <https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FslInstallation/Windows#Windows_Subsystem_for_Linux>`_.
2. Add an :code:`FSLDIR` enviroment variable on the host Windows machine. This should be set to :code:`\\wsl$\usr\local\fsl` assuming the default install directory for FSL on the WSL guest. In Powershell this can be done with the command :code:`$env:FSLDIR = "\\wsl$\usr\local\fsl"`.
2. Add an :code:`FSLDIR` enviroment variable on the host Windows machine. This should be set to :code:`\\\\wsl$\\usr\\local\\fsl` assuming the default install directory for FSL on the WSL guest. In Powershell this can be done with the command :code:`$env:FSLDIR = "\\\\wsl$\\usr\\local\\fsl"` to set it for a single session or :code:`[System.Environment]::SetEnvironmentVariable("FSLDIR", "\\wsl$\usr\local\fsl", [System.EnvironmentVariableTarget]::User)` to set it permanently.
3. Install FSL-MRS on the native Windows machine by following the conda installation guide in Option 1.
\ No newline at end of file
3. Install FSL-MRS on the native Windows machine by following the conda installation guide in Option 1.
For FSL-MRS to access the FSL scripts installed on the WSL machine, it must be running.
Verifying the installation
~~~~~~~~~~~~~~~~~~~~~~~~~~
Please run the packaged :code:`fsl_mrs_verify` script to confirm that installation has successfully completed.
\ No newline at end of file
......@@ -10,8 +10,7 @@
# Quick imports
import argparse
import os.path as op
from os import remove
from pathlib import Path
from fsl.wrappers import fsl_anat
from fsl.wrappers.fnirt import applywarp
import numpy as np
......@@ -27,21 +26,30 @@ def main():
parser.add_argument('mrsi', type=str, metavar='MRSI',
help='MRSI nifti file')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-t', '--t1', type=str, metavar='T1',
group.add_argument('-t', '--t1', type=Path, metavar='T1',
help='T1 nifti file')
group.add_argument('-a', '--anat', type=str,
group.add_argument('-a', '--anat', type=Path,
help='fsl_anat output directory.')
parser.add_argument('-o', '--output', type=str,
parser.add_argument('-o', '--output', type=Path,
help='Output directory', default='.')
parser.add_argument('-f', '--filename', type=str,
help='Output file name', default='mrsi_seg')
args = parser.parse_args()
# For windows implementations we must supply absolute
# paths. This enables conversion to wsl paths.
# The fslpy wrapper code requires a string rather than pathlib Path.
def str_resolve_path(pathlib_path):
return str(pathlib_path.resolve())
# If not prevented run fsl_anat for fast segmentation
if (args.anat is None) and (not args.mask_only):
anat = op.join(args.output, 'fsl_anat')
fsl_anat(args.t1, out=anat, nosubcortseg=True)
anat += '.anat'
anat = args.output / 'fsl_anat'
fsl_anat(
str_resolve_path(args.t1),
out=str_resolve_path(anat),
nosubcortseg=True)
anat = anat.with_suffix('.anat')
else:
anat = args.anat
......@@ -49,27 +57,24 @@ def main():
mrsi_in = Image(args.mrsi)
tmp_img = np.zeros(mrsi_in.shape[0:3])
tmp_img = Image(tmp_img, xform=mrsi_in.voxToWorldMat)
tmp_img.save(op.join(args.output, 'tmp.nii.gz'))
# Register the pvseg to the MRSI data using flirt
def applywarp_func(i, o):
applywarp(i,
op.join(args.output, 'tmp.nii.gz'),
o,
applywarp(str_resolve_path(i),
tmp_img,
str_resolve_path(o),
usesqform=True,
super=True,
superlevel='a')
# T1_fast_pve_0, T1_fast_pve_1, T1_fast_pve_2
# partial volume segmentations (CSF, GM, WM respectively)
applywarp_func(op.join(anat, 'T1_fast_pve_0.nii.gz'),
op.join(args.output, args.filename + '_csf.nii.gz'))
applywarp_func(op.join(anat, 'T1_fast_pve_1.nii.gz'),
op.join(args.output, args.filename + '_gm.nii.gz'))
applywarp_func(op.join(anat, 'T1_fast_pve_2.nii.gz'),
op.join(args.output, args.filename + '_wm.nii.gz'))
remove(op.join(args.output, 'tmp.nii.gz'))
applywarp_func(anat / 'T1_fast_pve_0.nii.gz',
args.output / (args.filename + '_csf.nii.gz'))
applywarp_func(anat / 'T1_fast_pve_1.nii.gz',
args.output / (args.filename + '_gm.nii.gz'))
applywarp_func(anat / 'T1_fast_pve_2.nii.gz',
args.output / (args.filename + '_wm.nii.gz'))
if __name__ == '__main__':
......
......@@ -11,8 +11,7 @@
# Quick imports
import argparse
import os.path as op
from os import remove
from pathlib import Path
import nibabel as nib
import numpy as np
from fsl.wrappers import flirt, fslstats, fsl_anat, fslmaths
......@@ -27,15 +26,15 @@ def main():
" - Construct mask in T1 space of an SVS voxel"
" and generate a tissue segmentation file.")
parser.add_argument('svs', type=str, metavar='SVS',
parser.add_argument('svs', type=Path, metavar='SVS',
help='SVS nifti file')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-t', '--t1', type=str, metavar='T1',
group.add_argument('-t', '--t1', type=Path, metavar='T1',
help='T1 nifti file')
group.add_argument('-a', '--anat', type=str,
group.add_argument('-a', '--anat', type=Path,
help='fsl_anat output directory.')
parser.add_argument('-o', '--output', type=str,
help='Output directory', default='.')
parser.add_argument('-o', '--output', type=Path,
help='Output directory', default=Path.cwd())
parser.add_argument('-f', '--filename', type=str,
help='file name stem. _mask.nii.gz'
' or _segmentation.json will be added.',
......@@ -43,15 +42,22 @@ def main():
parser.add_argument('-m', '--mask_only', action="store_true",
help='Only perform masking stage,'
' do not run fsl_anat if only T1 passed.')
parser.add_argument('--no_clean', action="store_false",
help="Don't clean intermediate output.", dest='clean')
args = parser.parse_args()
# For windows implementations we must supply absolute
# paths. This enables conversion to wsl paths.
# The fslpy wrapper code requires a string rather than pathlib Path.
def str_resolve_path(pathlib_path):
return str(pathlib_path.resolve())
# If not prevented run fsl_anat for fast segmentation
if (args.anat is None) and (not args.mask_only):
anat = op.join(args.output, 'fsl_anat')
fsl_anat(args.t1, out=anat, nosubcortseg=True)
anat += '.anat'
anat = args.output / 'fsl_anat'
fsl_anat(
str_resolve_path(args.t1),
out=str_resolve_path(anat),
nosubcortseg=True)
anat = anat.with_suffix('.anat')
else:
anat = args.anat
......@@ -60,24 +66,22 @@ def main():
# Create 3D mock data
mockData = np.zeros((2, 2, 2))
mockData[0, 0, 0] = 1.0
img = nib.Nifti1Image(mockData, affine=data_hdr.affine)
flirt_in = op.join(args.output, 'tmp_mask.nii')
nib.save(img, flirt_in)
tmp = nib.Nifti1Image(mockData, affine=data_hdr.affine)
# Run flirt
if anat is not None:
flirt_ref = op.join(anat, 'T1_biascorr.nii.gz')
flirt_ref = anat / 'T1_biascorr.nii.gz'
else:
flirt_ref = args.t1
flirt_ref = str_resolve_path(flirt_ref)
if args.filename is None:
mask_name = 'mask.nii.gz'
else:
mask_name = args.filename + '_mask.nii.gz'
flirt_out = op.join(args.output, mask_name)
flirt_out = str_resolve_path(args.output / mask_name)
flirt(flirt_in,
flirt(tmp,
flirt_ref,
out=flirt_out,
usesqform=True,
......@@ -87,29 +91,24 @@ def main():
setbackground=0,
paddingsize=1)
# Clean up
if args.clean:
remove(flirt_in)
# Provide tissue segmentation if anat is available
if anat is not None:
# Check that the svs mask intersects with brain, issue warning if not.
fslmaths(flirt_out).add(
op.join(anat, 'T1_biascorr_brain_mask.nii.gz')) \
.mas(flirt_out).run(op.join(args.output, 'tmp.nii.gz'))
mask_path = str_resolve_path(anat / 'T1_biascorr_brain_mask.nii.gz')
tmp_out = fslmaths(flirt_out)\
.add(mask_path)\
.mas(flirt_out)\
.run()
meanInVox = fslstats(op.join(args.output, 'tmp.nii.gz')).M.run()
meanInVox = fslstats(tmp_out).M.run()
if meanInVox < 2.0:
warnings.warn('The mask does not fully intersect'
' with the brain mask. Check manually.')
if args.clean:
remove(op.join(args.output, 'tmp.nii.gz'))
# Count up segmentation values in mask.
seg_csf = op.join(anat, 'T1_fast_pve_0.nii.gz')
seg_gm = op.join(anat, 'T1_fast_pve_1.nii.gz')
seg_wm = op.join(anat, 'T1_fast_pve_2.nii.gz')
seg_csf = str_resolve_path(anat / 'T1_fast_pve_0.nii.gz')
seg_gm = str_resolve_path(anat / 'T1_fast_pve_1.nii.gz')
seg_wm = str_resolve_path(anat / 'T1_fast_pve_2.nii.gz')
# fslstats -t /fast_output/fast_output_pve_0 -k SVS_mask.nii –m
CSF = fslstats(seg_csf).k(flirt_out).m.run()
......@@ -123,7 +122,7 @@ def main():
else:
seg_name = args.filename + '_segmentation.json'
with open(op.join(args.output, seg_name), 'w', encoding='utf-8') as f:
with open(args.output / seg_name, 'w', encoding='utf-8') as f:
json.dump(segresults, f, ensure_ascii=False, indent='\t')
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment