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 __loadAtlas(self): """Calls the :func:`loadAtlas` function. """ if len(atlases.listAtlases()) == 0: atlases.rescanAtlases() loadAtlas(self.__frame)
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 _random_atlas(atype, res, summary=False): if atype == 'prob': atype = 'probabilistic' atlases = fslatlases.listAtlases() atlases = [a for a in atlases if a.atlasType == atype] desc = atlases[np.random.randint(0, len(atlases))] return _get_atlas(desc.atlasID, res, summary)
def identifyAtlas(idOrName): """Given a partial atlas ID or name, tries to find an atlas which uniquely matches it. """ # TODO Use difflib or some fuzzy matching library? idOrName = idOrName.lower().strip() atlases = fslatlases.listAtlases() allNames = [a.name.lower() for a in atlases] allIDs = [a.atlasID.lower() for a in atlases] # First test for an exact match nameMatches = [idOrName == n for n in allNames] idMatches = [idOrName == i for i in allIDs] nameMatches = [i for i in range(len(nameMatches)) if nameMatches[i]] idMatches = [i for i in range(len(idMatches)) if idMatches[i]] if len(nameMatches) + len(idMatches) == 1: if len(nameMatches) == 1: return atlases[nameMatches[0]] else: return atlases[idMatches[0]] # If no exact match, test for a partial match nameMatches = [idOrName in n for n in allNames] idMatches = [idOrName in i for i in allIDs] nameMatches = [i for i in range(len(nameMatches)) if nameMatches[i]] idMatches = [i for i in range(len(idMatches)) if idMatches[i]] totalMatches = len(nameMatches) + len(idMatches) # No matches if totalMatches == 0: raise IdentifyError('Could not find any atlas ' 'matching {}'.format(idOrName)) # More than two matches, or a # different ID/name pair matched if totalMatches > 2 or (totalMatches == 2 and nameMatches != idMatches): possible = [allNames[m] for m in nameMatches] + \ [allIDs[ m] for m in idMatches] raise IdentifyError('{} matched multiple atlases! Could match one ' 'of: {}'.format(idOrName, ', '.join(possible))) # Either one exact match to an ID or name, # or a match to an equivalent ID/name if len(nameMatches) == 1: return atlases[nameMatches[0]] else: return atlases[idMatches[0]]
def test_dumpatlases(): """Test the ohi --dumpatlases option. """ capture = CaptureStdout() with capture: fslatlasq.main('ohi --dumpatlases'.split()) atlases = fslatlases.listAtlases() atlases = [a.name for a in atlases] atlases = sorted(atlases) assert capture.stdout.strip() == '\n'.join(atlases)
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 __buildAtlasList(self): """Clears and recreates the atlas list. Also clears all existing region lists. """ atlasDescs = atlases.listAtlases() # This method is called whenever any # atlases are added/removed. We want # to preserve any region lists for # atlases that are still in the atlas # registry. regionLists = dict(self.__regionLists) # If a region list is currently # being shown, clear it. regionList = self.__regionSizer.GetItem(1).GetWindow() if regionList is not None: regionList.Show(False) self.__regionSizer.Remove(1) self.__regionSizer.AddStretchSpacer() self.__regionLists = {} # Now clear and re-populate the atlas list self.__atlasList.Clear() for i, atlasDesc in enumerate(atlasDescs): self.__atlasList.Append(atlasDesc.name, atlasDesc) self.__updateAtlasState(atlasDesc) widget = OverlayListWidget(self.__atlasList, atlasDesc.atlasID, self.__atlasPanel, self) self.__atlasList.SetItemWidget(i, widget) # Restore references to region lists # for atlases that still exist for atlasID, regionList in regionLists.items(): if atlases.hasAtlas(atlasID): self.__regionLists[atlasID] = regionList else: regionList.Destroy() self.__regionSizer.Layout()
def __onRegionFilter(self, ev): """Called when the user enters some text in the region filter. Filters the region list (see the :meth:`.EditableListBox.ApplyFilter` method), and updates all items in the atlas list (see the :meth:`__updateAtlasState` method). """ filterStr = self.__regionFilter.GetValue().lower().strip() for atlasDesc in atlases.listAtlases(): self.__updateAtlasState(atlasDesc) listBox = self.__regionLists.get(atlasDesc.atlasID, None) if listBox is not None: listBox.ApplyFilter(filterStr, ignoreCase=True)
def test_bad_atlas(): """Test the ohi -a "atlas" ..., with a non-existent atlas. """ capture = CaptureStdout() atlases = fslatlases.listAtlases() atlases = sorted([a.name for a in atlases]) expected = ['Invalid atlas name. Try one of:'] + atlases expected = '\n'.join(expected) cmds = ['ohi -a "non-existent atlas" -c "0,0,0"', 'ohi -a "non-existent atlas" -m "nomask"'] for cmd in cmds: capture.reset() with capture: fslatlasq.main(shlex.split(cmd)) assert capture.stdout.strip() == expected
def __init__(self, parent, overlayList, displayCtx, frame, atlasPanel): """Create an ``AtlasManagementPanel``. :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. :arg atlasPanel: The :class:`.AtlasPanel` instance that has created this ``AtlasManagementPanel``. """ fslpanel.FSLeyesPanel.__init__( self, parent, overlayList, displayCtx, frame) descs = atlases.listAtlases() names = [d.name for d in descs] paths = [d.specPath for d in descs] self.__atlasList = elistbox.EditableListBox( self, labels=names, clientData=descs, tooltips=paths, style=(elistbox.ELB_NO_MOVE | elistbox.ELB_TOOLTIP_DOWN)) self.__sizer = wx.BoxSizer(wx.HORIZONTAL) self.__sizer.Add(self.__atlasList, flag=wx.EXPAND, proportion=1) self.SetSizer(self.__sizer) self.SetMinSize(self.__sizer.GetMinSize()) self.__atlasList.Bind(elistbox.EVT_ELB_ADD_EVENT, self.__onListAdd) self.__atlasList.Bind(elistbox.EVT_ELB_REMOVE_EVENT, self.__onListRemove) atlases.registry.register(self.name, self.__atlasAdded, 'add') atlases.registry.register(self.name, self.__atlasRemoved, 'remove')
def listAtlases(namespace): """List all available atlases. """ atlases = fslatlases.listAtlases() if namespace.extended: for a in atlases: print('{} [{}]'.format(a.name, a.atlasID)) print(' Spec: {}'.format(a.specPath)) print(' Type: {}'.format(a.atlasType)) print(' Labels: {}'.format(len(a.labels))) for i in a.images: print(' Image: {}'.format(i)) for i in a.summaryImages: print(' Summary image: {}'.format(i)) print() else: ids = [a.atlasID for a in atlases] names = [a.name for a in atlases] printColumns((ids, names), ('ID', 'Full name'))
def __buildAtlasList(self): """Clears and then builds the list of available atlases. The This is performed asynchronously, via the :func:`.idle.run` function, although the atlas list widget is updated on the ``wx`` idle loop. """ # This method gets called whenever atlases # are added to/from the list. # We want to preserve the 'enabled' state of # any atlases that are still present in the # atlas registry. enabledAtlases = dict(self.__enabledAtlases) self.__enabledAtlases = {} self.__atlasList.Clear() atlasDescs = atlases.listAtlases() for i, atlasDesc in enumerate(atlasDescs): atlasID = atlasDesc.atlasID self.__atlasList.Append(atlasDesc.name, atlasID) enabled = atlasID in enabledAtlases widget = AtlasListWidget(self.__atlasList, i, self, atlasID, enabled=enabled) self.__atlasList.SetItemWidget(i, widget) if enabled: self.__enabledAtlases[atlasID] = enabledAtlases[atlasID]
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 ohi(namespace): """Emulates the FSL ``atlasquery`` tool.""" atlasDesc = None def dumpatlases(): atlases = [a.name for a in fslatlases.listAtlases()] print('\n'.join(sorted(atlases))) if namespace.dumpatlases: dumpatlases() return for a in fslatlases.listAtlases(): if a.name == namespace.atlas: atlasDesc = a break if atlasDesc is None: print('Invalid atlas name. Try one of:') dumpatlases() return # Mask query. if namespace.ohiMask is not None: # atlasquery always uses 2mm atlas versions mask = fslimage.Image(namespace.ohiMask) labels, props = maskQuery(atlasDesc, [mask], resolution=2) labels = labels[0] props = props[0] for lbl, prop in zip(labels, props): lbl = atlasDesc.labels[int(lbl)].name print('{}:{:0.4f}'.format(lbl, prop)) # Coordinate query else: coord = namespace.coord.strip('"') coord = [float(c) for c in coord.split(',')] labels, props = coordQuery(atlasDesc, [coord], False, resolution=2) labels = labels[0] props = props[0] if atlasDesc.atlasType == 'label': labels = labels[0] if labels is None: label = 'Unclassified' else: label = atlasDesc.labels[int(labels)].name print('<b>{}</b><br>{}'.format(atlasDesc.name, label)) elif atlasDesc.atlasType == 'probabilistic': labelStrs = [] if len(labels) > 0: props, labels = zip(*reversed(sorted(zip(props, labels)))) for label, prop in zip(labels, props): label = atlasDesc.labels[int(label)].name labelStrs.append('{:d}% {}'.format(int(round(prop)), label)) if len(labelStrs) == 0: labels = 'No label found!' else: labels = ', '.join(labelStrs) print('<b>{}</b><br>{}'.format(atlasDesc.name, labels))
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 dumpatlases(): atlases = [a.name for a in fslatlases.listAtlases()] print('\n'.join(sorted(atlases)))