def _test_query(tests): fslatlases.rescanAtlases() capture = CaptureStdout() print() for atlas, use_label, q_type, q_in, res, o_type in tests: with tempdir() as td: if q_type in ('voxel', 'coordinate'): genfunc = _gen_coord_voxel_query evalfunc = _eval_coord_voxel_query else: genfunc = _gen_mask_query evalfunc = _eval_mask_query print('Test: {} {}mm label={} type={} in={} type={}'.format( atlas, res, use_label, q_type, q_in, o_type)) query = genfunc(atlas, use_label, q_type, q_in, res) cmd = _build_command_line(atlas, query, use_label, q_type, res, o_type) print('fslatlasq {}'.format(' '.join(cmd))) capture.reset() with capture: fslatlasq.main(cmd) evalfunc(capture.stdout, atlas, query, use_label, q_type, q_in, res, o_type)
def __loadAtlas(self): """Calls the :func:`loadAtlas` function. """ if len(atlases.listAtlases()) == 0: atlases.rescanAtlases() loadAtlas(self.__frame)
def main(args=None): """Entry point for ``atlasq``. Parses arguments, and runs the requested command. """ if args is None: args = sys.argv[1:] # Parse command line arguments namespace = parseArgs(args) # Initialise the atlas library fslatlases.rescanAtlases() # Run the command try: if namespace.command == 'list': listAtlases(namespace) elif namespace.command == 'query': queryAtlas(namespace) elif namespace.command == 'summary': summariseAtlas(namespace) elif namespace.command == 'ohi': ohi(namespace) except (IdentifyError, fslatlases.MaskError) as e: print(str(e)) return 1 return 0
def get_ventricular_csf_mask(fslanatdir, interpolation=3): """ Get a ventricular mask in T1 space. Register the ventricles mask from the harvardoxford-subcortical 2mm atlas to T1 space, using the T1_to_MNI_nonlin_coeff.nii.gz in the provided fsl_anat directory. Parameters ---------- fslanatdir: pathlib.Path Path to an fsl_anat output directory. interpolation: int Order of interpolation to use when performing registration. Default is 3. """ # get ventricles mask from Harv-Ox atlases.rescanAtlases() harvox = atlases.loadAtlas('harvardoxford-subcortical', resolution=2.0) vent_img = Image( harvox.data[:,:,:,2] + harvox.data[:,:,:,13], header=harvox.header ) vent_img = fslmaths(vent_img).thr(0.1).bin().ero().run(LOAD) # apply inv(T1->MNI) registration t1_brain = (fslanatdir/"T1_biascorr_brain.nii.gz").resolve(strict=True) struct2mni = (fslanatdir/"T1_to_MNI_nonlin_coeff.nii.gz").resolve(strict=True) mni2struct = invwarp(str(struct2mni), str(t1_brain), LOAD)['out'] vent_t1_img = applywarp(vent_img, str(t1_brain), LOAD, warp=mni2struct)['out'] vent_t1_img = fslmaths(vent_t1_img).thr(0.9).bin().run(LOAD) return vent_t1_img
def test_summary(): fslatlases.rescanAtlases() adescs = fslatlases.listAtlases() capture = CaptureStdout() for desc in adescs: tests = [desc.atlasID, desc.name] for test in tests: capture.reset() with capture: fslatlasq.main(['summary', test]) stdout = capture.stdout assert desc.atlasID in stdout assert desc.name in stdout assert desc.specPath in stdout assert desc.atlasType in stdout for image in it.chain(desc.images, desc.summaryImages): assert image in stdout for label in desc.labels: assert label.name in stdout
def test_list(): fslatlases.rescanAtlases() adescs = fslatlases.listAtlases() capture = CaptureStdout() tests = ['list', 'list --extended'] extendeds = [False, True] for test, extended in zip(tests, extendeds): capture.reset() with capture: fslatlasq.main(test.split()) stdout = capture.stdout for desc in adescs: assert desc.atlasID in stdout assert desc.name in stdout assert (desc.specPath in stdout) == extended for image in it.chain(desc.images, desc.summaryImages): assert (image in stdout) == extended assert (image in stdout) == extended
def __init__(self, parent, overlayList, displayCtx, frame): """Create an ``AtlasPanel``. :arg parent: The :mod:`wx` parent object. :arg overlayList: The :class:`.OverlayList` instance. :arg displayCtx: The :class:`.DisplayContext` instance. :arg frame: The :class:`.FSLeyesFrame` instance. """ ctrlpanel.ControlPanel.__init__(self, parent, overlayList, displayCtx, frame) # Make sure the atlas # registry is up to date atlases.rescanAtlases() # See the enableAtlasPanel method # for info about this attribute. self.__atlasPanelEnableStack = 0 # Cache of loaded atlases # and enabled atlas overlays. self.__enabledOverlays = {} self.__loadedAtlases = {} self.__notebook = notebook.Notebook(self) self.__sizer = wx.BoxSizer(wx.HORIZONTAL) self.__sizer.Add(self.__notebook, flag=wx.EXPAND, proportion=1) self.SetSizer(self.__sizer) self.__infoPanel = atlasinfopanel.AtlasInfoPanel( self.__notebook, overlayList, displayCtx, frame, self) # Overlay panel, containing a list of regions, # allowing the user to add/remove overlays self.__overlayPanel = atlasoverlaypanel.AtlasOverlayPanel( self.__notebook, overlayList, displayCtx, frame, self) self.__managePanel = atlasmanagementpanel.AtlasManagementPanel( self.__notebook, overlayList, displayCtx, frame, self) self.__notebook.AddPage(self.__infoPanel, strings.titles[self.__infoPanel]) self.__notebook.AddPage(self.__overlayPanel, strings.titles[self.__overlayPanel]) self.__notebook.AddPage(self.__managePanel, strings.titles[self.__managePanel]) self.overlayList.addListener('overlays', self.name, self.__overlayListChanged) self.Layout() self.SetMinSize(self.__sizer.GetMinSize())
def Atlas(name): """ Reads in the atlas from the FSL standard atlases :param name: name of the atlas :return: fsl.data.atlases.Atlas representation of an FSL atlas """ atlases.rescanAtlases() if not atlases.hasAtlas(name): atlas_names = tuple(desc.atlasID for desc in atlases.listAtlases()) raise argparse.ArgumentTypeError('Requested atlas %r not one of: %r' % (name, atlas_names)) return atlases.loadAtlas(name)
def test_bad_mask(seed): fslatlases.rescanAtlases() capture = CaptureStdout() with tempdir() as td: for atlasID, use_label in it.product(atlases, use_labels): atlas = fslatlases.loadAtlas(atlasID, loadSummary=use_label, indexed=True, loadData=False, calcRange=False) ashape = list(atlas.shape[:3]) wrongdims = fslimage.Image( np.array(np.random.random(list(ashape) + [2]), dtype=np.float32)) wrongspace = fslimage.Image(np.random.random((20, 20, 20)), xform=transform.concat( atlas.voxToWorldMat, np.diag([2, 2, 2, 1]))) print(wrongdims.shape) print(wrongspace.shape) wrongdims.save('wrongdims.nii.gz') wrongspace.save('wrongspace.nii.gz') cmd = ['query', atlasID, '-m', 'wrongdims'] expected = 'Mask has wrong number of dimensions' capture.reset() with capture: assert fslatlasq.main(cmd) != 0 assert capture.stdout.strip() == expected cmd = ['query', atlasID, '-m', 'wrongspace'] expected = 'Mask is not in the same space as atlas' capture.reset() with capture: assert fslatlasq.main(cmd) != 0 assert capture.stdout.strip() == expected
def setup_module(): if os.environ.get('FSLDIR', None) is None: raise Exception('FSLDIR is not set - atlas tests cannot be run') fslatlases.rescanAtlases()
def test_coords(seed): """Test the ohi -a "atlas" -c "coords" mode. """ def expectedProbOutput(atlas, coords): probs = atlas.values(coords) expected = '<b>{}</b><br>'.format(atlas.desc.name) nzprobs = [] for i, p in enumerate(probs): if p > 0: label = atlas.desc.labels[i].name nzprobs.append((p, label)) if len(nzprobs) > 0: nzprobs = reversed(sorted(nzprobs, key=lambda b: b[0])) nzprobs = [ '{:d}% {}'.format(int(round(p)), l) for (p, l) in nzprobs ] expected += ', '.join(nzprobs) else: expected += 'No label found!' return expected def expectedLabelOutput(atlas, coords): label = atlas.label(coords) expected = '<b>{}</b><br>'.format(atlas.desc.name) if label is None: return expected + 'Unclassified' else: return expected + atlas.desc.find(value=int(label)).name capture = CaptureStdout() # random coordinates in MNI152 space, # with some coordinates out of bounds ncoords = 50 xc = -100 + 190 * np.random.random(ncoords) yc = -130 + 220 * np.random.random(ncoords) zc = -80 + 120 * np.random.random(ncoords) coords = np.vstack((xc, yc, zc)).T fslatlases.rescanAtlases() atlases = fslatlases.listAtlases() for ad in atlases: # atlasquery/ohi always uses 2mm resolution atlas = fslatlases.loadAtlas(ad.atlasID, resolution=2) print(ad.name) for x, y, z in coords: cmd = 'ohi -a "{}" -c "{},{},{}"'.format(ad.name, x, y, z) capture.reset() with capture: fslatlasq.main(shlex.split(cmd)) if isinstance(atlas, fslatlases.ProbabilisticAtlas): expected = expectedProbOutput(atlas, (x, y, z)) # LabelAtlas else: expected = expectedLabelOutput(atlas, (x, y, z)) assert capture.stdout.strip() == expected.strip()
def test_mask(seed): """Test the ohi -a "atlas" -m "mask" mode, with label and probabilistic atlases. """ def expectedLabelOutput(mask, atlas): labels, props = atlas.maskLabel(mask) exp = [] for lbl, prop in zip(labels, props): name = desc.find(value=int(lbl)).name exp.append('{}:{:0.4f}'.format(name, prop)) return '\n'.join(exp) def expectedProbOutput(mask, atlas): props = atlas.maskValues(mask) labels = [l.index for l in atlas.desc.labels] exp = [] for lbl, prop in zip(labels, props): if prop > 0: exp.append('{}:{:0.4f}'.format(desc.labels[int(lbl)].name, prop)) return '\n'.join(exp) fslatlases.rescanAtlases() capture = CaptureStdout() atlases = fslatlases.listAtlases() with tempdir() as td: maskfile = op.join(td, 'mask.nii') for desc in atlases: # atlasquery always uses 2mm # resolution versions of atlases atlas2mm = fslatlases.loadAtlas(desc.atlasID, resolution=2) # Test with 1mm and 2mm masks for res in [1, 2]: atlasimg = fslatlases.loadAtlas(desc.atlasID, resolution=res) maskimg = make_random_mask(maskfile, atlasimg.shape[:3], atlasimg.voxToWorldMat) cmd = 'ohi -a "{}" -m {}'.format(desc.name, maskfile) print(cmd) capture.reset() with capture: fslatlasq.main(shlex.split(cmd)) if isinstance(atlasimg, fslatlases.LabelAtlas): expected = expectedLabelOutput(maskimg, atlas2mm) elif isinstance(atlasimg, fslatlases.ProbabilisticAtlas): expected = expectedProbOutput(maskimg, atlas2mm) assert capture.stdout.strip() == expected
def setup_mtestimation(subject_dir, rois=[ 'wm', ]): """ Perform the initial processing needed for estimation of the MT Effect. This includes: - Creating sub-directories for storing the results - Finding T1 and mbPCASL directories and scans - Split mbPCASL sequence into its constituent components - Run fsl_anat - Create a json to keep track of important files and directories """ # get subject name subject_name = subject_dir.parts[-1] print(subject_name) # create results directories asl_dir = subject_dir / 'ASL' calib0_dir = asl_dir / 'Calib/Calib0' calib1_dir = asl_dir / 'Calib/Calib1' create_dirs([asl_dir, calib0_dir, calib1_dir]) # obtain calibration images from ASL sequence mbpcasl_dir = list( (subject_dir / f'{subject_name}_V1_B/scans').glob('**/*mbPCASLhr'))[0] mbpcasl = mbpcasl_dir / f'resources/NIFTI/files/{subject_name}_V1_B_mbPCASLhr_PA.nii.gz' calib0_name = calib0_dir / 'calib0.nii.gz' calib1_name = calib1_dir / 'calib1.nii.gz' fslroi(str(mbpcasl), calib0_name, 88, 1) fslroi(str(mbpcasl), calib1_name, 89, 1) # initialise dict json_name = asl_dir / 'ASL.json' important_dict = { "calib_dir": str(calib0_dir.parent), "calib0_dir": str(calib0_dir), "calib1_dir": str(calib1_dir), "calib0_img": str(calib0_name), "calib1_img": str(calib1_dir), "json_name": str(json_name) } # structural directory t1_dir = list( (subject_dir / f'{subject_name}_V1_A/scans').glob('**/*T1w'))[0] struc_dir = t1_dir / 'resources/NIFTI/files' struc_name = list(struc_dir.glob(f'**/{subject_name}_*.nii.gz'))[0] fsl_anat_dir = struc_dir / 'ASL/struc' calib0struct_dir = struc_dir / 'ASL/Calib/Calib0' calib1struct_dir = struc_dir / 'ASL/Calib/Calib1' create_dirs([calib0struct_dir, calib1struct_dir]) # run fsl_anat fsl_anat(str(struc_name), str(fsl_anat_dir), clobber=True, nosubcortseg=True) fsl_anat_dir = fsl_anat_dir.parent / f'{fsl_anat_dir.stem}.anat' t1_name = fsl_anat_dir / 'T1_biascorr.nii.gz' t1_brain_name = fsl_anat_dir / 'T1_biascorr_brain.nii.gz' # get ventricles mask if csf if 'csf' in rois: # initialise atlas list atlases.rescanAtlases() harv_ox_prob_2mm = atlases.loadAtlas('harvardoxford-subcortical', resolution=2.0) vent_img = Image(harv_ox_prob_2mm.data[:, :, :, 2] + harv_ox_prob_2mm.data[:, :, :, 13], header=harv_ox_prob_2mm.header) vent_img = fslmaths(vent_img).thr(0.1).bin().ero().run(LOAD) # we already have registration from T1 to MNI struc2mni_warp = fsl_anat_dir / 'MNI_to_T1_nonlin_field.nii.gz' # apply warp to ventricles image vent_t1_name = fsl_anat_dir / 'ventricles_mask.nii.gz' applywarp(vent_img, str(t1_brain_name), str(vent_t1_name), warp=str(struc2mni_warp)) # re-threshold vent_t1 = fslmaths(str(vent_t1_name)).thr( PVE_THRESHOLDS['csf']).bin().run(LOAD) # mask pve estimate by ventricles mask fslmaths(str(fsl_anat_dir / PVE_NAMES['csf'])).mas(vent_t1).run( str(vent_t1_name)) # bias-field correction for calib_name in (calib0_name, calib1_name): calib_name_stem = calib_name.stem.split('.')[0] # run bet betted_m0 = bet(str(calib_name), LOAD) # create directories to save results fast_dir = calib_name.parent / 'FAST' biascorr_dir = calib_name.parent / 'BiasCorr' create_dirs([fast_dir, biascorr_dir]) # run FAST on brain-extracted m0 image fast_base = fast_dir / calib_name_stem fast(betted_m0['output'], out=str(fast_base), type=3, b=True, nopve=True) bias_name = fast_dir / f'{calib_name_stem}_bias.nii.gz' # apply bias field to original m0 image (i.e. not BETted) biascorr_name = biascorr_dir / f'{calib_name_stem}_restore.nii.gz' fslmaths(str(calib_name)).div(str(bias_name)).run(str(biascorr_name)) # obtain registration from structural to calibration image mask_dir = biascorr_name.parent / 'masks' create_dirs([ mask_dir, ]) cmd = [ 'asl_reg', f'-i {biascorr_name}', f'-s {t1_name}', f'--sbet {t1_brain_name}', f'-o {mask_dir}' ] subprocess.run(" ".join(cmd), shell=True) # apply transformation to the pve map for tissue in rois: roi_dir = mask_dir / tissue create_dirs([ roi_dir, ]) if tissue == 'combined': # check that gm and wm pves already exist gm_pve = mask_dir / 'gm/pve_gm.nii.gz' wm_pve = mask_dir / 'wm/pve_wm.nii.gz' if not (gm_pve.exists() and wm_pve.exists()): raise Exception("WM and GM PVEs don't exist.") gm_mask_name = roi_dir / 'gm_mask.nii.gz' wm_mask_name = roi_dir / 'wm_mask.nii.gz' fslmaths(str(gm_pve)).thr(PVE_THRESHOLDS[tissue]).bin().run( str(gm_mask_name)) fslmaths(str(wm_pve)).thr(PVE_THRESHOLDS[tissue]).bin().run( str(wm_mask_name)) gm_masked_name = roi_dir / f'{calib_name_stem}_gm_masked' wm_masked_name = roi_dir / f'{calib_name_stem}_wm_masked' fslmaths(str(biascorr_name)).mul(str(gm_mask_name)).run( str(gm_masked_name)) fslmaths(str(biascorr_name)).mul(str(wm_mask_name)).run( str(wm_masked_name)) # update dictionary new_dict = { f'{calib_name_stem}_{tissue}_masked': [str(gm_masked_name), str(wm_masked_name)] } else: if tissue == 'csf': pve_struct_name = vent_t1_name else: pve_struct_name = fsl_anat_dir / PVE_NAMES[tissue] pve_asl_name = roi_dir / f'pve_{tissue}.nii.gz' struct2asl_name = mask_dir / 'struct2asl.mat' applyxfm(str(pve_struct_name), str(biascorr_name), str(struct2asl_name), str(pve_asl_name)) # threshold and binarise the ASL-space pve map mask_name = roi_dir / f'{tissue}_mask.nii.gz' fslmaths(str(pve_asl_name)).thr( PVE_THRESHOLDS[tissue]).bin().run(str(mask_name)) # apply the mask to the calibration image masked_name = mask_dir / f'{tissue}_masked.nii.gz' fslmaths(str(biascorr_name)).mul(str(mask_name)).run( str(masked_name)) # update dictionary new_dict = { f'{calib_name_stem}_{tissue}_masked': str(masked_name) } important_dict.update(new_dict) # save json with open(json_name, 'w') as fp: json.dump(important_dict, fp, sort_keys=True, indent=4) return (subject_dir, 0)