/// <summary>
        /// Normalizes the NNF in the specified dest area.
        /// </summary>
        /// <param name="nnf">The NNF.</param>
        /// <param name="destArea">The dest area.</param>
        /// <exception cref="ArgumentNullException">
        /// nnf
        /// or
        /// destArea
        /// </exception>
        public static void Normalize(this Nnf nnf, Area2D destArea)
        {
            if (nnf == null)
            {
                throw new ArgumentNullException(nameof(nnf));
            }

            if (destArea == null)
            {
                throw new ArgumentNullException(nameof(destArea));
            }

            var nnfdata = nnf.GetNnfItems();

            checked
            {
                var distances    = new double[destArea.ElementsCount];
                var pointIndexes = new int[destArea.ElementsCount];
                destArea.FillMappedPointsIndexes(pointIndexes, nnf.DstWidth);

                var dinstancesSum = 0.0;
                for (int destPointIndex = 0; destPointIndex < distances.Length; destPointIndex++)
                {
                    var    nnfPos   = pointIndexes[destPointIndex] * 2;
                    double distance = nnfdata[nnfPos + 1];
                    distances[destPointIndex] = distance;
                    dinstancesSum            += distance;
                }
                double mean = dinstancesSum / distances.Length;

                var    squareDistances   = new double[distances.Length];
                double squreDistancesSum = 0.0;
                for (int i = 0; i < distances.Length; i++)
                {
                    var    distToMean     = distances[i] - mean;
                    double distToMeanCube = distToMean * distToMean;
                    squareDistances[i] = distToMeanCube;
                    squreDistancesSum += distToMeanCube;
                }
                double sigma = System.Math.Sqrt(squreDistancesSum / (squareDistances.Length - 1));

                for (int destPointIndex = 0; destPointIndex < distances.Length; destPointIndex++)
                {
                    var nnfPos = pointIndexes[destPointIndex] * 2;
                    var dist   = distances[destPointIndex];
                    dist = (dist - mean) / sigma;
                    if (dist < 0)
                    {
                        dist = -dist;
                    }
                    nnfdata[nnfPos + 1] = dist;
                }
            }
        }
        /// <summary>
        /// Merges provided NNF into.
        /// </summary>
        /// <param name="destNnf">The dest NNF.</param>
        /// <param name="srcNnf">The source NNF.</param>
        /// <param name="destNnfMap">The dest NNF areas mapping.</param>
        /// <param name="srcNnfMap">The source NNF areas mapping.</param>
        /// <param name="options">The parallel processing options.</param>
        /// <exception cref="ArgumentNullException">
        /// destNnf
        /// or
        /// srcNnf
        /// or
        /// destNnfMap
        /// or
        /// srcNnfMap
        /// </exception>
        /// <exception cref="ArgumentException">NNFs should be built for the same source and dest images</exception>
        public static unsafe void Merge(this Nnf destNnf, Nnf srcNnf, Area2DMap destNnfMap, Area2DMap srcNnfMap, ParallelOptions options = null)
        {
            if (destNnf == null)
            {
                throw new ArgumentNullException(nameof(destNnf));
            }
            if (srcNnf == null)
            {
                throw new ArgumentNullException(nameof(srcNnf));
            }
            if (destNnfMap == null)
            {
                throw new ArgumentNullException(nameof(destNnfMap));
            }
            if (srcNnfMap == null)
            {
                throw new ArgumentNullException(nameof(srcNnfMap));
            }

            // We assume that all the inputs contain NNFs
            // for the same dest image.
            // The source images can not be different as well.
            // When different source images are desired those
            // images should be merged to one image and mappings
            // should be adjusted to map to a particular region
            // on the source image.
            // Differen source images problem. In that case we
            // have a problem with different mappings when we
            // merge NNfs. Mappings can not be merged as they
            // would have totally different source areas.

            // make sure that both NNFs are built for the same dest and source images
            if (destNnf.DstWidth != srcNnf.DstWidth || destNnf.DstHeight != srcNnf.DstHeight ||
                destNnf.SourceWidth != srcNnf.SourceWidth || destNnf.SourceHeight != srcNnf.SourceHeight)
            {
                throw new ArgumentException("NNFs should be built for the same source and dest images");
            }

            if (options == null)
            {
                options = new ParallelOptions();
            }

            var destImageWidth = destNnf.DstWidth;
            var srcImageWidth  = destNnf.SourceWidth;

            // Nnf that needs to be merged to our dest nnf.
            var srcNnfData  = srcNnf.GetNnfItems();
            var destNnfData = destNnf.GetNnfItems();

            var destNnfPointsIndexes = (destNnfMap as IAreasMapping).DestArea.GetPointsIndexes(destImageWidth);
            var srcNnfPointsIndexes  = (srcNnfMap as IAreasMapping).DestArea.GetPointsIndexes(destImageWidth);
            var mappings             = srcNnfMap.ExtractMappedAreasInfo(destImageWidth, srcImageWidth);

            // Decide on how many partitions we should divade the processing
            // of the elements.
            int partsCount = srcNnfMap.DestElementsCount > options.NotDividableMinAmountElements
                ? options.ThreadsCount
                : 1;
            var partSize = (int)(srcNnfMap.DestElementsCount / partsCount);

            Parallel.For(0, partsCount, partIndex =>
            {
                // Colne mapping to avoid conflicts in multithread
                var destNnfPointsIndexesSet = new HashSet <int>(destNnfPointsIndexes);

                // Clone mappings to avoid problems in multithread
                var mappedAreasInfos = new MappedAreasInfo[mappings.Length];
                for (int j = 0; j < mappings.Length; j++)
                {
                    mappedAreasInfos[j] = mappings[j].Clone();
                }

                var firstPointIndex = partIndex * partSize;
                var lastPointIndex  = firstPointIndex + partSize - 1;
                if (partIndex == partsCount - 1)
                {
                    lastPointIndex = srcNnfMap.DestElementsCount - 1;
                }
                if (lastPointIndex > srcNnfMap.DestElementsCount)
                {
                    lastPointIndex = srcNnfMap.DestElementsCount - 1;
                }

                fixed(double *destNnfP         = destNnfData)
                fixed(double *srcNnfP          = srcNnfData)
                fixed(int *srcNnfPointIndexesP = srcNnfPointsIndexes)
                {
                    for (var srcNnfMapDestPointIndex = firstPointIndex; srcNnfMapDestPointIndex <= lastPointIndex; srcNnfMapDestPointIndex++)
                    {
                        var destPointIndex = *(srcNnfPointIndexesP + srcNnfMapDestPointIndex);

                        if (destNnfPointsIndexesSet.Contains(destPointIndex))
                        {
                            // The value of the NNF in the dest point can
                            // present in the resulting destNnf as well.
                            // In that case we need to merge NNFs at the point
                            // by taking the best value.

                            // compare and set the best
                            var srcVal  = *(srcNnfP + destPointIndex * 2 + 1);
                            var destVal = *(destNnfP + destPointIndex * 2 + 1);

                            if (srcVal < destVal)
                            {
                                *(destNnfP + destPointIndex * 2 + 0) = *(srcNnfP + destPointIndex * 2 + 0);
                                *(destNnfP + destPointIndex * 2 + 1) = srcVal;
                            }
                        }
                        else
                        {
                            // When the destNnf doesn't contain the value
                            // for that point we simply copy it
                            *(destNnfP + destPointIndex * 2 + 0) = *(srcNnfP + destPointIndex * 2 + 0);
                            *(destNnfP + destPointIndex * 2 + 1) = *(srcNnfP + destPointIndex * 2 + 1);
                        }
                    }
                }
            });
        }
        /// <summary>
        /// Clones the NNF and scales it up 2 times without 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="scaledMap">The areas mapping. By default whole area of the dest image is associated with the whole area of the source image.</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException">
        /// nnf
        /// or
        /// scaledDestImage
        /// or
        /// scaledSrcImage
        /// </exception>
        public static Nnf CloneAndScaleNnf2X(this Nnf nnf, ZsImage scaledDestImage, ZsImage scaledSrcImage, ParallelOptions options = null, Area2DMap scaledMap = null)
        {
            if (nnf == null)
            {
                throw new ArgumentNullException(nameof(nnf));
            }
            if (scaledDestImage == null)
            {
                throw new ArgumentNullException(nameof(scaledDestImage));
            }
            if (scaledSrcImage == null)
            {
                throw new ArgumentNullException(nameof(scaledSrcImage));
            }

            if (scaledMap == null)
            {
                var destArea = Area2D.Create(0, 0, scaledDestImage.Width, scaledDestImage.Height);
                var srcArea  = Area2D.Create(0, 0, scaledSrcImage.Width, scaledSrcImage.Height);

                scaledMap = new Area2DMapBuilder()
                            .InitNewMap(destArea, srcArea)
                            .Build();
            }

            if (options == null)
            {
                options = new ParallelOptions();
            }

            var destImageWidth = scaledDestImage.Width;
            var srcImageWidth  = scaledSrcImage.Width;
            var sameSrcAndDest = scaledDestImage == scaledSrcImage;

            var mappings = scaledMap.ExtractMappedAreasInfo(destImageWidth, srcImageWidth, true);

            var nnf2x = new Nnf(nnf.DstWidth * 2, nnf.DstHeight * 2, nnf.SourceWidth * 2, nnf.SourceHeight * 2, nnf.PatchSize);

            var nnfDestWidth     = nnf.DstWidth;
            var nnfSourceWidth   = nnf.SourceWidth;
            var nnf2xSourceWidth = nnf2x.SourceWidth;
            var nnf2xDstWidth    = nnf2x.DstWidth;

            var nnfData   = nnf.GetNnfItems();
            var nnf2xData = nnf2x.GetNnfItems();

            // Decide on how many partitions we should divade the processing
            // of the elements.
            int nnfPointsAmount = nnf.DstWidth * nnf.DstHeight;
            var partsCount      = nnfPointsAmount > options.NotDividableMinAmountElements
                ? options.ThreadsCount
                : 1;
            var partSize = nnfPointsAmount / partsCount;

            var offs = new[]
            {
                new[] { 0, 0 },
                new[] { 1, 0 },
                new[] { 0, 1 },
                new[] { 1, 1 }
            };

            Parallel.For(0, partsCount, partIndex =>
                         //for (int partIndex = 0; partIndex < partsCount; partIndex++)
            {
                bool isPatchFit = false;

                var mappedAreasInfos = new MappedAreasInfo[mappings.Length];
                for (var i = 0; i < mappings.Length; i++)
                {
                    mappedAreasInfos[i] = mappings[i].Clone();
                }

                var firstPointIndex = partIndex * partSize;
                var lastPointIndex  = firstPointIndex + partSize - 1;
                if (partIndex == partsCount - 1)
                {
                    lastPointIndex = nnfPointsAmount - 1;
                }
                if (lastPointIndex > nnfPointsAmount)
                {
                    lastPointIndex = nnfPointsAmount - 1;
                }

                MappedAreasInfo mappedAreasInfo = null;

                for (var j = firstPointIndex; j <= lastPointIndex; j++)
                {
                    var destPx = j;
                    var destX  = destPx % nnfDestWidth;
                    var destY  = destPx / nnfDestWidth;

                    // Find
                    var srcPointIndex = nnfData[destY * nnfDestWidth * 2 + destX * 2]; // / 2;
                    int srcY          = (int)(srcPointIndex / nnfSourceWidth);         // * 2;
                    int srcX          = (int)(srcPointIndex % nnfSourceWidth);         // * 2;

                    var dist = nnfData[destY * nnfDestWidth * 2 + destX * 2 + 1];

                    var nY = destY * 2;
                    var nX = destX * 2;

                    for (int i = 0; i < offs.Length; i++)
                    {
                        var destPointIndex = (nY + offs[i][1]) * nnf2xDstWidth + nX + offs[i][0];
                        mappedAreasInfo    = mappedAreasInfos.FirstOrDefault(mai => mai.DestAreaPointsIndexesSet.Contains(destPointIndex));
                        if (mappedAreasInfo != null)
                        {
                            nnf2xData[destPointIndex * 2]     = (srcY * 2 + offs[i][1]) * nnf2xSourceWidth + (srcX * 2 + offs[i][0]);
                            nnf2xData[destPointIndex * 2 + 1] = dist;
                        }
                        else
                        {
                            if (sameSrcAndDest)
                            {
                                // when the source and the dest image is the same one, the best
                                // corresponding patch is the patch itself!
                                nnf2xData[destPointIndex * 2]     = destPointIndex;
                                nnf2xData[destPointIndex * 2 + 1] = 0;
                            }
                            else
                            {
                                nnf2xData[destPointIndex * 2]     = (srcY * 2 + offs[i][1]) * nnf2xSourceWidth + (srcX * 2 + offs[i][0]);
                                nnf2xData[destPointIndex * 2 + 1] = nnfData[destY * nnfDestWidth * 2 + destX * 2 + 1];
                            }
                        }
                    }
                }
            }
                         );
            return(nnf2x);
        }
        /// <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="scaledMap">The areas mapping. By default whole area of the dest image is associated with the whole area of the source image.</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
        /// or
        /// scaledMap</exception>
        public static unsafe Nnf CloneAndScale2XWithUpdate(this Nnf nnf, ZsImage scaledDestImage, ZsImage scaledSrcImage, ParallelOptions options, Area2DMap scaledMap, ImagePatchDistanceCalculator patchDistanceCalculator = null, Area2D destPixelsArea = null)
        {
            if (nnf == null)
            {
                throw new ArgumentNullException(nameof(nnf));
            }
            if (scaledDestImage == null)
            {
                throw new ArgumentNullException(nameof(scaledDestImage));
            }
            if (scaledSrcImage == null)
            {
                throw new ArgumentNullException(nameof(scaledSrcImage));
            }
            if (scaledMap == null)
            {
                throw new ArgumentNullException(nameof(scaledMap));
            }

            if (destPixelsArea == null)
            {
                destPixelsArea = Area2D.Create(0, 0, scaledDestImage.Width, scaledDestImage.Height);
            }

            if (patchDistanceCalculator == null)
            {
                patchDistanceCalculator = ImagePatchDistance.Cie76;
            }

            if (options == null)
            {
                options = new ParallelOptions();
            }

            var patchSize      = nnf.PatchSize;
            var patchLength    = patchSize * patchSize;
            var destImageWidth = scaledDestImage.Width;
            var srcImageWidth  = scaledSrcImage.Width;
            var sameSrcAndDest = scaledDestImage == scaledSrcImage;

            var pixelsArea = (scaledMap as IAreasMapping).DestArea;

            //var destPointIndexes = GetDestPointsIndexes(pixelsArea, destImageWidth, NeighboursCheckDirection.Forward);
            pixelsArea = pixelsArea.Intersect(destPixelsArea);
            var destAvailablePixelsIndexes = pixelsArea.GetPointsIndexes(destImageWidth);
            var mappings = scaledMap.ExtractMappedAreasInfo(destImageWidth, srcImageWidth, true);

            var nnf2x = new Nnf(nnf.DstWidth * 2, nnf.DstHeight * 2, nnf.SourceWidth * 2, nnf.SourceHeight * 2, nnf.PatchSize);

            var nnfDestWidth     = nnf.DstWidth;
            var nnfSourceWidth   = nnf.SourceWidth;
            var nnf2xSourceWidth = nnf2x.SourceWidth;
            var nnf2xDstWidth    = nnf2x.DstWidth;

            var nnfData   = nnf.GetNnfItems();
            var nnf2xData = nnf2x.GetNnfItems();

            // Decide on how many partitions we should divade the processing
            // of the elements.
            int nnfPointsAmount = nnf.DstWidth * nnf.DstHeight;
            var partsCount      = nnfPointsAmount > options.NotDividableMinAmountElements
                ? options.ThreadsCount
                : 1;
            var partSize = nnfPointsAmount / partsCount;

            var offs = new[]
            {
                new[] { 0, 0 },
                new[] { 1, 0 },
                new[] { 0, 1 },
                new[] { 1, 1 }
            };

            Parallel.For(0, partsCount, partIndex =>
                         //for (int partIndex = 0; partIndex < partsCount; partIndex++)
            {
                bool isPatchFit = false;

                // Init the dest & source patch
                var destPatchPixelsIndexes = new int[patchLength];
                var srcPatchPixelsIndexes  = new int[patchLength];

                //var destPointsIndexesSet = new HashSet<int>(destPointIndexes);
                var destAvailablePixelsIndexesSet = new HashSet <int>(destAvailablePixelsIndexes);
                var mappedAreasInfos = new MappedAreasInfo[mappings.Length];
                for (var i = 0; i < mappings.Length; i++)
                {
                    mappedAreasInfos[i] = mappings[i].Clone();
                }

                var firstPointIndex = partIndex * partSize;
                var lastPointIndex  = firstPointIndex + partSize - 1;
                if (partIndex == partsCount - 1)
                {
                    lastPointIndex = nnfPointsAmount - 1;
                }
                if (lastPointIndex > nnfPointsAmount)
                {
                    lastPointIndex = nnfPointsAmount - 1;
                }

                fixed(double *destImagePixelsDataP   = scaledDestImage.PixelsData)
                fixed(double *sourceImagePixelsDataP = scaledSrcImage.PixelsData)
                fixed(int *srcPatchPixelsIndexesP    = srcPatchPixelsIndexes)
                fixed(int *destPatchPixelsIndexesP   = destPatchPixelsIndexes)
                {
                    MappedAreasInfo mappedAreasInfo = null;

                    for (var j = firstPointIndex; j <= lastPointIndex; j++)
                    {
                        var destPx = j;
                        var destX  = destPx % nnfDestWidth;
                        var destY  = destPx / nnfDestWidth;

                        // Find
                        var srcPointIndex = nnfData[destY * nnfDestWidth * 2 + destX * 2]; // / 2;
                        int srcY          = (int)(srcPointIndex / nnfSourceWidth);         // * 2;
                        int srcX          = (int)(srcPointIndex % nnfSourceWidth);         // * 2;

                        var nY = destY * 2;
                        var nX = destX * 2;

                        for (int i = 0; i < offs.Length; i++)
                        {
                            var destPointIndex = (nY + offs[i][1]) * nnf2xDstWidth + nX + offs[i][0];
                            mappedAreasInfo    = mappedAreasInfos.FirstOrDefault(mai => mai.DestAreaPointsIndexesSet.Contains(destPointIndex));
                            if (mappedAreasInfo != null)
                            {
                                nnf2xData[destPointIndex * 2] = (srcY * 2 + offs[i][1]) * nnf2xSourceWidth + (srcX * 2 + offs[i][0]);
                                Utils.PopulatePatchPixelsIndexes(srcPatchPixelsIndexesP, srcX + offs[i][0], srcY + offs[i][1], patchSize, nnf2xSourceWidth, mappedAreasInfo.SrcAreaPointsIndexesSet, out isPatchFit);
                                Utils.PopulatePatchPixelsIndexes(destPatchPixelsIndexesP, destX + offs[i][0], destY + offs[i][1], patchSize, destImageWidth, destAvailablePixelsIndexesSet, out isPatchFit);
                                nnf2xData[destPointIndex * 2 + 1] = patchDistanceCalculator.Calculate(destPatchPixelsIndexesP, srcPatchPixelsIndexesP, double.MaxValue, destImagePixelsDataP, sourceImagePixelsDataP, scaledDestImage, scaledSrcImage, patchLength);
                            }
                            else
                            {
                                if (sameSrcAndDest)
                                {
                                    // when the source and the dest image is the same one, the best
                                    // corresponding patch is the patch itself!
                                    nnf2xData[destPointIndex * 2]     = destPointIndex;
                                    nnf2xData[destPointIndex * 2 + 1] = 0;
                                }
                                else
                                {
                                    nnf2xData[destPointIndex * 2]     = (srcY * 2 + offs[i][1]) * nnf2xSourceWidth + (srcX * 2 + offs[i][0]);
                                    nnf2xData[destPointIndex * 2 + 1] = nnfData[destY * nnfDestWidth * 2 + destX * 2 + 1];
                                }
                            }
                        }
                    }
                }
            }
                         );
            return(nnf2x);
        }
Exemple #5
0
        private InpaintingResult Inpaint(ZsImage image, Area2D removeArea, Nnf nnf, double k, IInpaintSettings settings)
        {
            // Since the nnf was normalized, the sigma now is normalized as well and it
            // has not any more some specific value.
            //const double naturalLogBase = System.Math.E;
            //const double minusSigmaCube2 = -0.91125;//-2 * sigma * sigma; sigma = 0.675 = 75percentil
            //double NaturalLogBase2 = System.Math.Pow(System.Math.E, 1.0 / minusSigmaCube2);
            const double naturalLogBasePowMinusSigmaCube2 = 0.33373978049163078;

            const double maxSquareDistanceInLab = 32668.1151;
            // Gamma is used for calculation of alpha in markup. The confidence.
            const double gamma = 1.3;
            // The value of confidence in non marked areas.
            const double confidentValue = 1.50;

            var patchSize           = settings.PatchSize;
            var colorResolver       = settings.ColorResolver;
            var pixelChangeTreshold = settings.PixelChangeTreshold;

            // Get points' indexes that that needs to be inpainted.
            var pointIndexes = new int[removeArea.ElementsCount];

            removeArea.FillMappedPointsIndexes(pointIndexes, image.Width);

            var patchOffset = (patchSize - 1) / 2;

            var imageWidth = image.Width;
            var nnfWidth   = nnf.DstWidth;
            var nnfHeight  = nnf.DstHeight;
            var srcWidth   = nnf.SourceWidth;
            var srcHeight  = nnf.SourceHeight;

            var nnfdata = nnf.GetNnfItems();
            var cmpts   = image.NumberOfComponents;

            // Weighted color is color's components values + weight of the color.
            var weightedColors = new double[patchSize * patchSize * (cmpts + 1)];
            var confidence     = removeArea.CalculatePointsConfidence(confidentValue, gamma);
            var resolvedColor  = new double[cmpts];

            var totalDifference         = 0.0;
            var changedPixelsAmount     = 0;
            var changedPixelsDifference = 0.0;

            for (int ii = 0; ii < pointIndexes.Length; ii++)
            {
                var pointIndex = pointIndexes[ii];

                int pty = pointIndex / imageWidth;
                int ptx = pointIndex % imageWidth;

                int colorsCount = 0;

                //go thru the patch
                for (int y = pty - patchOffset, j = 0; j < patchSize; j++, y++)
                {
                    for (int x = ptx - patchOffset, i = 0; i < patchSize; i++, x++)
                    {
                        if (0 <= x && x < nnfWidth && 0 <= y && y < nnfHeight)
                        {
                            int destPatchPointIndex = y * nnfWidth + x;
                            int srcPatchPointIndex  = (int)nnfdata[destPatchPointIndex * 2];

                            int srcPatchPointX = srcPatchPointIndex % nnfWidth;
                            int srcPatchPointY = srcPatchPointIndex / nnfWidth;

                            int srcPixelX = srcPatchPointX - i + patchOffset;
                            int srcPixelY = srcPatchPointY - j + patchOffset;

                            if (0 <= srcPixelX && srcPixelX < srcWidth && 0 <= srcPixelY && srcPixelY < srcHeight)
                            {
                                int srcPixelIndex = srcPixelY * imageWidth + srcPixelX;

                                for (int ci = 0; ci < cmpts; ci++)
                                {
                                    weightedColors[colorsCount * (cmpts + 1) + ci] = image.PixelsData[srcPixelIndex * cmpts + ci];
                                }
                                weightedColors[colorsCount * (cmpts + 1) + cmpts] =
                                    System.Math.Pow(naturalLogBasePowMinusSigmaCube2, nnfdata[destPatchPointIndex * 2 + 1]) *
                                    confidence[ii];

                                colorsCount++;
                            }
                        }
                    }
                }
                // calculate color
                colorResolver.Resolve(weightedColors, 0, colorsCount, cmpts, k, resolvedColor, 0);

                // how different is resolvedColor from the old color?
                var labL       = resolvedColor[0] - image.PixelsData[pointIndex * cmpts + 0];
                var labA       = resolvedColor[1] - image.PixelsData[pointIndex * cmpts + 1];
                var labB       = resolvedColor[2] - image.PixelsData[pointIndex * cmpts + 2];
                var pixelsDiff = (labL * labL + labA * labA + labB * labB) / maxSquareDistanceInLab;
                totalDifference += pixelsDiff;

                if (pixelsDiff > pixelChangeTreshold)
                {
                    changedPixelsAmount++;
                    changedPixelsDifference += pixelsDiff;
                }

                for (var i = 0; i < cmpts; i++)
                {
                    image.PixelsData[pointIndex * cmpts + i] = resolvedColor[i];
                }
            }

            totalDifference /= pointIndexes.Length;
            _inpaintingResult.TotalDifference         = totalDifference / pointIndexes.Length;
            _inpaintingResult.PixelsChangedAmount     = changedPixelsAmount;
            _inpaintingResult.PixelsToInpaintAmount   = pointIndexes.Length;
            _inpaintingResult.ChangedPixelsDifference = changedPixelsDifference;

            return(_inpaintingResult);
        }
Exemple #6
0
        /// <summary>
        /// Runs the NNF build iteration for the associated areas of the dest and the source images.
        /// </summary>
        /// <param name="nnf">The NNF.</param>
        /// <param name="destImage">The dest image. For each patch at this image we will look for a similar one at the source image.</param>
        /// <param name="srcImage">The source image. Source of the patches for the dest image.</param>
        /// <param name="direction">The direction to look for a patches.</param>
        /// <param name="settings">The settings that control parameters of the algorithm.</param>
        /// <param name="patchDistanceCalculator">The calculator that calculates similarity of two patches. By deafult the Cie76 is used.</param>
        /// <param name="areasMapping">The areas mapping. By default whole area of the dest image is associated with the whole area of the source image.</param>
        /// <param name="destPixelsArea">Area on the dest image that actually containes pixels. By default is the area of the entire image.</param>
        /// <exception cref="ArgumentNullException">
        /// nnf
        /// or
        /// destImage
        /// or
        /// srcImage
        /// or
        /// settings
        /// or
        /// patchDistanceCalculator
        /// or
        /// areasMapping
        /// or
        /// destPixelsArea
        /// </exception>
        public unsafe void RunBuildNnfIteration(Nnf nnf, ZsImage destImage, ZsImage srcImage, NeighboursCheckDirection direction, PatchMatchSettings settings, ImagePatchDistanceCalculator patchDistanceCalculator, Area2DMap areasMapping, Area2D destPixelsArea)
        {
            if (nnf == null)
            {
                throw new ArgumentNullException(nameof(nnf));
            }
            if (destImage == null)
            {
                throw new ArgumentNullException(nameof(destImage));
            }
            if (srcImage == null)
            {
                throw new ArgumentNullException(nameof(srcImage));
            }
            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings));
            }
            if (patchDistanceCalculator == null)
            {
                throw new ArgumentNullException(nameof(patchDistanceCalculator));
            }
            if (areasMapping == null)
            {
                throw new ArgumentNullException(nameof(areasMapping));
            }
            if (destPixelsArea == null)
            {
                throw new ArgumentNullException(nameof(destPixelsArea));
            }

            sbyte offs = (sbyte)(direction == NeighboursCheckDirection.Forward ? 1 : -1);

            sbyte[][] offsets =
            {
                new[] { offs,     (sbyte)0 },
                new[] { (sbyte)0, offs     }
            };

            #region Cache settings and inputs in the local variables
            var nnfdata     = nnf.GetNnfItems();
            var nnfSrcWidth = nnf.SourceWidth;

            var patchSize          = settings.PatchSize;
            var patchLength        = patchSize * patchSize;
            var randomSearchRadius = settings.RandomSearchRadius;
            var alpha = settings.RandomSearchAlpha;
            var minSearchWindowSize = settings.MinSearchWindowSize;

            var destImageWidth = destImage.Width;
            var srcImageWidth  = srcImage.Width;
            #endregion

            // Obtain the data that allow us getting a point index
            // regarding to the process direction(forward or backward)
            var addModData = GetAddModData(direction, areasMapping.DestElementsCount);
            var add        = addModData.Item1;
            var mod        = addModData.Item2;

            // Decide on how many partitions we should divade the processing
            // of the elements.
            var partsCount = areasMapping.DestElementsCount > settings.NotDividableMinAmountElements
                ? settings.ThreadsCount
                : 1;
            var partSize = areasMapping.DestElementsCount / partsCount;

            var pixelsArea       = (areasMapping as IAreasMapping).DestArea;
            var destPointIndexes = pixelsArea.GetPointsIndexes(destImageWidth, direction == NeighboursCheckDirection.Forward);
            pixelsArea = pixelsArea.Intersect(destPixelsArea);
            var destAvailablePixelsIndexes = pixelsArea.GetPointsIndexes(destImageWidth, direction == NeighboursCheckDirection.Forward);

            var mappings = areasMapping.ExtractMappedAreasInfo(destImageWidth, srcImageWidth, direction == NeighboursCheckDirection.Forward);

            //for (int partIndex = 0; partIndex < partsCount; partIndex++)
            Parallel.For(0, partsCount, partIndex =>
            {
                var rnd = new FastRandom();
                int i;

                // Colne mapping to avoid conflicts in multithread
                //var destPointsIndexesSet = new HashSet<int>(destPointIndexes);
                var destAvailablePixelsIndexesSet = new HashSet <int>(destAvailablePixelsIndexes);
                var mappedAreasInfos = new MappedAreasInfo[mappings.Length];
                for (i = 0; i < mappings.Length; i++)
                {
                    mappedAreasInfos[i] = mappings[i].Clone();
                }

                var firstPointIndex = partIndex * partSize;
                var lastPointIndex  = firstPointIndex + partSize - 1;
                if (partIndex == partsCount - 1)
                {
                    lastPointIndex = destPointIndexes.Length - 1;
                }
                if (lastPointIndex > destPointIndexes.Length)
                {
                    lastPointIndex = destPointIndexes.Length - 1;
                }

                // Init the dest & source patch
                var destPatchPixelsIndexes = new int[patchSize * patchSize];
                var srcPatchPixelsIndexes  = new int[patchSize * patchSize];

                bool isPatchFit = false;

                fixed(double *nnfP                   = nnfdata)
                fixed(int *destPointIndexesP         = destPointIndexes)
                fixed(int *srcPatchPixelsIndexesP    = srcPatchPixelsIndexes)
                fixed(int *destPatchPixelsIndexesP   = destPatchPixelsIndexes)
                fixed(double *destImagePixelsDataP   = destImage.PixelsData)
                fixed(double *sourceImagePixelsDataP = srcImage.PixelsData)
                {
                    for (var j = firstPointIndex; j <= lastPointIndex; j++)
                    {
                        // Obtain a destination point from the mapping.
                        // The dest point is the position of the dest patch.
                        // Chocie of the destination point depends on the
                        // direction in which we iterate the points (forward or backward).
                        var destPointIndex = *(destPointIndexesP + add + j * mod);
                        var destPointX     = destPointIndex % destImageWidth;
                        var destPointY     = destPointIndex / destImageWidth;

                        // We going to find the most similar source patch to the dest patch
                        // by comparing them.
                        // Note: we don't care about the coverage state of the dest patch
                        // because it is at least partially covered. The situation when it is
                        // not covered at all is impossible, since the dest point is a part of the
                        // dest area.

                        // We store our patch in a flat array of points for simplicity.
                        // Populate dest patch array with corresponding dest image points indexes.
                        Utils.PopulatePatchPixelsIndexes(destPatchPixelsIndexesP, destPointX, destPointY, patchSize, destImageWidth, destAvailablePixelsIndexesSet, out isPatchFit);

                        // Obtain the mapped areas info with the destination area
                        // that contains the current dest point. In the associated
                        // source area we are allowed to look for the source patches.
                        MappedAreasInfo mappedAreasInfo = mappedAreasInfos.FirstOrDefault(mai => mai.DestAreaPointsIndexesSet.Contains(destPointIndex));

                        // To improve performance, we cache the value of the NNF
                        // at the current point in the local variables.
                        var nnfPos                 = destPointIndex * 2;
                        var bestSrcPointIndex      = (int)*(nnfP + nnfPos + 0);
                        var distanceToBestSrcPatch = *(nnfP + nnfPos + 1);

                        // We assume that the value of the NNF at the current
                        // point is the best so far and we have not found anything beter.
                        var isBetterSrcPatchFound = false;

                        // Now we check two close neighbours. First - horisontal, than vertical.

                        #region f(x - p) + (1, 0) & f(x - p) + (0, 1)

                        //1st iteration: f(x - p) + (1, 0)
                        //2nd iteration: f(x - p) + (0, 1)

                        int candidateSrcPointY = 0;
                        int candidateSrcPointX = 0;
                        double distance;
                        for (var offsetIndex = 0; offsetIndex < offsets.Length; offsetIndex++)
                        {
                            // Obtain the position of the neighbour.
                            // During the forward search
                            //  on the first iteration it is the neighbour at the left side
                            //  on the second iteration it is the neighbour at the top
                            // During the backward search
                            //  on the first iteration it is the neighbour at the right side
                            //  on the second iteration it is the neighbour at the bottom

                            var offset = offsets[offsetIndex];
                            var destPointNeighbourX     = destPointX - offset[0];
                            var destPointNeighbourY     = destPointY - offset[1];
                            var destPointNeighbourIndex = destPointNeighbourY * destImageWidth + destPointNeighbourX;

                            // Make sure that the neighbour point belongs to the mapping
                            if (!mappedAreasInfo.DestAreaPointsIndexesSet.Contains(destPointNeighbourIndex))
                            {
                                continue;
                            }

                            // There is a high chance that the neighbour's source patch neighbour
                            // is similar to the current dest patch. Let's check it.
                            // Obtain the position of the neighbour's source patch neighbour.
                            var neighbourNnfPos        = 2 * destPointNeighbourIndex;
                            var neighbourSrcPointIndex = (int)*(nnfP + neighbourNnfPos + 0);
                            candidateSrcPointY         = neighbourSrcPointIndex / nnfSrcWidth + offset[1];
                            candidateSrcPointX         = neighbourSrcPointIndex % nnfSrcWidth + offset[0];
                            var candidateSrcPointIndex = candidateSrcPointY * srcImageWidth + candidateSrcPointX;

                            // Make sure that the source patch resides inside the
                            // associated source area of the dest area.
                            if (!mappedAreasInfo.SrcAreaPointsIndexesSet.Contains(candidateSrcPointIndex))
                            {
                                continue;
                            }

                            // Populate source patch array with corresponding source image points indexes
                            Utils.PopulatePatchPixelsIndexes(srcPatchPixelsIndexesP, candidateSrcPointX, candidateSrcPointY, patchSize, srcImageWidth, mappedAreasInfo.SrcAreaPointsIndexesSet, out isPatchFit);

                            // Calculate distance between the patches
                            distance = patchDistanceCalculator.Calculate(destPatchPixelsIndexesP, srcPatchPixelsIndexesP, distanceToBestSrcPatch, destImagePixelsDataP, sourceImagePixelsDataP, destImage, srcImage, patchLength);

                            if (isPatchFit && distance < distanceToBestSrcPatch)
                            {
                                isBetterSrcPatchFound  = true;
                                bestSrcPointIndex      = candidateSrcPointIndex;
                                distanceToBestSrcPatch = distance;
                            }
                        }

                        #endregion

                        #region random search

                        //v0 = f(x)

                        // For the random search we need to find out
                        // the size of the window where to search
                        var srcAreaBound     = mappedAreasInfo.SrcBound;
                        var srcAreaWidth     = srcAreaBound.Width;
                        var srcAreaHeight    = srcAreaBound.Height;
                        var searchWindowSize = srcAreaHeight < srcAreaWidth ? srcAreaHeight : srcAreaWidth;
                        if (searchWindowSize > minSearchWindowSize)
                        {
                            searchWindowSize = minSearchWindowSize;
                        }

                        // Init the search radius;
                        var searchRadius  = randomSearchRadius;
                        var bestSrcPointX = bestSrcPointIndex % srcImageWidth;
                        var bestSrcPointY = bestSrcPointIndex / srcImageWidth;

                        for (i = 0; searchRadius >= 1; i++)
                        {
                            //TODO: change and measure inpact of changing call to Pow
                            searchRadius = searchWindowSize * System.Math.Pow(alpha, i);

                            isPatchFit   = false;
                            var attempts = 0;

                            while (!isPatchFit && attempts < 5)
                            {
                                attempts++;
                                candidateSrcPointX = (int)(bestSrcPointX + (rnd.NextDouble() * 2 - 1.0) * searchRadius);
                                candidateSrcPointY = (int)(bestSrcPointY + (rnd.NextDouble() * 2 - 1.0) * searchRadius);

                                // Populate source patch array with corresponding source image points indexes
                                Utils.PopulatePatchPixelsIndexes(srcPatchPixelsIndexesP, candidateSrcPointX, candidateSrcPointY, patchSize, srcImageWidth, mappedAreasInfo.SrcAreaPointsIndexesSet, out isPatchFit);
                            }

                            // Calculate distance between the patches
                            distance = patchDistanceCalculator.Calculate(destPatchPixelsIndexesP, srcPatchPixelsIndexesP, distanceToBestSrcPatch, destImagePixelsDataP, sourceImagePixelsDataP, destImage, srcImage, patchLength);

                            if (isPatchFit && distance < distanceToBestSrcPatch)
                            {
                                distanceToBestSrcPatch = distance;
                                bestSrcPointIndex      = candidateSrcPointY * srcImageWidth + candidateSrcPointX;
                                isBetterSrcPatchFound  = true;
                            }
                        }

                        #endregion

                        if (isBetterSrcPatchFound)
                        {
                            *(nnfP + nnfPos + 0) = bestSrcPointIndex;
                            *(nnfP + nnfPos + 1) = distanceToBestSrcPatch;
                        }
                    }
                }
            });
        }
Exemple #7
0
        /// <summary>
        /// Runs the random NNF initialization iteration for the associated areas of the dest and the source images.
        /// </summary>
        /// <param name="nnf">The NNF.</param>
        /// <param name="destImage">The dest image. For each patch at this image we will look for a similar one at the source image.</param>
        /// <param name="srcImage">The source image. Source of the patches for the dest image.</param>
        /// <param name="settings">The settings that control parameters of the algorithm.</param>
        /// <param name="patchDistanceCalculator">The calculator that calculates similarity of two patches. By deafult the Cie76 is used.</param>
        /// <param name="areasMapping">The areas mapping. By default whole area of the dest image is associated with the whole area of the source image.</param>
        /// <param name="destPixelsArea">Area on the dest image that actually containes pixels. By default is the area of the entire image.</param>
        /// <exception cref="ArgumentNullException">
        /// nnf
        /// or
        /// destImage
        /// or
        /// srcImage
        /// or
        /// settings
        /// or
        /// patchDistanceCalculator
        /// or
        /// areasMapping
        /// or
        /// destPixelsArea
        /// </exception>
        public unsafe void RunRandomNnfInitIteration(Nnf nnf, ZsImage destImage, ZsImage srcImage, PatchMatchSettings settings, ImagePatchDistanceCalculator patchDistanceCalculator, Area2DMap areasMapping, Area2D destPixelsArea)
        {
            if (nnf == null)
            {
                throw new ArgumentNullException(nameof(nnf));
            }
            if (destImage == null)
            {
                throw new ArgumentNullException(nameof(destImage));
            }
            if (srcImage == null)
            {
                throw new ArgumentNullException(nameof(srcImage));
            }
            if (settings == null)
            {
                throw new ArgumentNullException(nameof(settings));
            }
            if (patchDistanceCalculator == null)
            {
                throw new ArgumentNullException(nameof(patchDistanceCalculator));
            }
            if (areasMapping == null)
            {
                throw new ArgumentNullException(nameof(areasMapping));
            }
            if (destPixelsArea == null)
            {
                throw new ArgumentNullException(nameof(destPixelsArea));
            }

            const byte MaxAttempts = 32;

            var nnfdata           = nnf.GetNnfItems();
            var patchSize         = settings.PatchSize;
            var patchPointsAmount = patchSize * patchSize;
            var destImageWidth    = destImage.Width;
            var srcImageWidth     = srcImage.Width;

            // Decide on how many partitions we should divade the processing
            // of the elements.
            var partsCount = areasMapping.DestElementsCount > settings.NotDividableMinAmountElements
                ? settings.ThreadsCount
                : 1;
            var partSize = (int)(areasMapping.DestElementsCount / partsCount);

            var pixelsArea        = (areasMapping as IAreasMapping).DestArea;
            var destPointsIndexes = pixelsArea.GetPointsIndexes(destImageWidth);

            pixelsArea = pixelsArea.Intersect(destPixelsArea);
            var destAvailablePixelsIndexes = pixelsArea.GetPointsIndexes(destImageWidth);
            var mappings = areasMapping.ExtractMappedAreasInfo(destImageWidth, srcImageWidth);

            //for (int partIndex = 0; partIndex < partsCount; partIndex++)
            Parallel.For(0, partsCount, partIndex =>
            {
                // Colne mapping to avoid conflicts in multithread
                //var destPointsIndexesSet = new HashSet<int>(destPointsIndexes);
                var destAvailablePixelsIndexesSet = new HashSet <int>(destAvailablePixelsIndexes);

                var mappedAreasInfos = new MappedAreasInfo[mappings.Length];
                for (int i = 0; i < mappings.Length; i++)
                {
                    mappedAreasInfos[i] = mappings[i].Clone();
                }

                #region Variables definition
                int destPointIndex;
                int destPointX;
                int destPointY;

                int srcPointIndex = 0;
                int srcPointX;
                int srcPointY;

                double distance;
                int nnfPos;
                byte attemptsCount = 0;
                #endregion

                var rnd = new FastRandom();

                var firstPointIndex = partIndex * partSize;
                var lastPointIndex  = firstPointIndex + partSize - 1;
                if (partIndex == partsCount - 1)
                {
                    lastPointIndex = areasMapping.DestElementsCount - 1;
                }
                if (lastPointIndex > areasMapping.DestElementsCount)
                {
                    lastPointIndex = areasMapping.DestElementsCount - 1;
                }

                // Init the dest & source patch
                var destPatchPointIndexes = new int[patchPointsAmount];
                var srcPatchPointIndexes  = new int[patchPointsAmount];
                bool isPatchFit           = false;

                fixed(double *nnfdataP            = nnfdata)
                fixed(int *dstPointIndexesP       = destPointsIndexes)
                fixed(int *srcPatchPointIndexesP  = srcPatchPointIndexes)
                fixed(int *destPatchPointIndexesP = destPatchPointIndexes)
                fixed(double *destPixelsDataP     = destImage.PixelsData)
                fixed(double *sourcePixelsDataP   = srcImage.PixelsData)
                {
                    for (var pointIndex = firstPointIndex; pointIndex <= lastPointIndex; pointIndex++)
                    {
                        destPointIndex = *(dstPointIndexesP + pointIndex);
                        destPointX     = destPointIndex % destImageWidth;
                        destPointY     = destPointIndex / destImageWidth;

                        Utils.PopulatePatchPixelsIndexes(destPatchPointIndexesP, destPointX, destPointY, patchSize, destImageWidth, destAvailablePixelsIndexesSet, out isPatchFit);

                        nnfPos = destPointIndex * 2;

                        // Obtain the source area associated with the destination area
                        // to which the current dest point belongs to.
                        // In that area we are allowed to look for the source patches.
                        MappedAreasInfo mappedAreasInfo = mappedAreasInfos.FirstOrDefault(mai => mai.DestAreaPointsIndexesSet.Contains(destPointIndex));

                        isPatchFit          = false;
                        attemptsCount       = 0;
                        var srcPointsAmount = mappedAreasInfo.SrcAreaPointsIndexes.Length;
                        while (!isPatchFit && attemptsCount < MaxAttempts && attemptsCount < srcPointsAmount)
                        {
                            srcPointIndex = mappedAreasInfo.SrcAreaPointsIndexes[rnd.Next(srcPointsAmount)];
                            srcPointX     = srcPointIndex % srcImageWidth;
                            srcPointY     = srcPointIndex / srcImageWidth;

                            Utils.PopulatePatchPixelsIndexes(srcPatchPointIndexesP, srcPointX, srcPointY, patchSize, srcImageWidth, mappedAreasInfo.SrcAreaPointsIndexesSet, out isPatchFit);
                            attemptsCount++;
                        }

                        distance = patchDistanceCalculator.Calculate(destPatchPointIndexesP, srcPatchPointIndexesP, double.MaxValue,
                                                                     destPixelsDataP, sourcePixelsDataP, destImage, srcImage, patchPointsAmount);

                        *(nnfdataP + nnfPos + 0) = srcPointIndex;
                        *(nnfdataP + nnfPos + 1) = distance;
                    }
                }
            });
        }