def meta_data(self, img): d = super(self, MGHImageType).meta_data(img) hdr = to_image_header(img) try: d['vox2ras'] = hdr.get_vox2ras() except Exception: pass try: d['ras2vox'] = hdr.get_ras2vox() except Exception: pass try: d['vox2ras_tkr'] = hdr.get_vox2ras_tkr() except Exception: pass try: d['footer_offset'] = hdr.get_footer_offset() except Exception: pass try: zooms = hdr.get_zooms() (zooms, tr) = (zooms, None) if len(zooms) == 3 else (zooms[:3], zooms[3]) d['voxel_size'] = pimms.quant(zooms, 'mm') d['slice_duration'] = pimms.quant(tr, 'ms') if tr is not None else None except Exception: pass return d
def provide_default_options( pRF_sigma_slopes_by_label=pRF_sigma_slopes_by_label_Kay2013, pRF_sigma_offsets_by_label=pRF_sigma_offsets_by_label_Kay2013, contrast_constants_by_label=contrast_constants_by_label_Kay2013, compressive_constants_by_label=compressive_constants_by_label_Kay2013, saturation_constants_by_label=saturation_constants_by_label_Kay2013, divisive_exponents_by_label=divisive_exponents_by_label_Kay2013, gains_by_label=gains_by_label_Kay2013, max_eccentricity=pimms.quant(10.0, 'deg'), pixels_per_degree=pimms.quant(6.4, 'px/deg'), modality='surface', spatial_frequency_sensitivity_function=spatial_frequency_sensitivity): ''' provide_default_options is a calculator that optionally accepts values for all parameters for which default values are provided in the sco.impl.testing package and yields into the calc plan these parameter values or the default ones for any not provided. These options are: * pRF_sigma_slope_by_label (sco.impl.benson17.pRF_sigma_slope_by_label_Kay2013) * compressive_constant_by_label (sco.impl.benson17.compressive_constant_by_label_Kay2013) * contrast_constant_by_label (sco.impl.benson17.contrast_constant_by_label_Kay2013) * modality ('surface') * max_eccentricity (10) * spatial_frequency_sensitivity_function (from sco.impl.benson17) * saturation_constant (sco.impl.benson17.saturation_constant_Kay2013) * divisive_exponent (sco.impl.benson17.divisive_exponent_Kay2013) * gabor_orientations (8) ''' # the defaults are filled-in by virtue of being in the above argument list return True
def meta_data(self, img): from nibabel.nifti1 import (slice_order_codes, unit_codes) d = super(self, Nifti1ImageType).meta_data(img) hdr = to_image_header(img) try: d['dimension_information'] = hdr.get_dim_info() except: pass try: d['intent'] = hdr.get_intent() except: pass try: d['slice_count'] = hdr.get_n_slices() except: pass try: (sunit, tunit) = hdr.get_xyzt_units() except: (sunit, tunit) = ('unknown', 'unknown') if sunit != 'unknown': try: d['voxel_size'] = pimms.quant(d['voxel_size'], sunit) except: pass try: sd = hdr.get_slice_duration() if tunit != 'unknown': sd = pimms.quant(sd, tunit) d['slice_duration'] = sd except: pass try: (q,qc) = hdr.get_qform(True) d['qform_code'] = qc d['qform'] = q except: pass try: (s,sc) = hdr.get_sform(True) d['sform_code'] = sc d['sform'] = s except: pass try: sc = hdr['slice_code'] if sc != 0: d['slice_order'] = slice_order_codes.label[sc] except: pass try: ts = hdr.get_slice_times() ts = np.asarray([np.nan if t is None else t for t in ts]) if tunit != 'unknown': ts = pimms.quant(ts, tunit) d['slice_times'] = ts except: pass try: d['header_size'] = hdr['sizeof_hdr'] except: pass try: d['calibration'] = (hdr['cal_min'], hdr['cal_max']) except: pass try: t0 = hdr['toffset'] if tunits != 'unknown': t0 = pimms.quant(t0, tunits) d['time_offset'] = t0 except: pass try: d['description'] = hdr['descrip'] except: pass try: d['auxiliary_filename'] = hdr['aux_file'] except: pass return d
def calc_image_retinotopy(pixels_per_degree, max_eccentricity, output_pixels_per_degree=None, output_max_eccentricity=None): ''' calc_image_retinotopy calculates retinotopic coordinates (polar angle, eccentricity, label) for an output image the same size as the input images. I.e., each pixel in the input images get a single pRF center for V1, V2, and V3 (one each). ''' maxecc = pimms.mag(max_eccentricity, 'deg') if output_max_eccentricity is None else \ pimms.mag(output_max_eccentricity, 'deg') d2p = pimms.mag(pixels_per_degree, 'px / deg') if output_pixels_per_degree is None else \ pimms.mag(output_pixels_per_degree, 'px / deg') # we progressively downsample in order to make this more efficient... (ang, ecc, hem) = ([], [], []) esteps = np.concatenate([[0], 1.5 * 2**np.arange(0, np.ceil(np.log2(maxecc)), 1) ]) for (k0, k1) in zip(esteps[:-1], esteps[1:]): # make the image-data at resolution k1; we can ignore beyond k1 eccen for now dim = int(np.ceil((d2p / k1) * 2.0 * k1)) center = 0.5 * (dim - 1) # in pixels # x/y in degrees x = (np.arange(dim) - center) / (d2p / k1) # mesh grid... (x, y) = np.meshgrid(x, -x) # convert to eccen and theta/angle eccen = np.sqrt(x**2 + y**2) theta = np.arctan2(y, x) angle = np.mod(90 - 180.0 / np.pi * theta + 180, 360.0) - 180 # get hemispheres hemis = np.sign(angle) hemis[hemis == 0] = 1.0 # find the relevant pixels ii = (k0 <= eccen) & (eccen < k1) ang.append(angle[ii]) ecc.append(eccen[ii]) hem.append(hemis[ii]) # and turn these into lists with visual area labels (angle, eccen, hemis) = [np.concatenate(u) for u in (ang, ecc, hem)] label = np.concatenate([np.full(len(angle), k) for k in (1, 2, 3)]) (angle, eccen, hemis) = [np.concatenate((u, u, u)) for u in (angle, eccen, hemis)] for u in (angle, eccen, label, hemis): u.setflags(write=False) return { 'polar_angles': pimms.quant(angle, 'deg'), 'eccentricities': pimms.quant(eccen, 'deg'), 'labels': label, 'hemispheres': hemis }
def calc_pRFs(pRF_centers, pRF_sigmas, compressive_constants, labels, pRF_n_radii=3.0): ''' calc_pRFs is a calculator that adds to the datapool a set of objects of class PRFSpec; these objects represent the pRF and can calculate responses or sparse matrices representing the weights over an image of the pRF. Generally, if p is a PRFSpec object, then p(im, c) will yield the pRF response calculated in Kay et al (2013) with the parameter c; that is, it calculates the weighted second moment about c times the weighted mean of the pRF. Required afferent parameters: * pRF_centers, pRF_sigmas, compressive_constants Optional afferent parameters: @ pRF_n_radii May specify how many standard deviations should be included in the Gaussian blob that defines the pRF. Output efferent values: @ pRFs Will be the array of PRFSpec objects; this is a numpy array of the pRFs. @ pRF_radii Will be the effective pRF sizes, as determined by: radius = sigma / sqrt(n). ''' prfs = np.asarray([ PRFSpec(x0, sig, n, l, n_radii=pRF_n_radii) for (x0, sig, l, n) in zip(pRF_centers, pRF_sigmas, labels, compressive_constants) ]) prfs.setflags(write=False) radii = pimms.quant(np.asarray([pimms.mag(p.radius, 'deg') for p in prfs]), 'deg') radii.setflags(write=False) return (prfs, radii)
def sigma(sig): ''' prf.sigma is the pRF sigma parameter in degrees; see also radius. ''' sig = pimms.mag(sig, 'deg') if sig <= 0: raise ValueError('sigma must be positive') if sig < 0.05: sig = 0.05 return pimms.quant(sig, 'deg')
def calc_gabor_spatial_frequencies(image_array, pixels_per_degree, gabor_spatial_frequency_count=16, gabor_spatial_frequency_min=None, gabor_spatial_frequency_max=None): ''' calc_gabor_spatial_frequencies is a calculator that constructs a reasonable set of spatial frequencies (in cycles per degree) at which to filter an image given its pixels per degree. The reason for this calculator is that for a low-contrast image, a given frequency, even if visible to a human observer in theory, might be so high that we can't detect it at the given image resolution. This function picks a set of <gabor_spatial_frequency_count> frequencies that are evenly log-spaced such that no frequency is smaller than a minimum or larger than a maximum that are chosen based on image resolution. Afferent parameters: * image_array * pixels_per_degree @ gabor_spatial_frequency_count Must be the number of spatial frequencies at which to filter the images; by default this is 16. @ gabor_spatial_frequency_min Must be the minimum spatial frequency (in cycles per degree) at which to filter the images. If None, then the minimum is equivalent to one half of a cycle per image. By default, this is Ellipsis. @ gabor_spatial_frequency_max Must be the maximum spatial frequency (in cycles per degree) at which to filter the images. If None, then the maximum is equivalent to 0.5 cycles / pixel. Efferent values: @ gabor_spatial_frequencies Will be a numpy array of spatial frequency values to use in the filtering of the image arrays; values will be in cycles/degree. ''' # Process some arguments pixels_per_degree = pimms.mag(pixels_per_degree, 'pixels/degree') imsz = image_array.shape[1] imsz_deg = imsz / pixels_per_degree if gabor_spatial_frequency_min is None: gabor_spatial_frequency_min = 0.5 / imsz_deg gabor_spatial_frequency_min = pimms.mag(gabor_spatial_frequency_min, 'cycles/degree') if gabor_spatial_frequency_min == 0: raise ValueError('gabor_spatial_frequency_min must be positive') if gabor_spatial_frequency_max is None: gabor_spatial_frequency_max = 0.5 * pixels_per_degree gabor_spatial_frequency_max = pimms.mag(gabor_spatial_frequency_max, 'cycles/degree') if np.isinf(gabor_spatial_frequency_max): raise ValueError('gabor_spatial_frequency_min must be finite') # okay, lets transfer to log-space lmin = np.log(gabor_spatial_frequency_min) lmax = np.log(gabor_spatial_frequency_max) # make the spacing lfs = np.linspace(lmin, lmax, gabor_spatial_frequency_count) # return to exponential space fs = np.exp(lfs) # That's basically it! fs.setflags(write=False) return pimms.quant(fs, 'cycles/degree')
def test_itable(self): ''' test_itable() tests pimms itable objects and makes sure they work correctly. ''' class nloc: lazy_loads = 0 def _load_lazy(): nloc.lazy_loads += 1 return pimms.quant(np.random.rand(10), 'sec') dat = pimms.lazy_map({ 'a': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'b': pimms.quant(np.random.rand(10), 'mm'), 'c': [ 'abc', 'def', 'ghi', 'jkl', 'mno', 'pqr', 'stu', 'vwx', 'yz!', '!!!' ], 'd': _load_lazy }) tbl = pimms.itable(dat) # make sure the data is the right size for k in tbl.keys(): self.assertTrue(tbl[k].shape == (10, )) self.assertTrue(tbl.row_count == 10) self.assertTrue(len(tbl.rows) == 10) self.assertTrue(len(tbl.column_names) == 4) self.assertTrue(len(tbl.columns) == 4) # Check a few of the entries for (i, ki) in zip(np.random.randint(0, tbl.row_count, 50), np.random.randint(0, 4, 50)): ki = tbl.column_names[ki] self.assertTrue(tbl.rows[i][ki] == tbl[ki][i]) self.assertTrue(nloc.lazy_loads == 1) # see if we can discard stuff self.assertTrue('a' in tbl) self.assertFalse('a' in tbl.discard('a')) self.assertFalse('b' in tbl.discard('b')) self.assertFalse('c' in tbl.discard('b').discard('c')) self.assertTrue('c' in tbl.discard('b').discard('a'))
def test_units(self): ''' test_units ensures that the various pimms functions related to pint integration work correctly; these functions include pimms.unit, .mag, .quant, .is_quantity, etc. ''' # make a few pieces of data with types x = np.asarray([1.0, 2.0, 3.0, 4.0]) * pimms.units.mm y = pimms.quant([2, 4, 6, 8], 'sec') for u in [x, y]: self.assertTrue(pimms.is_quantity(u)) for u in ('abc', 123, 9.0, []): self.assertFalse(pimms.is_quantity(u)) for u in [x, y]: self.assertFalse(pimms.is_quantity(pimms.mag(u))) self.assertTrue(pimms.like_units(pimms.unit(x), pimms.unit('yards'))) self.assertTrue(pimms.like_units(pimms.unit(y), pimms.unit('minutes'))) self.assertFalse(pimms.like_units(pimms.unit(y), pimms.unit('mm'))) z = x / y self.assertTrue(pimms.is_vector(x, 'real')) self.assertTrue(pimms.is_vector(y, 'real')) self.assertFalse(pimms.is_vector(x, 'int')) self.assertTrue(pimms.is_vector(y, 'int')) self.assertTrue(pimms.is_vector(y, 'float')) self.assertTrue(pimms.is_vector(z, 'real'))
def import_benson14_retinotopy(freesurfer_subject, max_eccentricity, measured_labels=None, modality='surface'): ''' import_benson14_retinotopy is a calculation that imports (or creates then imports) the Benson et al. (2014) template of retinotopy for the subject, whose neuropythy.freesurfer Subject object must be provided in the parameter freesurfer_subject. The optional parameter modality (default: 'surface') may be either 'volume' or 'surface', and determines if the loaded modality is volumetric or surface-based. Required afferent parameters: @ freesurfer_subject Must be a valid neuropythy.freesurfer.Subject object. Optional afferent parameters: * measured_labels is used to determine if measurement data was included. @ modality May be 'volume' or 'surface' to specify the anatomical modality. @ max_eccentricity May specifies the maximum eccentricity value to use. Provided efferent values: @ benson14_polar_angles Polar angle values for each vertex/voxel. @ benson14_eccentricities Eccentricity values for each vertex/voxel. @ benson14_labels An integer label 1, 2, or 3 for V1, V2, or V3, one per vertex/voxel. @ benson14_hemispheres 1 if left, -1 if right for all vertex/voxel. @ benson14_cortex_indices For vertices, the vertex index (in the appropriate hemisphere) for each; for voxels, the (i,j,k) voxel index for each. @ benson14_cortex_coordinates For voxels, this is the (i,j,k) voxel index (same as benson14_cortex_indices); for surfaces, this is ths (x,y,z) position of each vertex in surface-space. Notes: * polar_angles are always given such that a negative polar angle indicates a RH value and a positive polar angle inidicates a LH value * cortex_indices is different for surface and volume modalities * labels will always be 1, 2, or 3 indicating V1, V2, or V3 ''' if freesurfer_subject is None or measured_labels is not None: # we aren't loading benson14 data return (None, None, None, None, None, None) max_eccentricity = max_eccentricity.to(units.deg) if pimms.is_quantity(max_eccentricity) else \ max_eccentricity*units.deg subject = freesurfer_subject if modality.lower() == 'volume': # make sure there are template volume files that match this subject ang = os.path.join(subject.path, 'mri', 'benson14_angle.mgz') ecc = os.path.join(subject.path, 'mri', 'benson14_eccen.mgz') lab = os.path.join(subject.path, 'mri', 'benson14_varea.mgz') if not os.path.exists(ang) or not os.path.exists( ecc) or not os.path.exists(lab): # Apply the template first... nycmd.benson14_retinotopy.main(subject.path) if not os.path.exists(ang) or not os.path.exists( ecc) or not os.path.exists(lab): raise ValueError('No areas template found/created for subject: ' + lab) angle_mgz = fs.mghformat.load(ang) eccen_mgz = fs.mghformat.load(ecc) label_mgz = fs.mghformat.load(lab) ribbon_mgzs = (subject.mgh_images['lh.ribbon'], subject.mgh_images['rh.ribbon']) # The variables are all mgz volumes, so we need to extract the values: labels = np.round(np.abs(label_mgz.dataobj.get_unscaled())) angles = angle_mgz.dataobj.get_unscaled() eccens = eccen_mgz.dataobj.get_unscaled() (lrib, rrib) = [r.dataobj.get_unscaled() for r in ribbon_mgzs] # Find the voxel indices first: # for now we only look at v1-v3 labels[labels > 3] = 0 coords = np.asarray(np.where(labels.astype(bool))).T # Grab the hemispheres; filter down if something isn't in the ribbon tmp = [(1 if lrib[i, j, k] == 1 else -1, (i, j, k)) for (i, j, k) in coords if lrib[i, j, k] != 0 or rrib[i, j, k] != 0 if eccens[i, j, k] < max_eccentricity.m] hemis = np.asarray([r[0] for r in tmp], dtype=np.int) idcs = np.asarray([r[1] for r in tmp], dtype=np.int) coords = np.asarray(idcs, dtype=np.float) # Pull out the angle/eccen data angs0 = np.asarray([angles[i, j, k] for (i, j, k) in idcs]) angles = angs0 * hemis eccens = np.asarray([eccens[i, j, k] for (i, j, k) in idcs], dtype=np.float) labels = np.asarray([labels[i, j, k] for (i, j, k) in idcs], dtype=np.int) elif modality.lower() == 'surface': rx = freesurfer_subject.RH.midgray_surface.coordinates.T lx = freesurfer_subject.LH.midgray_surface.coordinates.T # make sure there are template volume files that match this subject lang = os.path.join(subject.path, 'surf', 'lh.benson14_angle.mgz') lecc = os.path.join(subject.path, 'surf', 'lh.benson14_eccen.mgz') llab = os.path.join(subject.path, 'surf', 'lh.benson14_varea.mgz') rang = os.path.join(subject.path, 'surf', 'rh.benson14_angle.mgz') recc = os.path.join(subject.path, 'surf', 'rh.benson14_eccen.mgz') rlab = os.path.join(subject.path, 'surf', 'rh.benson14_varea.mgz') (lang, lecc, llab, rang, recc, rlab) = [ flnm if os.path.isfile(flnm) else flnm[:-4] for flnm in (lang, lecc, llab, rang, recc, rlab) ] if not os.path.exists(lang) or not os.path.exists(rang) or \ not os.path.exists(lecc) or not os.path.exists(recc) or \ not os.path.exists(llab) or not os.path.exists(rlab): # Apply the template first... nycmd.benson14_retinotopy.main(subject.path) if not os.path.exists(lang) or not os.path.exists(rang) or \ not os.path.exists(lecc) or not os.path.exists(recc) or \ not os.path.exists(llab) or not os.path.exists(rlab): raise ValueError( 'No anatomical template found/created for subject') (lang, lecc, llab, rang, recc, rlab) = [ny.load(fl) for fl in (lang, lecc, llab, rang, recc, rlab)] llab = np.round(np.abs(llab)) rlab = np.round(np.abs(rlab)) (angs0, eccs, labs) = [ np.concatenate([ldat, rdat], axis=0) for (ldat, rdat) in zip([lang, lecc, llab], [rang, recc, rlab]) ] idcs = np.concatenate([range(len(lang)), range(len(rang))], axis=0) valid = np.intersect1d( np.intersect1d(np.where(labs > 0)[0], np.where(labs < 4)[0]), np.where(eccs < max_eccentricity.m)[0]) idcs = idcs[valid] coords = np.concatenate([lx, rx], axis=0)[valid] hemis = np.concatenate([[1 for a in lang], [-1 for a in rang]], axis=0)[valid] # old versions of the template had positive numbers in both hemispheres... if np.mean(angs0[valid[hemis == -1]]) > 0: angles = angs0[valid] * hemis else: angles = angs0[valid] eccens = eccs[valid] labels = np.asarray(labs[valid], dtype=np.int) else: raise ValueError('Option modality must be \'surface\' or \'volume\'') # do the filtering and convert to pvectors res = { 'benson14_polar_angles': pimms.quant(angles, 'deg'), 'benson14_eccentricities': pimms.quant(eccens, 'deg'), 'benson14_labels': labels, 'benson14_cortex_indices': idcs, 'benson14_cortex_coordinates': coords, 'benson14_hemispheres': hemis } # make sure they're all write-only for v in six.itervalues(res): v.setflags(write=False) return res
fs = np.exp(lfs) # That's basically it! fs.setflags(write=False) return pimms.quant(fs, 'cycles/degree') def _convolve_from_arg(arg): 'Private function used for multiprocessing the convolution of images.' (imgs, kern, bg) = arg re = np.asarray([ndi.convolve(img, kern.real, mode='constant', cval=bg) for img in imgs]) im = np.asarray([ndi.convolve(img, kern.imag, mode='constant', cval=bg) for img in imgs]) return re + 1j*im def _spyr_from_arg(arg): 'Private function used for multiprocessing the steerable-pyramid filtering of images.' (imgs, th, cpp, nth) = arg return spyr_filter(imgs, th, cpp, 1, nth) _default_gabor_orientations = pimms.quant(np.asarray(range(8), dtype=np.float) * np.pi/8.0, 'rad') @pimms.calc('oriented_contrast_images', 'scaled_pixels_per_degree', 'scaled_image_arrays', 'gabor_filters', cache=True) def calc_oriented_contrast_images(image_array, pixels_per_degree, background, gabor_spatial_frequencies, gabor_orientations=_default_gabor_orientations, max_image_size=250, min_pixels_per_degree=0.5, max_pixels_per_filter=27, ideal_filter_size=17, use_spatial_gabors=True, multiprocess=True): ''' calc_oriented_contrast_images is a calculator that takes as input an image array along with its resolution (pixels_per_degree) and a set of gabor orientations and spatial frequencies, and
sw = params['screen_width'] sd = params['screen_distance'] d2p = 180 / np.pi * 2 * np.arctan2(sw / 2, sd) else: warn('Could not deduce screen pixels per degree for test dir %s!' % (tdir, )) continue else: d2p = params['pixels_per_degree'] # Get the TR length and the frame rate trlen = params.get('TR_length', None) if trlen is None: trlen = data.header.get_zooms()[-1] truni = data.header.get_xyzt_units()[-1] trlen = pimms.mag( pimms.quant(trlen, 'sec' if truni is None else truni), 'second') frate = params.get('frame_rate', None) if frate is None: frate = stim.header.get_zooms()[-1] fruni = stim.header.get_xyzt_units()[-1] frate = pimms.mag( pimms.quant(frate, 'sec' if fruni is None else fruni), 'second') # see if there is a mask: mask = None if 'mask_file' in params: maskfile = params['mask_file'] if not os.path.isabs(maskfile): maskfile = os.path.join(tdir, maskfile) if not os.path.isfile(stimfile): warn('Could not find mask file (%s) in test dir %s!' % (maskfile, tdir)) else:
output_dir = './data/output' image_files = [ os.path.abspath('./data/images/validate_0000.png'), os.path.abspath('./data/images/validate_0001.png'), os.path.abspath('./data/images/validate_0002.png'), os.path.abspath('./data/images/validate_0003.png'), os.path.abspath('./data/images/validate_0004.png'), os.path.abspath('./data/images/validate_0005.png'), os.path.abspath('./data/images/validate_0006.png'), os.path.abspath('./data/images/validate_0007.png'), os.path.abspath('./data/images/validate_0008.png'), os.path.abspath('./data/images/validate_0009.png') ] d2p = pimms.quant(128.0/20.0, 'px/deg') max_eccen = pimms.quant(10.0, 'deg') opts = { 'stimulus': image_files, 'subject': subject_dir, 'stimulus_edge_value': 0.5, 'gabor_orientations' : 8, 'pixels_per_degree': d2p, 'normalized_pixels_per_degree' : d2p, 'max_eccentricity': max_eccen, 'aperture_edge_width': 0, 'aperture_radius': max_eccen, 'output_directory': output_dir, 'measurements_filename': func_filename }
# Some experimental parameters by labels ones_by_label = _pyr.pmap({1: 1.0, 2: 1.0, 3: 1.0, 4: 1.0}) zeros_by_label = _pyr.pmap({1: 0.0, 2: 0.0, 3: 0.0, 4: 1.0}) # Frequency Sensitivity ############################################################################ #_sensitivity_frequencies_cpd = _pimms.quant(_np.asarray([0.75 * 2.0**(0.5 * k) for k in range(6)]), # 'cycles/deg') #_sensitivity_frequencies_cpd = _pimms.quant(_np.asarray([5, 3.5355, 2.5000, 1.7678, 1.2500, 0.8839, # 0.6250, 0.4419, 0.3125]), # 'cycles/deg') #_sensitivity_frequencies_cpd = _pimms.quant(_np.asarray([0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 6.0]), # 'cycles/deg') #_sensitivity_frequencies_cpd = _pimms.quant( # _np.asarray(_np.exp(_np.log(18.0)/6.0 * _np.asarray(range(1,8)) - _np.log(3.0))), # 'cycles/deg') _sensitivity_frequencies_cpd = _pimms.quant( _np.asarray(_np.exp(0.5 * _np.asarray(range(1, 8)) - 1.5)), 'cycles/deg') _cpd_sensitivity_cache = {} def cpd_sensitivity(e, s, l): ''' cpd_sensitivity(ecc, prfsz, lbl) yields the predicted spatial frequency sensitivity of a pRF whose center has the given eccentricity ecc, pRF radius prfsz, and V1/V2/V3 label lbl. This is returned as a map whose keys are sampled frequencies (in cycles per degree) and whose values sum to 1. ''' e = _pimms.mag(e, 'deg') if e in _cpd_sensitivity_cache: return _cpd_sensitivity_cache[e] if e < 0.1: e = 0.1
def test_predicates(self): ''' test_predicates ensures that the various pimms functions of the form is_<type>(obj) are working properly. ''' # some arbitrary values to use in testing qr = pimms.quant([1.5, 3.3, 9.5, 10.4, 6.2, 0.1], 'mm') qi = pimms.quant([1, 3, 9, 10, 6, 0], 'seconds') mr = np.random.rand(10, 3) vi = np.random.randint(0, 5000, size=12) sr = np.array(10.0) si = np.array(2) * pimms.units.deg l = [1, 2.0, 'abc'] lx = [[1, 1], [2.0, 2.0], [4, 7.7]] u = u'a unicode string of stuff' b = b'a byte string of stuff' f0 = lambda: np.linspace(0, 100, 117) f1 = lambda x: x**2 + 1 d = { 'a': 12, 'b': None, 'c': f0, 'd': f1, 'e': lambda: 'some string', 'f': lambda: None } pm = pyr.pmap(d) lm = pimms.lazy_map(d) # a function for testing predicates def tpred(p, tvals, fvals): for s in tvals: self.assertTrue(p(s)) for s in fvals: self.assertFalse(p(s)) # Map types tpred(pimms.is_lazy_map, [lm], [qr, qi, mr, vi, sr, si, l, u, b, f0, f1, d, pm]) tpred(pimms.is_map, [lm, d, pm], [qr, qi, mr, vi, sr, si, l, u, b, f0, f1]) tpred(pimms.is_pmap, [lm, pm], [qr, qi, mr, vi, sr, si, l, u, b, f0, f1, d]) # Numpy types require a little attention due to their optional arguments and the # complexities of the type relationships tpred(pimms.is_nparray, [qr, qi, mr, vi, sr], [l, lx, u, b, f0, f1, d, pm, lm]) self.assertTrue(pimms.is_nparray(qr, 'real')) self.assertTrue(pimms.is_nparray(qr, 'any', 1)) self.assertFalse(pimms.is_nparray(qr, 'any', 2)) self.assertFalse(pimms.is_nparray(qi, 'string')) self.assertTrue(pimms.is_nparray(qi, ('real', 'int'), (1, 3))) self.assertFalse(pimms.is_nparray(qi, ('real', 'int'), 2)) self.assertFalse( pimms.is_nparray(qr, ('string', 'bool', 'bytes'), (2, 3))) self.assertTrue(pimms.is_nparray(mr, None, 2)) self.assertFalse(pimms.is_nparray(mr, None, 1)) self.assertFalse(pimms.is_nparray(vi, 'int', 2)) tpred(pimms.is_npscalar, [sr], [qr, qi, mr, vi, l, lx, u, b, f0, f1, d, pm, lm]) self.assertTrue(pimms.is_npscalar(np.array(12.0), 'real')) self.assertFalse(pimms.is_npscalar(np.array(12.0), 'string')) self.assertTrue(pimms.is_npscalar(np.array(12.0), ('real', 'complex'))) tpred(pimms.is_npmatrix, [mr, pimms.quant(mr, 'm/s')], [sr, si, qr, qi, vi, l, lx, u, b, f0, f1, d, pm, lm]) self.assertTrue(pimms.is_npmatrix(mr, ('int', 'real', 'string'))) self.assertTrue(pimms.is_npmatrix(mr, 'number')) self.assertFalse(pimms.is_npmatrix(mr, ('bool', 'string'))) tpred(pimms.is_npvector, [qr, qi, vi, vi * pimms.units.mol, qr, qi], [sr, si, mr, l, lx, u, b, f0, f1, d, pm, lm]) self.assertTrue(pimms.is_npvector(vi, 'real')) self.assertTrue(pimms.is_npvector(qi, 'int')) self.assertFalse(pimms.is_npvector(qr, ('bool', 'string'))) self.assertTrue(pimms.is_npvalue('abc', 'string')) self.assertTrue(pimms.is_npvalue(u'abc', ('unicode', 'real'))) self.assertFalse(pimms.is_npvalue(np.array(5.6), ('unicode', 'real'))) self.assertFalse(pimms.is_npvalue(np.array(5.6), ('unicode', 'bool'))) self.assertFalse(pimms.is_npvalue(np.array([5.6]), ('unicode', 'real'))) # Also the un-nump'ified versions tpred(pimms.is_array, [qr, qi, vi, sr, si, mr, qr, qi, l, lx, u, b, f0, f1], [d, pm, lm]) self.assertTrue(pimms.is_array(qr, 'real')) self.assertTrue(pimms.is_array(qr, 'any', 1)) self.assertTrue(pimms.is_array(qr, 'any', 1)) self.assertFalse(pimms.is_array(qi, 'string')) self.assertTrue(pimms.is_array(qi, ('real', 'int'), (1, 3))) self.assertFalse(pimms.is_array(qi, ('real', 'int'), 2)) self.assertFalse( pimms.is_array(qr, ('string', 'bool', 'bytes'), (2, 3))) self.assertTrue(pimms.is_array(mr, None, 2)) self.assertFalse(pimms.is_array(mr, None, 1)) self.assertFalse(pimms.is_array(vi, 'int', 2)) self.assertFalse(pimms.is_array(l, 'number', 1)) self.assertTrue(pimms.is_array(lx, 'any', (1, 2))) tpred(pimms.is_scalar, [u, b, f0, f1, sr, si], [qr, qi, vi, mr, l, d, pm, lm, lx]) tpred(pimms.is_int, [vi[0], si, 1, 10], [u, b, f0, f1, d, pm, lm, sr, qr, mr]) tpred(pimms.is_real, [vi[0], si, 1, 10, sr], [4j, u, b, f0, f1, d, pm, lm, lx, mr, qr]) tpred(pimms.is_complex, [vi[0], si, 1, 10, sr, 4j], [u, b, f0, f1, d, pm, lm, lx, mr, qr]) tpred(pimms.is_number, [vi[0], si, 1, 10, sr], [u, b, f0, f1, d, pm, lm, lx, mr, qr]) tpred(pimms.is_str, ['abc'], [vi, si, 1, 10, sr, qr, f0, f1, d, pm, lm, lx, mr]) tpred(pimms.is_class, [str, int], [vi, si, 1, 10, sr, qr, u, b, f0, f1, d, pm, lm, lx, mr]) tpred(pimms.is_quantity, [qr, qi, si], [vi, 10, sr, u, b, f0, f1, d, pm, lm, lx, mr]) tpred(pimms.is_unit, ('seconds', 's', 'mm', 'deg', pimms.units.seconds, pimms.units.s, pimms.units.mm, pimms.units.deg), (1, 10.0, np.asarray([10]), None, 'nonunitstring', qr)) self.assertTrue(pimms.is_nparray(mr, np.inexact)) self.assertFalse(pimms.is_nparray(vi, np.inexact))
def _load_lazy(): nloc.lazy_loads += 1 return pimms.quant(np.random.rand(10), 'sec')
def calc_retinotopy(benson14_polar_angles, benson14_eccentricities, benson14_labels, benson14_hemispheres, benson14_cortex_indices, benson14_cortex_coordinates, measured_polar_angles, measured_eccentricities, measured_labels, measured_hemispheres, measured_cortex_indices, measured_cortex_coordinates, freesurfer_cortex_affine, measured_cortex_affine, modality, import_filter=None): ''' calc_retinotopy is a calculator that unifies the various modes of importing retinotopic data and yields a set of general values (polar angle, eccentricity, labels, etc) that are used downstream by the SCO model. Afferent values: @ import_filter If specified, may give a function that accepts four parameters: f(polar_angle, eccentricity, label, hemi); if this function fails to return True for the appropriate values of a particular vertex/voxel, then that vertex/voxel is not included in the prediction. ''' b14 = (benson14_polar_angles, benson14_eccentricities, benson14_labels, benson14_hemispheres, benson14_cortex_indices, benson14_cortex_coordinates, freesurfer_cortex_affine) msd = (measured_polar_angles, measured_eccentricities, measured_labels, measured_hemispheres, measured_cortex_indices, measured_cortex_coordinates, measured_cortex_affine) if all(x is not None for x in msd): dat = msd elif all(x is not None for x in b14): dat = b14 else: raise ValueError( 'Neither benson14 nor measured retinotopy import succeeded') (angles, eccens, labels, hemis, idcs, coords, tx) = dat if tx is None and freesurfer_cortex_affine is not None: tx = freesurfer_cortex_affine angles = pimms.mag(angles, 'deg') eccens = pimms.mag(eccens, 'deg') if import_filter is not None: sels = [ i for (i, (p, e, l, h)) in enumerate(zip(angles, eccens, labels, hemis)) if import_filter(p, e, l, h) ] (angles, eccens, labels, hemis, idcs, coords) = [ x[sels] for x in (angles, eccens, labels, hemis, idcs, coords) ] res = { 'polar_angles': pimms.quant(angles, 'deg'), 'eccentricities': pimms.quant(eccens, 'deg'), 'labels': labels, 'cortex_indices': idcs, 'cortex_coordinates': coords, 'hemispheres': hemis, 'cortex_affine': tx } return res