def saveROIFunc(self): #save out segmentation aff = self.affine2 outImage = deepcopy(self.segImg)#np.rot90(np.fliplr(self.segImg),-1) [x_si,y_si,z_si] = np.shape(outImage) #This method works (for fastsegs)... but need more robust #for i in range(0,z_si): # outImage[:,:,i] = np.rot90(self.segImg[:,:,z_si-1-i],-1) #try new method (more robust to header and affine mix-ups) ornt = orx.axcodes2ornt((self.Orx,self.Ory,self.Orz)) refOrnt = orx.axcodes2ornt(('R','S','A')) newOrnt = orx.ornt_transform(refOrnt,ornt) #reversed these outImage= orx.apply_orientation(np.rot90(np.fliplr(outImage),-1),newOrnt) #outImage = orx.apply_orientation(outImage,newOrnt) #outImage = np.rot90(np.fliplr(outImage),-1) new_image = nib.Nifti1Image(outImage,aff) os.chdir(os.path.dirname(str(self.getFileNAME))) self.roiSaveName=QFileDialog.getSaveFileName() #nib.save(new_image,fileNAME[:-7]+'_FASTseg_TK_edit.nii.gz') nib.save(new_image,str(self.roiSaveName))
def get_affine_trackvis_to_rasmm(header): """ Get affine mapping trackvis voxelmm space to RAS+ mm space The streamlines in a trackvis file are in 'voxelmm' space, where the coordinates refer to the corner of the voxel. Compute the affine matrix that will bring them back to RAS+ mm space, where the coordinates refer to the center of the voxel. Parameters ---------- header : dict or ndarray Dict or numpy structured array containing trackvis header. Returns ------- aff_tv2ras : shape (4, 4) array Affine array mapping coordinates in 'voxelmm' space to RAS+ mm space. """ # TRK's streamlines are in 'voxelmm' space, we will compute the # affine matrix that will bring them back to RAS+ and mm space. affine = np.eye(4) # The affine matrix found in the TRK header requires the points to # be in the voxel space. # voxelmm -> voxel scale = np.eye(4) scale[range(3), range(3)] /= header[Field.VOXEL_SIZES] affine = np.dot(scale, affine) # TrackVis considers coordinate (0,0,0) to be the corner of the # voxel whereas streamlines returned assumes (0,0,0) to be the # center of the voxel. Thus, streamlines are shifted by half a voxel. offset = np.eye(4) offset[:-1, -1] -= 0.5 affine = np.dot(offset, affine) # If the voxel order implied by the affine does not match the voxel # order in the TRK header, change the orientation. # voxel (header) -> voxel (affine) vox_order = header[Field.VOXEL_ORDER] # Input header can be dict or structured array if hasattr(vox_order, 'item'): # structured array vox_order = header[Field.VOXEL_ORDER].item() affine_ornt = "".join(aff2axcodes(header[Field.VOXEL_TO_RASMM])) header_ornt = axcodes2ornt(vox_order.decode('latin1').upper()) affine_ornt = axcodes2ornt(affine_ornt) ornt = nib.orientations.ornt_transform(header_ornt, affine_ornt) M = nib.orientations.inv_ornt_aff(ornt, header[Field.DIMENSIONS]) affine = np.dot(M, affine) # Applied the affine found in the TRK header. # voxel -> rasmm voxel_to_rasmm = header[Field.VOXEL_TO_RASMM] affine_voxmm_to_rasmm = np.dot(voxel_to_rasmm, affine) return affine_voxmm_to_rasmm.astype(np.float32)
def rescale_centroids(ctd_list, img, voxel_spacing=(1, 1, 1)): """rescale centroid coordinates to new spacing in current x-y-z-orientation Parameters: ---------- ctd_list: list of centroids img: nibabel image voxel_spacing: desired spacing Returns: ---------- out_list: rescaled list of centroids """ ornt_img = nio.io_orientation(img.affine) ornt_ctd = nio.axcodes2ornt(ctd_list[0]) if np.array_equal(ornt_img, ornt_ctd): zms = img.header.get_zooms() else: ornt_trans = nio.ornt_transform(ornt_img, ornt_ctd) aff_trans = nio.inv_ornt_aff(ornt_trans, img.dataobj.shape) new_aff = np.matmul(img.affine, aff_trans) zms = nib.affines.voxel_sizes(new_aff) ctd_arr = np.transpose(np.asarray(ctd_list[1:])) v_list = ctd_arr[0].astype(int).tolist() # vertebral labels ctd_arr = ctd_arr[1:] ctd_arr[0] = np.around(ctd_arr[0] * zms[0] / voxel_spacing[0], decimals=1) ctd_arr[1] = np.around(ctd_arr[1] * zms[1] / voxel_spacing[1], decimals=1) ctd_arr[2] = np.around(ctd_arr[2] * zms[2] / voxel_spacing[2], decimals=1) out_list = [ctd_list[0]] ctd_list = np.transpose(ctd_arr).tolist() for v, ctd in zip(v_list, ctd_list): out_list.append([v] + ctd) print("[*] Rescaled centroid coordinates to spacing (x, y, z) =", voxel_spacing, "mm") return out_list
def reorient_to(img, axcodes_to=('P', 'I', 'R'), verb=False): """Reorients the nifti from its original orientation to another specified orientation Parameters: ---------- img: nibabel image axcodes_to: a tuple of 3 characters specifying the desired orientation Returns: ---------- newimg: The reoriented nibabel image """ aff = img.affine arr = np.asanyarray(img.dataobj, dtype=img.dataobj.dtype) ornt_fr = nio.io_orientation(aff) ornt_to = nio.axcodes2ornt(axcodes_to) ornt_trans = nio.ornt_transform(ornt_fr, ornt_to) arr = nio.apply_orientation(arr, ornt_trans) aff_trans = nio.inv_ornt_aff(ornt_trans, arr.shape) newaff = np.matmul(aff, aff_trans) newimg = nib.Nifti1Image(arr, newaff) if verb: print("[*] Image reoriented from", nio.ornt2axcodes(ornt_fr), "to", axcodes_to) return newimg
def read_nifti_series(filename): proxy_img = nib.load(filename) # less efficent get image data into memory all at once # image_data = proxy_img.get_fdata() hdr = proxy_img.header image_shape = hdr.get_data_shape() image_dim = len(image_shape) num_images = 1 if image_dim >= 3: num_images = image_shape[2] (m, b) = hdr.get_slope_inter() axcodes = nib.aff2axcodes(proxy_img.affine) # TODO-- There does not seem to be a good NIfti method to obtain the input volume's labels? if ((axcodes != ('R', 'A', 'S')) and (axcodes != ('L', 'A', 'S')) and (axcodes != ('L', 'P', 'S'))): print( "Input NIfti series is in unsupported orientation. Please convert to RAS, LAS, or LPS orientation:" + filename) sys.exit(1) # specifiy LPS for DICOM # https://nipy.org/nibabel/dicom/dicom_orientation.html, if we want to apply the formula above to array indices in pixel_array, we first have to apply a column / row flip to the indices. codes = ('L', 'P', 'S') labels = (('A', 'P'), ('R', 'L'), ('I', 'S')) orients = orientations.axcodes2ornt(codes, labels) img_reorient = proxy_img.as_reoriented(orients) hdr = img_reorient.header # We reset m and b here ourselves for downstream rescale/slope b = 0 m = 1 return img_reorient, hdr, num_images, b, m, axcodes
def write_nifti_mask(img_reorient, axcodes, mask_data, outputdirectory, base_fname, filepattern=".nii"): # We only currently support NIfti LAS and RAS orientations # TODO-- To support more NIfti orientations add more key/values to nifti_in_codes_labels nifti_in_codes_labels = { ('L', 'A', 'S'): (('P', 'A'), ('R', 'L'), ('I', 'S')), ('R', 'A', 'S'): (('P', 'A'), ('L', 'R'), ('I', 'S')), ('L', 'P', 'S'): (('A', 'P'), ('R', 'L'), ('I', 'S')) } filename = outputdirectory + os.path.sep + base_fname + "_mask1" + filepattern new_header = header = img_reorient.header.copy() new_header.set_slope_inter(1, 0) # no scaling new_header['cal_min'] = np.min(mask_data) new_header['cal_max'] = np.max(mask_data) new_header['bitpix'] = 16 new_header['descrip'] = "NIfti mask volume from Caffe 1.0" mask2 = np.zeros(img_reorient.shape, MASK_DTYPE) mask2[ ..., 0] = mask_data # Add a 4th dim for Nifti, not sure if last dim is number of channels? nifti_mask_img = nib.nifti1.Nifti1Image(mask2, img_reorient.affine, header=new_header) # Need to xform numpy from supposed DICOM LPS to NIFTI original orientation (i.e. LAS, RAS, etc.) orients = orientations.axcodes2ornt(axcodes, nifti_in_codes_labels[axcodes]) mask_reorient = nifti_mask_img.as_reoriented(orients) nib.save(mask_reorient, filename)
def do_nibabel_transform_to_ras(img): print(f'Transforming Images to {DEFAULT_ORIENTATION}.....') affine = img.affine orig_ornt = nb.io_orientation(affine) targ_ornt = axcodes2ornt(DEFAULT_ORIENTATION) transform = ornt_transform(orig_ornt, targ_ornt) img = img.as_reoriented(transform) return img
def load3DSegFunc(self): if self.micos_flag == 1: getFileNAME = self.micos_name self.micos_flag = 0 self.roiload_flag = 0 else: getFileNAME = QFileDialog.getOpenFileName() self.roiload_flag = 1 segImgObj = nib.load(str(getFileNAME)) segImgData = segImgObj.get_data() ornt = orx.axcodes2ornt((self.Orx,self.Ory,self.Orz)) refOrnt = orx.axcodes2ornt(('R','S','A')) newOrnt = orx.ornt_transform(ornt,refOrnt) segImgData = orx.apply_orientation(segImgData,newOrnt) segImgData = np.fliplr(np.rot90(segImgData,1)) self.segImg = deepcopy(segImgData) self.ui.overlaySeg.setChecked(True) self.imshowFunc()
def save_slices(subject, fname, x, y, z, modality='mri'): """ Function to display row of image slices """ header = nib.load(fname) affine = np.array(header.affine, float) data = header.get_data() images_fol = op.join(MMVT_DIR, subject, 'figures', 'slices') utils.make_dir(images_fol) clim = np.percentile(data, (1., 99.)) codes = axcodes2ornt(aff2axcodes(affine)) order = np.argsort([c[0] for c in codes]) flips = np.array([c[1] < 0 for c in codes])[order] sizes = [data.shape[order] for order in order] scalers = voxel_sizes(affine) coordinates = np.array([x, y, z])[order].astype(int) r = [ scalers[order[2]] / scalers[order[1]], scalers[order[2]] / scalers[order[0]], scalers[order[1]] / scalers[order[0]] ] for ii, xax, yax, ratio, prespective in zip( [0, 1, 2], [1, 0, 0], [2, 2, 1], r, ['Sagital', 'Coronal', 'Axial']): fig = plt.figure() fig.set_size_inches(1. * sizes[xax] / sizes[yax], 1, forward=False) ax = plt.Axes(fig, [0., 0., 1., 1.]) ax.set_axis_off() fig.add_axes(ax) d = get_image_data(data, order, flips, ii, coordinates) ax.imshow(d, vmin=clim[0], vmax=clim[1], aspect=1, cmap='gray', interpolation='nearest', origin='lower') lims = [0, sizes[xax], 0, sizes[yax]] ax.axis(lims) ax.set_aspect(ratio) ax.patch.set_visible(False) ax.set_frame_on(False) ax.axes.get_yaxis().set_visible(False) ax.axes.get_xaxis().set_visible(False) x, y, z = coordinates image_fname = op.join( images_fol, '{}_{}_{}_{}_{}.png'.format(modality, prespective, x, y, z)) print('Saving {}'.format(image_fname)) plt.savefig(image_fname, dpi=sizes[xax])
def reorient_centroids_to(ctd_list, img, decimals=1, verb=False): """reorient centroids to image orientation Parameters: ---------- ctd_list: list of centroids img: nibabel image decimals: rounding decimal digits Returns: ---------- out_list: reoriented list of centroids """ ctd_arr = np.transpose(np.asarray(ctd_list[1:])) if len(ctd_arr) == 0: print("[#] No centroids present") return ctd_list v_list = ctd_arr[0].astype(int).tolist() # vertebral labels ctd_arr = ctd_arr[1:] ornt_fr = nio.axcodes2ornt(ctd_list[0]) # original centroid orientation axcodes_to = nio.aff2axcodes(img.affine) ornt_to = nio.axcodes2ornt(axcodes_to) trans = nio.ornt_transform(ornt_fr, ornt_to).astype(int) perm = trans[:, 0].tolist() shp = np.asarray(img.dataobj.shape) ctd_arr[perm] = ctd_arr.copy() for ax in trans: if ax[1] == -1: size = shp[ax[0]] ctd_arr[ax[0]] = np.around(size - ctd_arr[ax[0]], decimals) out_list = [axcodes_to] ctd_list = np.transpose(ctd_arr).tolist() for v, ctd in zip(v_list, ctd_list): out_list.append([v] + ctd) if verb: print("[*] Centroids reoriented from", nio.ornt2axcodes(ornt_fr), "to", axcodes_to) return out_list
def test_reorientation_backport(): pixdims = ((1, 1, 1), (2, 2, 3)) data = np.random.normal(size=(17, 18, 19, 2)) for pixdim in pixdims: # Generate a randomly rotated affine angles = np.random.uniform(-np.pi, np.pi, 3) * [1, 0.5, 1] rot = nb.eulerangles.euler2mat(*angles) scale = np.diag(pixdim) translation = np.array((17, 18, 19)) / 2 affine = nb.affines.from_matvec(rot.dot(scale), translation) # Create image img = nb.Nifti1Image(data, affine) dim_info = {"freq": 0, "phase": 1, "slice": 2} img.header.set_dim_info(**dim_info) # Find a random, non-identity transform targ_ornt = orig_ornt = nb.io_orientation(affine) while np.array_equal(targ_ornt, orig_ornt): new_code = np.random.choice(_orientations) targ_ornt = axcodes2ornt(new_code) identity = ornt_transform(orig_ornt, orig_ornt) transform = ornt_transform(orig_ornt, targ_ornt) # Identity transform returns exact image assert img.as_reoriented(identity) is img assert _as_reoriented_backport(img, identity) is img reoriented_a = img.as_reoriented(transform) reoriented_b = _as_reoriented_backport(img, transform) flips_only = img.shape == reoriented_a.shape # Reorientation changes affine and data array assert not np.allclose(img.affine, reoriented_a.affine) assert not (flips_only and np.allclose(img.get_fdata(), reoriented_a.get_fdata())) # Dimension info changes iff axes are reordered assert flips_only == np.array_equal(img.header.get_dim_info(), reoriented_a.header.get_dim_info()) # Both approaches produce equivalent images assert np.allclose(reoriented_a.affine, reoriented_b.affine) assert np.array_equal(reoriented_a.get_fdata(), reoriented_b.get_fdata()) assert np.array_equal(reoriented_a.header.get_dim_info(), reoriented_b.header.get_dim_info())
def test_reorientation_backport(): pixdims = ((1, 1, 1), (2, 2, 3)) data = np.random.normal(size=(17, 18, 19, 2)) for pixdim in pixdims: # Generate a randomly rotated affine angles = np.random.uniform(-np.pi, np.pi, 3) * [1, 0.5, 1] rot = nb.eulerangles.euler2mat(*angles) scale = np.diag(pixdim) translation = np.array((17, 18, 19)) / 2 affine = nb.affines.from_matvec(rot.dot(scale), translation) # Create image img = nb.Nifti1Image(data, affine) dim_info = {'freq': 0, 'phase': 1, 'slice': 2} img.header.set_dim_info(**dim_info) # Find a random, non-identity transform targ_ornt = orig_ornt = nb.io_orientation(affine) while np.array_equal(targ_ornt, orig_ornt): new_code = np.random.choice(_orientations) targ_ornt = axcodes2ornt(new_code) identity = ornt_transform(orig_ornt, orig_ornt) transform = ornt_transform(orig_ornt, targ_ornt) # Identity transform returns exact image assert img.as_reoriented(identity) is img assert _as_reoriented_backport(img, identity) is img reoriented_a = img.as_reoriented(transform) reoriented_b = _as_reoriented_backport(img, transform) flips_only = img.shape == reoriented_a.shape # Reorientation changes affine and data array assert not np.allclose(img.affine, reoriented_a.affine) assert not (flips_only and np.allclose(img.get_data(), reoriented_a.get_data())) # Dimension info changes iff axes are reordered assert flips_only == np.array_equal(img.header.get_dim_info(), reoriented_a.header.get_dim_info()) # Both approaches produce equivalent images assert np.allclose(reoriented_a.affine, reoriented_b.affine) assert np.array_equal(reoriented_a.get_data(), reoriented_b.get_data()) assert np.array_equal(reoriented_a.header.get_dim_info(), reoriented_b.header.get_dim_info())
def _run_interface(self, runtime): import numpy as np import nibabel as nb from nibabel.orientations import (axcodes2ornt, ornt_transform, inv_ornt_aff) fname = self.inputs.in_file orig_img = nb.load(fname) # Find transform from current (approximate) orientation to # target, in nibabel orientation matrix and affine forms orig_ornt = nb.io_orientation(orig_img.affine) targ_ornt = axcodes2ornt(self.inputs.orientation) transform = ornt_transform(orig_ornt, targ_ornt) affine_xfm = inv_ornt_aff(transform, orig_img.shape) # Check can be eliminated when minimum nibabel version >= 2.2 if hasattr(orig_img, 'as_reoriented'): reoriented = orig_img.as_reoriented(transform) else: reoriented = _as_reoriented_backport(orig_img, transform) # Image may be reoriented if reoriented is not orig_img: suffix = '_' + self.inputs.orientation.lower() out_name = fname_presuffix(fname, suffix=suffix, newpath=runtime.cwd) reoriented.to_filename(out_name) else: out_name = fname mat_name = fname_presuffix(fname, suffix='.mat', newpath=runtime.cwd, use_ext=False) np.savetxt(mat_name, affine_xfm, fmt='%.08f') self._results['out_file'] = out_name self._results['transform'] = mat_name return runtime
def _run_interface(self, runtime): import numpy as np import nibabel as nb from nibabel.orientations import ( axcodes2ornt, ornt_transform, inv_ornt_aff) fname = self.inputs.in_file orig_img = nb.load(fname) # Find transform from current (approximate) orientation to # target, in nibabel orientation matrix and affine forms orig_ornt = nb.io_orientation(orig_img.affine) targ_ornt = axcodes2ornt(self.inputs.orientation) transform = ornt_transform(orig_ornt, targ_ornt) affine_xfm = inv_ornt_aff(transform, orig_img.shape) # Check can be eliminated when minimum nibabel version >= 2.4 if LooseVersion(nb.__version__) >= LooseVersion('2.4.0'): reoriented = orig_img.as_reoriented(transform) else: reoriented = _as_reoriented_backport(orig_img, transform) # Image may be reoriented if reoriented is not orig_img: suffix = '_' + self.inputs.orientation.lower() out_name = fname_presuffix(fname, suffix=suffix, newpath=runtime.cwd) reoriented.to_filename(out_name) else: out_name = fname mat_name = fname_presuffix(fname, suffix='.mat', newpath=runtime.cwd, use_ext=False) np.savetxt(mat_name, affine_xfm, fmt='%.08f') self._results['out_file'] = out_name self._results['transform'] = mat_name return runtime
def loadDataFunc(self): #Function for loading image file self.ui.autogray.setCheckState(2) self.autoGrayFlag = 1 getFileNAME = QFileDialog.getOpenFileName() if str(getFileNAME)[-4:] == '.avw': #if avw file, do conversion, and then work with nifti file MR = loadAVW(str(getFileNAME)) avw2nifti(str(getFileNAME[:-4]) + 'avw', MR, seg=None) getFileNAME = str(getFileNAME[:-4]) + 'avw.nii.gz' #print getFileNAME self.getFileNAME = getFileNAME self.ui.displayFileName.setText(str(os.path.basename(str(self.getFileNAME)))) self.z =0 self.z1 = 0 self.z2 = 0 self.rotD = -90 imgObj2= nib.load(str(getFileNAME)) imgObj1 = imgObj2 self.affine2 = imgObj2.get_affine() self.PSx = imgObj1.get_header()['pixdim'][1] self.PSy = imgObj1.get_header()['pixdim'][2] self.PSz = imgObj1.get_header()['pixdim'][3] (x,y,z) = orx.aff2axcodes(self.affine2) self.Orx = x self.Ory = y self.Orz = z ornt = orx.axcodes2ornt((x,y,z)) refOrnt = orx.axcodes2ornt(('R','S','A')) #was 'R', 'A', 'S' newOrnt = orx.ornt_transform(ornt,refOrnt) self.ornt = ornt self.refOrnt = refOrnt self.img_data2 = imgObj2.get_data() self.img_data2 = orx.apply_orientation(self.img_data2,newOrnt) self.img_data2 = np.fliplr(np.rot90(self.img_data2,1)) self.img_data1 = self.img_data2 self.imageFile2 = str(getFileNAME) #changed self.ui.T2Image.currentText() to getFileNAME self.imageFile1 = self.imageFile2 indx2 = self.imageFile2.rfind('/') indx1 = indx2 self.filePath1 = self.imageFile1[0:indx1+1] self.fileName1 = self.imageFile1[indx1+1:] self.filePath2 = self.imageFile2[0:indx2+1] self.fileName2 = self.imageFile2[indx2+1:] # sizeT1C = self.img_data1.shape try: (x1,y1,z1) = self.img_data1.shape (x2,y2,z2) = self.img_data2.shape except: (x1,y1,z1,d1) = self.img_data1.shape (x2,y2,z2,d1) = self.img_data2.shape self.img_data1 = self.img_data1[:,:,:,0] self.img_data2 = self.img_data2[:,:,:,0] self.sliceNum1 = z1 self.sliceNum2 = z2 self.shape = self.img_data2.shape self.img1 = self.img_data1[:,:,self.z] self.img2 = self.img_data2[:,:,self.z] self.segImg = self.img_data2 * 0 self.imshowFunc() (x,y,z) = self.shape self.ui.figure3.canvas.ax.clear() # self.ui.figure3.canvas.ax.imshow(((self.img_data2[:,round(x/2),:])),cmap=plt.cm.gray) self.ui.figure3.canvas.ax.imshow(((self.img_data2[:,round(x/2),:])),cmap=plt.cm.gray) #self.ui.figure3.canvas.ax.set_aspect('auto') self.ui.figure3.canvas.ax.get_xaxis().set_visible(False) self.ui.figure3.canvas.ax.get_yaxis().set_visible(False) #self.ui.figure3.canvas.ax.set_title('Sagittal View', color = 'white') #this is where had sagittal view self.ui.figure3.canvas.draw() self.ui.figure4.canvas.ax.clear() self.ui.figure4.canvas.ax.imshow(np.rot90((self.img_data2[round(y/2),:,:]),1),cmap=plt.cm.gray) #self.ui.figure4.canvas.ax.set_aspect('auto') self.ui.figure4.canvas.ax.get_xaxis().set_visible(False) self.ui.figure4.canvas.ax.get_yaxis().set_visible(False) #self.ui.figure4.canvas.ax.set_title('Axial View', color = 'white') self.ui.figure4.canvas.draw() # self.imhistFunc() self.ui.imageSlider.setMinimum(0) self.ui.imageSlider.setMaximum(z2-1) self.ui.imageSlider.setSingleStep(1) self.maxSlice = z2 - 1 (row,col,dep) = self.img_data2.shape self.overlayImgAX = np.zeros((row,col)) return
def MICOS(fileNAME): tic = time.clock() selfz =0 selfz1 = 0 selfz2 = 0 selfrotD = -90 imgObj2= nib.load(str(fileNAME)) imgObj1 = imgObj2 im = imgObj2 selfaffine2 = imgObj2.get_affine() selfheaderdtype = imgObj2.get_data_dtype() selfPSx = imgObj1.get_header()['pixdim'][1] selfPSy = imgObj1.get_header()['pixdim'][2] selfPSz = imgObj1.get_header()['pixdim'][3] (x,y,z) = orx.aff2axcodes(selfaffine2) selfOrx = x selfOry = y selfOrz = z ornt = orx.axcodes2ornt((x,y,z)) refOrnt = orx.axcodes2ornt(('R','S','A')) #was 'R', 'A', 'S' newOrnt = orx.ornt_transform(ornt,refOrnt) selfornt = ornt selfrefOrnt = refOrnt selfimg_data2 = imgObj2.get_data() selfimg_data2 = orx.apply_orientation(selfimg_data2,newOrnt) selfimg_data2 = np.fliplr(np.rot90(selfimg_data2,1)) im_data = selfimg_data2 [x_si,y_si,z_si] = np.shape(im_data) #do 99% norm to 1000 im_data = np.array(im_data,dtype='float') im_data = im_data * 1000/np.percentile(im_data,99) #print np.shape(im_data) initialSeg = im_data.copy() * 0 #begin user roi drawing... #go from middle up... for i in xrange(np.round(z_si/2),z_si,3): img = (im_data[:,:,i]) # show the image if i > np.round(z_si/2): plt.figure(figsize=(ROI1.figwidth,ROI1.figheight)) plt.imshow(img,cmap='gray') plt.colorbar() plt.title("outline one kidney, slice = " + str(i)) # let user draw first ROI ROI1 = polydraw(roicolor='r') #let user draw first ROI # show the image with the first ROI plt.figure(figsize=(ROI1.figwidth,ROI1.figheight)) plt.imshow(img,cmap='gray') plt.colorbar() ROI1.displayROI() plt.title("outline other kidney, slice = " + str(i)) # let user draw second ROI ROI2 = polydraw(roicolor='b') #let user draw ROI initialSeg[:,:,i] = ROI1.getMask(img) + ROI2.getMask(img) #go from middle up... for i in xrange(np.round(z_si/2)-1,0,-3): img = (im_data[:,:,i]) # show the image plt.figure(figsize=(ROI1.figwidth,ROI1.figheight)) plt.imshow(img,cmap='gray') plt.colorbar() plt.title("outline one kidney, slice = " + str(i)) # let user draw first ROI ROI1 = polydraw(roicolor='r') #let user draw first ROI # show the image with the first ROI plt.figure(figsize=(ROI1.figwidth,ROI1.figheight)) plt.imshow(img,cmap='gray') plt.colorbar() ROI1.displayROI() plt.title("outline other kidney, slice = " + str(i)) # let user draw second ROI ROI2 = polydraw(roicolor='b') #let user draw ROI initialSeg[:,:,i] = ROI1.getMask(img) + ROI2.getMask(img) toc = time.clock() #save out drawn polygon aff = selfaffine2 outImage = deepcopy(initialSeg)#np.rot90(np.fliplr(self.segImg),-1) [x_si,y_si,z_si] = np.shape(outImage) #print np.shape(outImage) #This method works (for fastsegs)... but need more robust #for i in range(0,z_si): # outImage[:,:,i] = np.rot90(self.segImg[:,:,z_si-1-i],-1) #try new method (more robust to header and affine mix-ups) ornt = orx.axcodes2ornt((selfOrx,selfOry,selfOrz)) refOrnt = orx.axcodes2ornt(('R','S','A')) newOrnt = orx.ornt_transform(refOrnt,ornt) #reversed these outImage= orx.apply_orientation(np.rot90(np.fliplr(outImage),-1),newOrnt) #outImage = orx.apply_orientation(outImage,newOrnt) #outImage = np.rot90(np.fliplr(outImage),-1) #print np.shape(outImage) #outImage = np.array(outImage,dtype=selfheaderdtype) new_image = nib.Nifti1Image(outImage,aff) nib.save(new_image,fileNAME[:-7]+'_polygon_MICOS.nii.gz')
def to_affine( orientation, spacing: Sequence[Union[int, float]] = None, origin: Sequence[Union[int, float]] = None, ): """Convert orientation, spacing, and origin data into affine matrix. Args: orientation (Sequence[str]): Image orientation in the standard orientation format (e.g. ``("LR", "AP", "SI")``). spacing (int(s) | float(s)): Number(s) corresponding to pixel spacing of each direction. If a single value, same pixel spacing is used for all directions. If sequence is less than length of ``orientation``, remaining direction have unit spacing (i.e. ``1``). Defaults to unit spacing ``(1, 1, 1)`` origin (int(s) | float(s)): The ``(x0, y0, z0)`` origin for the scan. If a single value, same origin is used for all directions. If sequence is less than length of ``orientation``, remaining direction have standard origin (i.e. ``0``). Defaults to ``(0, 0, 0)`` Returns: ndarray: A 4x4 ndarray representing the affine matrix. Examples: >>> to_affine(("SI", "AP", "RL"), spacing=(0.5, 0.5, 1.5), origin=(10, 20, 0)) array([[-0. , -0. , -1.5, 10. ], [-0. , -0.5, -0. , 20. ], [-0.5, -0. , -0. , 30. ], [ 0. , 0. , 0. , 1. ]]) Note: This method assumes all direction follow the standard principal directions in the normative patient orientation. Moving along one direction of the array only moves along one fo the normative directions. """ def _format_numbers(input, default_val, name, expected_num): """Formats (sequence of) numbers (spacing, origin) into standard 3-length tuple.""" if input is None: return (default_val, ) * expected_num if isinstance(input, (int, float)): return (input, ) * expected_num if not isinstance(input, (np.ndarray, Sequence)) or len(input) > expected_num: raise ValueError( f"`{name}` must be a real number or sequence (length<={expected_num}) " f"of real numbers. Got {input}") input = tuple(input) if len(input) < expected_num: input += (default_val, ) * (expected_num - len(input)) assert len(input) == expected_num return input if len(orientation) == 2: orientation = _infer_orientation(orientation) __check_orientation__(orientation) spacing = _format_numbers(spacing, 1, "spacing", len(orientation)) origin = _format_numbers(origin, 0, "origin", len(orientation)) affine = np.eye(4) start_ornt = nibo.io_orientation(affine) end_ornt = nibo.axcodes2ornt(orientation_standard_to_nib(orientation)) ornt = nibo.ornt_transform(start_ornt, end_ornt) transpose_idxs = ornt[:, 0].astype(np.int) flip_idxs = ornt[:, 1] affine[:3] = affine[:3][transpose_idxs] affine[:3] *= flip_idxs[..., np.newaxis] affine[:3, :3] *= np.asarray(spacing) affine[:3, 3] = np.asarray(origin) return affine
def MICOS(fileNAME): tic = time.clock() selfz =0 selfz1 = 0 selfz2 = 0 selfrotD = -90 imgObj2= nib.load(str(fileNAME)) imgObj1 = imgObj2 im = imgObj2 selfaffine2 = imgObj2.get_affine() selfheaderdtype = imgObj2.get_data_dtype() selfPSx = imgObj1.get_header()['pixdim'][1] selfPSy = imgObj1.get_header()['pixdim'][2] selfPSz = imgObj1.get_header()['pixdim'][3] (x,y,z) = orx.aff2axcodes(selfaffine2) selfOrx = x selfOry = y selfOrz = z ornt = orx.axcodes2ornt((x,y,z)) refOrnt = orx.axcodes2ornt(('R','S','A')) #was 'R', 'A', 'S' newOrnt = orx.ornt_transform(ornt,refOrnt) selfornt = ornt selfrefOrnt = refOrnt selfimg_data2 = imgObj2.get_data() selfimg_data2 = orx.apply_orientation(selfimg_data2,newOrnt) selfimg_data2 = np.fliplr(np.rot90(selfimg_data2,1)) im_data = selfimg_data2 [x_si,y_si,z_si] = np.shape(im_data) #do 99% norm to 1000 im_data = np.array(im_data,dtype='float') im_data = im_data * 1000/np.percentile(im_data,99) #print np.shape(im_data) initialSeg = im_data.copy() * 0 #begin user roi drawing... #go from middle up... for i in xrange(np.round(z_si/2),z_si,3): img = (im_data[:,:,i]) # show the image if i > np.round(z_si/2): plt.figure(figsize=(ROI1.figwidth,ROI1.figheight)) plt.imshow(img,cmap='gray') plt.colorbar() plt.title("outline one kidney, slice = " + str(i)) # let user draw first ROI ROI1 = polydraw(roicolor='r') #let user draw first ROI # show the image with the first ROI plt.figure(figsize=(ROI1.figwidth,ROI1.figheight)) plt.imshow(img,cmap='gray') plt.colorbar() ROI1.displayROI() plt.title("outline other kidney, slice = " + str(i)) # let user draw second ROI ROI2 = polydraw(roicolor='b') #let user draw ROI initialSeg[:,:,i] = ROI1.getMask(img) + ROI2.getMask(img) #go from middle up... for i in xrange(np.round(z_si/2)-1,0,-3): img = (im_data[:,:,i]) # show the image plt.figure(figsize=(ROI1.figwidth,ROI1.figheight)) plt.imshow(img,cmap='gray') plt.colorbar() plt.title("outline one kidney, slice = " + str(i)) # let user draw first ROI ROI1 = polydraw(roicolor='r') #let user draw first ROI # show the image with the first ROI plt.figure(figsize=(ROI1.figwidth,ROI1.figheight)) plt.imshow(img,cmap='gray') plt.colorbar() ROI1.displayROI() plt.title("outline other kidney, slice = " + str(i)) # let user draw second ROI ROI2 = polydraw(roicolor='b') #let user draw ROI initialSeg[:,:,i] = ROI1.getMask(img) + ROI2.getMask(img) toc = time.clock() #save out drawn polygon aff = selfaffine2 outImage = deepcopy(initialSeg)#np.rot90(np.fliplr(self.segImg),-1) [x_si,y_si,z_si] = np.shape(outImage) #print np.shape(outImage) #This method works (for fastsegs)... but need more robust #for i in range(0,z_si): # outImage[:,:,i] = np.rot90(self.segImg[:,:,z_si-1-i],-1) #try new method (more robust to header and affine mix-ups) ornt = orx.axcodes2ornt((selfOrx,selfOry,selfOrz)) refOrnt = orx.axcodes2ornt(('R','S','A')) newOrnt = orx.ornt_transform(refOrnt,ornt) #reversed these outImage= orx.apply_orientation(np.rot90(np.fliplr(outImage),-1),newOrnt) #outImage = orx.apply_orientation(outImage,newOrnt) #outImage = np.rot90(np.fliplr(outImage),-1) #print np.shape(outImage) #outImage = np.array(outImage,dtype=selfheaderdtype) new_image = nib.Nifti1Image(outImage,aff) nib.save(new_image,fileNAME[:-7]+'_polygon_MICOS.nii.gz') # Dilate and fill in missing slices initialSeg = dilation(initialSeg,iterations = 1) finalSeg = initialSeg.copy() * 0 # now try convex hull method instead to better approximate missing slices (previous method is above) # This works but way too long. Also, would likely need to do object finding first, so compute # Convex hull for each kidney separately. while 0: xlist,ylist,zlist = find_3D_object_voxel_list(initialSeg) voxlist = np.zeros(shape=(np.shape(xlist)[0],3),dtype='int16') voxlist[:,0] = xlist voxlist[:,1] = ylist voxlist[:,2] = zlist tri = dtri(voxlist) # construct full voxel list xxlist,yylist,zzlist = find_3D_object_voxel_list((initialSeg+1)>0) fullvoxlist = np.zeros(shape=(np.shape(xxlist)[0],3),dtype='int16') fullvoxlist[:,0] = xxlist fullvoxlist[:,1] = yylist fullvoxlist[:,2] = zzlist finalSeg = np.array(in_hull(fullvoxlist,tri),dtype=float) finalSeg = np.reshape(finalSeg,(x_si,y_si,z_si)) # Now do gaussian blur of polygon to smooth initialSeg = (filt.gaussian_filter(initialSeg.copy()*255,sigma=[3,3,1])) > 100 #Begin optimized method... for i in xrange(0,z_si): img = (im_data[:,:,i]) if np.max(initialSeg[:,:,i]>0): mgac = [] gI = msnake.gborders(img,alpha=1E5,sigma=3.0) # increasing sigma allows more changes in contour mgac = msnake.MorphGAC(gI,smoothing=3,threshold=0.01,balloon=0.0) #was 2.5 mgac.levelset = initialSeg[:,:,i]>0.5 for ijk123 in xrange(100): mgac.step() finalSeg[:,:,i] = mgac.levelset #print i # Now do gaussian blur and threshold to finalize segmentation... finalSeg = (filt.gaussian_filter(finalSeg.copy()*255,sigma=[3,3,1])) > 100 #using this helps with single slice errors of the active contour # Try adding now narrow band sobel/watershed technique. for i in xrange(0,z_si): img = (im_data[:,:,i]) segslice = finalSeg[:,:,i] if np.max(finalSeg[:,:,i]>0): erodeimg = erosion(segslice.copy(),iterations=1) dilateimg = dilation(segslice.copy(),iterations=1) seeds = img * 0 seeds[:] = 1 seeds[dilateimg>0] = 0 seeds[erodeimg>0] = 2 sobelFilt = sobel(np.array(img.copy(),dtype='int16')) mgac = watershed(sobelFilt,seeds)>1 finalSeg[:,:,i] = mgac>0 #save out segmentation aff = selfaffine2 outImage = deepcopy(finalSeg)#np.rot90(np.fliplr(self.segImg),-1) outImage = np.array(outImage,dtype='float') [x_si,y_si,z_si] = np.shape(outImage) #This method works (for fastsegs)... but need more robust #for i in range(0,z_si): # outImage[:,:,i] = np.rot90(self.segImg[:,:,z_si-1-i],-1) #try new method (more robust to header and affine mix-ups) ornt = orx.axcodes2ornt((selfOrx,selfOry,selfOrz)) refOrnt = orx.axcodes2ornt(('R','S','A')) newOrnt = orx.ornt_transform(refOrnt,ornt) #reversed these outImage= orx.apply_orientation(np.rot90(np.fliplr(outImage),-1),newOrnt) #outImage = orx.apply_orientation(outImage,newOrnt) #outImage = np.rot90(np.fliplr(outImage),-1) new_image = nib.Nifti1Image(outImage,aff) nib.save(new_image,fileNAME[:-7]+'_FASTseg_MICOS.nii.gz') print 'time = ' print toc - tic return (fileNAME[:-7]+'_FASTseg_MICOS.nii.gz')
def __init__(self, data, affine=None, axes=None, cmap='gray', pcnt_range=(1., 99.), figsize=(8, 8), title=None): """ Parameters ---------- data : ndarray The data that will be displayed by the slicer. Should have 3+ dimensions. affine : array-like | None Affine transform for the data. This is used to determine how the data should be sliced for plotting into the saggital, coronal, and axial view axes. If None, identity is assumed. The aspect ratio of the data are inferred from the affine transform. axes : tuple of mpl.Axes | None, optional 3 or 4 axes instances for the 3 slices plus volumes, or None (default). cmap : str | instance of cmap, optional String or cmap instance specifying colormap. pcnt_range : array-like, optional Percentile range over which to scale image for display. figsize : tuple Figure size (in inches) to use if axes are None. """ # Nest imports so that matplotlib.use() has the appropriate # effect in testing plt, _, _ = optional_package('matplotlib.pyplot') mpl_img, _, _ = optional_package('matplotlib.image') mpl_patch, _, _ = optional_package('matplotlib.patches') self._title = title self._closed = False data = np.asanyarray(data) if data.ndim < 3: raise ValueError('data must have at least 3 dimensions') affine = np.array(affine, float) if affine is not None else np.eye(4) if affine.ndim != 2 or affine.shape != (4, 4): raise ValueError('affine must be a 4x4 matrix') # determine our orientation self._affine = affine.copy() codes = axcodes2ornt(aff2axcodes(self._affine)) self._order = np.argsort([c[0] for c in codes]) self._flips = np.array([c[1] < 0 for c in codes])[self._order] self._flips = list(self._flips) + [False] # add volume dim self._scalers = np.abs(self._affine).max(axis=0)[:3] self._inv_affine = np.linalg.inv(affine) # current volume info self._volume_dims = data.shape[3:] self._current_vol_data = data[:, :, :, 0] if data.ndim > 3 else data self._data = data vmin, vmax = np.percentile(data, pcnt_range) del data if axes is None: # make the axes # ^ +---------+ ^ +---------+ # | | | | | | # | Sag | | Cor | # S | 0 | S | 1 | # | | | | # | | | | # +---------+ +---------+ # A --> <-- R # ^ +---------+ +---------+ # | | | | | # | Axial | | Vol | # A | 2 | | 3 | # | | | | # | | | | # +---------+ +---------+ # <-- R <-- t --> fig, axes = plt.subplots(2, 2) fig.set_size_inches(figsize, forward=True) self._axes = [axes[0, 0], axes[0, 1], axes[1, 0], axes[1, 1]] plt.tight_layout(pad=0.1) if self.n_volumes <= 1: fig.delaxes(self._axes[3]) self._axes.pop(-1) if self._title is not None: fig.canvas.set_window_title(str(title)) else: self._axes = [axes[0], axes[1], axes[2]] if len(axes) > 3: self._axes.append(axes[3]) # Start midway through each axis, idx is current slice number self._ims, self._data_idx = list(), list() # set up axis crosshairs self._crosshairs = [None] * 3 r = [self._scalers[self._order[2]] / self._scalers[self._order[1]], self._scalers[self._order[2]] / self._scalers[self._order[0]], self._scalers[self._order[1]] / self._scalers[self._order[0]]] self._sizes = [self._data.shape[o] for o in self._order] for ii, xax, yax, ratio, label in zip([0, 1, 2], [1, 0, 0], [2, 2, 1], r, ('SAIP', 'SLIR', 'ALPR')): ax = self._axes[ii] d = np.zeros((self._sizes[yax], self._sizes[xax])) im = self._axes[ii].imshow(d, vmin=vmin, vmax=vmax, aspect=1, cmap=cmap, interpolation='nearest', origin='lower') self._ims.append(im) vert = ax.plot([0] * 2, [-0.5, self._sizes[yax] - 0.5], color=(0, 1, 0), linestyle='-')[0] horiz = ax.plot([-0.5, self._sizes[xax] - 0.5], [0] * 2, color=(0, 1, 0), linestyle='-')[0] self._crosshairs[ii] = dict(vert=vert, horiz=horiz) # add text labels (top, right, bottom, left) lims = [0, self._sizes[xax], 0, self._sizes[yax]] bump = 0.01 poss = [[lims[1] / 2., lims[3]], [(1 + bump) * lims[1], lims[3] / 2.], [lims[1] / 2., 0], [lims[0] - bump * lims[1], lims[3] / 2.]] anchors = [['center', 'bottom'], ['left', 'center'], ['center', 'top'], ['right', 'center']] for pos, anchor, lab in zip(poss, anchors, label): ax.text(pos[0], pos[1], lab, horizontalalignment=anchor[0], verticalalignment=anchor[1]) ax.axis(lims) ax.set_aspect(ratio) ax.patch.set_visible(False) ax.set_frame_on(False) ax.axes.get_yaxis().set_visible(False) ax.axes.get_xaxis().set_visible(False) self._data_idx.append(0) self._data_idx.append(-1) # volume # Set up volumes axis if self.n_volumes > 1 and len(self._axes) > 3: ax = self._axes[3] ax.set_axis_bgcolor('k') ax.set_title('Volumes') y = np.zeros(self.n_volumes + 1) x = np.arange(self.n_volumes + 1) - 0.5 step = ax.step(x, y, where='post', color='y')[0] ax.set_xticks(np.unique(np.linspace(0, self.n_volumes - 1, 5).astype(int))) ax.set_xlim(x[0], x[-1]) yl = [self._data.min(), self._data.max()] yl = [l + s * np.diff(lims)[0] for l, s in zip(yl, [-1.01, 1.01])] patch = mpl_patch.Rectangle([-0.5, yl[0]], 1., np.diff(yl)[0], fill=True, facecolor=(0, 1, 0), edgecolor=(0, 1, 0), alpha=0.25) ax.add_patch(patch) ax.set_ylim(yl) self._volume_ax_objs = dict(step=step, patch=patch) self._figs = set([a.figure for a in self._axes]) for fig in self._figs: fig.canvas.mpl_connect('scroll_event', self._on_scroll) fig.canvas.mpl_connect('motion_notify_event', self._on_mouse) fig.canvas.mpl_connect('button_press_event', self._on_mouse) fig.canvas.mpl_connect('key_press_event', self._on_keypress) fig.canvas.mpl_connect('close_event', self._cleanup) # actually set data meaningfully self._position = np.zeros(4) self._position[3] = 1. # convenience for affine multn self._changing = False # keep track of status to avoid loops self._links = [] # other viewers this one is linked to plt.draw() for fig in self._figs: fig.canvas.draw() self._set_volume_index(0, update_slices=False) self._set_position(0., 0., 0.) self._draw()
def __init__(self, data, affine=None, axes=None, title=None): """ Parameters ---------- data : array-like The data that will be displayed by the slicer. Should have 3+ dimensions. affine : array-like or None, optional Affine transform for the data. This is used to determine how the data should be sliced for plotting into the sagittal, coronal, and axial view axes. If None, identity is assumed. The aspect ratio of the data are inferred from the affine transform. axes : tuple of mpl.Axes or None, optional 3 or 4 axes instances for the 3 slices plus volumes, or None (default). title : str or None, optional The title to display. Can be None (default) to display no title. """ # Use these late imports of matplotlib so that we have some hope that # the test functions are the first to set the matplotlib backend. The # tests set the backend to something that doesn't require a display. self._plt = plt = optional_package('matplotlib.pyplot')[0] mpl_patch = optional_package('matplotlib.patches')[0] self._title = title self._closed = False data = np.asanyarray(data) if data.ndim < 3: raise ValueError('data must have at least 3 dimensions') if np.iscomplexobj(data): raise TypeError("Complex data not supported") affine = np.array(affine, float) if affine is not None else np.eye(4) if affine.shape != (4, 4): raise ValueError('affine must be a 4x4 matrix') # determine our orientation self._affine = affine codes = axcodes2ornt(aff2axcodes(self._affine)) self._order = np.argsort([c[0] for c in codes]) self._flips = np.array([c[1] < 0 for c in codes])[self._order] self._flips = list(self._flips) + [False] # add volume dim self._scalers = voxel_sizes(self._affine) self._inv_affine = np.linalg.inv(affine) # current volume info self._volume_dims = data.shape[3:] self._current_vol_data = data[:, :, :, 0] if data.ndim > 3 else data self._data = data self._clim = np.percentile(data, (1., 99.)) del data if axes is None: # make the axes # ^ +---------+ ^ +---------+ # | | | | | | # | Sag | | Cor | # S | 0 | S | 1 | # | | | | # | | | | # +---------+ +---------+ # A --> <-- R # ^ +---------+ +---------+ # | | | | | # | Axial | | Vol | # A | 2 | | 3 | # | | | | # | | | | # +---------+ +---------+ # <-- R <-- t --> fig, axes = plt.subplots(2, 2) fig.set_size_inches((8, 8), forward=True) self._axes = [axes[0, 0], axes[0, 1], axes[1, 0], axes[1, 1]] plt.tight_layout(pad=0.1) if self.n_volumes <= 1: fig.delaxes(self._axes[3]) self._axes.pop(-1) if self._title is not None: fig.canvas.set_window_title(str(title)) else: self._axes = [axes[0], axes[1], axes[2]] if len(axes) > 3: self._axes.append(axes[3]) # Start midway through each axis, idx is current slice number self._ims, self._data_idx = list(), list() # set up axis crosshairs self._crosshairs = [None] * 3 r = [self._scalers[self._order[2]] / self._scalers[self._order[1]], self._scalers[self._order[2]] / self._scalers[self._order[0]], self._scalers[self._order[1]] / self._scalers[self._order[0]]] self._sizes = [self._data.shape[order] for order in self._order] for ii, xax, yax, ratio, label in zip([0, 1, 2], [1, 0, 0], [2, 2, 1], r, ('SAIP', 'SLIR', 'ALPR')): ax = self._axes[ii] d = np.zeros((self._sizes[yax], self._sizes[xax])) im = self._axes[ii].imshow( d, vmin=self._clim[0], vmax=self._clim[1], aspect=1, cmap='gray', interpolation='nearest', origin='lower') self._ims.append(im) vert = ax.plot([0] * 2, [-0.5, self._sizes[yax] - 0.5], color=(0, 1, 0), linestyle='-')[0] horiz = ax.plot([-0.5, self._sizes[xax] - 0.5], [0] * 2, color=(0, 1, 0), linestyle='-')[0] self._crosshairs[ii] = dict(vert=vert, horiz=horiz) # add text labels (top, right, bottom, left) lims = [0, self._sizes[xax], 0, self._sizes[yax]] bump = 0.01 poss = [[lims[1] / 2., lims[3]], [(1 + bump) * lims[1], lims[3] / 2.], [lims[1] / 2., 0], [lims[0] - bump * lims[1], lims[3] / 2.]] anchors = [['center', 'bottom'], ['left', 'center'], ['center', 'top'], ['right', 'center']] for pos, anchor, lab in zip(poss, anchors, label): ax.text(pos[0], pos[1], lab, horizontalalignment=anchor[0], verticalalignment=anchor[1]) ax.axis(lims) ax.set_aspect(ratio) ax.patch.set_visible(False) ax.set_frame_on(False) ax.axes.get_yaxis().set_visible(False) ax.axes.get_xaxis().set_visible(False) self._data_idx.append(0) self._data_idx.append(-1) # volume # Set up volumes axis if self.n_volumes > 1 and len(self._axes) > 3: ax = self._axes[3] try: ax.set_facecolor('k') except AttributeError: # old mpl ax.set_axis_bgcolor('k') ax.set_title('Volumes') y = np.zeros(self.n_volumes + 1) x = np.arange(self.n_volumes + 1) - 0.5 step = ax.step(x, y, where='post', color='y')[0] ax.set_xticks(np.unique(np.linspace(0, self.n_volumes - 1, 5).astype(int))) ax.set_xlim(x[0], x[-1]) yl = [self._data.min(), self._data.max()] yl = [l + s * np.diff(lims)[0] for l, s in zip(yl, [-1.01, 1.01])] patch = mpl_patch.Rectangle([-0.5, yl[0]], 1., np.diff(yl)[0], fill=True, facecolor=(0, 1, 0), edgecolor=(0, 1, 0), alpha=0.25) ax.add_patch(patch) ax.set_ylim(yl) self._volume_ax_objs = dict(step=step, patch=patch) self._figs = set([a.figure for a in self._axes]) for fig in self._figs: fig.canvas.mpl_connect('scroll_event', self._on_scroll) fig.canvas.mpl_connect('motion_notify_event', self._on_mouse) fig.canvas.mpl_connect('button_press_event', self._on_mouse) fig.canvas.mpl_connect('key_press_event', self._on_keypress) fig.canvas.mpl_connect('close_event', self._cleanup) # actually set data meaningfully self._position = np.zeros(4) self._position[3] = 1. # convenience for affine multiplication self._changing = False # keep track of status to avoid loops self._links = [] # other viewers this one is linked to self._plt.draw() for fig in self._figs: fig.canvas.draw() self._set_volume_index(0, update_slices=False) self._set_position(0., 0., 0.) self._draw()
'Reorient to LIA and resample to 1mm iso-voxel resolution if required') parser.add_argument('source', type=str, help='Input volume') parser.add_argument('destination', type=str, help='Normalized volume') args = parser.parse_args() src_nib = nib_funcs.squeeze_image(nib.load(args.source)) current_orientation = ''.join(nib.aff2axcodes(src_nib.affine)) print('Input: {} [{}]'.format(src_nib.header.get_zooms(), current_orientation)) # Avoid resampling if already 1mm iso-voxel # Note: Also in cases of tiny rounding error, e.g. (1.0000001, 1.0000001, 1.0) if not np.allclose(src_nib.header.get_zooms(), [1, 1, 1]): # requires re-sampling print('Resampling') dst_nib = nib_processing.conform(src_nib, orientation='LIA') elif current_orientation != 'LIA': # requires just reorient print('Reorientating {} to LIA'.format(current_orientation)) start_ornt = nib_orientations.io_orientation(src_nib.affine) end_ornt = nib_orientations.axcodes2ornt('LIA') transform = nib_orientations.ornt_transform(start_ornt, end_ornt) dst_nib = src_nib.as_reoriented(transform) else: dst_nib = src_nib nib.save(dst_nib, args.destination)
def __init__(self, volume, affine=None, title=None, cmap='gray', clim=None, alpha=1.): """ Parameters ---------- volume : array-like The data that will be displayed by the slicer. Should have 3 dimensions. affine : array-like or None, optional Affine transform for the data. This is used to determine how the data should be sliced for plotting into the sagittal, coronal, and axial view axes. If None, identity is assumed. The aspect ratio of the data are inferred from the affine transform. title : str or None, optional The title to display. Can be None (default) to display no title. cmap: matplotlib colormap, optional Colormap to use for ploting. Default: 'gray' clim: [min, max] or None Limits to use for plotting. Default: 1 and 99th percentiles alpha: float Transparency value """ # Use these late imports of matplotlib so that we have some hope that # the test functions are the first to set the matplotlib backend. The # tests set the backend to something that doesn't require a display. self._title = title self._closed = False self._cross = True volume = np.asanyarray(volume) if volume.ndim < 3: raise ValueError('volume must have at least 3 dimensions') if np.iscomplexobj(volume): raise TypeError("Complex data not supported") affine = np.array(affine, float) if affine is not None else np.eye(4) if affine.shape != (4, 4): raise ValueError('affine must be a 4x4 matrix') # determine our orientation self._affine = affine codes = axcodes2ornt(aff2axcodes(self._affine)) self._order = np.argsort([c[0] for c in codes]) self._flips = np.array([c[1] < 0 for c in codes])[self._order] self._flips = list(self._flips) + [False] # add volume dim self._scalers = voxel_sizes(self._affine) self._inv_affine = np.linalg.inv(affine) # current volume info self._volume_dims = volume.shape[3:] if len(self._volume_dims) > 0: raise NotImplementedError('Cannot handle 4-D Datasets') self._volumes = [] # ^ +---------+ ^ +---------+ # | | | | | | # | Sag | | Cor | # S | 0 | S | 1 | # | | | | # | | | | # +---------+ +---------+ # A --> # ^ +---------+ # | | | # | Axial | # A | 2 | # | | # | | # +---------+ # <-- R fig, axes = plt.subplots(2, 2) fig.set_size_inches((8, 8), forward=True) self._axes = [axes[0, 0], axes[0, 1], axes[1, 0]] plt.tight_layout(pad=0.1) fig.delaxes(axes[1, 1]) if self._title is not None: fig.canvas.set_window_title(str(title)) # Start midway through each axis, idx is current slice number self._ims, self._data_idx = list(), list() # set up axis crosshairs self._crosshairs = [None] * 3 r = [self._scalers[self._order[2]] / self._scalers[self._order[1]], self._scalers[self._order[2]] / self._scalers[self._order[0]], self._scalers[self._order[1]] / self._scalers[self._order[0]]] self._sizes = [volume.shape[order] for order in self._order] for ii, xax, yax, ratio, label in zip([0, 1, 2], [1, 0, 0], [2, 2, 1], r, ('SAIP', 'SRIL', 'ARPL')): ax = self._axes[ii] vert = ax.plot([0] * 2, [-0.5, self._sizes[yax] - 0.5], color=(0, 1, 0), linestyle='-')[0] horiz = ax.plot([-0.5, self._sizes[xax] - 0.5], [0] * 2, color=(0, 1, 0), linestyle='-')[0] self._crosshairs[ii] = dict(vert=vert, horiz=horiz) # add text labels (top, right, bottom, left) lims = [0, self._sizes[xax], 0, self._sizes[yax]] bump = 0.01 poss = [[lims[1] / 2., lims[3]], [(1 + bump) * lims[1], lims[3] / 2.], [lims[1] / 2., 0], [lims[0] - bump * lims[1], lims[3] / 2.]] anchors = [['center', 'bottom'], ['left', 'center'], ['center', 'top'], ['right', 'center']] for pos, anchor, lab in zip(poss, anchors, label): ax.text(pos[0], pos[1], lab, horizontalalignment=anchor[0], verticalalignment=anchor[1]) ax.axis(lims) ax.set_aspect(ratio) ax.patch.set_visible(False) ax.set_frame_on(False) ax.axes.get_yaxis().set_visible(False) ax.axes.get_xaxis().set_visible(False) self._data_idx.append(0) self._data_idx.append(-1) # volume self._figs = set([a.figure for a in self._axes]) for fig in self._figs: fig.canvas.mpl_connect('scroll_event', self._on_scroll) fig.canvas.mpl_connect('motion_notify_event', self._on_mouse) fig.canvas.mpl_connect('button_press_event', self._on_mouse) # actually set data meaningfully self.add_overlay(volume, cmap=cmap, clim=clim, alpha=alpha, draw=False) self._position = np.zeros(4) self._position[3] = 1. # convenience for affine multiplication self._changing = False # keep track of status to avoid loops plt.draw() for fig in self._figs: fig.canvas.draw_idle() fig.canvas.draw() plt.pause(1e-3) # give a little bit of time for the renderer (needed on MacOS) self._set_position(0., 0., 0.) self._draw()