def test_warpedImageToFixedSpaceRotateTransform(self): WarpedImagePath = os.path.join( self.ImportedDataPath, "0017_TEM_Leveled_image__feabinary_Cel64_Mes8_sp4_Mes8.png") self.assertTrue(os.path.exists(WarpedImagePath), "Missing test input") angle = 30 arecord = AlignmentRecord(peak=(0, 0), weight=100, angle=angle) fixedImage = nornir_imageregistration.LoadImage(WarpedImagePath) warpedImage = nornir_imageregistration.LoadImage(WarpedImagePath) transform = arecord.ToTransform(fixedImage.shape, warpedImage.shape) transformedImage = assemble.WarpedImageToFixedSpace( transform, fixedImage.shape, warpedImage) imsave("C:\\Temp\\17Rotate.png", transformedImage) rotatedWarped = interpolation.rotate(warpedImage.astype(numpy.float32), angle=angle) # self.assertTrue( ShowComparison( [fixedImage, rotatedWarped, transformedImage], title= "Rotate transform should match scipy.interpolate.rotate result", PassFail=True))
def testBrightfieldShading(self): ReferenceImagePath = self.GetImagePath("400.png") ShadedImagePath = self.GetImagePath("400_Shaded.png") ShadingReferencePath = self.GetImagePath("BrightfieldShading.png") self.assertTrue(os.path.exists(ReferenceImagePath)) self.assertTrue(os.path.exists(ShadedImagePath)) self.assertTrue(os.path.exists(ShadingReferencePath)) originalImage = nir.LoadImage(ReferenceImagePath) shadedImage = nir.LoadImage(ShadedImagePath) shadingMask = nir.LoadImage(ShadingReferencePath) shadedImageV2 = originalImage * shadingMask shadedImageV2Path = os.path.join(self.TestOutputPath, "TestGeneratedShadedImage.png") nir.SaveImage(shadedImageV2Path, shadedImageV2) OutputPaths = tiles.ShadeCorrect( [ShadedImagePath, shadedImageV2Path], shadingMask, self.TestOutputPath, correction_type=tiles.ShadeCorrectionTypes.BRIGHTFIELD) shownImages = [originalImage, shadedImage, shadingMask] for path in OutputPaths: correctedImage = nir.LoadImage(path) shownImages.append(correctedImage) # nir.ShowGrayscale(shownImages) pass
def __CompositeTiles(imagepaths, func): '''Takes two images, merges, and returns the max image :param list imagepaths: list of paths to images :param str outputpath: Path of output image :param function func: function taking two images as arguments. Called on image pairs. ''' stack = copy.copy(imagepaths) CompositeImage = nornir_imageregistration.LoadImage(stack.pop()) while len(stack) > 0: imageA = nornir_imageregistration.LoadImage(stack.pop()) CompositeImage = func(CompositeImage, imageA) del imageA return CompositeImage
def test_warpedImageToFixedSpaceIdentityTransform(self): WarpedImagePath = os.path.join( self.ImportedDataPath, "0017_TEM_Leveled_image__feabinary_Cel64_Mes8_sp4_Mes8.png") self.assertTrue(os.path.exists(WarpedImagePath), "Missing test input") arecord = AlignmentRecord(peak=(0, 0), weight=100, angle=0.0) fixedImage = nornir_imageregistration.LoadImage(WarpedImagePath) warpedImage = nornir_imageregistration.LoadImage(WarpedImagePath) transform = arecord.ToTransform(fixedImage.shape, warpedImage.shape) transformedImage = assemble.WarpedImageToFixedSpace( transform, fixedImage.shape, warpedImage, (0, 0), (64, 64)) # imsave("C:\\Temp\\17.png", transformedImage) delta = fixedImage[0:64, 0:64] - transformedImage # nornir_imageregistration.ShowGrayscale([fixedImage[0:64, 0:64], transformedImage, delta]) self.assertTrue((delta < 0.01).all())
def test_warpedImageToFixedSpace(self): WarpedImagePath = os.path.join( self.ImportedDataPath, "0017_TEM_Leveled_image__feabinary_Cel64_Mes8_sp4_Mes8.png") self.assertTrue(os.path.exists(WarpedImagePath), "Missing test input") FixedImagePath = os.path.join( self.ImportedDataPath, "mini_TEM_Leveled_image__feabinary_Cel64_Mes8_sp4_Mes8.png") self.assertTrue(os.path.exists(FixedImagePath), "Missing test input") arecord = AlignmentRecord(peak=(22, -4), weight=100, angle=-132.0) fixedImage = nornir_imageregistration.LoadImage(FixedImagePath) warpedImage = nornir_imageregistration.LoadImage(WarpedImagePath) transform = arecord.ToTransform(fixedImage.shape, warpedImage.shape) transformedImage = assemble.WarpedImageToFixedSpace( transform, fixedImage.shape, warpedImage) imsave( os.path.join(self.VolumeDir, "test_warpedImageToFixedSpace.png"), transformedImage)
def ExamineBrightfieldShading(self, ShadedImagePath, ShadingReferencePath): self.assertTrue(os.path.exists(ShadedImagePath)) self.assertTrue(os.path.exists(ShadingReferencePath)) shadedImage = nir.LoadImage(ShadedImagePath) shadingMask = nir.LoadImage(ShadingReferencePath) OutputPaths = tiles.ShadeCorrect( [ShadedImagePath], shadingMask, self.TestOutputPath, correction_type=tiles.ShadeCorrectionTypes.BRIGHTFIELD) shownImages = [shadedImage, shadingMask] for path in OutputPaths: correctedImage = nir.LoadImage(path) shownImages.append(correctedImage) # nir.ShowGrayscale(shownImages) pass
def __CorrectBrightfieldShadingOneImage(input_fullpath, output_fullpath, imagescalar, bpp): max_pixel_value = ((1 << bpp) - 1) image = nornir_imageregistration.LoadImage(input_fullpath) image = image.astype(np.float16) / max_pixel_value correctedimage = image / imagescalar del image correctedimage[np.isinf(correctedimage)] = 0 np.clip(correctedimage, a_min=0, a_max=1.0, out=correctedimage) correctedimage = correctedimage * max_pixel_value nornir_imageregistration.SaveImage(output_fullpath, correctedimage, bpp=bpp) del correctedimage
def __CorrectDarkfieldShading(imagepaths, shadeimage, outputpath, bpp=None): outputPaths = [] if bpp is None: bpp = nornir_shared.images.GetImageBpp(imagepaths[0]) for imagepath in imagepaths: image = nornir_imageregistration.LoadImage(imagepath) imageFilename = os.path.basename(imagepath) outputFilename = os.path.join(outputpath, imageFilename) correctedimage = image - shadeimage nornir_imageregistration.SaveImage(outputFilename, correctedimage, bpp=bpp) outputPaths.append(outputFilename) del image del correctedimage return outputPaths
def __tile_offset_remote(A_Filename, B_Filename, scaled_overlapping_source_rect_A, scaled_overlapping_source_rect_B, OffsetAdjustment, excess_scalar): ''' :param A_Filename: Path to tile A :param B_Filename: Path to tile B :param scaled_overlapping_source_rect_A: Region of overlap on tile A with tile B :param scaled_overlapping_source_rect_B: Region of overlap on tile B with tile A :param OffsetAdjustment: scaled_offset to account for the (center) position of tile B relative to tile A. If the overlapping rectangles are perfectly aligned the reported offset would be (0,0). OffsetAdjustment would be added to that (0,0) result to ensure Tile B remained in the same position. :param float excess_scalar: How much additional area should we pad the overlapping rectangles with. Return the offset required to align to image files. This function exists to minimize the inter-process communication ''' ShowImages = False A = nornir_imageregistration.LoadImage(A_Filename) B = nornir_imageregistration.LoadImage(B_Filename) # I had to add the .astype call above for DM4 support, but I recall it broke PMG input. Leave this comment here until the tests are passing # A = nornir_imageregistration.LoadImage(A_Filename) #.astype(dtype=np.float16) # B = nornir_imageregistration.LoadImage(B_Filename) #.astype(dtype=np.float16) # I tried a 1.0 overlap. It works better for light microscopy where the reported stage position is more precise # For TEM the stage position can be less reliable and the 1.5 scalar produces better results # For the latest version of the code that uses only the overlapping region 3 is appropriate because it allows the alignment point to be anywhere on the image without ambiguity OverlappingRegionA = __get_overlapping_image( A, scaled_overlapping_source_rect_A, excess_scalar=excess_scalar) OverlappingRegionB = __get_overlapping_image( B, scaled_overlapping_source_rect_B, excess_scalar=excess_scalar) if ShowImages: o_a = __get_overlapping_image(A, scaled_overlapping_source_rect_A, excess_scalar=1.0, cval=0) o_b = __get_overlapping_image(B, scaled_overlapping_source_rect_B, excess_scalar=1.0, cval=0) #nornir_imageregistration.ShowGrayscale([[OverlappingRegionA, OverlappingRegionB],[o_a,o_b]]) OverlappingRegionA = OverlappingRegionA.astype(np.float32) OverlappingRegionB = OverlappingRegionB.astype(np.float32) # It is fairly common to underflow when dividing float16 images, so just warn and move on. # I spent a day debugging why a mosaic was not building correctly to find the underflow # issue, so don't remove it. The underflow error removes one of the ties between a tile # and its neighbors. # Note error levelshould now be set in nornir_imageregistration.__init__ # old_float_err_settings = np.seterr(under='warn') # If the entire region is a solid color, then return an alignment record with no offset and a weight of zero if (OverlappingRegionA.min() == OverlappingRegionA.max()) or \ (OverlappingRegionA.max() == 0) or \ (OverlappingRegionB.min() == OverlappingRegionB.max()) or \ (OverlappingRegionB.max() == 0): return nornir_imageregistration.AlignmentRecord(peak=OffsetAdjustment, weight=0) OverlappingRegionA -= OverlappingRegionA.min() OverlappingRegionA /= OverlappingRegionA.max() OverlappingRegionB -= OverlappingRegionB.min() OverlappingRegionB /= OverlappingRegionB.max() # nornir_imageregistration.ShowGrayscale([OverlappingRegionA, OverlappingRegionB]) nornir_imageregistration.ShowGrayscale([[o_a, o_b],[OverlappingRegionA, OverlappingRegionB]]) record = nornir_imageregistration.FindOffset( OverlappingRegionA, OverlappingRegionB, FFT_Required=True ) #, FixedImageShape=scaled_overlapping_source_rect_A.shape, MovingImageShape=scaled_overlapping_source_rect_B.shape) #overlapping_rect_B_AdjustedToPeak = nornir_imageregistration.Rectangle.translate(scaled_overlapping_source_rect_B, -record.peak) #overlapping_rect_B_AdjustedToPeak = nornir_imageregistration.Rectangle.change_area(overlapping_rect_B_AdjustedToPeak, scaled_overlapping_source_rect_A.Size) #median_diff = __AlignmentScoreRemote(A, B, scaled_overlapping_source_rect_A, overlapping_rect_B_AdjustedToPeak) #nornir_imageregistration.ShowGrayscale([[OverlappingRegionA, OverlappingRegionB], [overlapping_rect_B_AdjustedToPeak, median_diff]]) #diff_weight = 1.0 - median_diff #np.seterr(**old_float_err_settings) #nornir_imageregistration.views.plot_aligned_images(record, o_a, o_b) adjusted_record = nornir_imageregistration.AlignmentRecord( np.array(record.peak) + OffsetAdjustment, record.weight) if ShowImages: nornir_imageregistration.views.plot_aligned_images(record, o_a, o_b) del o_a del o_b del A del B del OverlappingRegionA del OverlappingRegionB return adjusted_record
def _ShrinkNumpyImageFile(InFile, OutFile, Scalar): image = nornir_imageregistration.LoadImage(InFile) resized_image = nornir_imageregistration.ResizeImage(image, Scalar) nornir_imageregistration.SaveImage(OutFile, resized_image)
def Image(self): if self._image is None: self._image = nornir_imageregistration.LoadImage(self._imagepath) return self._image
def SliceToSliceBruteForce(FixedImageInput, WarpedImageInput, FixedImageMaskPath=None, WarpedImageMaskPath=None, LargestDimension=None, AngleSearchRange=None, MinOverlap=0.75, SingleThread=False, Cluster=False, TestFlip=True): '''Given two images this function returns the rotation angle which best aligns them Largest dimension determines how large the images used for alignment should be''' logger = logging.getLogger(__name__ + '.SliceToSliceBruteForce') imFixed = None if isinstance(FixedImageInput, str): imFixed = nornir_imageregistration.LoadImage(FixedImageInput, FixedImageMaskPath, dtype=np.float32) else: imFixed = FixedImageInput imWarped = None if isinstance(WarpedImageInput, str): imWarped = nornir_imageregistration.LoadImage(WarpedImageInput, WarpedImageMaskPath, dtype=np.float32) else: imWarped = WarpedImageInput scalar = 1.0 if not LargestDimension is None: scalar = nornir_imageregistration.ScalarForMaxDimension( LargestDimension, [imFixed.shape, imWarped.shape]) if scalar < 1.0: imFixed = nornir_imageregistration.ReduceImage(imFixed, scalar) imWarped = nornir_imageregistration.ReduceImage(imWarped, scalar) # Replace extrema with noise imFixed = nornir_imageregistration.ReplaceImageExtramaWithNoise( imFixed, ImageMedian=0.5, ImageStdDev=0.25) imWarped = nornir_imageregistration.ReplaceImageExtramaWithNoise( imWarped, ImageMedian=0.5, ImageStdDev=0.25) UserDefinedAngleSearchRange = not AngleSearchRange is None if not UserDefinedAngleSearchRange: AngleSearchRange = list(range(-180, 180, 2)) BestMatch = FindBestAngle(imFixed, imWarped, AngleSearchRange, MinOverlap=MinOverlap, SingleThread=SingleThread, Cluster=Cluster) IsFlipped = False if TestFlip: imWarpedFlipped = np.copy(imWarped) imWarpedFlipped = np.flipud(imWarpedFlipped) BestMatchFlipped = FindBestAngle(imFixed, imWarpedFlipped, AngleSearchRange, MinOverlap=MinOverlap, SingleThread=SingleThread, Cluster=Cluster) BestMatchFlipped.flippedud = True # Determine if the best match is flipped or not IsFlipped = BestMatchFlipped.weight > BestMatch.weight if IsFlipped: imWarped = imWarpedFlipped BestMatch = BestMatchFlipped if not UserDefinedAngleSearchRange: BestRefinedMatch = FindBestAngle(imFixed, imWarped, [(x * 0.1) + BestMatch.angle - 1 for x in range(0, 20)], MinOverlap=MinOverlap, SingleThread=SingleThread) BestRefinedMatch.flippedud = IsFlipped else: BestRefinedMatch = BestMatch if scalar > 1.0: AdjustedPeak = (BestRefinedMatch.peak[0] * scalar, BestRefinedMatch.peak[1] * scalar) BestRefinedMatch = nornir_imageregistration.AlignmentRecord( AdjustedPeak, BestRefinedMatch.weight, BestRefinedMatch.angle, IsFlipped) # BestRefinedMatch.CorrectPeakForOriginalImageSize(imFixed.shape, imWarped.shape) return BestRefinedMatch