private Area2D GetImageAreaToProcess(ZsImage imageArgb, ZsImage markupArgb, byte levelsAmount) { var size = Calculate(imageArgb.Width, imageArgb.Height, levelsAmount); var markupArgbArea = markupArgb.FromArgbToArea2D(); var offset = CalculateOffset(size.Item1, size.Item2, markupArgbArea); var imageSrcArea = Area2D.Create((int)offset.X, (int)offset.Y, size.Item1, size.Item2); return(imageSrcArea); }
/// <summary> /// Calculates the levels amount. /// </summary> /// <param name="image">The image.</param> /// <param name="removeMarkup">The remove markup.</param> /// <param name="patchSize">Size of the patch.</param> /// <returns></returns> /// <exception cref="ArgumentNullException"> /// image /// or /// removeMarkup /// </exception> /// <exception cref="ArgumentOutOfRangeException">Thrown when patchSize is less than 2.</exception> /// <exception cref="AreaRemovedException">Thrown when area to remove covers whole image.</exception> public byte CalculateLevelsAmount(ZsImage image, ZsImage removeMarkup, byte patchSize) { if (image == null) { throw new ArgumentNullException(nameof(image)); } if (removeMarkup == null) { throw new ArgumentNullException(nameof(removeMarkup)); } if (patchSize < 2) { throw new ArgumentOutOfRangeException(nameof(patchSize)); } var imageArea = Area2D.Create(0, 0, image.Width, image.Height); var removeArea = removeMarkup .FromArgbToArea2D() .Intersect(imageArea); if (imageArea.Substract(removeArea).IsEmpty) { throw new AreaRemovedException(); } if (removeArea.IsEmpty) { return(1); } const double patchProcentOfMarkup = 0.15; byte levels = 1; var patchArea = patchSize * patchSize; var markupPointsAmount = removeArea.ElementsCount; //in the smart version of the method we take into account the density //of the marking, so we wouldnt get a lots of levels when need to remove //many small things //var searchArea = new RectArea(0, 0, markup.Width, markup.Height); ////get all the pieces of the marking //var areas = Utils.ImageUtils.GetAllObjects(markup.Data, searchArea); //if (areas.Count > 1) //{ // //find the biggest area and use its pixels amount // markupPointsAmount = areas.Max(x => x.PixAmount); //} double curPpm = patchArea / (double)markupPointsAmount; while (curPpm < patchProcentOfMarkup) { levels++; markupPointsAmount /= 4; curPpm = patchArea / (double)markupPointsAmount; } return(levels); }
/// <summary> /// Build pyramids by downscaling the image and the markup. /// We also apply a smoothing filter to the scaled images /// to reduce high spatial ferquency introduced by scaling /// (the filter is not applied to the inoainted area to avoid /// inpainted object propagation out of its boundaries) /// </summary> /// <param name="levelsAmount">The levels amount.</param> /// <param name="patchSize">Size of the patch.</param> /// <returns></returns> /// <exception cref="ArgumentOutOfRangeException">levelsAmount</exception> /// <exception cref="Zavolokas.ImageProcessing.Inpainting.InitializationException"></exception> /// <exception cref="Zavolokas.ImageProcessing.Inpainting.WrongImageSizeException"></exception> /// <exception cref="Zavolokas.ImageProcessing.Inpainting.NoAreaToInpaintException"></exception> /// <exception cref="AreaRemovedException"></exception> public Pyramid Build(byte levelsAmount, byte patchSize = 11) { if (levelsAmount < 1) { throw new ArgumentOutOfRangeException(nameof(levelsAmount)); } if (_imageArgb == null) { throw new InitializationException(); } int w = _imageArgb.Width; int h = _imageArgb.Height; if (w == 1 || h == 1) { throw new WrongImageSizeException(); } for (int levelInadex = 1; levelInadex < levelsAmount; levelInadex++) { if (w % 2 > 0 || h % 2 > 0) { throw new WrongImageSizeException(); } w /= 2; h /= 2; if (w == 1 || h == 1) { throw new WrongImageSizeException(); } } var imageArea = Area2D.Create(0, 0, _imageArgb.Width, _imageArgb.Height); if (_inpaintMarkup.Width != _imageArgb.Width || _inpaintMarkup.Height != _imageArgb.Height) { // Adjus inpaint markup image so that it become the same size as // the image is _inpaintMarkup = AlignImage(_inpaintMarkup, _imageArgb); } for (int i = 0; i < _donors.Count; i++) { if (_donors[i].Width != _imageArgb.Width || (_donors[i].Height != _imageArgb.Height)) { _donors[i] = AlignImage(_donors[i], _imageArgb); } } var inpaintArea = _inpaintMarkup.FromArgbToArea2D(); if (inpaintArea.IsEmpty) { throw new NoAreaToInpaintException(); } if (imageArea.Substract(inpaintArea).IsEmpty) { throw new AreaRemovedException(); } // Build pyramids var images = new List <ZsImage>(levelsAmount); var mappings = new List <Area2DMap>(levelsAmount); var markups = new List <Area2D>(levelsAmount); var mapBuilder = new InpaintMapBuilder(new Area2DMapBuilder()); for (byte levelIndex = 0; levelIndex < levelsAmount; levelIndex++) { // convert image to Lab color space and store it var imageCopy = _imageArgb.Clone() .FromArgbToRgb(_backgroundColor) .FromRgbToLab(); images.Add(imageCopy); inpaintArea = _inpaintMarkup.FromArgbToArea2D(); imageArea = Area2D.Create(0, 0, _imageArgb.Width, _imageArgb.Height); var nnfSourceArea = imageArea.Substract(inpaintArea); Area2D nnfTargetArea; // Obtain target area for the NNF building based on the level if (levelIndex == levelsAmount - 1) { // For the top level, the target area should be the whole image area nnfTargetArea = imageArea; } else { // For the rest levels, the target area should be // slightly bigger then the inpaint area. nnfTargetArea = inpaintArea .Dilation(patchSize) .Intersect(imageArea); } // Create a mapping for the level. mapBuilder.InitNewMap(imageArea); foreach (var donor in _donors) { var donorArea = donor.FromArgbToArea2D(); if (!donorArea.IsEmpty) { donorArea = donorArea.Dilation(patchSize / 2); // This is very questionable dilation mapBuilder.AddDonor(donorArea); } } var mapping = mapBuilder .SetInpaintArea(inpaintArea) .ReduceDestArea(nnfTargetArea) .Build(); mappings.Add(mapping); markups.Add(inpaintArea); if (levelIndex < levelsAmount - 1) { // downscale for the next level // NOTE: we shouldn't blur out the inpainted area so it is not getting bigger!! _imageArgb.PyramidDownArgb(nnfSourceArea); _inpaintMarkup.PyramidDownArgb(false); for (int i = 0; i < _donors.Count; i++) { _donors[i].PyramidDownArgb(false); } } } return(new Pyramid(images, markups, mappings)); }