Exemple #1
0
        static void Main(string[] args)
        {
            const string basePath = "..\\..\\..\\..\\images";

            ZsImage srcImage  = GetArgbImage(basePath, "t009.jpg");
            ZsImage destImage = srcImage.Clone();

            var srcMarkup  = GetArea2D(basePath, "m009.png");
            var destMarkup = srcMarkup.Translate(-300, -30);

            destImage.CopyFromImage(destMarkup, destImage, srcMarkup);
            destImage.FromArgbToBitmap()
            .SaveTo("..\\..\\..\\target.png", ImageFormat.Png);

            // Prepage setting for the PM algorithm
            const byte patchSize = 5;
            var        settings  = new PatchMatchSettings {
                PatchSize = patchSize
            };

            // Init an nnf
            var nnf = new Nnf(destImage.Width, destImage.Height, srcImage.Width, srcImage.Height, patchSize);

            srcImage.FromArgbToRgb(new[] { 1.0, 1.0, 1.0 })
            .FromRgbToLab();

            destImage.FromArgbToRgb(new[] { 1.0, 1.0, 1.0 })
            .FromRgbToLab();

            destImage
            .Clone()
            .FromLabToRgb()
            .FromRgbToBitmap()
            .SaveTo($"..\\..\\..\\dest.png", ImageFormat.Png);

            var patchMatchNnfBuilder = new PatchMatchNnfBuilder();

            patchMatchNnfBuilder.RunRandomNnfInitIteration(nnf, destImage, srcImage, settings);

            for (int j = 0; j < 3; j++)
            {
                patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings);
                Console.WriteLine($"\tIteration {j * 2}");
                patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Backward, settings);
                Console.WriteLine($"\tIteration {j * 2 + 1}");
            }

            nnf.ToRgbImage()
            .FromRgbToBitmap()
            .SaveTo($"..\\..\\..\\nnf.png", ImageFormat.Png);

            nnf.RestoreImage(srcImage, 3, patchSize)
            .FromLabToRgb()
            .FromRgbToBitmap()
            .SaveTo($"..\\..\\..\\restored.png", ImageFormat.Png);
        }
Exemple #2
0
        static void Main(string[] args)
        {
            const string basePath = "..\\..\\..\\images";

            var destImageName      = "pm2small.png";
            var srcImageName       = "pm1small.png";
            var emptyAreaImageName = "pm2small_ignore.png";

            // This is our input data.
            var destImage = GetLabImage(basePath, destImageName);
            var srcImage  = GetLabImage(basePath, srcImageName);
            var destArea  = Area2D.Create(0, 0, destImage.Width, destImage.Height);
            var srcArea   = Area2D.Create(0, 0, srcImage.Width, srcImage.Height);

            var emptyArea      = GetArea2D(basePath, emptyAreaImageName);
            var destPixelsArea = destArea.Substract(emptyArea);

            var map = new Area2DMapBuilder()
                      .InitNewMap(destArea, srcArea)
                      .Build();

            const byte patchSize = 5;
            var        settings  = new PatchMatchSettings {
                PatchSize = patchSize
            };
            var nnf        = new Nnf(destImage.Width, destImage.Height, srcImage.Width, srcImage.Height, patchSize);
            var calculator = ImagePatchDistance.Cie76;

            var patchMatchNnfBuilder = new PatchMatchNnfBuilder();

            // Create the nnf the images
            // with a couple of iterations.
            patchMatchNnfBuilder.RunRandomNnfInitIteration(nnf, destImage, srcImage, settings, calculator, map, destPixelsArea);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map, destPixelsArea);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map, destPixelsArea);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map, destPixelsArea);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map, destPixelsArea);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map, destPixelsArea);

            // Restore dest image from the NNF and source image.
            nnf
            .RestoreImage(srcImage, 3, settings.PatchSize)
            .FromLabToRgb()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\restored.png", ImageFormat.Png);

            // Convert the NNF to an image, save and show it
            nnf
            .ToRgbImage()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\nnf.png", ImageFormat.Png)
            .ShowFile();

            Console.WriteLine($"PatchMatchPipeline processing is finished.");
        }
Exemple #3
0
        public static async Task NnfBuildIteration([ActivityTrigger] NnfInputData input)
        {
            var container = BlobHelper.OpenBlobContainer(input.Container);

            var imageBlob = container.GetBlockBlobReference(input.Image);
            var imageArgb = await BlobHelper.ConvertBlobToArgbImage(imageBlob);

            var image = imageArgb
                        .FromArgbToRgb(new[] { 0.0, 0.0, 0.0 })
                        .FromRgbToLab();

            var imageArea  = Area2D.Create(0, 0, image.Width, image.Height);
            var pixelsArea = imageArea;

            var nnfSettings = input.Settings.PatchMatch;
            var calculator  = input.IsCie79Calc
                ? ImagePatchDistance.Cie76
                : ImagePatchDistance.Cie2000;

            var nnfState = BlobHelper.ReadFromBlob <NnfState>(input.NnfName, container);
            var nnf      = new Nnf(nnfState);

            var mappingState = BlobHelper.ReadFromBlob <Area2DMapState>(input.Mapping, container);
            var mapping      = new Area2DMap(mappingState);

            if (input.ExcludeInpaintArea)
            {
                var inpaintAreaState = BlobHelper.ReadFromBlob <Area2DState>(input.InpaintAreaName, container);
                var inpaintArea      = Area2D.RestoreFrom(inpaintAreaState);
                pixelsArea = imageArea.Substract(inpaintArea);
            }

            var nnfBuilder = new PatchMatchNnfBuilder();

            var direction = input.IsForward
                ? NeighboursCheckDirection.Forward
                : NeighboursCheckDirection.Backward;

            nnfBuilder.RunBuildNnfIteration(nnf, image, image, direction, nnfSettings, calculator, mapping, pixelsArea);

            var nnfData = JsonConvert.SerializeObject(nnf.GetState());

            BlobHelper.SaveJsonToBlob(nnfData, container, input.NnfName);
        }
Exemple #4
0
        static void Main(string[] args)
        {
            const string basePath  = "..\\..\\..\\..\\images";
            const int    patchSize = 5;

            var srcImageName = "t009.jpg";

            // Prepare images
            var srcImage  = GetLabImage(basePath, srcImageName);
            var destImage = GetLabImage(basePath, srcImageName);

            var ignoreArea = GetArea2D(basePath, "m009.png");
            var destArea   = ignoreArea.Dilation(patchSize * 2 + 1);

            // Init an nnf
            var nnf = new Nnf(destImage.Width, destImage.Height, srcImage.Width, srcImage.Height, patchSize);

            // Create a mapping of the areas on the dest and source areas.
            var imageArea = Area2D.Create(0, 0, srcImage.Width, srcImage.Height);
            var map       = new Area2DMapBuilder()
                            .InitNewMap(imageArea, imageArea)
                            .SetIgnoredSourcedArea(ignoreArea)
                            .Build();

            // Prepage setting for the PM algorithm
            var settings = new PatchMatchSettings {
                PatchSize = patchSize
            };
            var calculator           = ImagePatchDistance.Cie76;
            var patchMatchNnfBuilder = new PatchMatchNnfBuilder();

            // Create the nnf for the image(while ignoring some area)
            // with a couple of iterations.
            patchMatchNnfBuilder.RunRandomNnfInitIteration(nnf, destImage, srcImage, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map);

            // Create a mapping for the area that is a bit bigger
            // then ignored area.
            map = new Area2DMapBuilder()
                  .InitNewMap(imageArea, imageArea)
                  .ReduceDestArea(destArea)
                  .SetIgnoredSourcedArea(ignoreArea)
                  .Build();

            patchMatchNnfBuilder.RunRandomNnfInitIteration(nnf, destImage, srcImage, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map);

            string fileName1 = @"..\..\..\nnf1_pure.png";

            nnf
            .ToRgbImage()
            .FromRgbToBitmap()
            .SaveTo(fileName1, ImageFormat.Png);

            // Normalize the NNF in the ignored area.
            nnf.Normalize(ignoreArea);

            // Prepare results, save and show them
            string fileName2 = @"..\..\..\nnf2_normalized.png";

            nnf
            .ToRgbImage()
            .FromRgbToBitmap()
            .SaveTo(fileName2, ImageFormat.Png)
            .ShowFile();

            Console.WriteLine($"Nnf normalization is finished.");
        }
Exemple #5
0
        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.");
        }
Exemple #6
0
        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);
        }
Exemple #7
0
        static void Main(string[] args)
        {
            const string basePath = "..\\..\\..\\..\\images";

            var destImageName      = "pm1.png";
            var srcImageName       = "pm2.png";
            var destArea1ImageName = "pm1_target1.png";
            var destArea2ImageName = "pm1_target2.png";

            // this is our input data.
            var destImage = GetLabImage(basePath, destImageName);
            var srcImage  = GetLabImage(basePath, srcImageName);

            var destArea1 = GetArea2D(basePath, destArea1ImageName);
            var destArea2 = GetArea2D(basePath, destArea2ImageName);
            var srcArea   = Area2D.Create(0, 0, srcImage.Width, srcImage.Height);

            var map1 = new Area2DMapBuilder()
                       .InitNewMap(destArea1, srcArea)
                       .Build();

            var map2 = new Area2DMapBuilder()
                       .InitNewMap(destArea2, srcArea)
                       .Build();

            var settings = new PatchMatchSettings {
                PatchSize = 5
            };
            var patchMatchNnfBuilder = new PatchMatchNnfBuilder();
            var calculator           = ImagePatchDistance.Cie76;

            // Create nnf for the images
            // with a couple of iterations.
            var nnf1 = new Nnf(destImage.Width, destImage.Height, srcImage.Width, srcImage.Height, settings.PatchSize);

            patchMatchNnfBuilder.RunRandomNnfInitIteration(nnf1, destImage, srcImage, settings, calculator, map1);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf1, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map1);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf1, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map1);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf1, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map1);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf1, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map1);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf1, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map1);

            // Create the nnf for the images
            // with a couple of iterations.
            var nnf2 = new Nnf(destImage.Width, destImage.Height, srcImage.Width, srcImage.Height, settings.PatchSize);

            patchMatchNnfBuilder.RunRandomNnfInitIteration(nnf2, destImage, srcImage, settings, calculator, map2);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf2, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map2);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf2, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map2);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf2, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map2);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf2, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map2);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf2, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map2);


            // Show the built NNFs and restored images
            nnf2
            .RestoreImage(srcImage, 3, settings.PatchSize)
            .FromLabToRgb()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\..\restored1.png", ImageFormat.Png);

            nnf1
            .RestoreImage(srcImage, 3, settings.PatchSize)
            .FromLabToRgb()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\..\restored2.png", ImageFormat.Png);

            nnf1
            .ToRgbImage()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\..\nnf1.png", ImageFormat.Png);

            nnf2
            .ToRgbImage()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\..\nnf2.png", ImageFormat.Png);


            // Let's now merge the built NNFs and try to restore an image
            nnf2.Merge(nnf1, map2, map1);

            nnf2
            .RestoreImage(srcImage, 3, settings.PatchSize)
            .FromLabToRgb()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\..\restored_whole.png", ImageFormat.Png);

            nnf2
            .ToRgbImage()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\..\merged_nnf.png", ImageFormat.Png)
            .ShowFile();

            Console.WriteLine($"PatchMatchPipeline processing is finished.");
        }
Exemple #8
0
        static void Main(string[] args)
        {
            var sw = new Stopwatch();

            sw.Start();

            const string basePath = "..\\..\\..\\..\\images";

            var destImageName       = "pm1.png";
            var srcImageName        = "pm2.png";
            var destTargetImageName = "dd1.png";

            // this is our input data.
            var destImage = GetLabImage(basePath, destImageName);
            var srcImage  = GetLabImage(basePath, srcImageName);

            var destTargetArea1 = GetArea2D(basePath, destTargetImageName);
            var srcArea1        = GetArea2D(basePath, "sd1.png");
            var destArea2       = GetArea2D(basePath, "dd2.png");
            var srcArea2        = GetArea2D(basePath, "sd2.png");
            var destArea        = Area2D.Create(0, 0, destImage.Width, destImage.Height);
            var srcArea         = Area2D.Create(0, 0, srcImage.Width, srcImage.Height);

            var map = new Area2DMapBuilder()
                      .InitNewMap(destArea, srcArea)
                      .AddAssociatedAreas(destTargetArea1, srcArea1)
                      .AddAssociatedAreas(destArea2, srcArea2)
                      .Build();

            const byte patchSize = 5;
            var        nnf       = new Nnf(destImage.Width, destImage.Height, srcImage.Width, srcImage.Height, patchSize);
            // Prepage setting for the PM algorithm
            var settings = new PatchMatchSettings
            {
                PatchSize = patchSize
            };
            var calculator = ImagePatchDistance.Cie76;

            var patchMatchNnfBuilder = new PatchMatchNnfBuilder();

            // Create the nnf for the small variant of the images
            // with a couple of iterations.
            patchMatchNnfBuilder.RunRandomNnfInitIteration(nnf, destImage, srcImage, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Backward, settings, calculator, map);
            patchMatchNnfBuilder.RunBuildNnfIteration(nnf, destImage, srcImage, NeighboursCheckDirection.Forward, settings, calculator, map);

            // Restore dest image from the NNF and source image.
            nnf
            .RestoreImage(srcImage, 3, patchSize)
            .FromLabToRgb()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\..\restored.png", ImageFormat.Png);

            // Convert the NNF to an image, save and show it
            nnf
            .ToRgbImage()
            .FromRgbToBitmap()
            .SaveTo(@"..\..\..\nnf.png", ImageFormat.Png)
            .ShowFile();

            sw.Stop();

            Console.WriteLine($"Elapsed time: {sw.Elapsed}");
            Console.WriteLine($"PatchMatchPipeline processing is finished.");
        }