def test_pvc(): """ FIXME we need to test the PVC initialization step and how to do this is not finalized """ d = np.random.rand(5, 5, 5, 6) img = AslImage(name="asldata", image=d, tis=[1.5], order="prt") wsp = Workspace(infertiss=True, inferbat=True) wsp.pgm = Image(np.random.rand(5, 5, 5)) wsp.pwm = Image(np.random.rand(5, 5, 5)) steps = basil.basil_steps(wsp, img) assert (len(steps) == 3) options = _get_defaults(img) # options.update({ # "incpve" : True, # }) _check_step(steps[0], desc_text="tissue", options=options) # _check_step(steps[1], desc_text="PVE", options=options) options.update({ "method": "spatialvb", "param-spatial-priors": "N+", "PSP_byname1": "ftiss", "PSP_byname1_type": "M", "max-iterations": 200, "convergence": "maxits", }) options.pop("max-trials") _check_step(steps[2], desc_text="spatial")
def parse_args(self, argv=None, values=None): if argv is None: argv = sys.argv[1:] options, args = OptionParser.parse_args(self, argv, values) if options.optfile: # When an option file is specifeid, extract the options, build # a new argv vector and re-parse it. This is the only way to ensure # that options in the file work identically to CLI options. new_argv = self._add_from_file(argv, options.optfile) options, args = OptionParser.parse_args(self, new_argv, values) # Deal with case where asldata is given as separate files if args and options.asldata is None: merged_data = None for idx, fname in enumerate(args): img = Image(fname) shape = list(img.shape) if img.ndim == 3: shape += [ 1, ] if merged_data is None: merged_data = np.zeros(shape[:3] + [shape[3] * len(args)]) merged_data[..., idx * shape[3]:(idx + 1) * shape[3]] = img.data merged_img = Image(merged_data, header=img.header) temp_asldata = tempfile.NamedTemporaryFile(prefix="oxasl", delete=True) options.asldata = temp_asldata.name merged_img.save(options.asldata) return options, args
def _test_getProperties(frame, overlayList, displayCtx): vol1 = Image(op.join(datadir, '3d.nii.gz'), name='vol1') vol2 = Image(op.join(datadir, '3d.nii.gz'), name='vol2') mesh = GiftiMesh(op.join(datadir, 'gifti', 'white.surf.gii')) overlayList.extend([vol1, vol2, mesh]) displayCtx.getDisplay(vol1).alpha = 97 displayCtx.getOpts(vol1).clipImage = vol2 vprops = ftman.getProperties(vol1, displayCtx) assert vprops['alpha'] == 97 assert vprops['interpolation'] == 'none' assert 'transform' not in vprops ci = vprops['clipImage'] assert isinstance(ci, ftman.ToReplace) and ci.value == 'vol2' vprops = ftman.getProperties(vol2, displayCtx) assert vprops['alpha'] == 100 assert vprops['interpolation'] == 'none' assert 'transform' not in vprops assert 'clipImage' not in vprops displayCtx.getDisplay(mesh).alpha = 43 mprops = ftman.getProperties(mesh, displayCtx) assert mprops['alpha'] == 43 assert 'refImage' not in mprops displayCtx.getOpts(mesh).refImage = vol1 mprops = ftman.getProperties(mesh, displayCtx) assert mprops['alpha'] == 43 ri = mprops['refImage'] assert isinstance(ri, ftman.ToReplace) and ri.value == 'vol1'
def run(self, prev_output, log=sys.stdout, fsllog=None, **kwargs): """ Update the MVN from a previous step to include initial estimates for PVC parameters """ log.write("Initialising partial volume correction...\n") mask = self.options["mask"] # set the inital GM amd WM values using a simple PV correction wm_cbf_ratio = 0.4 # Modified pvgm map temp_pgm = np.copy(self.options["pgm"].data) temp_pgm[temp_pgm < 0.2] = 0.2 # First part of correction psuedo WM CBF term prev_ftiss = prev_output["mean_ftiss"].data wm_cbf_term = (prev_ftiss * wm_cbf_ratio) * self.options["pwm"].data gmcbf_init = (prev_ftiss - wm_cbf_term) / temp_pgm wmcbf_init = gmcbf_init * wm_cbf_ratio mvn = prev_output["finalMVN"] gmcbf_init = Image(gmcbf_init, header=mvn.header) wmcbf_init = Image(wmcbf_init, header=mvn.header) # load these into the MVN mvn = prev_output["finalMVN"] from .wrappers import mvntool params = prev_output["paramnames"] mvn = mvntool(mvn, params.index("ftiss")+1, output=LOAD, mask=mask, write=True, valim=gmcbf_init, var=0.1, log=fsllog)["output"] mvn = mvntool(mvn, params.index("fwm")+1, output=LOAD, mask=mask, write=True, valim=wmcbf_init, var=0.1, log=fsllog)["output"] log.write("DONE\n") return {"finalMVN" : mvn, "gmcbf_init" : gmcbf_init, "wmcbf_init" : wmcbf_init}
def single_volume(wsp, img, moco=True, discard_first=True): """ Convert a potentially 4D image into a single 3D volume :param moco: If True, perform basic motion correction :param discard_first: If True, discard first volume if nvols > 1 """ if img is not None: wsp.log.write(" - Pre-processing image: %s\n" % img.name) if img.ndim == 4: if discard_first and img.shape[3] > 1: wsp.log.write( " - Removing first volume to ensure data is in steady state\n" ) img = Image(img.data[..., :-1], header=img.header) if moco and img.shape[3] > 1: if moco: wsp.log.write(" - Motion correcting\n") img = fsl.mcflirt(img, out=fsl.LOAD, log=wsp.fsllog)["out"] wsp.log.write(" - Taking mean across time axis\n") img = Image(np.mean(img.data, axis=-1), header=img.header) return img else: return None
def _test_SelectionActions(ortho, overlayList, displayCtx): img1 = Image(np.random.randint(1, 65536, (20, 20, 20))) img2 = Image(np.random.randint(1, 65536, (20, 20, 20))) overlayList[:] = [img1, img2] displayCtx.selectOverlay(img1) idle.block(2) ortho.profile = 'edit' idle.block(2) profile = ortho.getCurrentProfile() profile.mode = 'sel' profile.drawMode = False idle.block(2) ed = profile.editor(img1) # clear ed.getSelection().addToSelection(np.ones((4, 4, 4)), offset=(8, 8, 8)) idle.block(2) profile.clearSelection() assert np.all(ed.getSelection().getSelection() == 0) # fill ed.getSelection().addToSelection(np.ones((4, 4, 4)), offset=(8, 8, 8)) idle.block(2) profile.fillValue = 999 profile.fillSelection() exp = np.copy(img1[:]) exp[8:12, 8:12, 8:12] = 999 assert np.all(img1[:] == exp) # erase profile.clearSelection() ed.getSelection().addToSelection(np.ones((4, 4, 4)), offset=(3, 3, 3)) idle.block(2) profile.eraseValue = -999 profile.eraseSelection() exp = np.copy(img1[:]) exp[3:7, 3:7, 3:7] = -999 assert np.all(img1[:] == exp) # invert ed.getSelection().addToSelection(np.ones((4, 4, 4)), offset=(8, 8, 8)) idle.block(2) profile.invertSelection() exp = np.ones(img1.shape) exp[8:12, 8:12, 8:12] = 0 assert np.all(ed.getSelection().getSelection() == exp) # copy+paste profile.clearSelection() ed.getSelection().addToSelection(np.ones((4, 4, 4)), offset=(8, 8, 8)) displayCtx.selectOverlay(img2) profile.copySelection() displayCtx.selectOverlay(img1) idle.block(2) profile.pasteSelection() idle.block(2) exp = np.copy(img1[:]) exp[8:12, 8:12, 8:12] = img2[8:12, 8:12, 8:12] assert np.all(img1[:] == exp)
def get_perfusion_data(outdir, gm_pve_asl, wm_pve_asl, gm_thresh, wm_thresh, min_gm_thresh, min_wm_thresh, log=sys.stdout): perfusion_data = [ { "suffix": "", "f": Image(os.path.join(outdir, "perfusion_calib")), "var": Image(os.path.join(outdir, "perfusion_var_calib")), "mask": None, }, ] if os.path.isdir(os.path.join(outdir, "pvcorr")): log.write( " - Found partial volume corrected results - will mask ROIs using 'base' GM/WM masks (PVE thresholds: %.2f / %.2f)\n" % (min_gm_thresh, min_wm_thresh)) perfusion_data.extend([ { "suffix": "_gm", "f": Image(os.path.join(outdir, "pvcorr", "perfusion_calib")), "var": Image(os.path.join(outdir, "pvcorr", "perfusion_var_calib")), "mask": gm_pve_asl.data > min_gm_thresh, }, { "suffix": "_wm", "f": Image(os.path.join(outdir, "pvcorr", "perfusion_wm_calib")), "var": Image(os.path.join(outdir, "pvcorr", "perfusion_wm_var_calib")), "mask": wm_pve_asl.data > min_wm_thresh, }, ]) else: log.write( " - No partial volume corrected results - will mask ROIs using 'pure' GM/WM masks (PVE thresholds: %.2f / %.2f)\n" % (gm_thresh, wm_thresh)) perfusion_data.extend([ { "suffix": "_gm", "f": Image(os.path.join(outdir, "perfusion_calib")), "var": Image(os.path.join(outdir, "perfusion_var_calib")), "mask": gm_pve_asl.data > gm_thresh, }, { "suffix": "_wm", "f": Image(os.path.join(outdir, "perfusion_calib")), "var": Image(os.path.join(outdir, "perfusion_var_calib")), "mask": wm_pve_asl.data > wm_thresh, }, ]) return perfusion_data
def get_tissrefmask(wsp): """ Calculate a calibration reference mask for a particular known tissue type """ page = wsp.report.page("auto_calib_mask") page.heading("Calibration reference region") page.text("Reference region was automatically generated for tissue type: %s" % wsp.tissref.upper()) page.heading("Partial volume map for %s tissue (from structural segmentation)" % wsp.tissref.upper(), level=1) wsp.calibration.refpve = getattr(wsp.structural, "%s_pv" % wsp.tissref.lower()) page.image("refpve", LightboxImage(wsp.calibration.refpve, bgimage=wsp.structural.brain)) if wsp.tissref == "csf" and not wsp.csfmaskingoff: wsp.log.write(" - Doing automatic ventricle selection using standard atlas\n") # By deafult now we do FNRIT transformation of ventricle mask # FIXME disabled as not being used in ASL_CALIB at present #wsp.calibration.struc2stdfnirt = wsp.ifnone("stdmaskfnirt", True) # Select ventricles based on standard space atlas page.heading("Automatic ventricle selection", level=1) page.text("Standard space ventricles mask (from Harvard-Oxford atlas) eroded by 1 pixel") atlases = AtlasRegistry() atlases.rescanAtlases() atlas = atlases.loadAtlas("harvardoxford-subcortical", loadSummary=False, resolution=2) ventricles = ((atlas.data[..., 2] + atlas.data[..., 13]) > 0.1).astype(np.int) wsp.calibration.ventricles = Image(scipy.ndimage.binary_erosion(ventricles, structure=np.ones([3, 3, 3]), border_value=1).astype(np.int), header=atlas.header) std_img = Image(os.path.join(os.environ["FSLDIR"], "data", "standard", 'MNI152_T1_2mm_brain')) page.image("ventricles_std", LightboxImage(wsp.calibration.ventricles, bgimage=std_img)) page.heading("Structural space ventricles mask", level=1) # FIXME nearest neighbour interpolation? wsp.calibration.ventricles_struc = reg.change_space(wsp, wsp.calibration.ventricles, "struc") page.text("This is the above image transformed into structural space. The transformation was obtained by registering the structural image to the standard brain image") page.image("ventricles_struc", LightboxImage(wsp.calibration.ventricles_struc, bgimage=wsp.structural.brain)) wsp.log.write(" - Masking FAST output with standard space derived ventricle mask\n") wsp.calibration.refpve_pre_mask = wsp.calibration.refpve refpve_data = np.copy(wsp.calibration.refpve.data) refpve_data[wsp.calibration.ventricles_struc.data == 0] = 0 wsp.calibration.refpve = Image(refpve_data, header=wsp.calibration.refpve.header) wsp.calibration.refpve_post = wsp.calibration.refpve page.heading("Structural space ventricles PVE", level=1) page.text("This is the CSF partial volume masked by the ventricles mask. It should select only the ventricles from the original partial volume image.") page.image("refpve_post", LightboxImage(wsp.calibration.refpve, bgimage=wsp.structural.brain)) wsp.log.write(" - Transforming tissue reference mask into ASL space\n") # FIXME calibration image may not be in ASL space! Oxford_asl does not handle this currently wsp.calibration.refpve_calib = reg.change_space(wsp, wsp.calibration.refpve, "native") #wsp.calibration.refpve_calib.data[wsp.calibration.refpve_calib.data < 0.001] = 0 # Better for display page.heading("Reference region in ASL space", level=1) page.text("Partial volume map") page.image("refpve_calib", LightboxImage(wsp.calibration.refpve_calib, bgimage=wsp.calibration.calib_img)) # Threshold reference mask conservatively to select only reference tissue wsp.log.write(" - Thresholding reference mask\n") wsp.calibration.refmask = Image((wsp.calibration.refpve_calib.data > 0.9).astype(np.int), header=wsp.calibration.refpve_calib.header) page.text("Reference Mask (thresholded at 0.9") page.image("refmask", LightboxImage(wsp.calibration.refmask, bgimage=wsp.calibration.calib_img))
def get_for_row(self, afq_object, row): nearest_warp, nearest_space = self.fnames[row['ses']][row['subject']] our_templ = afq_object.reg_template_img subj = Image(row['dwi_file']) their_templ = Image(nearest_space) warp = readFnirt(nearest_warp, their_templ, subj) return ConformedFnirtMapping(warp, our_templ.affine)
def qpdata_to_fslimage(qpd, grid=None): """ Convert QpData to fsl.data.image.Image """ from fsl.data.image import Image if grid is None: return Image(qpd.raw(), name=qpd.name, xform=qpd.grid.affine) else: return Image(qpd.resample(grid), name=qpd.name, xform=grid.affine)
def img(self): """ Return an Image object by loading from the file """ img = Image(self._fname, loadData=False) if self._md: for key, value in self._md: img.setMeta(key, value) return img
def get_for_subses(self, subses_dict, reg_template, reg_subject): nearest_warp, nearest_space = self.fnames[subses_dict['ses']][ subses_dict['subject']] our_templ = reg_template subj = Image(subses_dict['dwi_file']) their_templ = Image(nearest_space) warp = readFnirt(nearest_warp, their_templ, subj) return ConformedFnirtMapping(warp, our_templ.affine)
def _slicetiming_correction( asl_name, t1_name, tis, rpts, slicedt, sliceband, n_slices ): """ Performs rescaling of ASL series, `asl_name`, accounting for the estimated T1t in a given voxel, `t1_name`. satrecov_model: S(t) = M_0 * (1 - exp{-t/T1t}) Divides the value in a voxel by the above model evaluated at t = TI + n*slicedt where n is the slice number of the voxel within a band. This accounts for the voxel being imaged at a different time than its supposed TI. Multiplies the value in a voxel by the above model evaluated at t = TI, i.e. scales the values as if they had been imaged at the TI that was specified in the ASL sequence. Returns the slice-timing corrected ASL series, `stcorr_img`, and the scaling factors used to perform the correction, `stcorr_factors_img` where: `stcorr_factors` = S(TI) / S(TI + n*slicedt) `stcorr_img` = `stcorr_factors` * `asl_name` """ # timing information for scan # supposed measurement time of slice tis_array = np.repeat(np.array(tis), 2*np.array(rpts)).reshape(1, 1, 1, -1) # actual measurment time of slice slice_numbers = np.tile( np.arange(0, sliceband), n_slices // sliceband ).reshape(1, 1, -1, 1) slice_times = tis_array + (slicedt * slice_numbers) # load images asl_img = Image(str(asl_name)) t1_img = Image(str(t1_name)) # check dimensions of t1 image to see if time series or not if t1_img.ndim == 3: t1_data = t1_img.data[..., np.newaxis] elif t1_img.ndim == 4: t1_data = t1_img.data # multiply asl sequence by satrecov model evaluated at TI numexp = - tis_array / t1_data num = 1 - np.exp(numexp) # divide asl sequence by satrecov model evaluated at actual slice-time denexp = - slice_times / t1_data den = 1 - np.exp(denexp) # evaluate scaling factors stcorr_factors = num / den stcorr_factors_img = Image(stcorr_factors, header=asl_img.header) # correct asl series stcorr_data = asl_img.data * stcorr_factors stcorr_img = Image(stcorr_data, header=asl_img.header) return stcorr_img, stcorr_factors_img
def binarise(pve_name, threshold=0.5): """ Binarise a PVE image given a threshold. The default threshold is 0.5, the threshold used by asl_reg to obtain a white matter segmentation from a white matter PVE image. """ pve = Image(str(pve_name)) seg = Image(np.where(pve.data>threshold, 1., 0.), header=pve.header) return seg
def _get_imgs(shape=(5, 5, 5)): """ Create test images """ perf_d = np.random.rand(5, 5, 5) perf_img = Image(name="perfusion", image=perf_d) calib_d = np.random.rand(5, 5, 5) calib_img = Image(name="calib", image=calib_d) return perf_img, calib_img
def run(wsp): """ Do initialization on supplied structural data - copy relevant image and do brain extraction FIXME copy across all supplied structural data """ wsp.log.write("\nInitialising structural data\n") wsp.sub("structural") if wsp.fslanat: wsp.log.write( " - Using FSL_ANAT output directory for structural data: %s\n" % wsp.fslanat) biascorr = os.path.join(wsp.fslanat, "T1_biascorr") biascorr_brain = os.path.join(wsp.fslanat, "T1_biascorr_brain") if glob.glob(biascorr + ".*") and glob.glob(biascorr_brain + ".*"): wsp.log.write(" - Using bias-corrected structural images\n") wsp.structural.struc = Image(biascorr) wsp.structural.brain = Image(biascorr_brain) else: wsp.log.write(" - Using non bias-corrected structural images\n") wsp.structural.struc = Image(os.path.join(wsp.fslanat, "T1")) wsp.structural.brain = Image(os.path.join(wsp.fslanat, "T1_brain")) elif wsp.struc: wsp.log.write(" - Using structural image provided by user: %s\n" % wsp.struc.name) wsp.structural.struc = wsp.struc wsp.structural.brain = wsp.struc_brain #elif wsp.structural.struc_lores # wsp.log.write("Low-resolution tructural image: %s\n" % wsp.structural.struc_lores.name) else: wsp.log.write( " - No structural data supplied - output will be native space only\n" ) if wsp.structural.struc is not None and wsp.structural.brain is None: wsp.log.write(" - Brain-extracting structural image\n") bet_result = fsl.bet(wsp.structural.struc, output=fsl.LOAD, seg=True, mask=True, log=wsp.fsllog) wsp.structural.brain = bet_result["output"] #wsp.structural.brain_mask = bet_result["output_mask"] if wsp.structural.brain is not None and wsp.structural.brain_mask is None: # FIXME - for now get the mask by binarising the brain image for compatibility with oxford_asl # although this gives slightly different results compared to using the mask returned by BET wsp.structural.brain_mask = Image( (wsp.structural.brain.data != 0).astype(np.int), header=wsp.structural.struc.header) if wsp.structural.struc is not None: segment(wsp)
def correct_img(wsp, img, linear_mat): """ Apply combined warp/linear transformations to an image in ASL space :param img: fsl.data.image.Image to correct :param linear_mat: img->ASL space linear transformation matrix. :return: Corrected Image If a jacobian is present, also corrects for quantitative signal magnitude as volume has been locally scaled FIXME there are slight differences to oxford_asl here due to use of spline interpolation rather than applyxfm4D which uses sinc interpolation. Required workspace attributesd ----------------------------- - ``asldata_mean`` : Mean ASL image used as reference space Optional workspace attributes ----------------------------- - ``total_warp`` : Combined warp image - ``jacobian`` : Jacobian associated with warp image - ``senscorr`` : Sensitivity correction """ if wsp.corrected.total_warp is not None: img = reg.transform(wsp, img, trans=wsp.corrected.total_warp, ref=wsp.preproc.aslspace, premat=linear_mat) else: img = reg.transform(wsp, img, trans=linear_mat, ref=wsp.preproc.aslspace) if wsp.corrected.jacobian is not None: wsp.log.write( " - Correcting for local volume scaling using Jacobian\n") jdata = wsp.corrected.jacobian.data if img.data.ndim == 4: # Required to make broadcasting work jdata = jdata[..., np.newaxis] img = Image(img.data * jdata, header=img.header) if wsp.senscorr is not None: wsp.log.write(" - Applying sensitivity correction\n") sens_data = reg.change_space(wsp, wsp.senscorr.sensitivity, img).data if img.ndim == 4: sens_data = sens_data[..., np.newaxis] img = Image(img.data / sens_data, header=img.header) return img
def test_fillSelection_2d(): img = Image(np.random.randint(1, 65536, (60, 60, 1))) tfs = ft.partial(_test_fillSelection, img=img, canvas=2) run_with_orthopanel(tfs) img = Image(np.random.randint(1, 65536, (60, 1, 60))) tfs = ft.partial(_test_fillSelection, img=img, canvas=1) run_with_orthopanel(tfs) img = Image(np.random.randint(1, 65536, (1, 60, 60))) tfs = ft.partial(_test_fillSelection, img=img, canvas=0) run_with_orthopanel(tfs)
def test_pvc_no_tissue(): """ Check that PVC correction fails if you do not infer the tissue component """ d = np.random.rand(5, 5, 5, 6) img = AslImage(name="asldata", image=d, tis=[1.5], order="prt") wsp = Workspace(infertiss=False, inferbat=True) wsp.pgm = Image(np.random.rand(5, 5, 5)) wsp.pwm = Image(np.random.rand(5, 5, 5)) with pytest.raises(ValueError): basil.basil_steps(wsp, img)
def test_bad_method(): """ Check we get an error on an invalid method """ d = np.random.rand(5, 5, 5, 6) perf_img = Image(name="perfusion", image=d) d = np.random.rand(5, 5, 5, 6) calib_img = Image(name="calib", image=d) wsp = Workspace(calib=calib_img, calib_method="random") with pytest.raises(ValueError): calib.calibrate(wsp, perf_img)
def test_resample_image_dim(): with tempdir(): img = Image(make_random_image('image.nii.gz', dims=(10, 10, 10))) resample_image.main('image resampled -d 0.5,0.5,0.5'.split()) res = Image('resampled') expv2w = affine.concat(img.voxToWorldMat, affine.scaleOffsetXform([0.5, 0.5, 0.5], 0)) assert np.all(np.isclose(res.shape, (20, 20, 20))) assert np.all(np.isclose(res.pixdim, (0.5, 0.5, 0.5))) assert np.all(np.isclose(res.voxToWorldMat, expv2w))
def __init__(self, *args, **kwargs): """ Constructor. Can take same arguments as the fsl.data.image.Image constructor but also supports keyword ``param_names`` argument. This should be a list of parameter names which matches the size of the MVN """ self.param_names = kwargs.get("param_names", None) Image.__init__(self, *args, **kwargs) self.nparams = self._get_num_params() if self.param_names is None: self.param_names = ["Parameter %i" % idx for idx in range(self.nparams)] elif len(self.param_names) != self.nparams: raise ValueError("MVN contains %i parameters, but %i parameter names were provided")
def test_refregion_gain(): """ Modify calibration gain """ GAIN = 3.4 perf_img, calib_img = _get_imgs() ref_d = np.ones((5, 5, 5)) ref_img = Image(name="refmask", image=ref_d) wsp = Workspace(calib=calib_img, calib_method="refregion", refmask=ref_img, calib_aslreg=True, tissref="wm", calib_gain=GAIN) perf_calib = calib.calibrate(wsp, perf_img) calibrated_d = perf_calib.data m0_expected = _expected_m0(np.mean(calib_img.data), 1.0, 50, 0.82, gain=GAIN) np.testing.assert_allclose(calibrated_d, perf_img.data / m0_expected)
def test_image_save(): """ Test images are saved in the savedir """ tempdir = tempfile.mkdtemp("_oxasl") try: wsp = Workspace(savedir=tempdir) img = Image(np.random.rand(5, 5, 5)) wsp.testimg = img path = os.path.join(tempdir, "testimg.nii.gz") assert(os.path.isfile(path)) otherimg = Image(path) assert(np.all(img.data == wsp.testimg.data)) assert(np.all(img.data == otherimg.data)) finally: shutil.rmtree(tempdir)
def _test_newMask(ortho, overlayList, displayCtx): img = Image(np.random.randint(1, 65536, (20, 20, 20))) overlayList[:] = [img] idle.block(2.5) ortho.profile = 'edit' idle.block(2.5) profile = ortho.getCurrentProfile() profile.createMask() idle.block(2.5) assert len(overlayList) == 2 mask = overlayList[1] assert mask.sameSpace(img) assert np.all(mask[:] == 0) overlayList.remove(mask) idle.block(2.5) profile.mode = 'sel' idle.block(2.5) ed = profile.editor(img) ed.getSelection().addToSelection(np.ones((4, 4, 4)), offset=(8, 8, 8)) idle.block(2.5) profile.createMask() idle.block(2.5) assert len(overlayList) == 2 mask = overlayList[1] assert mask.sameSpace(img) exp = np.zeros(mask.shape) exp[8:12, 8:12, 8:12] = 1 assert np.all(mask[:] == exp) overlayList[:] = [] idle.block(2.5)
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 _calc_slicedt(wsp, options): """ Calculate the slicedt for basil given that we may be quantifying in a space other than the usual ASL space We do this by generating a slice time offset image and transforming it to quantification space. Since this could be rotated wrt to the asl data we may need to warn if the resulting image has significant slice time variation across X or Y axes """ img_space = wsp.ifnone("image_space", "native") if img_space != "native": asldata = options["data"] _x, _y, z, _t = np.indices( list(asldata.data.shape[:3]) + [ asldata.ntis, ]) print(z.shape) tis_arr = np.array( asldata.tis) + (z.astype(np.float32) * options["slicedt"]) print(tis_arr.shape) tis_img = Image(tis_arr, header=options["data"].header) wsp.tiimg = reg.change_space(wsp, tis_img, wsp.ifnone("image_space", "native")) #print(ztrans.data) print(wsp.tiimg.data.shape) del options["slicedt"] ti_idx = 1 while "ti%i" % ti_idx in options: del options["ti%i" % ti_idx] ti_idx += 1 options["tiimg"] = wsp.tiimg
def _test_ComplexTimeSeries(panel, overlayList, displayCtx): displayCtx = panel.displayCtx data = np.random.randint(1, 255, (10, 10, 10, 20)) + \ 1j * np.random.randint(1, 255, (10, 10, 10, 20)) data = np.array(data, dtype=np.complex64) img = Image(data, xform=np.eye(4)) overlayList.append(img) realYield(100) opts = displayCtx.getOpts(img) ts = panel.getDataSeries(img) displayCtx.location = opts.transformCoords((7, 8, 9), 'voxel', 'display') exp = img[7, 8, 9, :] xdata, ydata = ts.getData() assert np.all(xdata == np.arange(img.shape[3])) assert np.all(ydata == exp.real) ts.plotReal = False xdata, ydata = ts.getData() assert xdata is None assert ydata is None ts.plotImaginary = True ts.plotMagnitude = True ts.plotPhase = True its, mts, pts = ts.extraSeries() assert np.all(its.getData()[1] == exp.imag) expmag = np.sqrt(exp.real**2 + exp.imag**2) expphase = np.arctan2(exp.imag, exp.real) assert np.all(np.isclose(expmag, mts.getData()[1])) assert np.all(np.isclose(expphase, pts.getData()[1]))
def _test_ComplexHistogramSeries(panel, overlayList, displayCtx): displayCtx = panel.displayCtx data = np.random.randint(1, 255, (10, 10, 10)) + \ 1j * np.random.randint(1, 255, (10, 10, 10)) data.flags.writeable = False data = np.array(data, dtype=np.complex64) img = Image(data, xform=np.eye(4)) overlayList.append(img) realYield() hs = panel.getDataSeries(img) hs.plotReal = True hs.plotImaginary = True hs.plotMagnitude = True hs.plotPhase = True his, hms, hps = hs.extraSeries() real = data.real imag = data.imag mag = (real**2 + imag**2)**0.5 phase = np.arctan2(imag, real) for hdata, hhs in zip([real, imag, mag, phase], [hs, his, hms, hps]): dmin, dmax = hdata.min(), hdata.max() drange = dmin, dmax + (dmax - dmin) / 10000 nbins = hseries.autoBin(hdata, drange) hx, hy, _ = hseries.histogram(hdata, nbins, drange, drange) gotx, goty = hhs.getData() assert np.all(gotx == hx) assert np.all(goty == hy)
def apply_sensitivity_correction(wsp, *imgs): """ Apply sensitivity correction :param imgs: Sequence of Image objects :return: Tuple of corrected Image objects corresponding to input. If no sensitivity correction is defined, returns the same images as input. Optional workspace attributes ----------------------------- - ``sensitivity`` : Sensitivity correction image - ``senscorr_off`` : If True, no correction will be applied even if ``sensitivity`` image exists """ if wsp.senscorr is not None: wsp.log.write(" - Applying sensitivity correction\n") ret = [] for img in imgs: ret.append( Image(img.data / wsp.senscorr.sensitivity.data, header=img.header)) return tuple(ret) else: return tuple(imgs)