private void GenerateDecLevelsAndFragments(Bitmap image,
                                                   out LevelImage origLevelImage, out LevelImage[] decLevelImages)
        {
            decLevelImages = new LevelImage[DecLevelsCount];

            origLevelImage = new LevelImage(image, Blur, BlurKernelSize, BlurSigma);
            double blockIncX = BlockIncRatioX * BlockWidth;

            if (blockIncX < 1)
            {
                blockIncX = 1;
            }
            double blockIncY = BlockIncRatioY * BlockHeight;

            if (blockIncY < 1)
            {
                blockIncY = 1;
            }
            origLevelImage.PrepareFragments(Blur, blockIncX, blockIncY, BlockWidth, BlockHeight);

            double decBlockIncX = DecBlockIncXRatio * BlockWidth;

            if (decBlockIncX < 1)
            {
                decBlockIncX = 1;
            }
            double decBlockIncY = DecBlockIncYRatio * BlockHeight;

            if (decBlockIncY < 1)
            {
                decBlockIncY = 1;
            }

            for (int i = 0; i < DecLevelsCount; i++)
            {
                double coef      = 1 / (Math.Pow(DecZoomCoef, (i + 1)));
                int    newWidth  = (int)Math.Round(image.Width * coef);
                int    newHeight = (int)Math.Round(image.Height * coef);
                image             = Utils.ChangeSize(image, newWidth, newHeight);
                decLevelImages[i] = new LevelImage(image, Blur, BlurKernelSize, BlurSigma);
                decLevelImages[i].PrepareFragments(Blur, decBlockIncX, decBlockIncY, BlockWidth, BlockHeight);
            }
        }
        private Bitmap ReplaceFragments(LevelImage origLevelImage, LevelImage[] decLevelImages, List <FragmentsMapping> mapping)
        {
            int width     = origLevelImage.Image.Width;
            int height    = origLevelImage.Image.Height;
            var resultRGB = new byte[width * height * Utils.ColorComponentsCount];

            if (!Parallelization)
            {
                for (int y = 0; y < height; y++)
                {
                    ReplaceFragmentsPart(origLevelImage, decLevelImages, mapping, width, height, y, resultRGB);
                }
            }
            else
            {
                Parallel.For(0, height, y => ReplaceFragmentsPart(origLevelImage, decLevelImages, mapping, width, height, y, resultRGB));
            }

            var resultImage = Utils.BytesArrayToBitmap(resultRGB, width, height);

            return(resultImage);
        }
        private void KdSearch(int incLevelNumber, LevelImage levelImage, int startInd, int endInd, List <FragmentsMapping> mapping, int[] fragmentLevelsIndexes)
        {
            var fragments = levelImage.Fragments;
            var indsDists = AnnWrapper.AnnKdSearch(levelImage.FragmentYCompsToDoubleArray(startInd, endInd), (endInd - startInd), levelImage.Fragments[0].Size);

            for (int i = 0; i < indsDists.Count; i++)
            {
                var fragmentMapping = new FragmentsMapping
                {
                    LevelIndex = FindLevelIndex(indsDists[i].Item1, fragmentLevelsIndexes),
                    OrigIndex  = startInd + i,
                    Distance   = indsDists[i].Item2
                };
                fragmentMapping.DecIndex = indsDists[i].Item1 - fragmentLevelsIndexes[fragmentMapping.LevelIndex];
                mapping.Add(fragmentMapping);
            }

            if (FragmentFounded != null)
            {
                FragmentFounded(this, new FragmentEventArgs(incLevelNumber, IncLevelsCount, endInd, fragments.Count));
            }
        }
        private List<FragmentsMapping> SearchCorrespondingFragments(LevelImage origLevelImage, LevelImage[] decLevelImages)
        {
            int[] fragmentLevelsIndexes;
            var fragmentsYComps = LevelFragmentPointsToArray(decLevelImages, out fragmentLevelsIndexes);
            AnnWrapper.InitKdTree(fragmentsYComps,
                fragmentsYComps.Length / (BlockWidth * BlockHeight),
                BlockWidth * BlockHeight);

            var mapping = new List<FragmentsMapping>();
            if (!Parallelization)
            {
                for (int i = 0; i < origLevelImage.Fragments.Count; i++)
                    KdSearch(origLevelImage.Fragments, i, mapping, fragmentLevelsIndexes);
            }
            else
                Parallel.For(0, origLevelImage.Fragments.Count, i => KdSearch(origLevelImage.Fragments, i, mapping, fragmentLevelsIndexes));

            AnnWrapper.AnnFree();

            return mapping;
        }
        private Bitmap ReplaceFragments(LevelImage origLevelImage, LevelImage[] decLevelImages, LevelImage[] incLevelImages, List<FragmentsMapping> mapping)
        {
            var resultImage = incLevelImages[0].Image;
            var origImage = origLevelImage.Image;
            var resultBlockWidth = (double)BlockWidth / origImage.Width * resultImage.Width;
            var resultBlockHeight = (double)BlockHeight / origImage.Height * resultImage.Height;

            var orderedByDistances = mapping.OrderBy(m => m.Distance);
            var minDist = orderedByDistances.First();
            var maxDist = orderedByDistances.Last();
            var avg = mapping.Average(m => m.Distance);

            using (var graphics = Graphics.FromImage(resultImage))
            {
                int decReplaceCount = 0;
                int origReplaceCount = 0;
                foreach (var fragmentMapping in mapping)
                {
                    PointD origImageFragmentOffset, decImageFragmentOffset;
                    if (fragmentMapping.Distance < ReplaceDistance)
                    {
                        origImageFragmentOffset = origLevelImage.Fragments[fragmentMapping.OrigIndex].Offset;
                        decImageFragmentOffset = decLevelImages[fragmentMapping.LevelIndex].Fragments[fragmentMapping.DecIndex].Offset;
                        decReplaceCount++;
                    }
                    else
                    {
                        origImageFragmentOffset = origLevelImage.Fragments[fragmentMapping.OrigIndex].Offset;
                        decImageFragmentOffset = origLevelImage.Fragments[fragmentMapping.OrigIndex].Offset;
                        origReplaceCount++;
                    }
                    graphics.DrawImage(origImage, new Rectangle(
                            (int)Math.Round(origImageFragmentOffset.X * resultImage.Width),
                            (int)Math.Round(origImageFragmentOffset.Y * resultImage.Height),
                            (int)Math.Round(resultBlockWidth),
                            (int)Math.Round(resultBlockHeight)),
                            (float)(decImageFragmentOffset.X * origImage.Width),
                            (float)(decImageFragmentOffset.Y * origImage.Height),
                            (float)(BlockWidth),
                            (float)(BlockHeight),
                            GraphicsUnit.Pixel);
                }
            }

            return resultImage;
        }
        private void GenerateLevelsAndFragments(out LevelImage origLevelImage, out LevelImage[] decLevelImages, out LevelImage[] incLevelImages)
        {
            decLevelImages = new LevelImage[DecLevelsCount];
            incLevelImages = new LevelImage[IncLevelsCount];
            origLevelImage = new LevelImage(_inputBitmap);

            for (int i = 0; i < DecLevelsCount; i++)
            {
                double coef = 1 / (Math.Pow(DecZoomCoef, (i + 1)));
                int newWidth = (int)Math.Round(_inputBitmap.Width * coef);
                int newHeight = (int)Math.Round(_inputBitmap.Height * coef);
                decLevelImages[i] = new LevelImage(_inputBitmap, newWidth, newHeight);
                decLevelImages[i].PrepareFragments(DecBlockIncX, DecBlockIncY, BlockWidth, BlockHeight);
            }

            for (int i = 0; i < IncLevelsCount; i++)
            {
                double coef = ZoomCoef * (i + 1) / IncLevelsCount;
                int newWidth = (int)Math.Round(_inputBitmap.Width * coef);
                int newHeight = (int)Math.Round(_inputBitmap.Height * coef);
                incLevelImages[i] = new LevelImage(_inputBitmap, newWidth, newHeight);
            }

            origLevelImage.PrepareFragments(BlockWidth, BlockHeight, BlockWidth, BlockHeight);
        }
 private static double[] LevelFragmentPointsToArray(LevelImage[] levelImages, out int[] fragmentLevelsIndexes)
 {
     fragmentLevelsIndexes = new int[levelImages.Length];
     List<double> result = new List<double>(levelImages.Aggregate(0, (i, levelImage) => i += levelImage.Fragments.Count));
     int ind = 0;
     for (int i = 0; i < levelImages.Length; i++)
     {
         fragmentLevelsIndexes[i] = ind;
         var yComps = levelImages[i].FragmentYCompsToDoubleArray();
         result.AddRange(yComps);
         ind += yComps.Length / (levelImages[i].Fragments[0].YComponents.Length);
     }
     return result.ToArray();
 }
        private void ReplaceFragmentsPart(LevelImage origLevelImage, LevelImage[] decLevelImages, List <FragmentsMapping> mapping, int width, int height, int y, byte[] resultRGB)
        {
            double blockIncX        = BlockIncRatioX * BlockWidth;
            double blockIncY        = BlockIncRatioY * BlockHeight;
            int    fragmentsInLineX = (int)((width - BlockWidth) / blockIncX);
            int    fragmentsInLineY = (int)((height - BlockHeight) / blockIncY);
            int    overlapsCountX   = (int)Math.Round(BlockWidth / blockIncX);
            int    overlapsCountY   = (int)Math.Round(BlockHeight / blockIncY);
            double blockWidth2      = BlockWidth * 0.5;
            double blockHeight2     = BlockHeight * 0.5;

            for (int x = 0; x < width; x++)
            {
                int ix = (int)(x / blockIncX);
                int iy = (int)(y / blockIncY);

                var overlapBlockNumbers = new List <int>(overlapsCountX + overlapsCountY);
                var distances           = new List <double>(overlapsCountX + overlapsCountY);
                for (int iy1 = 0; iy1 < overlapsCountY; iy1++)
                {
                    int blockNumberY = iy - iy1;
                    if (blockNumberY >= 0 && blockNumberY < fragmentsInLineY)
                    {
                        for (int ix1 = 0; ix1 < overlapsCountX; ix1++)
                        {
                            int blockNumberX = ix - ix1;
                            if (blockNumberX >= 0 && blockNumberX < fragmentsInLineX)
                            {
                                var overlapBlockNumber = blockNumberY * fragmentsInLineX + blockNumberX;

                                var blockOffset  = origLevelImage.Fragments[overlapBlockNumber].Offset;
                                var blockCenterX = blockOffset.X * width + blockWidth2;
                                var blockCenterY = blockOffset.Y * height + blockHeight2;

                                double distance;
                                if (Math.Abs(x - blockCenterX) > Math.Abs(y - blockCenterY))
                                {
                                    distance = Math.Abs(x - blockCenterX) / blockWidth2;
                                }
                                else
                                {
                                    distance = Math.Abs(y - blockCenterY) / blockHeight2;
                                }

                                overlapBlockNumbers.Add(overlapBlockNumber);
                                if (distance < 0)
                                {
                                    distance = 0;
                                }
                                else if (distance > 1)
                                {
                                    distance = 1;
                                }
                                distances.Add(1.0 - distance);
                            }
                        }
                    }
                }

                var invDistVectorLength = distances.Aggregate(0.0, (sum, d) => sum += d);
                if (invDistVectorLength == 0.0)
                {
                    invDistVectorLength = 1.0;
                    for (int i = 0; i < distances.Count; i++)
                    {
                        distances[i] = 1.0 / distances.Count;
                    }
                }
                else
                {
                    invDistVectorLength = 1.0 / invDistVectorLength;
                }

                double r = 0, g = 0, b = 0;
                if (overlapBlockNumbers.Count != 0)
                {
                    for (int i = 0; i < overlapBlockNumbers.Count; i++)
                    {
                        var blockNumber     = overlapBlockNumbers[i];
                        var fragmentMapping = mapping[blockNumber];

                        PointD origImageFragmentOffset, decImageFragmentOffset;
                        origImageFragmentOffset = origLevelImage.Fragments[fragmentMapping.OrigIndex].Offset;

                        int decReplaceCount  = 0;
                        int origReplaceCount = 0;
                        if (fragmentMapping.Distance < ReplaceDistance && fragmentMapping.DecIndex < decLevelImages[fragmentMapping.LevelIndex].Fragments.Count)
                        {
                            decImageFragmentOffset = decLevelImages[fragmentMapping.LevelIndex].Fragments[fragmentMapping.DecIndex].Offset;
                            decReplaceCount++;
                        }
                        else
                        {
                            decImageFragmentOffset = origLevelImage.Fragments[blockNumber].Offset;
                            origReplaceCount++;
                        }

                        double dx   = x - origImageFragmentOffset.X * width;
                        double dy   = y - origImageFragmentOffset.Y * height;
                        int    srcX = (int)Math.Round(decImageFragmentOffset.X * width + dx);
                        int    srcY = (int)Math.Round(decImageFragmentOffset.Y * height + dy);

                        int ind = (srcY * width + srcX) * Utils.ColorComponentsCount;
                        r += origLevelImage.RGB[ind + Utils.RedOffset] * distances[i] * invDistVectorLength;
                        g += origLevelImage.RGB[ind + Utils.GreenOffset] * distances[i] * invDistVectorLength;
                        b += origLevelImage.RGB[ind + Utils.BlurOffset] * distances[i] * invDistVectorLength;
                    }
                }
                else
                {
                    int ind = (y * width + x) * Utils.ColorComponentsCount;
                    r = origLevelImage.RGB[ind + Utils.RedOffset];
                    g = origLevelImage.RGB[ind + Utils.GreenOffset];
                    b = origLevelImage.RGB[ind + Utils.BlurOffset];
                }

                int resultInd = (y * width + x) * Utils.ColorComponentsCount;
                resultRGB[resultInd + Utils.RedOffset]   = (byte)Math.Round(r);
                resultRGB[resultInd + Utils.GreenOffset] = (byte)Math.Round(g);
                resultRGB[resultInd + Utils.BlurOffset]  = (byte)Math.Round(b);
                resultRGB[resultInd + Utils.AlphaOffset] = 255;
            }
        }
        private List <FragmentsMapping> SearchCorrespondingFragments(int incLevelNumber, LevelImage origLevelImage, LevelImage[] decLevelImages)
        {
            int[] fragmentLevelsIndexes;
            var   fragmentsYComps = LevelFragmentPointsToArray(decLevelImages, out fragmentLevelsIndexes);

            AnnWrapper.InitKdTree(fragmentsYComps,
                                  fragmentsYComps.Length / (BlockWidth * BlockHeight),
                                  BlockWidth * BlockHeight);

            var mapping = new List <FragmentsMapping>();

            int pointsCount = 1000;
            int queryCount  = (origLevelImage.Fragments.Count + pointsCount - 1) / pointsCount;

            for (int i = 0; i < queryCount; i++)
            {
                KdSearch(incLevelNumber, origLevelImage,
                         i * pointsCount, Math.Min((i + 1) * pointsCount, origLevelImage.Fragments.Count),
                         mapping, fragmentLevelsIndexes);
            }

            AnnWrapper.AnnFree();

            return(mapping);
        }