예제 #1
0
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)
예제 #2
0
    def __loadAtlas(self):
        """Calls the :func:`loadAtlas` function. """

        if len(atlases.listAtlases()) == 0:
            atlases.rescanAtlases()

        loadAtlas(self.__frame)
예제 #3
0
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
예제 #4
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
    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())
예제 #8
0
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)
예제 #9
0
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
예제 #10
0
def setup_module():
    if os.environ.get('FSLDIR', None) is None:
        raise Exception('FSLDIR is not set - atlas tests cannot be run')
    fslatlases.rescanAtlases()
예제 #11
0
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()
예제 #12
0
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
예제 #13
0
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)