def test_Shift(self): # two images identical up to a translation less than half the spacing should yield a gamma index # equal to the ratio of the length of the translation vector and the DTA. print('Test_GammaIndex3dUnequalMesh test_Shift') np.random.seed(1234568) for i in range(5): #print("shift image no. {}".format(i)) nxyz = np.random.randint(5, 15, 3) oxyz = np.random.uniform(-100., 100., 3) sxyz = np.random.uniform(0.5, 2.5, 3) txyz = np.random.uniform(-0.5, 0.5, 3) * sxyz data = np.random.normal(1., 0.1, nxyz) img_ref = itk.GetImageFromArray(data.swapaxes(0, 2).copy()) img_ref.SetSpacing(sxyz) img_ref.SetOrigin(oxyz) img_target = itk.GetImageFromArray(data.swapaxes( 0, 2).copy()) # same as ref img_target.SetSpacing(sxyz) # same as ref img_target.SetOrigin(oxyz + txyz) # translated! ddp = 3.0 # % dta = 2.0 # mm img_gamma = gamma_index_3d_unequal_geometry(img_ref, img_target, dd=ddp, dta=dta) agamma = itk.GetArrayViewFromImage(img_gamma).swapaxes(0, 2) gval_expected = np.sqrt(np.sum((txyz / dta)**2)) self.assertTrue(np.allclose(agamma, gval_expected))
def vesselsAndBackground(self, DirPath, file): imgPath = DirPath + "/" + file Imin = 50 Imax = 80 img = itk.imread(imgPath) dat = itk.GetArrayFromImage(img) dat = dat / np.max(dat) * (Imax - Imin) + Imin minValue = np.min(dat[dat > 0]) print(minValue) dat[dat == 0] = minValue dat = dat.astype(np.uint8) # for now if (dat.dtype == np.uint8): img = itk.GetImageFromArray(dat.astype(np.uint8)) else: img = itk.GetImageFromArray( dat.astype(np.float32) ) # data is in double but it is not supported in itk outputPath = DirPath + "/vesselsAndBackground.nii" itk.imwrite(img, outputPath)
def noisyImage(self, DirPath, file, outputFile, noiseType): imgPath = DirPath + "/" + file print(DirPath) print(imgPath) for id, i in enumerate(self.noiseLevels): img = itk.imread(imgPath) dat = itk.GetArrayFromImage(img) # simulated CT noise poisson + gaussian noise if (noiseType == "poisson"): datNoisy = 0.5 * np.random.poisson( dat, None) + 0.5 * np.random.normal(dat, i, None) elif (noiseType == "rician"): datNoisy = rice.rvs(dat / i, scale=i) else: print("error noise type not supported") datNoisy[datNoisy < 0] = 0 datNoisy[datNoisy > 255] = 255 # writing image on disk if (dat.dtype == np.uint8): noisyImg = itk.GetImageFromArray(datNoisy.astype(np.uint8)) else: noisyImg = itk.GetImageFromArray( datNoisy.astype(np.float32) ) # data is in double but it is not supported in itk print(i) outputPath = DirPath + "/" + outputFile + "_" + str(i) + ".nii" itk.imwrite(noisyImg, outputPath) print(outputPath)
def test_checkerboards(self): #print("test 3D checkerboards: have D=0.25 in even voxels and D=0.75 in odd voxels for ref image, vice versa for test image.") #for every voxel in the target image, the neighboring voxels in the ref image has the same dose #therefore the gamma index should be equal to spacing/dta for all voxels. print('Test_GammaIndex3dIdenticalMesh test_checkerboards') nx, ny, nz = 4, 5, 6 ix, iy, iz = np.meshgrid(np.arange(nx, dtype=int), np.arange(ny, dtype=int), np.arange(nz, dtype=int), indexing='ij') a_odd = 0.5 * (((ix + iy + iz) % 2) == 1).astype(float) + 0.25 a_even = 0.5 * (((ix + iy + iz) % 2) == 0).astype(float) + 0.25 img_odd = itk.GetImageFromArray(a_odd) img_even = itk.GetImageFromArray(a_even) img_gamma_even_odd = gamma_index_3d_equal_geometry(img_even, img_odd, dd=10., dta=2.) img_gamma_odd_even = gamma_index_3d_equal_geometry(img_odd, img_even, dd=10., dta=2.) self.assertTrue( np.allclose(itk.GetArrayViewFromImage(img_gamma_odd_even), itk.GetArrayViewFromImage(img_gamma_even_odd))) self.assertTrue( np.allclose(itk.GetArrayViewFromImage(img_gamma_odd_even), 0.5))
def vesselsIllumination(self, inputPath, outputPath, nbGaussianArtefacts, aSigmaMin, aSigmaMax, aImin, aImax): # Retrieving nifti data into arrays img = itk.imread(inputPath) dat = itk.GetArrayFromImage(img) dat = dat.astype(np.float32) # Artefacts a = np.zeros(dat.shape) if (nbGaussianArtefacts > 0): for i in range(nbGaussianArtefacts): a += self.makeGaussian(dat, aSigmaMin, aSigmaMax) a = a / a.max() * (aImax - aImin) + aImin a[a < aImin + 2] = 0 print("a", np.max(a), np.min(a)) dat = a + dat dat[dat < 0] = 0 dat[dat > 255] = 255 dat[0, 0, 0] = 0 # used for easier display in slicer dat[0, 0, 1] = 255 # used for easier display in slicer # writing image on disk # writing image on disk if (dat.dtype == np.uint8): illuminatedImg = itk.GetImageFromArray(dat.astype(np.uint8)) else: illuminatedImg = itk.GetImageFromArray( dat.astype(np.float32) ) # data is in double but it is not supported in itk print(dat.dtype) itk.imwrite(illuminatedImg, outputPath)
def imageFromGaussianPDF(self, inputPath, outputPath, dataType): if (dataType == "MRI"): print("using MRI") gaussianPDFLiver = norm(loc=108, scale=12) gaussianPDFVessels = norm(loc=119, scale=16) if (dataType == "CT"): print("using CT") gaussianPDFLiver = norm(loc=101, scale=14) gaussianPDFVessels = norm(loc=139, scale=16) img = itk.imread(inputPath) img_np = itk.GetArrayFromImage(img) mask = (img_np == 0) img_np[mask] = gaussianPDFLiver.rvs(img_np[mask].shape[0]) mask_vessels = (img_np > 0) img_np[mask_vessels] = gaussianPDFVessels.rvs( img_np[mask_vessels].shape[0]) dat = img_np dat = dat.astype(np.uint8) # for now if (dat.dtype == np.uint8): img = itk.GetImageFromArray(dat.astype(np.uint8)) else: img = itk.GetImageFromArray( dat.astype(np.float32) ) # data is in double but it is not supported in itk itk.imwrite(img, outputPath)
def test_identity(self): # two identical images should give gamma=0.0 in all voxels #print("test identity") print('Test_GammaIndex3dIdenticalMesh test_identity') np.random.seed(1234567) a_rnd = np.random.uniform(0., 10., (4, 5, 6)) img1 = itk.GetImageFromArray(a_rnd) img2 = itk.GetImageFromArray(a_rnd) img_gamma = gamma_index_3d_equal_geometry(img1, img2, dd=3., dta=2.0) self.assertTrue((itk.GetArrayViewFromImage(img_gamma) == 0.).all())
def test_scaling(self): #print("test scaling small") # two images identical up to a scaling factor 1.03 should give gamma(3%)<=1.0 in all voxels print('Test_GammaIndex3dIdenticalMesh test_scaling') np.random.seed(1234567) a_rnd = np.random.uniform(0., 10., (4, 5, 6)) img1 = itk.GetImageFromArray(a_rnd) img2 = itk.GetImageFromArray(1.03 * a_rnd) img_gamma = gamma_index_3d_equal_geometry(img1, img2, dd=3., dta=2.0) self.assertTrue((itk.GetArrayViewFromImage(img_gamma) < 1.0001).all())
def vtk_to_itk_image(key, vtk_image): pixel_data = vtk_image['pointData']['values'] pixel_type = vtk_image['pointData']['dataType'] pixel_data = _vtkjs_type_convert(pixel_data, pixel_type) # numpy indexes in ZYX order, where X varies the fastest dims = [ vtk_image['extent'][5] - vtk_image['extent'][4] + 1, vtk_image['extent'][3] - vtk_image['extent'][2] + 1, vtk_image['extent'][1] - vtk_image['extent'][0] + 1, ] direction = np.zeros((3, 3)) for x in range(3): for y in range(3): direction[x][y] = vtk_image['direction'][x * 3 + y] itkImage = itk.GetImageFromArray(np.reshape(pixel_data, dims)) # https://discourse.itk.org/t/set-image-direction-from-numpy-array/844/10 vnlmat = itk.GetVnlMatrixFromArray(direction) itkImage.GetDirection().GetVnlMatrix().copy_in(vnlmat.data_block()) itkImage.SetOrigin(vtk_image['origin']) itkImage.SetSpacing(vtk_image['spacing']) return itkImage
def test_connected_component(): path = 'C:/Users/SEELE/Desktop/11/a' file_list = os.listdir(path) file_list.sort() label_volume = np.zeros([len(file_list), 512, 512]) for i in range(len(file_list)): one_image_full_path = os.path.join(path, file_list[i]) one_image_data = cv.imread(one_image_full_path, cv.IMREAD_GRAYSCALE) cv.threshold(one_image_data, 127, 255, cv.THRESH_BINARY, one_image_data) label_volume[i] = one_image_data # cv.imshow('a', label_volume[87]) # cv.waitKey() PixelType = itk.ctype("unsigned char") ImageType = itk.Image[PixelType, 3] itkimage = itk.GetImageFromArray(label_volume.astype(np.uint8)) filter = itk.BinaryShapeKeepNObjectsImageFilter[ImageType].New() filter.SetAttribute('NumberOfPixels') filter.SetForegroundValue(255) filter.SetBackgroundValue(0) filter.SetNumberOfObjects(1) filter.SetInput(itkimage) filter.Update() output = filter.GetOutput() result_label_volume = itk.GetArrayFromImage(output) for i in range(result_label_volume.shape[0]): one_image_full_path = os.path.join(path, 'a.%d.jpg' % (i)) cv.imwrite(one_image_full_path, result_label_volume[i]) pass
def itk_to_vtk(itkImage): resample_factor = 10 array = itk.GetArrayFromImage(itkImage) #Downsample test spacing = itkImage.GetSpacing() spacing[2] *= resample_factor array = array[::resample_factor, :, :] dims = (array.shape[1], array.shape[2], array.shape[0]) downsampled_image = itk.GetImageFromArray(array) print(downsampled_image) vtk_array = numpy_support.numpy_to_vtk(num_array=array.ravel(), deep=True, array_type=vtk.VTK_FLOAT) print(dims, spacing) #MN vtkImage = vtk.vtkImageData() vtkImage.SetDimensions(dims) vtkImage.SetSpacing(spacing) vtkImage.GetPointData().SetScalars(vtk_array) return vtkImage
def refreshView(self): # revise the voxels values to draw a bounding box revisedVolMat = drawBB(self.volMat,self.arrayBB) # write the revised volume with the bounding box revisedVolFilename = self.imgfilename[:-4]+'_bb.hdr' itk.imwrite(itk.GetImageFromArray(revisedVolMat.astype(np.float32)),revisedVolFilename) imgfilename = revisedVolFilename # read the revised volume using vtk # Create source vtkReader = vtk.vtkNIFTIImageReader() vtkReader.SetFileName(imgfilename) vtkReader.Update() # set a vtkvolume self.mapper.SetInputData(vtkReader.GetOutput()) #self.mapper.Update() vtkvolume = vtk.vtkVolume() vtkvolume.SetMapper(self.mapper) vtkvolume.SetProperty(self.volumeProperty) # set renderer self.ren.SetBackground(1,1,1) self.ren.AddVolume(vtkvolume) self.ren.ResetCamera() #self.ren.GetActiveCamera().Zoom(1.5) self.tipnameLabel.setText(self.displayName) self.frame.setLayout(self.vl) self.setCentralWidget(self.frame) self.show() self.iren.Initialize()
def thinning_diameter(sparse, envelope, multiprocesses=2): """Computes twice the shortest distance to the outer shell along the medial axis of the object (i.e. diameter of the local maximal fitting sphere) This function requires: ITK""" if not _have_itk: raise ImportError("Please install ITK to use this function.") if not _have_itk_thickness: raise ImportError( "Please install itk-thickness3d to use this function.") if multiprocesses == 1: warnings.warn( "Disabled multiprocessing creates a pool of threads which does not get deallocated. " "Subsequent calls with multiprocessing **enabled** will be locked indefinitely." ) sparse.run( lambda data, prev: ( itk.GetArrayFromImage( itk.MedialThicknessImageFilter3D.New( itk.GetImageFromArray(data))), prev, ), envelope=envelope, skip_neighbours=True, multiprocesses=multiprocesses, )
def _CropImageManuallyWithNumpy(input_img, from_index, to_index): """ We would like to use itk.RegionOfInterestImageFilter but that filter does not recalculate the origin correctly. So now we do this manually, through numpy. """ aimg = itk.GetArrayViewFromImage(input_img) logger.debug("got input image, the array view {} contiguous".format( "IS" if aimg.flags.contiguous else "IS NOT")) assert ((from_index > 0).all()) assert ((to_index <= np.array(aimg.shape[::-1])).all()) logger.debug( "going to create new image, forcing slice of old array to be continuous" ) new_img = itk.GetImageFromArray( np.ascontiguousarray(aimg[from_index[2]:to_index[2], from_index[1]:to_index[1], from_index[0]:to_index[0]])) logger.debug("going to assign spacing and origin to new image") #new_img.CopyInformation(input_img) spacing = np.array(input_img.GetSpacing()) old_origin = np.array(input_img.GetOrigin()) new_origin = old_origin + (from_index) * spacing new_img.SetSpacing(spacing) new_img.SetOrigin(new_origin) logger.debug("cropping done, manually with numpy") return new_img
def get_intersection_volume(roilist,xvoxel=1.,yvoxel=1.): # There is probably a clever way to compute this by constructing # an "intersection contour" for each layer: for each contour, keep only # points that are inside all other contours in the list. But is tough to then # put those points in the right order. # Instead we'll just make a grid of points and get the volume of the combined mask. # With xvoxel and yvoxel the caller can tweak the voxel size of the mask in x and y. # In z the voxel size is given by the incoming ROIs. dz = min([r.dz for r in roilist]) assert(dz>0) assert(xvoxel>0) assert(yvoxel>0) bb = bounding_box(bb=roilist[0].bb) for roi in roilist[1:]: bb.intersect(roi.bb) if bb.empty: # too bad return 0. spacing = np.array([xvoxel,yvoxel,dz],dtype=float) bb.add_margins(2*spacing) dimsize = np.array(np.round((bb.maxcorner-bb.mincorner)/spacing),dtype=int) #img = sitk.Image(dimsize,sitk.sitkUInt8) img = itk.GetImageFromArray(np.zeros(dimsize[::-2],dtype=np.uint8)) img.SetOrigin(bb.mincorner) img.SetSpacing(spacing) itkmask = itk.GetArrayFromImage(roilist[0].get_mask(img)) for roi in roilist[1:]: itkmask *= itk.GetArrayFromImage(roi.get_mask(img)) return np.sum(itkmask)*np.prod(spacing)
def image_interpolate_recurrence(image1, image2, ind1, ind2, output_folder, img_origin, img_spacing): avg_img = (image1 + image2) / 2 new_ind = (ind1 + ind2) / 2 integer_ind = int(new_ind) if (new_ind == integer_ind): # even number # save this projection output_imagename = f'./{output_folder}/secondary{int(integer_ind):04d}.mha' outputimage = itk.GetImageFromArray(avg_img) outputimage.SetOrigin(img_origin) outputimage.SetSpacing(img_spacing) itk.imwrite(outputimage, output_imagename) # check if can divide more if integer_ind - ind1 > 1: # call this function again twice, once to the left and once to the right image_interpolate_recurrence(image1, avg_img, ind1, integer_ind, output_folder, img_origin, img_spacing) image_interpolate_recurrence(avg_img, image2, integer_ind, ind2, output_folder, img_origin, img_spacing) else: # check if can divide more if new_ind - ind1 > 1: # call this function again twice, once to the left and once to the right image_interpolate_recurrence(image1, avg_img, ind1, int(math.ceil(new_ind)), output_folder, img_origin, img_spacing) image_interpolate_recurrence(avg_img, image2, int(math.floor(new_ind)), ind2, output_folder, img_origin, img_spacing)
def makeHardClassificationPatches(self, DirPath, inputPath, maskPath, outputPath): imgInputPath = DirPath + "/" + inputPath #"/DATA/vascu_deepV2/maskWholeImage.nii" imgMaskPath = DirPath + "/" + maskPath imgROIPath = DirPath + "/" + outputPath imgInput = itk.imread(imgInputPath) imgMask = itk.imread(imgMaskPath) PixelType = itk.UC Dimension = 3 ImageType = itk.Image[PixelType, Dimension] radiusValue = 4 StructuringElementType = itk.FlatStructuringElement[Dimension] structuringElement = StructuringElementType.Ball(radiusValue) DilateFilterType = itk.BinaryDilateImageFilter[ImageType, ImageType, StructuringElementType] dilateFilter = DilateFilterType.New() dilateFilter.SetInput(imgMask) dilateFilter.SetKernel(structuringElement) dilateFilter.SetForegroundValue(255) imgMask = dilateFilter.GetOutput() imgInput = itk.GetArrayFromImage(imgInput) imgMask = itk.GetArrayFromImage(imgMask) imgROI = np.copy(imgInput) imgROI[imgMask > 0] = 0 itk.imwrite(itk.GetImageFromArray(imgROI.astype(np.uint8)), imgROIPath)
def RelabelComponents(inputImage, outputImageType = None): # relabel = itk.RelabelComponentImageFilter[input_type, output_type].New() # relabel.SetInput(inputImage) # relabel.Update() # return relabel.GetOutput() label_field = itk.GetArrayFromImage(inputImage) offset = 1 max_label = int(label_field.max()) # Ensure max_label is an integer labels, labels_counts= np.unique(label_field,return_counts=True) labels=labels[np.argsort(labels_counts)[::-1]] labels0 = labels[labels != 0] new_max_label = offset - 1 + len(labels0) new_labels0 = np.arange(offset, new_max_label + 1) output_type = label_field.dtype required_type = np.min_scalar_type(new_max_label) if np.dtype(required_type).itemsize > np.dtype(label_field.dtype).itemsize: output_type = required_type forward_map = np.zeros(max_label + 1, dtype=output_type) forward_map[labels0] = new_labels0 inverse_map = np.zeros(new_max_label + 1, dtype=output_type) inverse_map[offset:] = labels0 relabeled = forward_map[label_field] result = itk.GetImageFromArray(relabeled) result.SetOrigin(inputImage.GetOrigin()) result.SetSpacing(inputImage.GetSpacing()) result.SetDirection(inputImage.GetDirection()) if not outputImageType is None: s,d = itk.template(inputImage)[1] output_type = itk.Image[outputImageType,d] result = castImage(result, OutputType=output_type) return result
def execute(self, image: sitk.Image, params: fltr.IFilterParams = None) -> sitk.Image: # input image is an sitkImage. The itk.CoocurrenceTextureFeatureImageFilter needs an itkImage as input. So # convert to an itkImage via a numpy array. img1_arr = (sitk.GetArrayFromImage(image)).astype(np.uint16) img1 = itk.GetImageFromArray(img1_arr) mask = itk.BinaryThresholdImageFilter.New(img1) mask.SetLowerThreshold(1) mask.SetInsideValue(1) filtr = itk.CoocurrenceTextureFeaturesImageFilter.New(img1) filtr.SetMaskImage(mask) filtr.SetNumberOfBinsPerAxis(10) filtr.SetHistogramMinimum(0) filtr.SetHistogramMaximum(2**16 - 1) filtr.SetNeighborhoodRadius([5, 5, 5]) result1 = filtr.GetOutput() # convert to simpleITK image type, since the rest of the pipeline requires this datatype # we take care of the correct origin and orientation by copying this information from the input sitkImage result_arr = itk.GetArrayFromImage(result1) result = sitk.GetImageFromArray(result_arr) result.CopyInformation(image) return result
def multiscaleSheetness(multiScaleInput, scales, SmoothingImageType, roi = None, alpha = 0.5, beta = 0.5, gamma = 0.5): if not roi is None: roi = itk.GetArrayFromImage(roi) multiscaleSheetness = singlescaleSheetness(singleScaleInput = multiScaleInput, scale = scales[0], SmoothingImageType = SmoothingImageType, roi = roi, alpha = alpha, beta = beta, gamma = gamma) if len(scales) > 1: for scale in scales[1:]: singleScaleSheetness = singlescaleSheetness(multiScaleInput, scale = scale, SmoothingImageType = SmoothingImageType, roi = roi, alpha = alpha, beta = beta, gamma = gamma) refinement = abs(singleScaleSheetness) > abs(multiscaleSheetness) multiscaleSheetness[refinement] = singleScaleSheetness[refinement] multiscaleSheetness = itk.GetImageFromArray(multiscaleSheetness.astype(np.float32)) multiscaleSheetness.SetOrigin(multiScaleInput.GetOrigin()) multiscaleSheetness.SetSpacing(multiScaleInput.GetSpacing()) multiscaleSheetness.SetDirection(multiScaleInput.GetDirection()) return multiscaleSheetness
def binaryThresholding(inputImage, lowerThreshold, upperThreshold, outputImageType = None, insideValue = 1, outsideValue = 0): # Old version: # s,d = itk.template(inputImage)[1] # input_type = itk.Image[s,d] # output_type = input_type if outputImageType is None else itk.Image[outputImageType,d] # thresholder = itk.BinaryThresholdImageFilter[input_type, output_type].New() # thresholder.SetInput(inputImage) # thresholder.SetLowerThreshold( lowerThreshold ) # thresholder.SetUpperThreshold( upperThreshold ) # thresholder.SetInsideValue(insideValue) # thresholder.SetOutsideValue(outsideValue) # thresholder.Update() # return thresholder.GetOutput() values = itk.GetArrayFromImage(inputImage) cond = (values>=lowerThreshold) & (values<=upperThreshold) values[ cond ] = insideValue values[ np.logical_not(cond) ] = outsideValue result = itk.GetImageFromArray(values) result.SetOrigin(inputImage.GetOrigin()) result.SetSpacing(inputImage.GetSpacing()) result.SetDirection(inputImage.GetDirection()) if not outputImageType is None: s,d = itk.template(inputImage)[1] output_type = itk.Image[outputImageType,d] result = castImage(result, OutputType=output_type) return result
def noisyImage(self, inputPath, outputName, noiseType): for id, i in enumerate(self.noiseLevels): img = itk.imread(inputPath) dat = itk.GetArrayFromImage(img) dat = dat.astype(np.float32) # simulated CT noise poisson + gaussian noise if (noiseType == "poisson"): datNoisy = 0.5 * np.random.poisson( dat, None) + 0.5 * np.random.normal(dat, i, None) elif (noiseType == "rician"): datNoisy = rice.rvs(dat / i, scale=i) else: print("error noise type not supported") datNoisy[datNoisy < 0] = 0 datNoisy[datNoisy > 255] = 255 # writing image on disk noisyImg = itk.GetImageFromArray(datNoisy.astype(np.uint8)) print(i) outputPath = outputName + "_" + str(i) + ".nii" itk.imwrite(noisyImg, outputPath) print(outputPath)
def to_itk_image(other_image_datatype): if is_arraylike(other_image_datatype): array = np.asarray(other_image_datatype) case_use_view = array.flags['OWNDATA'] if have_dask and isinstance(other_image_datatype, dask.array.core.Array): case_use_view = False if case_use_view: image_from_array = itk.GetImageViewFromArray(array) else: image_from_array = itk.GetImageFromArray(array) return image_from_array elif have_vtk and isinstance(other_image_datatype, vtk.vtkImageData): from vtk.util import numpy_support as vtk_numpy_support array = vtk_numpy_support.vtk_to_numpy( other_image_datatype.GetPointData().GetScalars()) array.shape = tuple(other_image_datatype.GetDimensions())[::-1] image_from_array = itk.GetImageViewFromArray(array) image_from_array.SetSpacing(other_image_datatype.GetSpacing()) image_from_array.SetOrigin(other_image_datatype.GetOrigin()) return image_from_array elif have_imglyb and isinstance( other_image_datatype, imglyb.util.ReferenceGuardingRandomAccessibleInterval): array = imglyb.to_numpy(other_image_datatype) image_from_array = itk.GetImageViewFromArray(array) return image_from_array return None
def image_keep_max_region(label_volume_data): label_volume = image_threshold_binaray_3d(label_volume_data, 127) # labels = measure.label(image_3d_ref, connectivity=2) # props = measure.regionprops(labels) # max_area = 0 # max_area_index = 0 # for i in range(len(props)): # area = props[i].area # if area > max_area: # max_area_index = i # max_area = area # box = props[max_area_index].bbox # label_volume_with_max_region = np.zeros(image_3d_ref.shape, np.uint8) # label_volume_with_max_region[box[0]:box[3], box[1]:box[4], box[2]:box[5]] = \ # image_3d_ref[box[0]:box[3], box[1]:box[4], box[2]:box[5]] # assert box[3] - box[0] < depth + 1 # for i in range(label_volume_with_max_region.shape[0]): # cv.imwrite('D:\\tmp\\MIS\\other\\%d.jpg' % (i), label_volume_with_max_region[i]) PixelType = itk.ctype("unsigned char") ImageType = itk.Image[PixelType, 3] itkimage = itk.GetImageFromArray(label_volume.astype(np.uint8)) filter = itk.BinaryShapeKeepNObjectsImageFilter[ImageType].New() filter.SetAttribute('NumberOfPixels') filter.SetForegroundValue(255) filter.SetBackgroundValue(0) filter.SetNumberOfObjects(1) filter.SetInput(itkimage) filter.Update() output = filter.GetOutput() result_label_volume = itk.GetArrayFromImage(output) return result_label_volume
def main(): print("Start main()") example = return_example_volume() example_itk_pointer = itk.GetImageFromArray(example) image_type_input = type(example_itk_pointer) # Convert input image to itk.SS image_type = itk.Image[itk.SS, 3] ImageCast = itk.CastImageFilter[image_type_input, image_type].New() ImageCast.SetInput(example_itk_pointer) # Extract threshold volume from image ThresholdFilter = itk.BinaryThresholdImageFilter[image_type, image_type].New() ThresholdFilter.SetInput(ImageCast.GetOutput()) ThresholdFilter.SetLowerThreshold(19) ThresholdFilter.SetUpperThreshold(21) ThresholdFilter.SetOutsideValue(False) ThresholdFilter.SetInsideValue(True) # Convert to mesh mesh_type = itk.Mesh[itk.D, 3] MeshFilter = itk.BinaryMask3DMeshSource[image_type, mesh_type].New() MeshFilter.SetInput(ThresholdFilter.GetOutput()) MeshFilter.SetObjectValue(1) # Write file to hdd Writer = itk.MeshFileWriter[mesh_type].New() Writer.SetFileName("Test.obj") Writer.SetInput(MeshFilter.GetOutput()) Writer.Update()
def get_bounding_box(image_3d_ref, depth, xy_padding=15, z_padding=8): image_3d_ref = image_threshold_binaray_3d(image_3d_ref, 127) PixelType = itk.ctype("unsigned char") ImageType = itk.Image[PixelType, 3] itkimage = itk.GetImageFromArray(image_3d_ref.astype(np.uint8)) filter = itk.BinaryShapeKeepNObjectsImageFilter[ImageType].New() filter.SetAttribute('NumberOfPixels') filter.SetForegroundValue(255) filter.SetBackgroundValue(0) filter.SetNumberOfObjects(1) filter.SetInput(itkimage) filter.Update() output = filter.GetOutput() result_label_volume = itk.GetArrayFromImage(output) z, y, x = result_label_volume.nonzero() z_min = max(0, min(z) - z_padding) z_max = min(image_3d_ref.shape[0] - 1, z_min + depth) y_min = min(y) y_max = max(y) x_min = min(x) x_max = max(x) width = x_max - x_min height = y_max - y_min length = max(width, height) start_z = z_min end_z = z_max start_y = max(y_min - xy_padding, 0) end_y = min(start_y + length + 2 * xy_padding, image_3d_ref.shape[1] - 1) start_x = max(x_min - xy_padding, 0) end_x = min(start_x + length + 2 * xy_padding, image_3d_ref.shape[2] - 1) bbox_size = [start_z, end_z, start_y, end_y, start_x, end_x] return bbox_size
def reg_image_sitk_to_itk(self, cast_to_float32: bool = True) -> None: """ Convert SimpleITK to ITK for use in ITKElastix. Parameters ---------- cast_to_float32: bool Whether to make image float32 for ITK, needs to be true for registration. """ origin = self._reg_image.GetOrigin() spacing = self._reg_image.GetSpacing() # direction = image.GetDirection() is_vector = self._reg_image.GetNumberOfComponentsPerPixel() > 1 if cast_to_float32 is True: self._reg_image = sitk.Cast(self._reg_image, sitk.sitkFloat32) self._reg_image = sitk.GetArrayFromImage(self._reg_image) else: self._reg_image = sitk.GetArrayFromImage(self._reg_image) self._reg_image = itk.GetImageFromArray(self._reg_image, is_vector=is_vector) self._reg_image.SetOrigin(origin) self._reg_image.SetSpacing(spacing) if self._mask is not None: origin = self._mask.GetOrigin() spacing = self._mask.GetSpacing() # direction = image.GetDirection() is_vector = self._mask.GetNumberOfComponentsPerPixel() > 1 if cast_to_float32 is True: self._mask = sitk.Cast(self._mask, sitk.sitkFloat32) self._mask = sitk.GetArrayFromImage(self._mask) else: self._mask = sitk.GetArrayFromImage(self._mask) self._mask = itk.GetImageFromArray(self._mask, is_vector=is_vector) self._mask.SetOrigin(origin) self._mask.SetSpacing(spacing) mask_im_type = itk.Image[itk.UC, 2] self._mask = itk.binary_threshold_image_filter( self._mask, lower_threshold=1, inside_value=1, ttype=(type(self._mask), mask_im_type), )
def merge_pred(results_folder, ROI_list): # get the list of Task folders tasks_folders = glob.glob(f'{results_folder}/segm_results/Task*') if len(tasks_folders) != 3: print('Not all direction available, no merging was performed') sys.exit() # get the list of labels all_patients = glob.glob( f'{results_folder}/segm_results/{os.path.basename(tasks_folders[0])}/predicted_labels/*' ) merged_labels = f'{results_folder}/segm_results/merged_labels' merged_binaries = f'{results_folder}/segm_results/merged_binaries' maybe_mkdir_p(merged_labels) maybe_mkdir_p(merged_binaries) # loop through the patient for patient in all_patients: label_name = os.path.basename(patient) patient_name = label_name.replace('label_', '') merged_binaries_patient = f'{merged_binaries}/{os.path.splitext(patient_name)[0]}' maybe_mkdir_p(merged_binaries_patient) # load labels in each direction label_d1_img = itk.imread( f'{tasks_folders[0]}/predicted_labels/{label_name}') label_origin = label_d1_img.GetOrigin() label_spacing = label_d1_img.GetSpacing() label_direction = label_d1_img.GetDirection() label_d1 = itk.GetArrayFromImage(label_d1_img) label_d2 = itk.GetArrayFromImage( itk.imread(f'{tasks_folders[1]}/predicted_labels/{label_name}')) label_d3 = itk.GetArrayFromImage( itk.imread(f'{tasks_folders[2]}/predicted_labels/{label_name}')) # get the intersection of every 2 labels intersec1 = np.where(label_d1 == label_d2, label_d1, 0) intersec2 = np.where(label_d1 == label_d3, label_d1, 0) intersec3 = np.where(label_d2 == label_d3, label_d2, 0) # get where 2 labels agree merged_label = np.zeros(label_d1.shape) merged_label = np.where(intersec1 != 0, intersec1, merged_label) merged_label = np.where(intersec2 != 0, intersec2, merged_label) merged_label = np.where(intersec3 != 0, intersec3, merged_label) merged_label_img = itk.GetImageFromArray(merged_label) merged_label_img.SetOrigin(label_origin) merged_label_img.SetSpacing(label_spacing) merged_label_img.SetDirection(label_direction) itk.imwrite(merged_label_img, f'{merged_labels}/{label_name}') segmap_to_binaries(merged_label, merged_binaries_patient, label_origin, label_spacing, label_direction, ROI_list)
def _CropAndPadImageManuallyWithNumpy(input_img, from_index, to_index, hu_value_for_padding): """ We would like to use itk.RegionOfInterestFilter but that filter does recalculate the origin correctly. So now we do this manually, through numpy. """ logger.debug("crop and pad manually with numpy") aimg = itk.GetArrayViewFromImage(input_img) logger.debug("got input image, the array view {} contiguous".format( "IS" if aimg.flags.contiguous else "IS NOT")) if (from_index > 0).all() and (to_index <= np.array( aimg.shape[::-1])).all(): logger.debug("only cropping, no padding") return _CropImageManuallyWithNumpy(input_img, from_index, to_index) logger.debug("both cropping and padding") atype = aimg.dtype.type asize = np.array(aimg.shape)[::-1] new_size = to_index - from_index logger.debug("old size: {} new size: {}".format(asize, new_size)) from_old = np.array([max(i, 0) for i in from_index ]) # i<0: padding, i>0: cropping; i==0: no change to_old = np.array([min(s, j) for j, s in zip(to_index, asize) ]) # j>s: padding, j<s: cropping; j==s: no change from_new = np.array([max(-i, 0) for i in from_index ]) # i<0: padding, i>0: cropping; i==0: no change to_new = np.array([ inew + jorig - iorig for inew, iorig, jorig in zip(from_new, from_old, to_old) ]) logger.debug("from indices in orig: {}".format(from_old)) logger.debug("to indices in orig: {}".format(to_old)) logger.debug("from indices in output: {}".format(from_new)) logger.debug("to indices in output: {}".format(to_new)) assert ((to_new <= new_size).all()) assert ((to_new - from_new == to_old - from_old).all()) assert ((to_new - from_new > 0).all()) anew = np.full(new_size[::-1], fill_value=hu_value_for_padding, dtype=atype) logger.debug("new image array {} contiguous".format( "IS" if anew.flags.contiguous else "IS NOT")) anew[from_new[2]:to_new[2],from_new[1]:to_new[1],from_new[0]:to_new[0]] = \ aimg[from_old[2]:to_old[2],from_old[1]:to_old[1],from_old[0]:to_old[0]] logger.debug("new image array with shape {} is now filled".format( aimg.shape)) new_img = itk.GetImageFromArray(anew) logger.debug("new image created from array, it has size {}".format( new_img.GetLargestPossibleRegion().GetSize())) #new_img.CopyInformation(input_img) spacing = np.array(input_img.GetSpacing()) old_origin = np.array(input_img.GetOrigin()) new_origin = old_origin + (from_index) * spacing new_img.SetSpacing(spacing) new_img.SetOrigin(new_origin) logger.debug("cropping and padding done, manually with numpy") return new_img
def __call__(self, volume, spacing, para, img_shape, img_spacing): itk_vol = itk.GetImageFromArray(volume) itk_vol.SetSpacing(spacing) vol_origin = itk_vol.GetOrigin() # Origin is [0, 0, 0] vol_res = itk_vol.GetSpacing() vol_region = itk_vol.GetBufferedRegion() vol_size = vol_region.GetSize() InputImageType = itk.Image.SS3 pixeltype = itk.D vol_center = itk.Point[pixeltype, self.dimension]( np.array(vol_res) * np.array(vol_size) / 2) focalpoint = itk.Point[pixeltype, self.dimension]( (vol_center[0], vol_center[1], self.focal_length - self.distance)) rx, ry, rz, tx, ty, tz = para translation = itk.Vector[pixeltype, self.dimension]((tx, ty, tz)) transform = itk.CenteredEuler3DTransform.D.New() transform.SetCenter(vol_center) transform.SetComputeZYX(True) transform.SetTranslation(translation) transform.SetRotation(np.deg2rad(rx), np.deg2rad(ry), np.deg2rad(rz)) interpolator = itk.RayCastInterpolateImageFunction[ InputImageType, itk.ctype('double')].New() interpolator.SetTransform(transform) interpolator.SetThreshold(self.threshold) interpolator.SetFocalPoint(focalpoint) itk_vol.SetOrigin((0, 0, 0)) resample_filter = itk.ResampleImageFilter[InputImageType, InputImageType].New() resample_filter.SetInterpolator(interpolator) resample_filter.SetInput(itk_vol) resample_filter.SetDefaultPixelValue(0) resample_filter.SetTransform(transform) img_size = itk.Size[self.dimension]((img_shape[0], img_shape[1], 1)) resample_filter.SetSize(img_size) resample_filter.SetOutputSpacing((img_spacing[0], img_spacing[1], 1.0)) # outputorigin indicate the ralationship between projection image (0, 0) and CT volume(0,0,0) img_origin = np.array( vol_center)[:2] - np.array(img_shape) * img_spacing / 2 outputorigin = itk.Point[pixeltype, self.dimension]( (img_origin[0], img_origin[1], -self.distance)) # physical corrdinate resample_filter.SetOutputOrigin(outputorigin) resample_filter.Update() ray = resample_filter.GetOutput() drr = itk.GetArrayFromImage(ray) drr = drr.squeeze() # grayimg = (drr - drr.min())/(drr.max() - drr.min()) * 255 grayimg = (np.zeros_like(drr) - (drr - drr.min()) / (drr.max() - drr.min())) * 255 return grayimg