def LoadMosaic(self, mosaicFullPath, tiles_dir=None): '''Return a list of image transform views for the mosaic file''' mosaic = Mosaic.LoadFromMosaicFile(mosaicFullPath) if len(mosaic.ImageToTransform) == 0: return None mosaic.TranslateToZeroOrigin() tiles_dir = MosaicState.GetMosaicTilePath( list(mosaic.ImageToTransform.keys())[0], mosaicFullPath, tiles_dir) if tiles_dir is None: return None tilesPathList = mosaic.CreateTilesPathList(tiles_dir) transform_scale = nornir_imageregistration.tileset.MostCommonScalar( list(mosaic.ImageToTransform.values()), tilesPathList) ImageTransformViewList = [] z_step = 1.0 / float(len(mosaic.ImageToTransform)) z = z_step - (z_step / 2.0) output_len = 0 pools = nornir_pools.GetGlobalThreadPool() tasks = [] for image_filename, transform in list(mosaic.ImageToTransform.items()): tile_full_path = os.path.join(tiles_dir, image_filename) task = pools.add_task(str(z), self.AllocateMosaicTile, transform, tile_full_path, transform_scale) task.z = z tasks.append(task) # image_transform_view = self.AllocateMosaicTile(transform, tile_full_path, transform_scale) # image_transform_view.z = z z += z_step # ImageTransformViewList.append(image_transform_view) wx.Yield() for t in tasks: image_transform_view = t.wait_return() image_transform_view.z = t.z ImageTransformViewList.append(image_transform_view) output = '%g' % (z * 100.0) sys.stdout.write('\b' * output_len) sys.stdout.write(output) output_len = len(output) self.ImageTransformViewList = ImageTransformViewList return ImageTransformViewList
def test_MosaicBoundsEachMosaicType(self): for m in self.GetMosaicFiles(): mosaic = Mosaic.LoadFromMosaicFile(m) self.assertIsNotNone(mosaic.MappedBoundingBox, "No bounding box returned for mosiac") self.Logger.info(m + " mapped bounding box: " + str(mosaic.MappedBoundingBox)) self.assertIsNotNone(mosaic.FixedBoundingBox, "No bounding box returned for mosiac") self.Logger.info(m + " fixed bounding box: " + str(mosaic.FixedBoundingBox))
def CreateAssembleOptimizedTile(self, mosaicFilePath, TilesDir): mosaic = Mosaic.LoadFromMosaicFile(mosaicFilePath) mosaicBaseName = os.path.basename(mosaicFilePath) mosaicDir = os.path.dirname(mosaicFilePath) (mosaicBaseName, ext) = os.path.splitext(mosaicBaseName) imageKey = list(mosaic.ImageToTransform.keys())[0] transform = mosaic.ImageToTransform[imageKey] (MinY, MinX, MaxY, MaxX) = transform.FixedBoundingBox expectedScale = 1.0 / self.DownsampleFromTilePath(TilesDir) result = at.TransformTile(transform, os.path.join(TilesDir, imageKey), distanceImage=None, target_space_scale=None, TargetRegion=(MinY, MinX, MaxY, MinX + 256)) self.assertEqual( result.image.shape, (np.ceil(transform.FixedBoundingBox.Height * expectedScale), np.ceil(256 * expectedScale))) result = at.TransformTile(transform, os.path.join(TilesDir, imageKey), distanceImage=None, target_space_scale=None, TargetRegion=(MinY, MinX, MinY + 256, MaxX)) self.assertEqual( result.image.shape, (np.ceil(256 * expectedScale), np.ceil(transform.FixedBoundingBox.Width * expectedScale))) result = at.TransformTile(transform, os.path.join(TilesDir, imageKey), distanceImage=None, target_space_scale=None, TargetRegion=(MinY + 2048, MinX + 2048, MinY + 2048 + 512, MinX + 2048 + 512)) self.assertEqual( result.image.shape, (np.ceil(512 * expectedScale), np.ceil(512 * expectedScale)))
def AssembleMosaic(self, mosaicFilePath, tilesDir, outputMosaicPath=None, parallel=False, downsamplePath=None): SaveFiles = not outputMosaicPath is None mosaic = Mosaic.LoadFromMosaicFile(mosaicFilePath) mosaicBaseName = os.path.basename(mosaicFilePath) (mosaicBaseName, ext) = os.path.splitext(mosaicBaseName) mosaic.TranslateToZeroOrigin() assembleScale = tiles.MostCommonScalar( list(mosaic.ImageToTransform.values()), mosaic.TileFullPaths(tilesDir)) expectedScale = 1.0 / self.DownsampleFromTilePath(tilesDir) self.assertEqual( assembleScale, expectedScale, "Scale for assemble does not match the expected scale") timer = TaskTimer() timer.Start("AssembleImage " + tilesDir) (mosaicImage, mask) = mosaic.AssembleImage(tilesDir, usecluster=parallel) timer.End("AssembleImage " + tilesDir, True) self.assertEqual( mosaicImage.shape[0], np.ceil(mosaic.FixedBoundingBoxHeight * expectedScale), "Output mosaic height does not match .mosaic height %g vs %g" % (mosaicImage.shape[0], mosaic.FixedBoundingBoxHeight * expectedScale)) self.assertEqual( mosaicImage.shape[1], np.ceil(mosaic.FixedBoundingBoxWidth * expectedScale), "Output mosaic width does not match .mosaic height %g vs %g" % (mosaicImage.shape[1], mosaic.FixedBoundingBoxWidth * expectedScale)) # img = im.fromarray(mosaicImage, mode="I") # img.save(outputImagePath, mosaicImage, bits=8) if SaveFiles: OutputDir = os.path.join(self.TestOutputPath, outputMosaicPath) if not os.path.exists(OutputDir): os.makedirs(OutputDir) outputImagePath = os.path.join(OutputDir, mosaicBaseName + '.png') outputImageMaskPath = os.path.join(OutputDir, mosaicBaseName + '_mask.png') core.SaveImage(outputImagePath, mosaicImage) core.SaveImage(outputImageMaskPath, mask) self.assertTrue(os.path.exists(outputImagePath), "OutputImage not found") outputMask = core.LoadImage(outputImageMaskPath) self.assertTrue( outputMask[int(outputMask.shape[0] / 2.0), int(outputMask.shape[1] / 2.0)] > 0, "Center of assembled image mask should be non-zero") del mosaicImage del mask else: return (mosaicImage, mask)
def CreateAssembleOptimizedTileTwo(self, mosaicFilePath, TilesDir, tile_dims=None, numColumnsPerPass=None): if tile_dims is None: tile_dims = (512, 512) tile_dims = np.asarray(tile_dims, dtype=np.int64) mosaic = Mosaic.LoadFromMosaicFile(mosaicFilePath) mosaic.TranslateToZeroOrigin() mosaicBaseName = os.path.basename(mosaicFilePath) mosaicDir = os.path.dirname(mosaicFilePath) (mosaicBaseName, ext) = os.path.splitext(mosaicBaseName) expectedScale = 1.0 / self.DownsampleFromTilePath(TilesDir) scaled_fixed_bounding_box_shape = np.ceil( mosaic.FixedBoundingBox.shape / (1 / expectedScale)).astype( np.int64) expected_grid_dims = nornir_imageregistration.TileGridShape( scaled_fixed_bounding_box_shape, tile_size=tile_dims) mosaic_expected_grid_dims = mosaic.CalculateGridDimensions( tile_dims, expectedScale) self.assertTrue( np.array_equal(expected_grid_dims, mosaic_expected_grid_dims), "Mosaic object grid dimensions should match manually calculated grid dimensions" ) max_temp_image_dims = expected_grid_dims * tile_dims if numColumnsPerPass is not None: max_temp_image_dims[1] = tile_dims[1] * numColumnsPerPass max_temp_image_area = np.prod(max_temp_image_dims) tiles = [[None for iCol in range(expected_grid_dims[1])] for iRow in range(expected_grid_dims[0]) ] #[[None] * expected_grid_dims[1]] * expected_grid_dims[0] tile_returned = np.zeros(expected_grid_dims, dtype=np.bool) #out = list(mosaic.GenerateOptimizedTiles(tilesPath=TilesDir, tile_dims=tile_dims, usecluster=False, target_space_scale=expectedScale)) for t in mosaic.GenerateOptimizedTiles( tilesPath=TilesDir, tile_dims=tile_dims, usecluster=False, max_temp_image_area=max_temp_image_area, target_space_scale=expectedScale): (iRow, iCol, tile_image) = t assert (iCol < expected_grid_dims[1]) assert (iRow < expected_grid_dims[0]) # nornir_imageregistration.ShowGrayscale(tile_image) self.assertFalse( tile_returned[iRow, iCol], "Duplicate Row,Column value returned by enumerator") tile_returned[iRow, iCol] = True print("{0},{1} Tile completed".format(iRow, iCol)) # origin = np.asarray((iCol, iRow), dtype=np.int64) * tile_dims # region = nornir_imageregistration.Rectangle.CreateFromPointAndArea(origin, tile_dims*(1/expectedScale)) # (controlImage, mask) = mosaic.AssembleImage(tilesPath=TilesDir, FixedRegion=region, # usecluster=True, target_space_scale=expectedScale) # self.assertTrue(np.array_equal(tile_image, controlImage)) tiles[iRow][iCol] = tile_image self.assertTrue(np.all(tile_returned.flat)) title = "" if numColumnsPerPass is not None: title = "Generated {0} columns in a pass.\n".format( numColumnsPerPass) title = title + "Tiles should not overlap and look reasonable" self.assertTrue( nornir_imageregistration.ShowGrayscale(tiles, title=title, PassFail=True))
def CompareMosaicAsssembleAndTransformTile(self, mosaicFilePath, tilesDir): ''' 1) Assemble the entire mosaic 2) Assemble subregion of the mosaic 3) Assemble subregion directly using _TransformTile 4) Check the output of all match ''' mosaic = Mosaic.LoadFromMosaicFile(mosaicFilePath) self.assertIsNotNone(mosaic, "Mosaic not loaded") mosaic.TranslateToZeroOrigin() (imageKey, transform) = sorted(list(mosaic.ImageToTransform.items()))[0] (MinY, MinX, MaxY, MaxZ) = transform.FixedBoundingBox print("Scaled fixed region %s" % str(transform.FixedBoundingBox)) FixedRegion = np.array( [MinY + 512, MinX + 1024, MinY + 1024, MinX + 2048]) ScaledFixedRegion = FixedRegion / self.DownsampleFromTilePath(tilesDir) (tileImage, tileMask) = mosaic.AssembleImage(tilesDir, usecluster=False, FixedRegion=FixedRegion) # self.assertEqual(tileImage.shape, (ScaledFixedRegion[3], ScaledFixedRegion[2])) (clustertileImage, clustertileMask) = mosaic.AssembleImage(tilesDir, usecluster=True, FixedRegion=FixedRegion) # self.assertEqual(tileImage.shape, (ScaledFixedRegion[3], ScaledFixedRegion[2])) self.assertTrue( np.sum(np.abs(clustertileImage - tileImage).flat) < 0.65, "Tiles generated with cluster should be identical to single threaded implementation" ) self.assertTrue( np.all(clustertileMask == tileMask), "Tiles generated with cluster should be identical to single threaded implementation" ) result = at.TransformTile(transform, os.path.join(tilesDir, imageKey), distanceImage=None, target_space_scale=None, TargetRegion=FixedRegion) self.assertEqual(result.image.shape, (ScaledFixedRegion[2] - ScaledFixedRegion[0], ScaledFixedRegion[3] - ScaledFixedRegion[1])) # core.ShowGrayscale([tileImage, result.image]) (wholeimage, wholemask) = self.AssembleMosaic(mosaicFilePath, tilesDir, outputMosaicPath=None, parallel=False) self.assertIsNotNone(wholeimage, "Assemble did not produce an image") self.assertIsNotNone(wholemask, "Assemble did not produce a mask") croppedWholeImage = core.CropImage( wholeimage, int(ScaledFixedRegion[1]), int(ScaledFixedRegion[0]), int(ScaledFixedRegion[3] - ScaledFixedRegion[1]), int(ScaledFixedRegion[2] - ScaledFixedRegion[0])) self.assertTrue( nornir_imageregistration.ShowGrayscale( [result.image, tileImage, croppedWholeImage, wholeimage], title="image: %s\n%s" % (imageKey, str(transform.FixedBoundingBox)), PassFail=True))
def ArrangeMosaic(self, mosaicFilePath, TilePyramidDir=None, downsample=None, openwindow=False, max_relax_iterations=None, max_relax_tension_cutoff=None, inter_tile_distance_scale=None, min_translate_iterations=None): if downsample is None: downsample = 1 if min_translate_iterations is None: min_translate_iterations = 5 if inter_tile_distance_scale is None: inter_tile_distance_scale = 0.5 downsamplePath = '%03d' % downsample mosaic = Mosaic.LoadFromMosaicFile(mosaicFilePath) mosaicBaseName = os.path.basename(mosaicFilePath) (mosaicBaseName, ext) = os.path.splitext(mosaicBaseName) TilesDir = None if TilePyramidDir is None: TilesDir = os.path.join(self.ImportedDataPath, self.Dataset, 'Leveled', 'TilePyramid', downsamplePath) else: TilesDir = os.path.join(TilePyramidDir, downsamplePath) mosaic.TranslateToZeroOrigin() original_score = mosaic.QualityScore(TilesDir) # self.__RemoveExtraImages(mosaic) assembleScale = tileset.MostCommonScalar( mosaic.ImageToTransform.values(), mosaic.TileFullPaths(TilesDir)) expectedScale = 1.0 / float(downsamplePath) self.assertEqual( assembleScale, expectedScale, "Scale for assemble does not match the expected scale") timer = TaskTimer() timer.Start("ArrangeTiles " + TilesDir) translated_mosaic = mosaic.ArrangeTilesWithTranslate( TilesDir, excess_scalar=1.5, min_translate_iterations=min_translate_iterations, max_relax_iterations=max_relax_iterations, max_relax_tension_cutoff=max_relax_tension_cutoff, inter_tile_distance_scale=inter_tile_distance_scale, min_overlap=0.05) timer.End("ArrangeTiles " + TilesDir, True) translated_score = translated_mosaic.QualityScore(TilesDir) print("Original Quality Score: %g" % (original_score)) print("Translate Quality Score: %g" % (translated_score)) OutputDir = os.path.join(self.TestOutputPath, mosaicBaseName + '.mosaic') OutputMosaicDir = os.path.join(self.TestOutputPath, mosaicBaseName + '.png') if openwindow: self._ShowMosaic(translated_mosaic, OutputMosaicDir)
def ArrangeMosaicDirect(self, mosaicFilePath, TilePyramidDir=None, downsample=None, openwindow=False, max_relax_iterations=None, max_relax_tension_cutoff=None, inter_tile_distance_scale=None, feature_score_threshold=0.5): if downsample is None: downsample = 1 #pool = nornir_pools.GetGlobalThreadPool() downsamplePath = '%03d' % downsample minWeight = 0 maxWeight = 1.0 mosaicBaseName = os.path.basename(mosaicFilePath) (mosaicBaseName, ext) = os.path.splitext(mosaicBaseName) scale = 1.0 / float(downsample) TilesDir = None if TilePyramidDir is None: TilesDir = os.path.join(self.ImportedDataPath, self.Dataset, 'Leveled', 'TilePyramid', downsamplePath) else: TilesDir = os.path.join(TilePyramidDir, downsamplePath) mosaic = Mosaic.LoadFromMosaicFile(mosaicFilePath) mosaic.EnsureTransformsHaveMappedBoundingBoxes(image_scale=scale, image_path=TilesDir) mosaic.TranslateToZeroOrigin() # mosaic.TranslateToZeroOrigin() # self.__RemoveExtraImages(mosaic) # assembleScale = tiles.MostCommonScalar(mosaic.ImageToTransform.values(), mosaic.TileFullPaths(TilesDir)) # expectedScale = 1.0 / float(downsamplePath) # self.assertEqual(assembleScale, expectedScale, "Scale for assemble does not match the expected scale") timer = TaskTimer() timer.Start("ArrangeTiles " + TilesDir) tilesPathList = sorted(mosaic.CreateTilesPathList(TilesDir)) transforms = list(mosaic._TransformsSortedByKey()) imageScale = self.ReadOrCreateVariable(self.id() + "_imageScale_%03d" % downsample, tileset.MostCommonScalar, transforms=transforms, imagepaths=tilesPathList) first_pass_excess_scalar = 3 #This needs to be 3 to ensure we can detect any offset, otherwise quadrant of the peak is ambiguous excess_scalar = first_pass_excess_scalar initial_tiles = nornir_imageregistration.tile.CreateTiles( transforms=transforms, imagepaths=tilesPathList) min_overlap = 0.070 if inter_tile_distance_scale is None: inter_tile_distance_scale = 1.0 #tile_overlap_feature_scores = arrange.ScoreTileOverlaps(tiles=initial_tiles, imageScale=imageScale) last_pass_overlaps = None translated_layout = None stage_reported_overlaps = None for iPass in range(0, 5): (distinct_overlaps, new_overlaps, updated_overlaps, removed_overlap_IDs, non_overlapping_IDs) = arrange.GenerateTileOverlaps( tiles=initial_tiles, existing_overlaps=last_pass_overlaps, offset_epsilon=1.0, image_scale=imageScale, min_overlap=min_overlap, inter_tile_distance_scale=inter_tile_distance_scale) if stage_reported_overlaps is None: stage_reported_overlaps = { to.ID: to.offset for to in new_overlaps } #After the first pass trust the mosaic layout inter_tile_distance_scale = 1.0 new_or_updated_overlaps = list(new_overlaps) new_or_updated_overlaps.extend(updated_overlaps) arrange.ScoreTileOverlaps(distinct_overlaps) overlap_colors = ['green'] * len(new_overlaps) overlap_colors.extend(['blue'] * len(updated_overlaps)) if translated_layout is not None: for ID in removed_overlap_IDs: translated_layout.RemoveOverlap(ID) for ID in non_overlapping_IDs: self.assertTrue( translated_layout.nodes[ID].ConnectedIDs.shape[0] == 0, "Non-overlapping node should not have overlaps") # translated_layout.RemoveNode(ID) # tile_overlap_feature_scores = self.ReadOrCreateVariable(self.id() + "tile_overlap_feature_scores_%03d_%03g" % (downsample, first_pass_excess_scalar), # arrange.GenerateScoredTileOverlaps, # tiles=initial_tiles, # imageScale=imageScale) Scores = [] for overlap in distinct_overlaps: Scores.extend(overlap.feature_scores) Scores.sort() h = nornir_shared.histogram.Histogram.Init( minVal=np.min(Scores), maxVal=np.max(Scores), numBins=int(np.sqrt(len(Scores)) * 10)) h.Add(Scores) #nornir_shared.plot.Histogram(h) nornir_shared.plot.Histogram( h, ImageFilename=os.path.join( self.TestOutputPath, "{0:d}pass_FeatureScoreHistogram.png".format(iPass)), Title="Tile overlap feature scores") # nornir_shared.plot.Histogram(h, ImageFilename=os.path.join(mosaicBaseName + "_{0d}pass_FeatureScoreHistogram.png", Title="Tile overlap feature scores") #pool.add_task('Plot prune histogram', nornir_shared.plot.Histogram,h, ImageFilename=mosaicBaseName + "_PrunePlotHistogram.png", Title="Tile overlap feature scores") #pool.add_task('Plot prune histogram', nornir_shared.plot.Histogram,h, ImageFilename=mosaicBaseName + "_PrunePlotHistogram.png", Title="Tile overlap feature scores") #self.assertAlmostEqual(imageScale, 1.0 / downsample, "Calculated image scale should match downsample value passed to test") #Create a list of offsets requiring updates filtered_overlaps_needing_offsets = [] for overlap in new_or_updated_overlaps: if overlap.feature_scores[ 0] >= feature_score_threshold and overlap.feature_scores[ 1] >= feature_score_threshold: filtered_overlaps_needing_offsets.append(overlap) else: if translated_layout is not None: translated_layout.RemoveOverlap(overlap) #Create a list of every offset that should be found in the layout for debugging filtered_distinct_offsets = [] for overlap in distinct_overlaps: if overlap.feature_scores[ 0] >= feature_score_threshold and overlap.feature_scores[ 1] >= feature_score_threshold: filtered_distinct_offsets.append(overlap) #Find the overlaps that are locked new_or_updated_dict = {o.ID for o in new_or_updated_overlaps} locked_overlaps = [] for d in distinct_overlaps: if not d.ID in new_or_updated_dict: locked_overlaps.append(d) if translated_layout.ContainsOffset(d.ID): overlap_colors.extend(['gold']) else: overlap_colors.extend(['red']) self.SaveVariable( (new_overlaps, updated_overlaps, locked_overlaps, overlap_colors), os.path.join(self.TestOutputPath, "pass_{0}_tile_overlaps.pickle".format(iPass))) nornir_imageregistration.views.plot_tile_overlaps( new_overlaps + updated_overlaps + locked_overlaps, colors=overlap_colors, OutputFilename=os.path.join( self.TestOutputPath, "pass_{0}_tile_overlaps.svg".format(iPass))) translated_layout = self.ReadOrCreateVariable( self.id() + "_{0:d}pass_tile_layout_{1:03d}_{2:03g}".format( iPass, downsample, first_pass_excess_scalar), self.CalculateOffsetsForTiles, tile_offsets_to_update=filtered_overlaps_needing_offsets, all_tile_offsets=filtered_distinct_offsets, excess_scalar=excess_scalar, imageScale=imageScale, existing_layout=translated_layout) excess_scalar = 3.0 # Each tile should contain a dictionary with the known offsets. Show the overlapping images using the calculated offsets (tileA_ID, tileB_ID) = _GetWorstOffsetPair(translated_layout) self.ShowTilesWithOffset(translated_layout, initial_tiles, tileA_ID, tileB_ID, "_{0:d}pass_Worst1stPass".format(iPass), openwindow=openwindow) (tileA_ID, tileB_ID) = _GetBestOffsetPair(translated_layout) self.ShowTilesWithOffset(translated_layout, initial_tiles, tileA_ID, tileB_ID, "_{0:d}pass_Best1stPass".format(iPass), openwindow=openwindow) #self.ShowTilesWithOffset(translated_layout, initial_tiles, 0, 1, "{0:d}_pass".format(iPass), openwindow=openwindow) #self.ShowTilesWithOffset(translated_layout, initial_tiles, 0, 60, "{0:d}_pass".format(iPass), openwindow=openwindow) #self.ShowTilesWithOffset(translated_layout, initial_tiles, 1, 61, "{0:d}_pass".format(iPass), openwindow=openwindow) #self.ShowTilesWithOffset(translated_layout, initial_tiles, 60, 61, "{0:d}_pass".format(iPass), openwindow=openwindow) #self.ShowTilesWithOffset(translated_layout, initial_tiles, 53, 56, "{0:d}_pass".format(iPass), openwindow=openwindow) # mosaic.ArrangeTilesWithTranslate(TilesDir, usecluster=parallel) scaled_translated_layout = translated_layout.copy() nornir_imageregistration.layout.ScaleOffsetWeightsByPopulationRank( scaled_translated_layout, min_allowed_weight=minWeight, max_allowed_weight=maxWeight) # nornir_imageregistration.layout.NormalizeOffsetWeights(scaled_translated_layout) self.PlotLayoutWeightHistogram( scaled_translated_layout, mosaicBaseName + "_{0:d}pass_weight_histogram".format(iPass), openwindow=False) translated_final_layouts = nornir_imageregistration.layout.BuildLayoutWithHighestWeightsFirst( scaled_translated_layout) translated_final_layout = nornir_imageregistration.layout.MergeDisconnectedLayoutsWithOffsets( translated_final_layouts, stage_reported_overlaps) translated_mosaic = self.CreateSaveShowMosaic( mosaicBaseName + "_{0:d}pass_Weighted".format(iPass), translated_final_layout, initial_tiles, openwindow=openwindow, target_space_scale=1 / 4.0, source_space_scale=None) # relaxed_layout = self._Relax_Layout(scaled_translated_layout, # max_iter=max_relax_iterations, # max_tension_cutoff=max_relax_tension_cutoff, # dirname_postfix="_pass{0:d}".format(iPass)) relaxed_layouts = [] for layout in translated_final_layouts: relaxed_layout = nornir_imageregistration.layout.RelaxLayout( layout, max_iter=max_relax_iterations, max_tension_cutoff=max_relax_tension_cutoff, plotting_output_path=os.path.join( self.TestOutputPath, "relax_pass_{0:d}".format(iPass)), plotting_interval=10) relaxed_layouts.append(relaxed_layout) relaxed_layout = nornir_imageregistration.layout.MergeDisconnectedLayoutsWithOffsets( relaxed_layouts, stage_reported_overlaps) relaxed_layout.UpdateTileTransforms(initial_tiles) relaxed_mosaic = self.CreateSaveShowMosaic( mosaicBaseName + "_{0:d}pass_relaxed".format(iPass), relaxed_layout, initial_tiles, openwindow=openwindow, target_space_scale=1 / 4.0, source_space_scale=None) last_pass_overlaps = distinct_overlaps #Copy the relaxed layout positions back into the translated layout for ID, node in relaxed_layout.nodes.items(): tnode = translated_layout.nodes[ID] tnode.Position = node.Position nornir_pools.WaitOnAllPools() #self.CreateSaveShowMosaic(mosaicBaseName + "_{0:d}pass_relaxed".format(iPass), relaxed_layout, initial_tiles, openwindow) # original_score = mosaic.QualityScore(TilesDir) translated_score = translated_mosaic.QualityScore(TilesDir) relaxed_score = relaxed_mosaic.QualityScore(TilesDir) #translated_refined_score = translated_refined_mosaic.QualityScore(TilesDir) #translated_refined_relaxed_score = translated_refined_relaxed_mosaic.QualityScore(TilesDir) print("Original Quality Score: %g" % (original_score))