def eval_spharm_grid(self, vendor, coeffs): ''' We evaluate the spherical harmonics on a less sampled grid. This is a spacetime vs accuracy tradeoff. ''' # init the grid first if not self.fovmin: fovmin = globals.siemens_fovmin else: fovmin = self.fovmin if not self.fovmax: fovmax = globals.siemens_fovmax else: fovmax = self.fovmax if not self.numpoints: numpoints = globals.siemens_numpoints else: numpoints = self.numpoints # convert to mm fovmin = fovmin * 1000. fovmax = fovmax * 1000. # the grid in meters. this is needed for spherical harmonics vec = np.linspace(fovmin, fovmax, numpoints) gvx, gvy, gvz = utils.meshgrid(vec, vec, vec) # mm cf = (fovmax - fovmin) / numpoints # deduce the transformation from rcs to grid g_rcs2xyz = np.array( [[0, cf, 0, fovmin], [cf, 0, 0, fovmin], [0, 0, cf, fovmin], [0, 0, 0, 1]], dtype=np.float32 ) # get the grid to rcs transformation also g_xyz2rcs = np.linalg.inv(g_rcs2xyz) # indices into the gradient displacement vol gr, gc, gs = utils.meshgrid(np.arange(numpoints), np.arange(numpoints), np.arange(numpoints), dtype=np.float32) log.info('Evaluating spherical harmonics') log.info('on a ' + str(numpoints) + '^3 grid') log.info('with extents ' + str(fovmin) + 'mm to ' + str(fovmax) + 'mm') gvxyz = CV(gvx, gvy, gvz) _dv, _dxyz = eval_spherical_harmonics(coeffs, vendor, gvxyz) return CV(_dv.x, _dv.y, _dv.z), CV(gr, gc, gs), g_xyz2rcs
def _get_anchor_boxes(self, input_size): '''Compute anchor boxes for each feature map. Args: input_size: (tensor) model input size of (w,h). Returns: boxes: (list) anchor boxes for each feature map. Each of size [#anchors,4], where #anchors = fmw * fmh * #anchors_per_cell ''' num_fms = len(self.anchor_areas) fm_sizes = [(input_size/pow(2.,i+3)).ceil() for i in range(num_fms)] # p3 -> p7 feature map sizes boxes = [] for i in range(num_fms): fm_size = fm_sizes[i] grid_size = input_size / fm_size fm_w, fm_h = int(fm_size[0]), int(fm_size[1]) xy = meshgrid(fm_w,fm_h) + 0.5 # [fm_h*fm_w, 2] xy = (xy*grid_size).view(fm_h,fm_w,1,2).expand(fm_h,fm_w,9,2) wh = self.anchor_wh[i].view(1,1,9,2).expand(fm_h,fm_w,9,2) box = torch.cat([xy,wh], 3) # [x,y,w,h] boxes.append(box.view(-1,4)) return torch.cat(boxes, 0)
def get_anchor_boxes(self, input_size): ''' Compute anchor boxes for each feature map Args: input_size: (tensor) model input size of (z, h, w). Returns: boxes: (tensor) anchor boxes for each feature map. Each of size [#anchors, 6], where #anchors = fm_z * fm_h * fm_w * #anchors_per_cell ''' fm_sizes = [(input_size / pow(2, i + 1)).ceil() for i in range(self.num_levels)] # Compute every size in p3 boxes = [] for i in range(self.num_levels): fm_size = fm_sizes[i] grid_size = (input_size / fm_size).floor() fm_d, fm_h, fm_w = int(fm_size[0]), int(fm_size[1]), int(fm_size[2]) zyx = meshgrid(fm_d, fm_h, fm_w) + 0.5 # [fm_d * fm_h * fm_w, 3] zyx = (zyx * grid_size).view(fm_d, fm_h, fm_w, 1, 3).expand(fm_d, fm_h, fm_w, 9, 3) dhw = self.anchor_edges[i].view(1, 1, 1, 9, 3).expand(fm_d, fm_h, fm_w, 9, 3) box = torch.cat([zyx, dhw], 4) # (fm_d, fm_h, fm_w, 9, 6) boxes.append(box.view(-1, 6)) # (num_levels, fm_d*fm_h*fm_w*9, 6) return torch.cat(boxes, 0) # (num_levels*fm_d*fm_h*fm_w*9, 6) -> (36864, 6)
def non_linear_unwarp_siemens(self, volshape, dv, dxyz, m_rcs2lai, m_rcs2lai_nohalf, g_xyz2rcs): ''' Performs the crux of the unwarping. It's agnostic to Siemens or GE and uses more functions to do the processing separately. Needs self.vendor, self.coeffs, self.warp, self.nojac to be set Parameters ---------- vxyz : CoordsVector (namedtuple) contains np.array has 3 elements x,y and z each representing the grid coordinates dxyz : CoordsVector (namedtuple) differential coords vector Returns ------- TODO still vague what to return vwxyz : CoordsVector (namedtuple) contains np.array x,y and z coordinates of the unwarped coordinates vjacmult_lps : np.array the jacobian multiplier (determinant) ''' log.info('Evaluating the jacobian multiplier') nr, nc, ns = self.vol.shape[:3] if not self.nojac: jim2 = np.zeros((nr, nc), dtype=np.float32) vjacdet_lpsw = np.zeros((nr, nc), dtype=np.float32) if dxyz == 0: vjacdet_lps = 1 else: vjacdet_lps = eval_siemens_jacobian_mult(dv, dxyz) # essentially pre-allocating everything out = np.zeros((nr, nc, ns), dtype=np.float32) fullWarp = np.zeros((nr, nc, ns, 3), dtype=np.float32) vjacout = np.zeros((nr, nc, ns), dtype=np.float32) im2 = np.zeros((nr, nc), dtype=np.float32) dvx = np.zeros((nr, nc), dtype=np.float32) dvy = np.zeros((nr, nc), dtype=np.float32) dvz = np.zeros((nr, nc), dtype=np.float32) im_ = np.zeros((nr, nc), dtype=np.float32) # init jacobian temp image vc, vr = utils.meshgrid(np.arange(nc), np.arange(nr)) log.info('Unwarping slice by slice') # for every slice for s in xrange(ns): # pretty print sys.stdout.flush() if (s + 1) % 10 == 0: print s + 1, else: print '.', # hopefully, free memory gc.collect() # init to 0 dvx.fill(0.) dvy.fill(0.) dvz.fill(0.) im_.fill(0.) vs = np.ones(vr.shape) * s vrcs = CV(vr, vc, vs) vxyz = utils.transform_coordinates(vrcs, m_rcs2lai_nohalf) vrcsg = utils.transform_coordinates(vxyz, g_xyz2rcs) ndimage.interpolation.map_coordinates(dv.x, vrcsg, output=dvx, order=self.order) ndimage.interpolation.map_coordinates(dv.y, vrcsg, output=dvy, order=self.order) ndimage.interpolation.map_coordinates(dv.z, vrcsg, output=dvz, order=self.order) # new locations of the image voxels in XYZ ( LAI ) coords #dvx.fill(0.) #dvy.fill(0.) #dvz.fill(0.) vxyzw = CV(x=vxyz.x + self.polarity * dvx, y=vxyz.y + self.polarity * dvy, z=vxyz.z + self.polarity * dvz) # convert the locations got into RCS indices vrcsw = utils.transform_coordinates(vxyzw, np.linalg.inv(m_rcs2lai)) # map the internal voxel coordinates to FSL scaled mm coordinates pixdim1 = float((subprocess.Popen( ['fslval', self.name, 'pixdim1'], stdout=subprocess.PIPE).communicate()[0]).strip()) pixdim2 = float((subprocess.Popen( ['fslval', self.name, 'pixdim2'], stdout=subprocess.PIPE).communicate()[0]).strip()) pixdim3 = float((subprocess.Popen( ['fslval', self.name, 'pixdim3'], stdout=subprocess.PIPE).communicate()[0]).strip()) dim1 = float((subprocess.Popen( ['fslval', self.name, 'dim1'], stdout=subprocess.PIPE).communicate()[0]).strip()) outputOrient = subprocess.Popen( ['fslorient', self.name], stdout=subprocess.PIPE).communicate()[0] if outputOrient.strip() == 'NEUROLOGICAL': # if neurological then flip x coordinate (both here in premat and later in postmat) m_vox2fsl = np.array( [[-1.0 * pixdim1, 0.0, 0.0, pixdim1 * (dim1 - 1)], [0.0, pixdim2, 0.0, 0.0], [0.0, 0.0, pixdim3, 0.0], [0.0, 0.0, 0.0, 1.0]], dtype=np.float) else: m_vox2fsl = np.array( [[pixdim1, 0.0, 0.0, 0.0], [0.0, pixdim2, 0.0, 0.0], [0.0, 0.0, pixdim3, 0.0], [0.0, 0.0, 0.0, 1.0]], dtype=np.float) vfsl = utils.transform_coordinates(vrcsw, m_vox2fsl) #im_ = utils.interp3(self.vol, vrcsw.x, vrcsw.y, vrcsw.z) ndimage.interpolation.map_coordinates(self.vol, vrcsw, output=im_, order=self.order) # find NaN voxels, and set them to 0 im_[np.where(np.isnan(im_))] = 0. im_[np.where(np.isinf(im_))] = 0. im2[vr, vc] = im_ #img = nib.Nifti1Image(dvx,np.eye(4)) #nib.save(img,"x"+str(s).zfill(3)+".nii.gz") #img = nib.Nifti1Image(dvy,np.eye(4)) #nib.save(img,"y"+str(s).zfill(3)+".nii.gz") #img = nib.Nifti1Image(dvz,np.eye(4)) #nib.save(img,"z"+str(s).zfill(3)+".nii.gz") # Multiply the intensity with the Jacobian det, if needed if not self.nojac: vjacdet_lpsw.fill(0.) jim2.fill(0.) # if polarity is negative, the jacobian is also inversed if self.polarity == -1: vjacdet_lps = 1. / vjacdet_lps ndimage.interpolation.map_coordinates(vjacdet_lps, vrcsg, output=vjacdet_lpsw, order=self.order) vjacdet_lpsw[np.where(np.isnan(vjacdet_lpsw))] = 0. vjacdet_lpsw[np.where(np.isinf(vjacdet_lpsw))] = 0. jim2[vr, vc] = vjacdet_lpsw im2 = im2 * jim2 vjacout[..., s] = jim2 fullWarp[..., s, 0] = vfsl.x fullWarp[..., s, 1] = vfsl.y fullWarp[..., s, 2] = vfsl.z out[..., s] = im2 print img = nib.Nifti1Image(fullWarp, self.m_rcs2ras) nib.save(img, "fullWarp_abs.nii.gz") # return image and the jacobian return out, vjacout
def run(self): ''' ''' #pdb.set_trace() # define polarity based on the warp requested self.polarity = 1. if self.warp: self.polarity = -1. # transform RAS-coordinates into LAI-coordinates m_ras2lai = np.array([[-1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], [0.0, 0.0, 0.0, 1.0]], dtype=np.float) m_rcs2lai = np.dot(m_ras2lai, self.m_rcs2ras) # indices of image volume nr, nc, ns = self.vol.shape[:3] vc3, vr3, vs3 = utils.meshgrid(np.arange(nr), np.arange(nc), np.arange(ns), dtype=np.float32) vrcs = CV(x=vr3, y=vc3, z=vs3) vxyz = utils.transform_coordinates(vrcs, m_rcs2lai) # account for half-voxel shift in R and C directions halfvox = np.zeros((4, 4)) halfvox[0, 3] = m_rcs2lai[0, 0] / 2.0 halfvox[1, 3] = m_rcs2lai[1, 1] / 2.0 m_rcs2lai = m_rcs2lai + halfvox # extract rotational and scaling parts of the transformation matrix # ignore the translation part r_rcs2lai = np.eye(4, 4) r_rcs2lai[:3, :3] = m_rcs2lai[:3, :3] # Jon Polimeni: # Since partial derivatives in Jacobian Matrix are differences # that depend on the ordering of the elements of the 3D array, the # coordinates may increase in the opposite direction from the array # indices, in which case the differential element should be negative. # The differentials can be determined by mapping a vector of 1s # through rotation and scaling, where any mirror # will impose a negation ones = CV(1., 1., 1.) dxyz = utils.transform_coordinates_old(ones, r_rcs2lai) # # convert image vol coordinates from RAS to LAI # vxyz = utils.transform_coordinates(vrcs, m_rcs2lai) # # compute new coordinates and the jacobian determinant # # TODO still not clear about what to return # self.out, self.vjacmult_lps = self.non_linear_unwarp(vxyz, dxyz, # m_rcs2lai) # for each slice ''' for slice in xrange(ns): sys.stdout.flush() if (slice + 1) % 10 == 0: print(slice + 1), else: print('.'), # we are doing it slice by slice vs = np.ones(vr.shape) * slice vrcs2d = CV(vr, vc, slice) # rcs2lai vxyz2d = utils.transform_coordinates(vrcs2d, m_rcs2lai) # compute new coordinates and the jacobian determinant moddv, modxyz = eval_spherical_harmonics(self.coeffs, self.vendor, vxyz2d) dvx[..., slice] = moddv.x dvy[..., slice] = moddv.y dvz[..., slice] = moddv.z ''' print # Evaluate spherical harmonics on a smaller grid dv, grcs, g_xyz2rcs = self.eval_spharm_grid(self.vendor, self.coeffs) # do the nonlinear unwarp self.out, self.vjacmult_lps = self.non_linear_unwarp(vxyz, grcs, dv, dxyz, m_rcs2lai, g_xyz2rcs)
def save_disagg_to_csv(nrml_disaggregation, output_dir, plot): """ Save disaggregation matrices to multiple .csv files. """ metadata, matrices = parse_nrml_disaggregation_file(nrml_disaggregation) skip_keys = ('Mag', 'Dist', 'Lon', 'Lat', 'Eps', 'TRT') base_header = ','.join( '%s=%s' % (key, value) for key, value in metadata.items() if value is not None and key not in skip_keys ) for disag_type, (poe, iml, matrix) in matrices.items(): header = '# %s,poe=%s,iml=%s\n' % (base_header, poe, iml) if disag_type == 'Mag,Lon,Lat': matrix = numpy.swapaxes(matrix, 0, 1) matrix = numpy.swapaxes(matrix, 1, 2) disag_type = 'Lon,Lat,Mag' variables = disag_type axis = [metadata[v] for v in variables] header += ','.join(v for v in variables) header += ',poe' # compute axis mid points axis = [(ax[: -1] + ax[1:]) / 2. if ax.dtype == float else ax for ax in axis] values = None if len(axis) == 1: values = numpy.array([axis[0], matrix.flatten()]).T else: try: grids = numpy.meshgrid(*axis, indexing='ij') except: grids = utils.meshgrid(*axis, indexing='ij') values = [g.flatten() for g in grids] values.append(matrix.flatten()) values = numpy.array(values).T output_file = '%s/%s.csv' % (output_dir, '_'.join(disag_type)) f = open(output_file, 'w') f.write(header+'\n') numpy.savetxt(f, values, fmt='%s', delimiter=',') f.close() if plot: call(['gmtset', 'LABEL_OFFSET=0.6c']) if disag_type == 'Mag': plot_1d_hist(output_file, 'Magnitude', '') elif disag_type == 'Dist': plot_1d_hist(output_file, 'JB distance', '') elif disag_type == 'TRT': ntrt = metadata['TRT'].size bin_edges = range(ntrt) annotation_file = open("annotation.dat", 'w') for i in range(ntrt): annotation_file.write( "%s %s %s %s %s %s %s\n" % ( bin_edges[i], numpy.max(matrix) + 0.05 * numpy.max(matrix), 12, 0.0, 0, 'MC', metadata['TRT'][i])) annotation_file.close() plot_1d_hist(output_file, 'Tectonic Region', '', annotation_file.name) elif disag_type == 'Mag,Dist': plot_2d_hist(output_file, 'Magnitude', 'JB distance', '') elif disag_type == 'Lon,Lat': plot_2d_hist(output_file, 'Longitude', 'Latitude', '') elif disag_type == 'Mag,Dist,Eps': plot_3d_hist( output_file, 'Magnitude', 'Distance', 'Epsilon', '') elif disag_type == 'Lon,Lat,Eps': plot_3d_hist( output_file, 'Longitude', 'Latitude', 'Epsilon', '') elif disag_type == 'Lon,Lat,Mag': plot_3d_hist( output_file, 'Longitude', 'Latitude', 'Magnitude', '') elif disag_type == 'Lon,Lat,TRT': plot_3d_hist( output_file, 'Longitude', 'Latitude', '', '')
def non_linear_unwarp_siemens(self, volshape, dv, dxyz, m_rcs2lai, g_xyz2rcs): ''' Performs the crux of the unwarping. It's agnostic to Siemens or GE and uses more functions to do the processing separately. Needs self.vendor, self.coeffs, self.warp, self.nojac to be set Parameters ---------- vxyz : CoordsVector (namedtuple) contains np.array has 3 elements x,y and z each representing the grid coordinates dxyz : CoordsVector (namedtuple) differential coords vector Returns ------- TODO still vague what to return vwxyz : CoordsVector (namedtuple) contains np.array x,y and z coordinates of the unwarped coordinates vjacmult_lps : np.array the jacobian multiplier (determinant) ''' log.info('Evaluating the jacobian multiplier') nr, nc, ns = self.vol.shape[:3] if not self.nojac: jim2 = np.zeros((nr, nc), dtype=np.float32) vjacdet_lpsw = np.zeros((nr, nc), dtype=np.float32) if dxyz == 0: vjacdet_lps = 1 else: vjacdet_lps = eval_siemens_jacobian_mult(dv, dxyz) # essentially pre-allocating everything out = np.zeros((nr, nc, ns), dtype=np.float32) fullWarp = np.zeros((nr, nc, ns, 3), dtype=np.float32) vjacout = np.zeros((nr, nc, ns), dtype=np.float32) im2 = np.zeros((nr, nc), dtype=np.float32) dvx = np.zeros((nr, nc), dtype=np.float32) dvy = np.zeros((nr, nc), dtype=np.float32) dvz = np.zeros((nr, nc), dtype=np.float32) im_ = np.zeros((nr, nc), dtype=np.float32) # init jacobian temp image vc, vr = utils.meshgrid(np.arange(nc), np.arange(nr)) log.info('Unwarping slice by slice') # for every slice for s in xrange(ns): # pretty print sys.stdout.flush() if (s+1) % 10 == 0: print s+1, else: print '.', # hopefully, free memory gc.collect() # init to 0 dvx.fill(0.) dvy.fill(0.) dvz.fill(0.) im_.fill(0.) vs = np.ones(vr.shape) * s vrcs = CV(vr, vc, vs) vxyz = utils.transform_coordinates(vrcs, m_rcs2lai) vrcsg = utils.transform_coordinates(vxyz, g_xyz2rcs) ndimage.interpolation.map_coordinates(dv.x, vrcsg, output=dvx, order=self.order) ndimage.interpolation.map_coordinates(dv.y, vrcsg, output=dvy, order=self.order) ndimage.interpolation.map_coordinates(dv.z, vrcsg, output=dvz, order=self.order) # new locations of the image voxels in XYZ ( LAI ) coords #dvx.fill(0.) #dvy.fill(0.) #dvz.fill(0.) vxyzw = CV(x=vxyz.x + self.polarity * dvx, y=vxyz.y + self.polarity * dvy, z=vxyz.z + self.polarity * dvz) # convert the locations got into RCS indices vrcsw = utils.transform_coordinates(vxyzw, np.linalg.inv(m_rcs2lai)) # map the internal voxel coordinates to FSL scaled mm coordinates pixdim1=float((subprocess.Popen(['fslval', self.name,'pixdim1'], stdout=subprocess.PIPE).communicate()[0]).strip()) pixdim2=float((subprocess.Popen(['fslval', self.name,'pixdim2'], stdout=subprocess.PIPE).communicate()[0]).strip()) pixdim3=float((subprocess.Popen(['fslval', self.name,'pixdim3'], stdout=subprocess.PIPE).communicate()[0]).strip()) dim1=float((subprocess.Popen(['fslval', self.name,'dim1'], stdout=subprocess.PIPE).communicate()[0]).strip()) outputOrient=subprocess.Popen(['fslorient', self.name], stdout=subprocess.PIPE).communicate()[0] if outputOrient.strip() == 'NEUROLOGICAL': # if neurological then flip x coordinate (both here in premat and later in postmat) m_vox2fsl = np.array([[-1.0*pixdim1, 0.0, 0.0, pixdim1*(dim1-1)], [0.0, pixdim2, 0.0, 0.0], [0.0, 0.0, pixdim3, 0.0], [0.0, 0.0, 0.0, 1.0]], dtype=np.float) else: m_vox2fsl = np.array([[pixdim1, 0.0, 0.0, 0.0], [0.0, pixdim2, 0.0, 0.0], [0.0, 0.0, pixdim3, 0.0], [0.0, 0.0, 0.0, 1.0]], dtype=np.float) vfsl = utils.transform_coordinates(vrcsw, m_vox2fsl) #im_ = utils.interp3(self.vol, vrcsw.x, vrcsw.y, vrcsw.z) ndimage.interpolation.map_coordinates(self.vol, vrcsw, output=im_, order=self.order) # find NaN voxels, and set them to 0 im_[np.where(np.isnan(im_))] = 0. im_[np.where(np.isinf(im_))] = 0. im2[vr, vc] = im_ #img = nib.Nifti1Image(dvx,np.eye(4)) #nib.save(img,"x"+str(s).zfill(3)+".nii.gz") #img = nib.Nifti1Image(dvy,np.eye(4)) #nib.save(img,"y"+str(s).zfill(3)+".nii.gz") #img = nib.Nifti1Image(dvz,np.eye(4)) #nib.save(img,"z"+str(s).zfill(3)+".nii.gz") # Multiply the intensity with the Jacobian det, if needed if not self.nojac: vjacdet_lpsw.fill(0.) jim2.fill(0.) # if polarity is negative, the jacobian is also inversed if self.polarity == -1: vjacdet_lps = 1. / vjacdet_lps ndimage.interpolation.map_coordinates(vjacdet_lps, vrcsg, output=vjacdet_lpsw, order=self.order) vjacdet_lpsw[np.where(np.isnan(vjacdet_lpsw))] = 0. vjacdet_lpsw[np.where(np.isinf(vjacdet_lpsw))] = 0. jim2[vr, vc] = vjacdet_lpsw im2 = im2 * jim2 vjacout[..., s] = jim2 fullWarp[...,s,0]=vfsl.x fullWarp[...,s,1]=vfsl.y fullWarp[...,s,2]=vfsl.z out[..., s] = im2 print img=nib.Nifti1Image(fullWarp,self.m_rcs2ras) nib.save(img,"fullWarp_abs.nii.gz") # return image and the jacobian return out, vjacout