def find(self, name): """Returns the first overlay with the given ``name`` or ``dataSource``, or ``None`` if there is no overlay with said ``name``/``dataSource``. """ if name is None: return None absname = op.abspath(name) for overlay in self.overlays: if overlay.name == name: return overlay if overlay.dataSource is None: continue # Ignore file extensions for NIFTI images. if isinstance(overlay, fslimage.Image): if fslimage.removeExt(overlay.dataSource) == \ fslimage.removeExt(absname): return overlay else: if overlay.dataSource == absname: return overlay return None
def makergb(infile, nchannels): img = fslimage.Image(infile) indata = img.data[..., :nchannels] if nchannels == 3: dtype = np.dtype([('R', 'uint8'), ('G', 'uint8'), ('B', 'uint8')]) else: dtype = np.dtype([('R', 'uint8'), ('G', 'uint8'), ('B', 'uint8'), ('A', 'uint8')]) outdata = np.zeros(indata.shape[:3], dtype=dtype) for cn, ci in zip('RGBA', range(nchannels)): cd = indata[..., ci].astype(np.float32) lo, hi = cd.min(), cd.max() cd = (ci + 1) * 255 * (cd - lo) / (hi - lo) outdata[cn] = np.round(cd).astype(np.uint8) img = fslimage.Image(outdata, header=img.header) name = fslimage.removeExt(op.basename(infile)) name = '{}_makergb_{}'.format(name, nchannels) img.save(name) return name
def asmgh(infile): outfile = op.basename(fslimage.removeExt(infile)) outfile = outfile + '.mgh' inimg = fslimage.Image(infile) outimg = nib.MGHImage(inimg.data, inimg.voxToWorldMat) outimg.to_filename(outfile) return outfile
def _statImageDisplay(overlay, overlayList, displayCtx, zthres=3.0, posCmap=None, negCmap=None): """Configure default display settings for the given statistic :class:`.Image` overlay. """ opts = displayCtx.getOpts(overlay) basename = op.basename(overlay.dataSource) basename = fslimage.removeExt(basename) pTokens = ['p', 'corrp'] statTokens = ['zstat', 'tstat', 'zfstat'] fStatTokens = ['fstat'] # Rendered stat images (e.g. # rendered_thres_zstat1) are # generated specifically for # use with the Render1 colour # map. if 'rendered' in basename: opts.cmap = 'Render1' # Give each normal stat image # a different colour map else: cmap = _statImageDisplay.cmaps[_statImageDisplay.currentCmap] if posCmap is None: posCmap = cmap if negCmap is None: negCmap = cmap _statImageDisplay.currentCmap += 1 _statImageDisplay.currentCmap %= len(_statImageDisplay.cmaps) opts.cmap = posCmap opts.negativeCmap = negCmap # The order of these tests is # important, due to name overlap # P-value image ? if any([token in basename for token in pTokens]): opts.displayRange = [0.95, 1.0] opts.clippingRange = [0.95, 1.0] # T or Z stat image? elif any([token in basename for token in statTokens]) and \ 'rendered' not in basename: maxVal = overlay.dataRange[1] opts.useNegativeCmap = True opts.clippingRange = [zthres, maxVal] opts.displayRange = [zthres, min((7.5, maxVal))] # F stat image? elif any([token in basename for token in fStatTokens]): opts.displayRange = [0, 10]
def test_removeExt(): exts = ['.nii.gz', '.nii', '.img', '.img.gz', '.hdr', '.hdr.gz'] for e in exts: prefix = 'blob' fname = '{}{}'.format(prefix, e) assert fslimage.removeExt(fname) == prefix
def zero_centre(infile): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_zero_centre.nii.gz'.format(basename) img = fslimage.Image(infile) data = img[:] img[:] = data - data.mean() img.save(outfile) return outfile
def gen_indices(infile): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_indices.nii.gz'.format(basename) img = fslimage.Image(infile, loadData=False) shape = img.shape data = np.arange(np.prod(shape)).reshape(shape) fslimage.Image(data, header=img.header).save(outfile) return outfile
def _isPEImage(overlay): """Returns ``True`` if the given :class:`.Image` overlay looks like a statistic image, ``False`` otherwise. """ basename = op.basename(overlay.dataSource) basename = fslimage.removeExt(basename) tokens = ['cope', 'pe'] pattern = '^({})\d+'.format('|'.join(tokens)) return re.search(pattern, basename) is not None
def binarise(infile, low, high, scale=1): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_binarised_{}_{}_{}.nii.gz'.format(basename, low, high, scale) img = fslimage.Image(infile) data = img[:] binned = ((data > low) & (data < high)).astype(np.uint8) * scale fslimage.Image(binned, header=img.header).save(outfile) return outfile
def roi(fname, roi): base = fslimage.removeExt(fname) outfile = '{}_roi_{}_{}_{}_{}_{}_{}'.format(base, *roi) img = fslimage.Image(fname) xs, xe, ys, ye, zs, ze = roi data = img[xs:xe, ys:ye, zs:ze, ...] img = fslimage.Image(data, header=img.header) img.save(outfile) return outfile
def oblique(infile): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_oblique.nii.gz'.format(basename) img = fslimage.Image(infile) xform = img.getAffine('voxel', 'world') rot = affine.compose([1, 1, 1], [0, 0, 0], [0, 0, 1]) xform = affine.concat(rot, xform) img = fslimage.Image(img.data, xform=xform) img.save(outfile) return outfile
def sqformcodes(infile, sform, qform): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_sqformcodes_{}_{}.nii.gz'.format(basename, sform, qform) img = nib.load(infile) xform = img.affine img.set_sform(xform, sform) img.set_qform(xform, qform) img.update_header() nib.save(img, outfile) return outfile
def translate(infile, x, y, z): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_translated_{}_{}_{}.nii.gz'.format(basename, x, y, z) img = fslimage.Image(infile) xform = img.voxToWorldMat shift = affine.scaleOffsetXform(1, (x, y, z)) xform = affine.concat(shift, xform) img.voxToWorldMat = xform img.save(outfile) return outfile
def imglob(paths, output=None): """Given a list of file names, identifies and returns the unique NIFTI/ANALYZE image files that exist. :arg paths: Sequence of paths/prefixes to glob. :arg output: One of ``'prefix'`` (the default), ``'all'``, or ``'primary'``: - ``'prefix'``: Returns the files without extensions. - ``'all'``: Returns all files that match (e.g. both ``.img`` and ``.hdr`` files will be returned). - ``'primary'``: Returns only the primary file of each matching file group, e.g. only the ``.hdr`` file would be returned from an ``.img``/``.hdr`` pair. :returns: A sequence of resolved path names, in the form specified by the ``output`` parameter. """ if output is None: output = 'prefix' if output not in ('prefix', 'all', 'primary'): raise ValueError('Unsupported output format: {}'.format(output)) imgfiles = [] # Build a list of all image files (both # hdr and img and otherwise) that match for path in paths: try: path = fslimage.removeExt(path) imgfiles.extend(fslimage.addExt(path, unambiguous=False)) except fslpath.PathError: continue if output == 'prefix': imgfiles = fslpath.removeDuplicates(imgfiles, allowedExts=exts, fileGroups=groups) imgfiles = [fslpath.removeExt(f, exts) for f in imgfiles] elif output == 'primary': imgfiles = fslpath.removeDuplicates(imgfiles, allowedExts=exts, fileGroups=groups) return list(sorted(set(imgfiles)))
def fliporient(filename): base = fslimage.removeExt(filename) outfile = '{}_flipped'.format(base) img = fslimage.Image(filename) aff = img.voxToWorldMat aff[0, 0] = -aff[0, 0] aff[0, 3] = aff[0, 3] - (img.shape[0] - 1) * img.pixdim[0] img.voxToWorldMat = aff img[:] = img[::-1, ...] img.save(outfile) return outfile
def rotate(infile, rx, ry, rz): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_rotated_{}_{}_{}.nii.gz'.format(basename, rx, ry, rz) img = fslimage.Image(infile) rx = rx * np.pi / 180 ry = ry * np.pi / 180 rz = rz * np.pi / 180 rot = affine.axisAnglesToRotMat(rx, ry, rz) rot = affine.rotMatToAffine(rot) img.voxToWorldMat = affine.concat(rot, img.voxToWorldMat) img.save(outfile) return outfile
def asrgb(infile): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_asrgb.nii.gz'.format(basename) img = fslimage.Image(infile) data = img.data shape = data.shape[:3] rgbdtype = np.dtype([('R', 'uint8'), ('G', 'uint8'), ('B', 'uint8')]) newdata = np.zeros(shape, dtype=rgbdtype) for c, ci in zip('RGB', range(3)): cd = (0.5 * data[..., ci] + 0.5) * 255 newdata[c] = np.round(cd).astype(np.uint8) fslimage.Image(newdata, xform=img.voxToWorldMat).save(outfile) return outfile
def isMelodicImage(path): """Returns ``True`` if the given path looks like it is a melodic component image file, ``False`` otherwise. """ try: path = fslimage.addExt(path) except fslimage.PathError: return False dirname = op.dirname(path) filename = op.basename(path) filename = fslimage.removeExt(filename) prefixes = ['melodic_IC', 'melodic_oIC'] return any([filename == p for p in prefixes]) and isMelodicDir(dirname)
def cast(infile, dtype): base = fslimage.removeExt(infile) outfile = '{}_cast_{}'.format(base, dtype.__name__) img = fslimage.Image(infile) data = img[:].astype(np.float) # force range to 0-255 so it looks # the same regardless of dtype data = 255 * (data - data.min()) / (data.max() - data.min()) if dtype in (np.int8, np.int16, np.int32): data = data - 128 fslimage.Image(data, header=img.header).save(outfile) return outfile
def swapdim(infile): basename = fslimage.removeExt(op.basename(infile)) outfile = '{}_swapdim.nii.gz'.format(basename) img = fslimage.Image(infile) data = img.data xform = img.voxToWorldMat data = data.transpose((2, 0, 1)) rot = affine.rotMatToAffine( affine.concat(affine.axisAnglesToRotMat(np.pi / 2, 0, 0), affine.axisAnglesToRotMat(0, 0, 3 * np.pi / 2))) xform = affine.concat(xform, affine.scaleOffsetXform((1, -1, -1), (0, 0, 0)), rot) fslimage.Image(data, xform=xform, header=img.header).save(outfile) return outfile
def roi(fname, roi): base = fslimage.removeExt(op.basename(fname)) outfile = '{}_roi_{}_{}_{}_{}_{}_{}'.format(base, *roi) img = fslimage.Image(fname) xs, xe, ys, ye, zs, ze = roi data = img[xs:xe, ys:ye, zs:ze, ...] xform = img.voxToWorldMat offset = [lo for lo in roi[::2]] offset = affine.scaleOffsetXform([1, 1, 1], offset) xform = affine.concat(xform, offset) img = fslimage.Image(data, xform=xform, header=img.header) img.save(outfile) return outfile
def makevol(infile, nchannels): img = fslimage.Image(infile) data = img.data[..., :nchannels] newdata = np.zeros(data.shape, dtype=np.uint8) for c in range(nchannels): d = data[..., c].astype(np.float32) * (c + 1) lo, hi = d.min(), d.max() newdata[..., c] = np.round(255 * (c + 1) * (d - lo) / (hi - lo)).astype(np.uint8) img = fslimage.Image(newdata, header=img.header) name = fslimage.removeExt(op.basename(infile)) name = '{}_makevol_{}'.format(name, nchannels) img.save(name) return name
def invert(infile): if fslimage.looksLikeImage(infile): basename = fslimage.removeExt(op.basename(infile)) img = fslimage.Image(infile) data = img.data dmin, dmax = data.min(), data.max() data = dmin + (dmax - data) outfile = '{}_inverted.nii.gz'.format(basename) fslimage.Image(data, header=img.header).save(outfile) # assume text file else: basename, ext = op.split(infile) data = np.loadtxt(infile) dmin, dmax = data.min(), data.max() data = dmin + (dmax - data) outfile = '{}_inverted.{}'.format(basename, ext) np.savetxt(outfile, data) return outfile
def discretise(infile, stepsize, min=None, max=None): basename = fslimage.removeExt(op.basename(infile)) img = fslimage.Image(infile) data = img[:] if min is None: min = data.min() if max is None: max = data.max() outfile = '{}_discretised_{}_{}_{}.nii.gz'.format(basename, stepsize, min, max) for i, li in enumerate(range(min, max, stepsize)): data[(data >= li) & (data < (li + stepsize))] = i img[:] = data img.save(outfile) return outfile
def _loadComplexImage(path): """Loads the specified ``path`` assumed to be a NIFTI image with complex data. The image is loaded as two separate :class:`.Image` instances, containing the real and imaginary components respectively. """ import nibabel as nib import fsl.data.image as fslimage image = nib.load(path) hdr = image.header data = image.get_data() name = op.basename(fslimage.removeExt(path)) rname = '{} [real]'.format(name) iname = '{} [imag]'.format(name) real = fslimage.Image(np.real(data), name=rname, header=hdr) imag = fslimage.Image(np.imag(data), name=iname, header=hdr) return real, imag
def saveOverlay(overlay, display=None): """Saves the currently selected overlay (only if it is a :class:`.Image`), by a call to :meth:`.Image.save`. If a ``display`` is provided, the :attr:`.Display.name` may be updated to match the new overlay file name. :arg overlay: The :class:`.Image` overlay to save :arg display: The :class:`.Display` instance associated with the overlay. """ import wx # TODO support for other overlay types if not isinstance(overlay, fslimage.Image): raise RuntimeError('Non-volumetric types not supported yet') # If this image has been loaded from a file, # ask the user whether they want to overwrite # that file, or save the image to a new file. # if overlay.dataSource is not None: # If the data source is not nifti (e.g. # mgz), we are not going to overwrite it, # so we don't ask. if fslimage.looksLikeImage(overlay.dataSource): msg = strings.messages['SaveOverlayAction.overwrite'].format( overlay.dataSource) title = strings.titles['SaveOverlayAction.overwrite'].format( overlay.dataSource) dlg = wx.MessageDialog(wx.GetTopLevelWindows()[0], message=msg, caption=title, style=(wx.ICON_WARNING | wx.YES_NO | wx.CANCEL | wx.NO_DEFAULT)) dlg.SetYesNoCancelLabels( strings.labels['SaveOverlayAction.overwrite'], strings.labels['SaveOverlayAction.saveNew'], strings.labels['SaveOverlayAction.cancel']) response = dlg.ShowModal() # Cancel == cancel the save # Yes == overwrite the existing file # No == save to a new file (prompt the user for the file name) if response == wx.ID_CANCEL: return if response == wx.ID_YES: doSave(overlay) return fromDir = op.dirname(overlay.dataSource) filename = fslimage.removeExt(op.basename(overlay.dataSource)) filename = '{}_copy'.format(filename) else: fromDir = fslsettings.read('loadSaveOverlayDir', os.getcwd()) if display is not None: filename = display.name else: filename = overlay.name filename = filename.replace('/', '_') filename = filename.replace('\\', '_') # Ask the user where they # want to save the image msg = strings.titles['SaveOverlayAction.saveFile'] dlg = wx.FileDialog(wx.GetApp().GetTopWindow(), message=msg, defaultDir=fromDir, defaultFile=filename, style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) if dlg.ShowModal() != wx.ID_OK: return # Make sure that the user chose a supported # extension. If not, use the default extension. savePath = dlg.GetPath() prefix, suffix = fslimage.splitExt(savePath) if suffix == '': savePath = '{}{}'.format(prefix, fslimage.defaultExt()) oldPath = overlay.dataSource saveDir = op.dirname(savePath) if doSave(overlay, savePath): # Cache the save directory for next time. fslsettings.write('loadSaveOverlayDir', saveDir) # If image was in memory, or its old # name equalled the old datasource # base name, update its name. if oldPath is None or \ fslimage.removeExt(op.basename(oldPath)) == overlay.name: overlay.name = fslimage.removeExt(op.basename(savePath)) if display is not None: display.name = overlay.name
def __init__(self, parent, srcFile, refOpts=None, refOptFiles=None, selectedRef=None, matFile=None, refFile=None, save=False): """Create a ``FlirtFileDialog``. :arg parent: The ``wx`` parent object. :arg srcFile: Path to the FLIRT source image file :arg refOpts: Options to use in the reference image drop down box. :arg refOptFiles: File paths which correspond to the ``refOpts``. :arg selectedRef: Index of initially selected ``refOpt``. :arg matFile: Initial path to a FLIRT transformation matrix file :arg refFile: Initial Path to a FLIRT reference image file :arg save: If ``True``, the user will be prompted to save a FLIRT matrix. Otherwise (the default), the user will be prompted to load an existing FLIRT matrix. """ titleKey = {True: 'save', False: 'load'}[save] wx.Dialog.__init__(self, parent, title=strings.titles[self, titleKey], style=(wx.DEFAULT_DIALOG_STYLE | wx.STAY_ON_TOP)) if refOpts is None: refOpts = [] if refOptFiles is None: refOptFiles = [] if selectedRef is None: selectedRef = 0 self.__srcFile = srcFile self.__refOpts = list(refOpts) self.__refOptFiles = list(refOptFiles) self.__matFile = None self.__refFile = None self.__save = save self.__affineTypes = ['flirt', 'v2w'] affTypeOpts = [ strings.labels[self, 'affType', at] for at in self.__affineTypes ] refOpts = list(refOpts) + [strings.labels[self, 'refChoiceSelectFile']] overlayName = wx.StaticText(self, style=wx.ALIGN_CENTRE_HORIZONTAL) label = wx.StaticText(self, style=wx.ALIGN_CENTRE_HORIZONTAL) affType = wx.Choice(self, choices=affTypeOpts) affTypeLabel = wx.StaticText(self) refChoiceLabel = wx.StaticText(self) matFileLabel = wx.StaticText(self) refFileLabel = wx.StaticText(self) refChoice = wx.Choice(self, choices=refOpts) matFileText = wx.TextCtrl(self) refFileText = wx.TextCtrl(self) matFileButton = wx.Button(self) refFileButton = wx.Button(self) okButton = wx.Button(self, wx.ID_OK) cancelButton = wx.Button(self, wx.ID_CANCEL) self.__matFileText = matFileText self.__refFileText = refFileText self.__refFileLabel = refFileLabel self.__refFileButton = refFileButton self.__refChoiceLabel = refChoiceLabel self.__refChoice = refChoice self.__affType = affType if srcFile is None: srcName = strings.labels[self, 'inmemory'] else: srcName = op.basename(srcFile) srcName = fslimage.removeExt(srcFile) overlayName.SetLabel(strings.labels[self, 'source'].format(srcName)) affTypeLabel.SetLabel(strings.labels[self, 'affType']) refChoiceLabel.SetLabel(strings.labels[self, 'refImage']) matFileLabel.SetLabel(strings.labels[self, 'matFile']) refFileLabel.SetLabel(strings.labels[self, 'refFile']) matFileButton.SetLabel(strings.labels[self, 'selectFile']) refFileButton.SetLabel(strings.labels[self, 'selectFile']) okButton.SetLabel(strings.labels[self, 'ok']) cancelButton.SetLabel(strings.labels[self, 'cancel']) refChoice.SetSelection(selectedRef) affType.SetSelection(0) if save: label.SetLabel(strings.labels[self, 'save.message']) else: label.SetLabel(strings.labels[self, 'load.message']) if matFile is not None: matFileText.SetValue(matFile) matFileText.SetInsertionPointEnd() if refFile is not None: refFileText.SetValue(refFile) refFileText.SetInsertionPointEnd() sizer = wx.BoxSizer(wx.VERTICAL) widgetSizer = wx.FlexGridSizer(4, 3, 0, 0) btnSizer = wx.BoxSizer(wx.HORIZONTAL) widgetSizer.AddGrowableCol(1) sizer.Add((1, 10), flag=wx.EXPAND) sizer.Add(overlayName, flag=wx.ALIGN_CENTRE) sizer.Add((1, 10), flag=wx.EXPAND) sizer.Add(label, flag=wx.ALIGN_CENTRE) sizer.Add((1, 10), flag=wx.EXPAND) sizer.Add(widgetSizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10) sizer.Add((1, 10), flag=wx.EXPAND) sizer.Add(btnSizer, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border=10) sizer.Add((1, 10), flag=wx.EXPAND) widgetSizer.Add(affTypeLabel, flag=wx.ALL, border=3) widgetSizer.Add(affType, flag=wx.EXPAND, proportion=1) widgetSizer.Add((-1, -1)) if refOpts is not None: widgetSizer.Add(refChoiceLabel, flag=wx.ALL, border=3) widgetSizer.Add(refChoice, flag=wx.EXPAND, proportion=1) widgetSizer.Add((-1, -1)) else: widgetSizer.Add((-1, -1)) widgetSizer.Add((-1, -1)) widgetSizer.Add((-1, -1)) widgetSizer.Add(refFileLabel, flag=wx.ALL, border=3) widgetSizer.Add(refFileText, flag=wx.EXPAND, proportion=1) widgetSizer.Add(refFileButton, flag=wx.EXPAND) widgetSizer.Add(matFileLabel, flag=wx.ALL, border=3) widgetSizer.Add(matFileText, flag=wx.EXPAND, proportion=1) widgetSizer.Add(matFileButton, flag=wx.EXPAND) btnSizer.Add((10, 1), flag=wx.EXPAND, proportion=1) btnSizer.Add(okButton, flag=wx.EXPAND) btnSizer.Add((10, 1), flag=wx.EXPAND) btnSizer.Add(cancelButton, flag=wx.EXPAND) btnSizer.Add((10, 1), flag=wx.EXPAND, proportion=1) matFileText.SetMinSize((400, -1)) refFileText.SetMinSize((400, -1)) okButton.SetDefault() self.SetSizer(sizer) okButton.Bind(wx.EVT_BUTTON, self.__onOkButton) cancelButton.Bind(wx.EVT_BUTTON, self.__onCancelButton) matFileButton.Bind(wx.EVT_BUTTON, self.__onMatFileButton) refFileButton.Bind(wx.EVT_BUTTON, self.__onRefFileButton) affType.Bind(wx.EVT_CHOICE, self.__onAffType) refChoice.Bind(wx.EVT_CHOICE, self.__onRefChoice) self.__okButton = okButton self.__cancelButton = cancelButton self.__matFileText = matFileText self.__refFileText = refFileText self.__affType = affType self.__refChoice = refChoice if len(refOpts) == 1: refChoice.Disable() refChoiceLabel.Disable() else: self.__onRefChoice(None)