/// <summary> /// Clones the NNF and scales it up 2 times with distances recalculation. /// </summary> /// <param name="nnf">The NNF.</param> /// <param name="scaledDestImage">The scaled dest image.</param> /// <param name="scaledSrcImage">The scaled source image.</param> /// <param name="options">The options for parallel processing.</param> /// <param name="patchDistanceCalculator">The calculator that calculates similarity of two patches. By deafult the Cie76 is used.</param> /// <param name="destPixelsArea">Area on the dest image that actually containes pixels. By default is the area of the entire image.</param> /// <returns></returns> /// <exception cref="ArgumentNullException"> /// nnf /// or /// scaledDestImage /// or /// scaledSrcImage /// </exception> public static Nnf CloneAndScale2XWithUpdate(this Nnf nnf, ZsImage scaledDestImage, ZsImage scaledSrcImage, ParallelOptions options = null, ImagePatchDistanceCalculator patchDistanceCalculator = null, Area2D destPixelsArea = null) { if (scaledDestImage == null) { throw new ArgumentNullException(nameof(scaledDestImage)); } if (scaledSrcImage == null) { throw new ArgumentNullException(nameof(scaledSrcImage)); } var destArea = Area2D.Create(0, 0, scaledDestImage.Width, scaledDestImage.Height); var srcArea = Area2D.Create(0, 0, scaledSrcImage.Width, scaledSrcImage.Height); Area2DMap scaledMap = new Area2DMapBuilder() .InitNewMap(destArea, srcArea) .Build(); return(nnf.CloneAndScale2XWithUpdate(scaledDestImage, scaledSrcImage, options, scaledMap, patchDistanceCalculator, destPixelsArea)); }
public static async Task ScaleNnf([ActivityTrigger] NnfInputData input) { var container = BlobHelper.OpenBlobContainer(input.Container); var imageBlob = container.GetBlockBlobReference(input.Image); var image = (await BlobHelper.ConvertBlobToArgbImage(imageBlob)) .FromArgbToRgb(new[] { 0.0, 0.0, 0.0 }) .FromRgbToLab(); var calculator = input.IsCie79Calc ? ImagePatchDistance.Cie76 : ImagePatchDistance.Cie2000; var mappingState = BlobHelper.ReadFromBlob <Area2DMapState>(input.Mapping, container); var mapping = new Area2DMap(mappingState); var nnfState = BlobHelper.ReadFromBlob <NnfState>($"nnf{input.LevelIndex - 1}.json", container); var nnf = new Nnf(nnfState); nnf = nnf.CloneAndScale2XWithUpdate(image, image, input.Settings.PatchMatch, mapping, calculator); var nnfData = JsonConvert.SerializeObject(nnf.GetState()); BlobHelper.SaveJsonToBlob(nnfData, container, input.NnfName); }
static void Main(string[] args) { const string basePath = "..\\..\\..\\images"; const string srcImageName = "pm1.png"; const string destImageName = "pm2.png"; const int patchSize = 5; // Prepare 2 source images - one small and another 2x bigger var srcBitmap = new Bitmap(Path.Combine(basePath, srcImageName)); var srcImage = srcBitmap .ToRgbImage() .FromRgbToLab(); var srcSmallBitmap = srcBitmap.CloneWithScaleTo(srcBitmap.Width / 2, srcBitmap.Height / 2); var srcSmallImage = srcSmallBitmap .ToRgbImage() .FromRgbToLab(); srcSmallBitmap.Dispose(); srcBitmap.Dispose(); var destBitmap = new Bitmap(Path.Combine(basePath, destImageName)); var destImage = destBitmap .ToRgbImage() .FromRgbToLab(); var destSmallBitmap = destBitmap.CloneWithScaleTo(destBitmap.Width / 2, destBitmap.Height / 2); var destSmallImage = destSmallBitmap .ToRgbImage() .FromRgbToLab(); destBitmap.Dispose(); destSmallBitmap.Dispose(); // Init an nnf var nnf = new Nnf(destSmallImage.Width, destSmallImage.Height, srcSmallImage.Width, srcSmallImage.Height, patchSize); // Prepage setting for the PM algorithm var settings = new PatchMatchSettings { PatchSize = patchSize }; var patchMatchNnfBuilder = new PatchMatchNnfBuilder(); // Create the nnf for the small variant of the images // with a couple of iterations. patchMatchNnfBuilder.RunRandomNnfInitIteration(nnf, destSmallImage, srcSmallImage, settings); patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destSmallImage, srcSmallImage, NeighboursCheckDirection.Backward, settings); patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destSmallImage, srcSmallImage, NeighboursCheckDirection.Forward, settings); // The scaling of the NNF from the small images to the bigger ones. var scaledNnf = nnf.CloneAndScale2XWithUpdate(destImage, srcImage, settings); // Prepare results, save and show them scaledNnf .RestoreImage(srcImage, 3, patchSize) .FromLabToRgb() .FromRgbToBitmap() .SaveTo(@"..\..\l2r.png", ImageFormat.Png); scaledNnf .ToRgbImage() .FromRgbToBitmap() .SaveTo(@"..\..\l2n.png", ImageFormat.Png) .ShowFile(); Console.WriteLine($"NnfScaleUp processing is finished."); }
private ZsImage InternalInpaint(Pyramid pyramid, InpaintSettings settings) { #region cache settings var patchSize = settings.PatchSize; var calculator = settings.PatchDistanceCalculator; var nnfSettings = settings.PatchMatch; var changedPixelsPercentTreshold = settings.ChangedPixelsPercentTreshold; var maxInpaintIterationsAmount = settings.MaxInpaintIterations; var kStep = settings.MeanShift.KDecreaseStep; var minK = settings.MeanShift.MinK; #endregion ZsImage image = null; // go thru all the pyramid levels starting from the top one Nnf nnf = null; for (byte levelIndex = 0; levelIndex < pyramid.LevelsAmount; levelIndex++) { image = pyramid.GetImage(levelIndex); var mapping = pyramid.GetMapping(levelIndex); var inpaintArea = pyramid.GetInpaintArea(levelIndex); var imageArea = Area2D.Create(0, 0, image.Width, image.Height); // if there is a NNF built on the prev level // scale it up nnf = nnf == null ? new Nnf(image.Width, image.Height, image.Width, image.Height, patchSize) : nnf.CloneAndScale2XWithUpdate(image, image, nnfSettings, mapping, calculator); // start inpaint iterations var k = settings.MeanShift.K; for (var inpaintIterationIndex = 0; inpaintIterationIndex < maxInpaintIterationsAmount; inpaintIterationIndex++) { // Obtain pixels area. // Pixels area defines which pixels are allowed to be used // for the patches distance calculation. We must avoid pixels // that we want to inpaint. That is why before the area is not // inpainted - we should exclude this area. var pixelsArea = imageArea; if (levelIndex == 0 && inpaintIterationIndex == 0 && settings.IgnoreInpaintedPixelsOnFirstIteration) { pixelsArea = imageArea.Substract(inpaintArea); } // skip building NNF for the first iteration in the level // unless it is top level (for the top one we haven't built NNF yet) if (levelIndex == 0 || inpaintIterationIndex > 0) { // in order to find best matches for the inpainted area, // we build NNF for this image as a dest and a source // but excluding the inpainted area from the source area // (our mapping already takes care of it) _nnfBuilder.RunRandomNnfInitIteration(nnf, image, image, nnfSettings, calculator, mapping, pixelsArea); _nnfBuilder.RunBuildNnfIteration(nnf, image, image, NeighboursCheckDirection.Forward, nnfSettings, calculator, mapping, pixelsArea); _nnfBuilder.RunBuildNnfIteration(nnf, image, image, NeighboursCheckDirection.Backward, nnfSettings, calculator, mapping, pixelsArea); _nnfBuilder.RunBuildNnfIteration(nnf, image, image, NeighboursCheckDirection.Forward, nnfSettings, calculator, mapping, pixelsArea); _nnfBuilder.RunBuildNnfIteration(nnf, image, image, NeighboursCheckDirection.Backward, nnfSettings, calculator, mapping, pixelsArea); _nnfBuilder.RunBuildNnfIteration(nnf, image, image, NeighboursCheckDirection.Forward, nnfSettings, calculator, mapping, pixelsArea); } var nnfNormalized = nnf.Clone(); nnfNormalized.Normalize(); // after we have the NNF - we calculate the values of the pixels in the inpainted area var inpaintResult = Inpaint(image, inpaintArea, nnfNormalized, k, settings); k = k > minK ? k - kStep : k; if (IterationFinished != null) { var eventArgs = new InpaintIterationFinishedEventArgs { InpaintedLabImage = image.Clone(), InpaintResult = inpaintResult, LevelIndex = levelIndex, InpaintIteration = inpaintIterationIndex }; IterationFinished(this, eventArgs); } // if the change is smaller then a treshold, we quit if (inpaintResult.ChangedPixelsPercent < changedPixelsPercentTreshold) { break; } //if (levelIndex == pyramid.LevelsAmount - 1) break; } } return(image); }