def data_xform(self, slice_idx, zyx_coords): """Given the a sliceplots slicing index and the current vox position, get a slice of data. """ slicer = [slice(0,d) for d in self.shape] slicer[slice_idx] = self.zyx2vox(zyx_coords)[slice_idx] slicer = tuple(slicer) Msub = self.plane_xform(slice_idx) Msub = reverse(reverse(Msub, axis=-1), axis=-2) xform = compose_xform(Msub) return xform(self[slicer])
def transform(self, new_mapping=None, transform=None, force=False): """Updates the voxel to real-space transform. There are two modes of usage-- 1) supply a new voxel to real mapping. In this case a voxel to voxel transform is found, and the image is rotated in-plane around the origin. Only transposes and reflections are supported. Image info is updated appropriately 2) supply a real to real transform to apply to the current mapping. In this case the data is not updated, but the mapping is updated. """ if new_mapping is None and transform is None: return if new_mapping is not None and transform is not None: print """ The user must specify either a new mapping to convert to, or a transform to apply, but cannot specify both.""" return if transform is not None: # this doesn't change the image array, it just updates the # transformation old_xform = self.orientation_xform.tomatrix() if isinstance(transform, Quaternion): transform = transform.tomatrix() dim_scale = np.array([self.isize, self.jsize, self.ksize]) r0 = np.array([self.x0, self.y0, self.z0]) origin_voxels = np.round(np.linalg.solve(old_xform*dim_scale, -r0)) # now derive r0 again.. Tmap*(i,j,k)^T + r0^T = (x,y,z)^T r0 = -np.dot(transform*dim_scale, origin_voxels) self.x0, self.y0, self.z0 = r0 self.orientation_xform = Quaternion(M=transform) return # else handle the new mapping from recon.slicerimage import nearest_simple_transform # Tr already maps [i,j,k]^T into [R,A,S] ... # Here I want to change coordinates with this relationship: # Tr*[i,j,k]^T = Tnew*[i',j',k']^T # so Tnew*(Tvx*[i,j,k]^T) = Tr*[i,j,k]^T # So inv(Tnew)*Tr*[i,j,k] = [i',j',k'] = orientation of choice! # The task is to analyze Tvx = (Tnew^-1 * Tr) to get rotation Tr = self.orientation_xform.tomatrix() Tvx = np.linalg.solve(new_mapping, Tr) Tvxp = nearest_simple_transform(Tvx) # allow a goodly amount of wiggle room for each element of the # rotation matrix if not np.allclose(Tvxp, Tvx, atol=1e-4): # Tvxp might be a good choice in this case.. so could suggest # Tnew'*Tvxp = Tr # (Tvxp^T * Tnew'^T) = Tr^T # Tnew' = solve(Tvxp^T, Tr^T)^T Tnew_suggest = np.linalg.solve(Tvxp.T, Tr.T).T if not force: raise ValueError("""This method will not transform the data to the stated new mapping because the transformation cannot be accomplished through transposes and reflections. The closest new mapping you can perform is:\n"""+str(Tnew_suggest)) else: print """It is not possible to rotate simply to the stated mapping; proceeding with this mapping:\n"""+str(Tnew_suggest)+\ """\nbut lying about the final mapping.""" #new_mapping = Tnew_suggest Tvx = Tvxp else: # if Tvx is adequately close to its trimmed version, # let's go ahead and use the simple transform Tvx = Tvxp if not Tvx[-1,-1]: raise ValueError("This operation only makes in-plane rotations. "\ "EG you cannot use the sagittal transform for "\ "an image in the coronal plane.") if (Tvx==np.identity(3)).all(): print "Already in place, not transforming" return # this is for simple mixing of indices Tvx_abs = np.abs(Tvx) r0 = np.array([self.x0, self.y0, self.z0]) dvx_0 = np.array([self.isize, self.jsize, self.ksize]) # mix up the voxel sizes dvx_1 = np.dot(Tvx_abs, dvx_0) (self.isize, self.jsize, self.ksize) = dvx_1 dim_sizes = np.array(self.shape[-3:][::-1]) # solve for (i,j,k) where Tr*(i,j,k)^T + r0 = (0,0,0) # columns are i,j,k space, so scale columns by vox sizes vx_0 = np.linalg.solve(Tr*dvx_0, -r0) # transform the voxels -- # can normalize to {0..I-1},{0..J-1},{0..K-1} due to periodicity vx_1 = (np.dot(Tvx, vx_0) + dim_sizes) % dim_sizes r0_prime = -np.dot(new_mapping*dvx_1, vx_1) (self.x0, self.y0, self.z0) = r0_prime if self.shape[-1] != self.shape[-2]: func = compose_xform(Tvx, view=False, square=False) if self.tdim: new_shape = (self.tdim,) else: new_shape = () new_shape += tuple(np.dot(Tvx_abs, self.shape[::-1]).astype('i'))[::-1] temp = func(self[:]) self.resize(new_shape) self[:] = temp.copy() del temp else: func = compose_xform(Tvx, view=False) func(self[:]) self.orientation_xform = Quaternion(M=new_mapping)
def transform(self, new_mapping=None, transform=None, force=False): """Updates the voxel to real-space transform. There are two modes of usage-- 1) supply a new voxel to real mapping. In this case a voxel to voxel transform is found, and the image is rotated in-plane around the origin. Only transposes and reflections are supported. Image info is updated appropriately 2) supply a real to real transform to apply to the current mapping. In this case the data is not updated, but the mapping is updated. """ if new_mapping is None and transform is None: return if new_mapping is not None and transform is not None: print """ The user must specify either a new mapping to convert to, or a transform to apply, but cannot specify both.""" return if transform is not None: # this doesn't change the image array, it just updates the # transformation old_xform = self.orientation_xform.tomatrix() if isinstance(transform, Quaternion): transform = transform.tomatrix() dim_scale = np.array([self.isize, self.jsize, self.ksize]) r0 = np.array([self.x0, self.y0, self.z0]) origin_voxels = np.round( np.linalg.solve(old_xform * dim_scale, -r0)) # now derive r0 again.. Tmap*(i,j,k)^T + r0^T = (x,y,z)^T r0 = -np.dot(transform * dim_scale, origin_voxels) self.x0, self.y0, self.z0 = r0 self.orientation_xform = Quaternion(M=transform) return # else handle the new mapping from recon.slicerimage import nearest_simple_transform # Tr already maps [i,j,k]^T into [R,A,S] ... # Here I want to change coordinates with this relationship: # Tr*[i,j,k]^T = Tnew*[i',j',k']^T # so Tnew*(Tvx*[i,j,k]^T) = Tr*[i,j,k]^T # So inv(Tnew)*Tr*[i,j,k] = [i',j',k'] = orientation of choice! # The task is to analyze Tvx = (Tnew^-1 * Tr) to get rotation Tr = self.orientation_xform.tomatrix() Tvx = np.linalg.solve(new_mapping, Tr) Tvxp = nearest_simple_transform(Tvx) # allow a goodly amount of wiggle room for each element of the # rotation matrix if not np.allclose(Tvxp, Tvx, atol=1e-4): # Tvxp might be a good choice in this case.. so could suggest # Tnew'*Tvxp = Tr # (Tvxp^T * Tnew'^T) = Tr^T # Tnew' = solve(Tvxp^T, Tr^T)^T Tnew_suggest = np.linalg.solve(Tvxp.T, Tr.T).T if not force: raise ValueError("""This method will not transform the data to the stated new mapping because the transformation cannot be accomplished through transposes and reflections. The closest new mapping you can perform is:\n""" + str(Tnew_suggest)) else: print """It is not possible to rotate simply to the stated mapping; proceeding with this mapping:\n"""+str(Tnew_suggest)+\ """\nbut lying about the final mapping.""" #new_mapping = Tnew_suggest Tvx = Tvxp else: # if Tvx is adequately close to its trimmed version, # let's go ahead and use the simple transform Tvx = Tvxp if not Tvx[-1, -1]: raise ValueError("This operation only makes in-plane rotations. "\ "EG you cannot use the sagittal transform for "\ "an image in the coronal plane.") if (Tvx == np.identity(3)).all(): print "Already in place, not transforming" return # this is for simple mixing of indices Tvx_abs = np.abs(Tvx) r0 = np.array([self.x0, self.y0, self.z0]) dvx_0 = np.array([self.isize, self.jsize, self.ksize]) # mix up the voxel sizes dvx_1 = np.dot(Tvx_abs, dvx_0) (self.isize, self.jsize, self.ksize) = dvx_1 dim_sizes = np.array(self.shape[-3:][::-1]) # solve for (i,j,k) where Tr*(i,j,k)^T + r0 = (0,0,0) # columns are i,j,k space, so scale columns by vox sizes vx_0 = np.linalg.solve(Tr * dvx_0, -r0) # transform the voxels -- # can normalize to {0..I-1},{0..J-1},{0..K-1} due to periodicity vx_1 = (np.dot(Tvx, vx_0) + dim_sizes) % dim_sizes r0_prime = -np.dot(new_mapping * dvx_1, vx_1) (self.x0, self.y0, self.z0) = r0_prime if self.shape[-1] != self.shape[-2]: func = compose_xform(Tvx, view=False, square=False) if self.tdim: new_shape = (self.tdim, ) else: new_shape = () new_shape += tuple(np.dot(Tvx_abs, self.shape[::-1]).astype('i'))[::-1] temp = func(self[:]) self.resize(new_shape) self[:] = temp.copy() del temp else: func = compose_xform(Tvx, view=False) func(self[:]) self.orientation_xform = Quaternion(M=new_mapping)