def variance(session, maps): sum = sum2 = None for v in maps: from numpy import float32 m = v.full_matrix().astype(float32) if sum is None: sum = m.copy() sum2 = m * m else: if m.shape != sum.shape: from chimerax.core.errors import UserError raise UserError('Map %s' % v.name_with_id() + ' size (%d,%d,%d)' % tuple(v.data.size) + ' does not match map %s' % maps[0].name_with_id() + ' size (%d,%d,%d)' % tuple(maps[0].data.size)) sum += m sum2 += m * m n = len(maps) var = sum2 / n - (sum * sum) / (n * n) d = maps[0].data from chimerax.map_data import ArrayGridData grid = ArrayGridData(var, origin=d.origin, step=d.step, cell_angles=d.cell_angles, rotation=d.rotation, name='variance of %d maps' % n) from chimerax.map import volume_from_grid_data v = volume_from_grid_data(grid, session) return v
def scaled_volume(v, scale=1, sd=None, rms=None, shift=0, type=None, step=None, subregion=None, model_id=None, session=None): if not sd is None or not rms is None: m = v.full_matrix() from chimerax.map.volume import mean_sd_rms mean, sdev, rmsv = mean_sd_rms(m) if not rms is None and rmsv > 0: scale = (1.0 if scale is None else scale) * rms / rmsv if not sd is None and sdev > 0: shift = -mean if shift is None else (-mean + shift) scale = (1.0 if scale is None else scale) * sd / sdev sg = scaled_grid(v, scale, shift, type, subregion, step) from chimerax.map import volume_from_grid_data sv = volume_from_grid_data(sg, session, model_id=model_id) sv.copy_settings_from(v, copy_thresholds=False) v.display = False # Hide original map return sv
def ones_volume(surfaces, pad, spacing, border, default_size=100, value_type=None): # Figure out array size bounds = scene_bounds(surfaces) bsize = [s + 2 * pad + 2 * border for s in bounds.size()] if spacing is None: s = max(bsize) / default_size spacing = (s, s, s) from math import ceil size = [1 + int(ceil(s / sp)) for s, sp in zip(bsize, spacing)] origin = [x - (pad + border) for x in bounds.xyz_min] # Create ones array from numpy import ones, int8 vtype = int8 if value_type is None else value_type varray = ones(size[::-1], vtype) from chimerax.map_data import ArrayGridData g = ArrayGridData(varray, origin, spacing, name='mask') # Create Volume model from chimerax.map import volume_from_grid_data v = volume_from_grid_data(g, surfaces[0].session, open_model=False, show_dialog=False) return v
def split_volume_by_color_zone(volume): '''Create new volumes for each color zoned region of a specified volume.''' zc = volume_zone_color(volume) if zc is None: from chimerax.core.errors import UserError raise UserError('Volume %s does not have zone coloring' % volume.name_with_id()) grids = split_zones_by_color(volume, zc.points, zc.point_colors, zc.distance) session = volume.session from chimerax.map import volume_from_grid_data vlist = [ volume_from_grid_data(g, session, open_model=False) for g in grids ] for v in vlist: v.copy_settings_from(volume, copy_region=False) rgba = tuple(c / 255 for c in v.data.zone_color) v.set_parameters(surface_colors=[rgba] * len(v.surfaces)) v.display = True volume.display = False if len(vlist) == 1: session.models.add(vlist) else: session.models.add_group(vlist, name=volume.name + ' split') return vlist
def fourier_transform(v, step=None, subregion=None, model_id=None, phase=False): m = v.matrix(step=step, subregion=subregion) from numpy.fft import fftn cftm = fftn(m) # Complex128 result, same array size as input from numpy import absolute, angle, float32 if phase: aftm = angle(cftm).astype(float32) # Radians else: aftm = absolute(cftm).astype(float32) aftm *= 1.0 / aftm.size # Normalization aftm[0, 0, 0] = 0 # Constant term often huge making histogram hard to use cftm = None # Release memory ftm = fftshift(aftm) aftm = None # Release memory # Place FT centered on data, scaled to keep same volume xyz_min, xyz_max = v.xyz_bounds() xyz_center = map(lambda a, b: 0.5 * (a + b), xyz_min, xyz_max) ijk_size = list(ftm.shape) ijk_size.reverse() if step is None: ijk_step = v.region[2] elif isinstance(step, int): ijk_step = (step, step, step) else: ijk_step = step xyz_size = [a - b for a, b in zip(xyz_max, xyz_min)] vol = xyz_size[0] * xyz_size[1] * xyz_size[2] cell_size = [a * b for a, b in zip(v.data.step, ijk_step)] cell_vol = cell_size[0] * cell_size[1] * cell_size[2] scale = pow(vol * cell_vol, 1.0 / 3) step = [scale / a for a in xyz_size] origin = [c - 0.5 * s * z for c, s, z in zip(xyz_center, step, ijk_size)] from chimerax.map_data import ArrayGridData ftd = ArrayGridData(ftm, origin, step) ftd.name = v.name + (' FT phase' if phase else ' FT') from chimerax.map import volume_from_grid_data ftr = volume_from_grid_data(ftd, v.session, model_id=model_id) ftr.copy_settings_from(v, copy_thresholds=False, copy_colors=False, copy_region=False) ftr.set_parameters(show_outline_box=True) v.display = False # Hide original map return ftr
def tile_planes(v, axis = 'z', pstep = 1, trim = 0, rows = None, columns = None, fill_order = 'ulh', step = None, subregion = None, model_id = None): vreg = v.subregion(step = step, subregion = subregion) reg = [list(ijk) for ijk in vreg] ac,ar,a = {'x':(1,2,0), 'y':(2,0,1), 'z':(0,1,2)}[axis] reg[0][a] += trim reg[1][a] -= trim reg[2][a] = pstep dorigin, dstep = v.region_origin_and_step(reg) m = v.region_matrix(reg) tcount = m.shape[2-a] if tcount == 0: return if rows is None and columns is None: # Choose columns to make square aspect ratio. w,h = m.shape[2-ac]*dstep[ac], m.shape[2-ar]*dstep[ar] from math import sqrt, ceil columns = min(tcount, int(ceil(sqrt(tcount*float(h)/w)))) rows = (tcount - 1 + columns) // columns elif rows is None: rows = (tcount - 1 + columns) // columns elif columns is None: columns = (tcount - 1 + rows) // rows s0, s1, s2 = m.shape if axis == 'z': tshape = (1,rows*s1,columns*s2) elif axis == 'y': tshape = (columns*s0,1,rows*s2) elif axis == 'x': tshape = (rows*s0,columns*s1,1) from numpy import zeros ta = zeros(tshape, m.dtype) for i in range(tcount): # Start with top image in upper left corner. p,r,c = tile_position(i, rows, columns, tcount, fill_order) if axis == 'z': ta[0,r*s1:(r+1)*s1,c*s2:(c+1)*s2] = m[p,:,:] elif axis == 'y': ta[c*s0:(c+1)*s0,0,r*s2:(r+1)*s2] = m[:,p,:] elif axis == 'x': ta[r*s0:(r+1)*s0,c*s1:(c+1)*s1,0] = m[:,:,p] from chimerax.map_data import ArrayGridData td = ArrayGridData(ta, dorigin, dstep) td.name = v.name + ' tiled %s' % axis from chimerax.map import volume_from_grid_data tv = volume_from_grid_data(td, v.session, model_id = model_id) tv.copy_settings_from(v, copy_region = False, copy_active = False, copy_xform = open) v.display = False # Hide original map return tv
def permute_axes(v, axis_order=(0, 1, 2), step=None, subregion=None, model_id=None): d = v.grid_data(subregion, step, mask_zone=False) pd = PermutedGrid(d, axis_order) from chimerax.map import volume_from_grid_data pv = volume_from_grid_data(pd, v.session, model_id=model_id) return pv
def ridges(volume, level=None, step=1, subregion=None, modelId=None): if level is None: level = volume.minimum_surface_level rg = ridge_grid(volume, level, step, subregion) from chimerax.map import volume_from_grid_data rv = volume_from_grid_data(rg, volume.session, model_id=modelId) rv.set_parameters(surface_levels=[0.5]) volume.display = False # Hide original map return rv
def bin(v, bin_size = (2,2,2), step = None, subregion = None, model_id = None, session = None): bd = bin_grid(v, bin_size, step, subregion) from chimerax.map import volume_from_grid_data bv = volume_from_grid_data(bd, session, model_id = model_id) bv.copy_settings_from(v, copy_region = False) bv.display = True v.display = False # Hide original map return bv
def unbend_volume(volume, path, yaxis, xsize, ysize, grid_spacing, subregion='all', step=1, model_id=None): # Compute correctly spaced cubic splined path points. points = spline_path(path, grid_spacing) axes = path_point_axes(points, yaxis) nx = int(xsize / grid_spacing) + 1 ny = int(ysize / grid_spacing) + 1 nz = len(points) # Create a rectangle of point positions to interpolate at. from numpy import empty, float32, arange section = empty((ny, nx, 3), float32) x = arange(nx, dtype=float32) * grid_spacing - 0.5 * (xsize - 1.0) y = arange(ny, dtype=float32) * grid_spacing - 0.5 * (ysize - 1.0) for j in range(ny): section[j, :, 0] = x for i in range(nx): section[:, i, 1] = y section[:, :, 2] = 0 s = section.reshape((ny * nx, 3)) # Interpolate planes to fill straightened array. from chimerax.geometry import translation m = empty((nz, ny, nx), float32) for k in range(nz): tf = translation(points[k]) * axes[k] m[k, :, :] = volume.interpolated_values(s, tf, subregion=subregion, step=step).reshape((ny, nx)) # Create volume. from chimerax.map_data import ArrayGridData step = [grid_spacing] * 3 origin = [0, 0, 0] g = ArrayGridData(m, origin, step, name='unbend') from chimerax.map import volume_from_grid_data v = volume_from_grid_data(g, volume.session, model_id=model_id) v.copy_settings_from(volume, copy_region=False, copy_active=False, copy_xform=open) return v
def gaussian_convolve(volume, sdev, step = 1, subregion = None, value_type = None, invert = False, modelId = None, task = None, session = None): gg = gaussian_grid(volume, sdev, step, subregion, value_type = value_type, invert = invert, task = task) from chimerax.map import volume_from_grid_data gv = volume_from_grid_data(gg, session, model_id = modelId) gv.copy_settings_from(volume, copy_region = False, copy_colors = False, copy_thresholds = False) volume.display = False # Hide original map return gv
def lattice_models(session, seg, max_surfaces=100): # Map lattice id to dictionary mapping segment index to (descrip, color) lattice_segs = {} for segment in seg.segments: v = segment.three_d_volume if v is not None: lseg = lattice_segs.setdefault(v.lattice_id, {}) if v.value is not None: lseg[int(v.value)] = (segment.biological_annotation, segment.colour) scale, shift = guess_scale_and_shift(seg) # Create Volume model of segment indices for each lattice. models = [] lattices = seg.lattices for i, lattice in enumerate(lattices): d = lattice.data_array # Docs say number array, but emd 1547 gives bytes name = 'region map' if len(lattices) == 1 else 'region map %d' % i from chimerax.map_data import ArrayGridData g = ArrayGridData(d, step=scale, origin=shift, name=name) from chimerax.map import volume_from_grid_data v = volume_from_grid_data(g, session, open_model=False) v.display = False if lattice.id in lattice_segs: v.segments = lseg = lattice_segs[lattice.id] set_segmentation_image_colors(v, lseg) # Make a surface for each segment. regions = list(lseg.items()) regions.sort() surfs = [ segment_surface(v, sindex, descrip, color) for sindex, (descrip, color) in regions[:max_surfaces] ] if surfs: ns, nr = len(surfs), len(regions) sname = ('%d surfaces' % ns) if ns == nr else ('%d of %d surfaces' % (ns, nr)) from chimerax.core.models import Model surf_group = Model(sname, v.session) surf_group.add(surfs) models.append(surf_group) # TODO: Don't see how to get the transform id for the lattice. # EMD 1547 segmentation has two transforms, identity and the # map transform (2.8A voxel size, shift to center) but I didn't # find any place where transform 1 is associated with the lattice. # Appears it should be in segment.three_d_volume.transform_id but this # attribute is optional and is None in this case. models.append(v) return models
def median_filter(volume, bin_size=3, iterations=1, step=1, subregion=None, modelId=None): mg = median_grid(volume, bin_size, iterations, step, subregion) from chimerax.map import volume_from_grid_data mv = volume_from_grid_data(mg, volume.session, model_id=modelId) mv.copy_settings_from(volume, copy_region=False) volume.display = False # Hide original map return mv
def sphere_volume(session, n, r, noise=1.0): from numpy import indices, single as floatc o = 0.5 * (n - 1) i = indices((n, n, n), floatc) - o a = (i[0, ...] * i[0, ...] + i[1, ...] * i[1, ...] + i[2, ...] * i[2, ...] < r * r).astype(floatc) if noise > 0: from numpy.random import normal a += normal(0, noise, a.shape) from chimerax.map_data import ArrayGridData g = ArrayGridData(a, origin=(-o, -o, -o), step=(1, 1, 1), name='sphere') from chimerax.map import volume_from_grid_data v = volume_from_grid_data(g, session) return v
def falloff(volume, iterations = 10, in_place = False, step = 1, subregion = None, modelId = None, session = None): if in_place: if not volume.data.writable: raise ValueError("Can't modify read-only volume %s in place" % volume.name) falloff_matrix(volume.full_matrix(), iterations) volume.matrix_changed() fv = volume else: fg = falloff_grid(volume, iterations, step, subregion) from chimerax.map import volume_from_grid_data fv = volume_from_grid_data(fg, model_id = modelId, session = session) fv.copy_settings_from(volume) volume.display = False # Hide original map return fv
def unroll_operation(v, r0, r1, h, center, axis, gsp, subregion, step, modelId): from math import ceil, pi zsize = int(max(1, ceil(h / gsp))) # cylinder height xsize = int(max(1, ceil((r1 - r0) / gsp))) # slab thickness rmid = 0.5 * (r0 + r1) circum = rmid * 2 * pi ysize = int(max(1, ceil(circum / gsp))) # circumference from chimerax.geometry import normalize_vector axis = normalize_vector(axis) agrid_points = annulus_grid(r0, r1, center, axis, ysize, xsize) grid_points = agrid_points.reshape((ysize * xsize, 3)) grid_points[:] += tuple([-0.5 * h * ai for ai in axis]) # Shift annulus. from numpy import empty values = empty((zsize, ysize, xsize), v.data.value_type) axis_step = tuple([h * float(ai) / (zsize - 1) for ai in axis]) for i in range(zsize): vval = v.interpolated_values(grid_points, subregion=subregion, step=step) values[i, :, :] = vval.reshape((ysize, xsize)) grid_points[:] += axis_step # Shift annulus. from chimerax.map_data import ArrayGridData gstep = (float(r1 - r0) / (xsize - 1), circum / (ysize - 1), float(h) / (zsize - 1)) gorigin = (center[0] + r0, center[1] - 0.5 * circum, center[2] - 0.5 * h) g = ArrayGridData(values, gorigin, gstep, name='unrolled %s' % v.name) from chimerax.map import volume_from_grid_data vu = volume_from_grid_data(g, v.session, model_id=modelId) vu.copy_settings_from(v, copy_region=False, copy_active=False, copy_colors=False) if axis[0] != 0 or axis[1] != 0: # Rotate so unrolled volume is tangential to cylinder from chimerax.geometry import orthonormal_frame vu.position = v.position * orthonormal_frame(axis) return vu
def threshold(volume, minimum=None, set_minimum=None, maximum=None, set_maximum=None, step=1, subregion=None, modelId=None, session=None): tg = threshold_grid(volume, minimum, set_minimum, maximum, set_maximum, step, subregion) from chimerax.map import volume_from_grid_data tv = volume_from_grid_data(tg, model_id=modelId, session=session) tv.copy_settings_from(volume) volume.display = False # Hide original map return tv
def array_to_model(mvol, volume, ijk_origin, model_id): # Create masked volume grid object. from chimerax.map_data import ArrayGridData g = volume.data morigin = g.ijk_to_xyz_transform * ijk_origin m = ArrayGridData(mvol, morigin, g.step, cell_angles=g.cell_angles, rotation=g.rotation, name=g.name + ' masked') # Create masked volume object. from chimerax.map import volume_from_grid_data v = volume_from_grid_data(m, volume.session, model_id=model_id) v.copy_settings_from(volume, copy_region=False) v.show() return v
def flatten(volume, method='multiply linear', step=1, subregion=None, fitregion=None, modelId=None, task=None): fg = flattened_grid(volume, method, step, subregion, fitregion=fitregion, task=task) from chimerax.map import volume_from_grid_data fv = volume_from_grid_data(fg, volume.session, model_id=modelId) fv.copy_settings_from(volume, copy_region=False) volume.display = False # Hide original map return fv
def mlp_map(session, atoms, method, spacing, max_dist, nexp, name, open_map): data, bounds = calculatefimap(atoms, method, spacing, max_dist, nexp) # m.pot is 1-dimensional if m.writedxfile() was called. Has indices in x,y,z order. origin = tuple(xmin for xmin, xmax in bounds) s = spacing step = (s, s, s) from chimerax.map_data import ArrayGridData g = ArrayGridData(data, origin, step, name=name) g.polar_values = True from chimerax.map import volume_from_grid_data v = volume_from_grid_data(g, session, open_model=open_map, show_dialog=open_map) if open_map: v.update_drawings() # Compute surface levels v.set_parameters( surface_colors=[(0, 139 / 255, 139 / 255, 1), (184 / 255, 134 / 255, 11 / 255, 1)]) return v
def laplacian(v, step=None, subregion=None, model_id=None): m = v.matrix(step=step, subregion=subregion) from numpy import float32, multiply, add lm = m.astype(float32) # Copy array multiply(lm, -6.0, lm) add(lm[:-1, :, :], m[1:, :, :], lm[:-1, :, :]) add(lm[1:, :, :], m[:-1, :, :], lm[1:, :, :]) add(lm[:, :-1, :], m[:, 1:, :], lm[:, :-1, :]) add(lm[:, 1:, :], m[:, :-1, :], lm[:, 1:, :]) add(lm[:, :, :-1], m[:, :, 1:], lm[:, :, :-1]) add(lm[:, :, 1:], m[:, :, :-1], lm[:, :, 1:]) lm[0, :, :] = 0 lm[-1, :, :] = 0 lm[:, 0, :] = 0 lm[:, -1, :] = 0 lm[:, :, 0] = 0 lm[:, :, -1] = 0 origin, step = v.data_origin_and_step(subregion=subregion, step=step) d = v.data from chimerax.map_data import ArrayGridData ld = ArrayGridData(lm, origin, step, d.cell_angles, d.rotation, name=v.name + ' Laplacian') ld.polar_values = True from chimerax.map import volume_from_grid_data lv = volume_from_grid_data(ld, v.session, model_id=model_id) lv.copy_settings_from(v, copy_thresholds=False, copy_colors=False) lv.set_parameters(cap_faces=False) v.display = False # Hide original map return lv
def boxes(session, volume, atoms, size = 0, isize = None, use_atom_size = False, step = None, subregion = None, base_model_id = None): vlist = [] vxfinv = volume.position.inverse() ijk_rmin, ijk_rmax = volume.ijk_bounds(step, subregion, integer = True) for i,a in enumerate(atoms): center = vxfinv * a.scene_coord r = 0.5*size if use_atom_size: r += a.radius cubify = True if isize is None else isize ijk_min, ijk_max, ijk_step = volume.bounding_region([center], r, step, cubify = cubify) ijk_min = [max(s,t) for s,t in zip(ijk_min, ijk_rmin)] ijk_max = [min(s,t) for s,t in zip(ijk_max, ijk_rmax)] region = (ijk_min, ijk_max, ijk_step) from chimerax.map.volume import is_empty_region if is_empty_region(region): continue from chimerax.map_data import GridSubregion g = GridSubregion(volume.data, *region) g.name = 'box %s' % str(a) if base_model_id is None: mid = None elif len(atoms) == 1: mid = base_model_id else: mid = base_model_id + (i+1,) from chimerax.map import volume_from_grid_data v = volume_from_grid_data(g, session, model_id = mid, show_dialog = False) v.copy_settings_from(volume, copy_region = False, copy_active = False, copy_zone = False) vlist.append(v) if vlist: if len(atoms) > 1: session.models.add_group(vlist, '%d boxes' % len(vlist), id = base_model_id) else: session.models.add(vlist) return vlist
def local_correlation(map1, map2, window_size, subtract_mean, model_id=None): d1 = map1.data m1 = map1.full_matrix() d2 = map2.data m2, same = map2.interpolate_on_grid(map1) mc = local_correlation_matrix(m1, m2, window_size, subtract_mean) hs = 0.5 * (window_size - 1) origin = tuple(o + hs * s for o, s in zip(d1.origin, d1.step)) from chimerax.map_data import ArrayGridData g = ArrayGridData(mc, origin, d1.step, d1.cell_angles, d2.rotation, name='local correlation') from chimerax.map import volume_from_grid_data mapc = volume_from_grid_data(g, map1.session, model_id=model_id) mapc.position = map1.position return mapc
def zone_volume(volume, points, radius, minimal_bounds=False, invert=False, subregion='all', step=1, model_id=None): region = volume.subregion(step, subregion) from chimerax import map_data sg = map_data.GridSubregion(volume.data, *region) mg = map_data.zone_masked_grid_data(sg, points, radius, invert, minimal_bounds) mg.name = volume.name + ' zone' from chimerax.map import volume_from_grid_data vz = volume_from_grid_data(mg, volume.session, model_id=model_id) vz.copy_settings_from(volume, copy_colors=False, copy_zone=False) volume.display = False return vz
def read_storm(session, filename, name): """Create density maps from STORM microscopy point list files. :param filename: either the name of a file or a file-like object Extra arguments are ignored. """ if hasattr(filename, 'read'): # it's really a file-like object f = filename else: f = open(filename, 'rb') f.readline() # Column headers # Old data: # Columns: Channel Xc Yc Zc Height Area Width Phi Ax BG # New data: # Columns channel Xc Yc Zc h Width BG # h = Gaussian height, width = FWHM, BG = background. points = {} while True: line = f.readline() if not line: break fields = line.split() channel = int(fields[0]) xyzhaw = [float(v) for v in fields[1:7]] points.setdefault(channel, []).append(xyzhaw) maps = [] step = 10 # TODO: Should adjust step based on data extent pad = 10 * step cutoff_range = 5 # Standard deviations grid = None from chimerax.map.molmap import bounding_grid, add_gaussians from chimerax.map import volume_from_grid_data for channel in sorted(points.keys()): plist = points[channel] from numpy import array, float32, sqrt xyzhwb = array(plist, float32) xyz = xyzhwb[:, :3].copy() if grid is None: grid = bounding_grid(xyz, step, pad) else: grid = matching_grid(grid) grid.name = 'channel %d' % channel if channel == 1: grid.rgba = (0, 1.0, 0, 1.0) elif channel == 2: grid.rgba = (1.0, 0, 1.0, 1.0) weights = xyzhwb[:, 3] from math import sqrt, log fwhm_sdev = 2 * sqrt( 2 * log(2)) # Convert full width half max to standard deviations sdev = xyzhwb[:, 4] / fwhm_sdev sdev /= 20 # TODO: Seems the width column is not FWHM. add_gaussians(grid, xyz, weights, sdev, cutoff_range, normalize=False) v = volume_from_grid_data(grid, session, style='image') v.show() maps.append(v) return maps, ( "Opened STORM file %s containing %d channels, %d points" % (f.name, len(points), sum( (len(plist) for plist in points.values()), 0)))