public FractalCompressionResult Compress(CustomImage image)
        {
            //Validate the image and the params
            if (image.Width % _options.RSquareSize > 0 || image.Height % _options.RSquareSize > 0 || _options.DSquareSize % _options.RSquareSize > 0)
            {
                throw new InvalidOperationException("The compression options specified can't compress this image as the R squares size is not a divisor of height and width. Ri Sizes must be divisor of Di sizes too.");
            }

            object finalResultLock = new object();
            int    rXCount         = image.Width / _options.RSquareSize;      //Number of R Squares in X
            int    rYCount         = image.Height / _options.RSquareSize;     //Number of R Squares in Y
            int    totalRCount     = rXCount * rYCount;
            int    maxDxPos        = image.Width - _options.DSquareSize + 1;  // POP: Ici, le coin supérieur droit du carré ne se rend pas au coin supérieur droit de l'image
            int    maxDyPos        = image.Height - _options.DSquareSize + 1; // POP: même commentaire mais avec le coin inférieur gauche
            byte   dSize           = _options.DSquareSize;
            byte   rSize           = _options.RSquareSize;
            int    n                      = _options.RSquareSize * _options.RSquareSize;
            double drRatio                = (double)_options.RSquareSize / (double)_options.DSquareSize;
            int    drInverseRatio         = _options.DSquareSize / _options.RSquareSize;
            double pixelAverageSquareSize = (double)drInverseRatio * (double)drInverseRatio;
            var    translateMatrixes      = GetTranslateMatrixes(_options.RSquareSize);
            int    transformationsCount   = _transformMatrixes.Length;
            var    transformations        = new FractalCompressionResult.FractalTransformation[totalRCount, 3]; //Transformations (2 dimension to keep transformations for each ri in each color component)
            int    rx;
            double finalRmsY = Double.MaxValue, finalRmsI = Double.MaxValue, finalRmsQ = Double.MaxValue,
                   bestBrightnessY = 0d, bestContrastY = 0d,
                   bestBrightnessI = 0d, bestContrastI = 0d,
                   bestBrightnessQ = 0d, bestContrastQ = 0d;
            var rawImgData = image.ImageData;
            var imgDataY   = new double[image.Width, image.Height];
            var imgDataI   = new double[image.Width, image.Height];
            var imgDataQ   = new double[image.Width, image.Height];

            double[, ][][,] tempDiY   = new double[maxDxPos, maxDyPos][][, ];
            double[, ][][,] tempDiI   = new double[maxDxPos, maxDyPos][][, ];
            double[, ][][,] tempDiQ   = new double[maxDxPos, maxDyPos][][, ];
            double[,] sumsOfAY        = new double[maxDxPos, maxDyPos];
            double[,] sumsOfAYSquared = new double[maxDxPos, maxDyPos];
            double[,] sumsOfAI        = new double[maxDxPos, maxDyPos];
            double[,] sumsOfAISquared = new double[maxDxPos, maxDyPos];
            double[,] sumsOfAQ        = new double[maxDxPos, maxDyPos];
            double[,] sumsOfAQSquared = new double[maxDxPos, maxDyPos];

            //Fill imgData Y, I and Q
            Parallel.For(0, image.Width, dx =>
            {
                for (int dy = 0; dy < image.Height; dy++)
                {
                    imgDataY[dx, dy] = rawImgData[dx, dy].YNormalized;
                    imgDataI[dx, dy] = rawImgData[dx, dy].IDownSampledAndNormalized;
                    imgDataQ[dx, dy] = rawImgData[dx, dy].QDownSampledAndNormalized;
                }
            });

            //Average pixels of each Di to size of Ri
            Parallel.For(0, maxDxPos, dx =>
            {
                int i, x, y, j, k, newX, newY, indexX = 0, indexY = 0;
                double[,] tempTransformMatrix;
                double[] tempTranslateMatrix;
                double[,] tempPixelY1 = null;
                double[,] tempPixelI1 = null;
                double[,] tempPixelQ1 = null;
                double tempSumY       = 0d, tempSumI = 0d, tempSumQ = 0d,
                tempAveragePixelY     = 0d, tempAveragePixelI = 0d, tempAveragePixelQ = 0d;

                for (int dy = 0; dy < maxDyPos; dy++)
                {
                    tempDiY[dx, dy] = new double[transformationsCount][, ];
                    tempDiI[dx, dy] = new double[transformationsCount][, ];
                    tempDiQ[dx, dy] = new double[transformationsCount][, ];

                    for (i = 0; i < transformationsCount; i++)
                    {
                        for (x = 0; x < dSize; x += drInverseRatio) //Increment of inverse ratio to get only parts of Di
                        {
                            for (y = 0; y < dSize; y += drInverseRatio)
                            {
                                //Get transformation
                                tempTransformMatrix = _transformMatrixes[i];
                                tempTranslateMatrix = translateMatrixes[i];

                                //Average pixels on first transform
                                if (i == 0)
                                {
                                    //Reset temp variables
                                    tempSumY    = 0;
                                    tempSumI    = 0;
                                    tempSumQ    = 0;
                                    tempPixelY1 = new double[rSize, rSize];
                                    tempPixelI1 = new double[rSize, rSize];
                                    tempPixelQ1 = new double[rSize, rSize];

                                    //Average pixels
                                    for (j = 0; j < drInverseRatio; j++)
                                    {
                                        for (k = 0; k < drInverseRatio; k++)
                                        {
                                            tempSumY += imgDataY[dx + x + j, dy + y + k];
                                            tempSumI += imgDataI[dx + x + j, dy + y + k];
                                            tempSumQ += imgDataQ[dx + x + j, dy + y + k];
                                        }
                                    }

                                    //Calculating average Pixel Components
                                    tempAveragePixelY = tempSumY > 0 ? tempSumY / pixelAverageSquareSize : 0d;
                                    tempAveragePixelI = tempSumI > 0 ? tempSumI / pixelAverageSquareSize : 0d;
                                    tempAveragePixelQ = tempSumQ > 0 ? tempSumQ / pixelAverageSquareSize : 0d;

                                    //Sum of "a" and "a^2" for future RMS calculation
                                    sumsOfAY[dx, dy]        += tempAveragePixelY;
                                    sumsOfAI[dx, dy]        += tempAveragePixelI;
                                    sumsOfAQ[dx, dy]        += tempAveragePixelQ;
                                    sumsOfAYSquared[dx, dy] += tempAveragePixelY * tempAveragePixelY;
                                    sumsOfAISquared[dx, dy] += tempAveragePixelI * tempAveragePixelI;
                                    sumsOfAQSquared[dx, dy] += tempAveragePixelQ * tempAveragePixelQ;
                                }

                                newX = (byte)(tempTransformMatrix[0, 0] * (x / drInverseRatio) + tempTransformMatrix[0, 1] * (y / drInverseRatio) + tempTranslateMatrix[0]);
                                newY = (byte)(tempTransformMatrix[1, 0] * (x / drInverseRatio) + tempTransformMatrix[1, 1] * (y / drInverseRatio) + tempTranslateMatrix[1]);

                                indexX = (int)(newX * drRatio);
                                indexY = (int)(newY * drRatio);

                                //Store pixel components for transformed Di (Fi)
                                tempPixelY1[indexX, indexY] = tempAveragePixelY;
                                tempPixelI1[indexX, indexY] = tempAveragePixelI;
                                tempPixelQ1[indexX, indexY] = tempAveragePixelQ;

                                //Store pixels of Di
                                tempDiY[dx, dy][i] = tempPixelY1;
                                tempDiI[dx, dy][i] = tempPixelI1;
                                tempDiQ[dx, dy][i] = tempPixelQ1;
                            }
                        }
                    }
                }
            });

            //Find the Transformations to do...
            //For each Ri
            for (rx = 0; rx < rXCount; rx++)
            {
                //Parallel execution of for loop, so it runs faster.
                Parallel.For(0, rYCount, ry =>
                {
                    //Calculate the sums of components in Ri for future RMS metric calculation
                    double sumOfBY        = 0d;
                    double sumOfBYSquared = 0d;
                    double sumOfBI        = 0d;
                    double sumOfBISquared = 0d;
                    double sumOfBQ        = 0d;
                    double sumOfBQSquared = 0d;
                    double sumOfAAndBY;
                    double sumOfAAndBI;
                    double sumOfAAndBQ;
                    int x, y, indexX, indexY, dx, dy, i,
                    bestRmsYIndexX       = -1, bestRmsYIndexY = -1, bestRmsYTransformationIndex = -1,
                    bestRmsIIndexX       = -1, bestRmsIIndexY = -1, bestRmsITransformationIndex = -1,
                    bestRmsQIndexX       = -1, bestRmsQIndexY = -1, bestRmsQTransformationIndex = -1;
                    double[,] tempPixelY = null;
                    double[,] tempPixelI = null;
                    double[,] tempPixelQ = null;
                    double tempContrastY, tempBrightnessY,
                    tempContrastI, tempBrightnessI,
                    tempContrastQ, tempBrightnessQ,
                    tempRmsY, tempRmsI, tempRmsQ;

                    for (x = 0; x < rSize; x++)
                    {
                        for (y = 0; y < rSize; y++)
                        {
                            //Sum of "b" and of "b^2"
                            indexX          = rx * rSize + x;
                            indexY          = ry * rSize + y;
                            sumOfBY        += imgDataY[indexX, indexY];
                            sumOfBI        += imgDataI[indexX, indexY];
                            sumOfBQ        += imgDataQ[indexX, indexY];
                            sumOfBYSquared += imgDataY[indexX, indexY] * imgDataY[indexX, indexY];
                            sumOfBISquared += imgDataI[indexX, indexY] * imgDataI[indexX, indexY];
                            sumOfBQSquared += imgDataQ[indexX, indexY] * imgDataQ[indexX, indexY];
                        }
                    }

                    //For Each Di
                    using (var timer = new CustomTimer("Transformation"))
                        for (dx = 0; dx < maxDxPos; dx++)
                        {
                            for (dy = 0; dy < maxDyPos; dy++)
                            {
                                //For Each possible transformations
                                for (i = 0; i < transformationsCount; i++)
                                {
                                    sumOfAAndBY = 0d;
                                    sumOfAAndBI = 0d;
                                    sumOfAAndBQ = 0d;
                                    tempPixelY  = tempDiY[dx, dy][i];
                                    tempPixelI  = tempDiI[dx, dy][i];
                                    tempPixelQ  = tempDiQ[dx, dy][i];

                                    //Compute sums necessary for calculating RMS metric
                                    for (x = 0; x < rSize; x++)
                                    {
                                        for (y = 0; y < rSize; y++)
                                        {
                                            //Calculate indexX and indexY
                                            indexX = dx + x;
                                            indexY = dy + y;

                                            //Sum of "a*b"
                                            sumOfAAndBY += tempPixelY[x, y] * imgDataY[indexX, indexY];
                                            sumOfAAndBI += tempPixelI[x, y] * imgDataI[indexX, indexY];
                                            sumOfAAndBQ += tempPixelQ[x, y] * imgDataQ[indexX, indexY];
                                        }
                                    }

                                    //Compute contrast, brightness and RMS metric
                                    tempContrastY = (n * sumOfAAndBY - sumsOfAY[dx, dy] * sumOfBY) / (n * sumsOfAYSquared[dx, dy] - Math.Pow(sumsOfAY[dx, dy], 2));
                                    tempContrastI = (n * sumOfAAndBI - sumsOfAI[dx, dy] * sumOfBI) / (n * sumsOfAISquared[dx, dy] - Math.Pow(sumsOfAI[dx, dy], 2));
                                    tempContrastQ = (n * sumOfAAndBQ - sumsOfAQ[dx, dy] * sumOfBQ) / (n * sumsOfAQSquared[dx, dy] - Math.Pow(sumsOfAQ[dx, dy], 2));

                                    tempBrightnessY = (1d / n) * (sumOfBY - tempContrastY * sumsOfAY[dx, dy]);
                                    tempBrightnessI = (1d / n) * (sumOfBI - tempContrastI * sumsOfAI[dx, dy]);
                                    tempBrightnessQ = (1d / n) * (sumOfBQ - tempContrastQ * sumsOfAQ[dx, dy]);

                                    tempRmsY = (1d / n) * (sumOfBYSquared + tempContrastY * (tempContrastY * sumsOfAYSquared[dx, dy] - 2 * sumOfAAndBY + 2 * tempBrightnessY * sumsOfAY[dx, dy]) + tempBrightnessY * (n * tempBrightnessY - 2 * sumOfBY));
                                    tempRmsI = (1d / n) * (sumOfBISquared + tempContrastI * (tempContrastI * sumsOfAISquared[dx, dy] - 2 * sumOfAAndBI + 2 * tempBrightnessI * sumsOfAI[dx, dy]) + tempBrightnessI * (n * tempBrightnessI - 2 * sumOfBI));
                                    tempRmsQ = (1d / n) * (sumOfBQSquared + tempContrastQ * (tempContrastQ * sumsOfAQSquared[dx, dy] - 2 * sumOfAAndBQ + 2 * tempBrightnessQ * sumsOfAQ[dx, dy]) + tempBrightnessQ * (n * tempBrightnessQ - 2 * sumOfBQ));

                                    lock (finalResultLock)
                                    {
                                        //For each component, keep best transformation
                                        if (finalRmsY > tempRmsY)
                                        {
                                            finalRmsY      = tempRmsY;
                                            bestRmsYIndexX = dx;
                                            bestRmsYIndexY = dy;
                                            bestRmsYTransformationIndex = i;
                                            bestContrastY   = tempContrastY;
                                            bestBrightnessY = tempBrightnessY;
                                        }

                                        if (finalRmsI > tempRmsI)
                                        {
                                            finalRmsI      = tempRmsI;
                                            bestRmsIIndexX = dx;
                                            bestRmsIIndexY = dy;
                                            bestRmsITransformationIndex = i;
                                            bestContrastI   = tempContrastI;
                                            bestBrightnessI = tempBrightnessI;
                                        }

                                        if (finalRmsQ > tempRmsQ)
                                        {
                                            finalRmsQ      = tempRmsQ;
                                            bestRmsQIndexX = dx;
                                            bestRmsQIndexY = dy;
                                            bestRmsQTransformationIndex = i;
                                            bestContrastQ   = tempContrastQ;
                                            bestBrightnessQ = tempBrightnessQ;
                                        }
                                    }
                                }
                            }
                        }

                    //Save Di retained for Ri
                    lock (finalResultLock)
                    {
                        transformations[ry * rXCount + rx, 0] = new FractalCompressionResult.FractalTransformation(bestRmsYIndexX, bestRmsYIndexY, (byte)bestBrightnessY, (byte)bestContrastY, (byte)bestRmsYTransformationIndex);
                        transformations[ry * rXCount + rx, 1] = new FractalCompressionResult.FractalTransformation(bestRmsIIndexX, bestRmsIIndexY, (byte)bestBrightnessI, (byte)bestContrastI, (byte)bestRmsITransformationIndex);
                        transformations[ry * rXCount + rx, 2] = new FractalCompressionResult.FractalTransformation(bestRmsQIndexX, bestRmsQIndexY, (byte)bestBrightnessQ, (byte)bestContrastQ, (byte)bestRmsQTransformationIndex);
                    }
                });
            }

            return(new FractalCompressionResult(transformations, (byte)Math.Ceiling(Math.Log(Math.Max(maxDxPos, maxDyPos), 2)), (short)rXCount));
        }
Beispiel #2
0
        /// <summary>
        /// Compress image.
        /// </summary>
        /// <param name="image">Image to compress.</param>
        /// <returns>Result of the compression.</returns>
        public FractalCompressionResult Compress(CustomImage image)
        {
            //Validate the image and the params
            if (image.Width % _options.RSquareSize > 0 || image.Height % _options.RSquareSize > 0 || _options.DSquareRatio <= 1)
            {
                throw new InvalidOperationException("The compression options specified can't compress this image as the R squares size is not a divisor of height and width. Ri Sizes must be divisor of Di sizes too.");
            }

            object finalResultLock = new object();
            int    rXCount         = image.Width / _options.RSquareSize;  //Number of R Squares in X
            int    rYCount         = image.Height / _options.RSquareSize; //Number of R Squares in Y
            int    totalRCount     = rXCount * rYCount;
            byte   dSquareSize     = Convert.ToByte(_options.RSquareSize * _options.DSquareRatio);
            int    maxDxPos        = image.Width - dSquareSize + 1;
            int    maxDyPos        = image.Height - dSquareSize + 1;
            byte   dSize           = dSquareSize;
            byte   rSize           = _options.RSquareSize;
            int    n                      = _options.RSquareSize * _options.RSquareSize;
            double drRatio                = (double)_options.RSquareSize / (double)dSquareSize;
            int    drInverseRatio         = dSquareSize / _options.RSquareSize;
            double pixelAverageSquareSize = (double)drInverseRatio * (double)drInverseRatio;
            var    translateMatrixes      = GetTranslateMatrixes(Convert.ToSByte(rSize));
            int    transformationsCount   = _transformMatrixes.Length;
            var    transformations        = new FractalCompressionResult.FractalTransformation[totalRCount, 3]; //Transformations (2 dimension to keep transformations for each ri in each color component)
            int    rx;
            var    rawImgData = image.ImageData;
            var    imgDataY   = new byte[image.Width, image.Height];
            var    imgDataI   = new short[image.Width, image.Height];
            var    imgDataQ   = new short[image.Width, image.Height];

            byte[, ][][,] tempDiY  = new byte[maxDxPos, maxDyPos][][, ];
            short[, ][][,] tempDiI = new short[maxDxPos, maxDyPos][][, ];
            short[, ][][,] tempDiQ = new short[maxDxPos, maxDyPos][][, ];
            int[,] sumsOfDY        = new int[maxDxPos, maxDyPos];
            int[,] sumsOfDYSquared = new int[maxDxPos, maxDyPos];
            int[,] sumsOfDI        = new int[maxDxPos, maxDyPos];
            int[,] sumsOfDISquared = new int[maxDxPos, maxDyPos];
            int[,] sumsOfDQ        = new int[maxDxPos, maxDyPos];
            int[,] sumsOfDQSquared = new int[maxDxPos, maxDyPos];

            //Fill imgData Y, I and Q
            Parallel.For(0, image.Width, dx =>
            {
                for (int dy = 0; dy < image.Height; dy++)
                {
                    imgDataY[dx, dy] = rawImgData[dx, dy].Y;
                    imgDataI[dx, dy] = rawImgData[dx, dy].I;
                    imgDataQ[dx, dy] = rawImgData[dx, dy].Q;
                }
            });

            //Average pixels of each Di to size of Ri
            Parallel.For(0, maxDxPos, dx =>
            {
                int i, x, y, j, k, newX, newY, indexX = 0, indexY = 0;
                sbyte[,] tempTransformMatrix;
                sbyte[] tempTranslateMatrix;
                byte[,] tempPixelY      = null;
                short[,] tempPixelI     = null;
                short[,] tempPixelQ     = null;
                int tempSumY            = 0, tempSumI = 0, tempSumQ = 0;
                byte tempAveragePixelY  = 0;
                short tempAveragePixelI = 0, tempAveragePixelQ = 0;
                byte[,] tempDY          = new byte[rSize, rSize];
                short[,] tempDI         = new short[rSize, rSize];
                short[,] tempDQ         = new short[rSize, rSize];

                for (int dy = 0; dy < maxDyPos; dy++)
                {
                    tempDiY[dx, dy] = new byte[transformationsCount][, ];
                    tempDiI[dx, dy] = new short[transformationsCount][, ];
                    tempDiQ[dx, dy] = new short[transformationsCount][, ];

                    for (i = 0; i < transformationsCount; i++)
                    {
                        tempPixelY = new byte[rSize, rSize];
                        tempPixelI = new short[rSize, rSize];
                        tempPixelQ = new short[rSize, rSize];

                        for (x = 0; x < dSize; x += drInverseRatio) //Increment of inverse ratio to get only parts of Di
                        {
                            for (y = 0; y < dSize; y += drInverseRatio)
                            {
                                //Get transformation
                                tempTransformMatrix = _transformMatrixes[i];
                                tempTranslateMatrix = translateMatrixes[i];

                                //Average pixels on first transform
                                if (i == 0)
                                {
                                    //Reset temp variables
                                    tempSumY = 0;
                                    tempSumI = 0;
                                    tempSumQ = 0;

                                    //Average pixels
                                    for (j = 0; j < drInverseRatio; j++)
                                    {
                                        for (k = 0; k < drInverseRatio; k++)
                                        {
                                            tempSumY += imgDataY[dx + x + j, dy + y + k];
                                            tempSumI += imgDataI[dx + x + j, dy + y + k];
                                            tempSumQ += imgDataQ[dx + x + j, dy + y + k];
                                        }
                                    }

                                    //Calculating average Pixel Components
                                    tempAveragePixelY = Convert.ToByte(tempSumY / pixelAverageSquareSize);
                                    tempAveragePixelI = Convert.ToInt16(tempSumI / pixelAverageSquareSize);
                                    tempAveragePixelQ = Convert.ToInt16(tempSumQ / pixelAverageSquareSize);

                                    //Keep reference to subsampled Di.
                                    tempDY[x / drInverseRatio, y / drInverseRatio] = tempAveragePixelY;
                                    tempDI[x / drInverseRatio, y / drInverseRatio] = tempAveragePixelI;
                                    tempDQ[x / drInverseRatio, y / drInverseRatio] = tempAveragePixelQ;

                                    //Sum of "a" and "a^2" for future RMS calculation
                                    sumsOfDY[dx, dy]        += tempAveragePixelY;
                                    sumsOfDI[dx, dy]        += tempAveragePixelI;
                                    sumsOfDQ[dx, dy]        += tempAveragePixelQ;
                                    sumsOfDYSquared[dx, dy] += tempAveragePixelY * tempAveragePixelY;
                                    sumsOfDISquared[dx, dy] += tempAveragePixelI * tempAveragePixelI;
                                    sumsOfDQSquared[dx, dy] += tempAveragePixelQ * tempAveragePixelQ;
                                }

                                newX = tempTransformMatrix[0, 0] * (x / drInverseRatio) + tempTransformMatrix[0, 1] * (y / drInverseRatio) + tempTranslateMatrix[0];
                                newY = tempTransformMatrix[1, 0] * (x / drInverseRatio) + tempTransformMatrix[1, 1] * (y / drInverseRatio) + tempTranslateMatrix[1];

                                indexX = x / drInverseRatio;
                                indexY = y / drInverseRatio;

                                //Store pixel components for transformed Di (Fi)
                                tempPixelY[newX, newY] = tempDY[indexX, indexY];
                                tempPixelI[newX, newY] = tempDI[indexX, indexY];
                                tempPixelQ[newX, newY] = tempDQ[indexX, indexY];
                            }
                        }

                        //Store pixels of Di
                        tempDiY[dx, dy][i] = tempPixelY;
                        tempDiI[dx, dy][i] = tempPixelI;
                        tempDiQ[dx, dy][i] = tempPixelQ;
                    }
                }
            });

            //Find the Transformations to do...
            //For each Ri
            for (rx = 0; rx < rXCount; rx++)
            {
                //Parallel execution of for loop, so it runs faster.
                Parallel.For(0, rYCount, ry =>
                {
                    //Calculate the sums of components in Ri for future RMS metric calculation
                    int sumOfRY        = 0;
                    int sumOfRYSquared = 0;
                    int sumOfRI        = 0;
                    int sumOfRISquared = 0;
                    int sumOfRQ        = 0;
                    int sumOfRQSquared = 0;
                    int sumOfDAndRY;
                    int sumOfDAndRI;
                    int sumOfDAndRQ;
                    int x, y, indexX, indexY, dx, dy, i, sYInt, oYInt, sIInt, oIInt, sQInt, oQInt,
                    bestRmsYIndexX      = -1, bestRmsYIndexY = -1, bestRmsYTransformationIndex = -1,
                    bestRmsIIndexX      = -1, bestRmsIIndexY = -1, bestRmsITransformationIndex = -1,
                    bestRmsQIndexX      = -1, bestRmsQIndexY = -1, bestRmsQTransformationIndex = -1,
                    bestOY              = 0, bestSY = 0,
                    bestOI              = 0, bestSI = 0,
                    bestOQ              = 0, bestSQ = 0;
                    byte[,] tempPixelY  = null;
                    short[,] tempPixelI = null;
                    short[,] tempPixelQ = null;
                    double sY, oY,
                    sI, oI,
                    sQ, oQ,
                    tempRmsY, tempRmsI, tempRmsQ,
                    tempSYDenominator, tempSIDenominator, tempSQDenominator,
                    finalRmsY = Double.MaxValue, finalRmsI = Double.MaxValue, finalRmsQ = Double.MaxValue;

                    for (x = 0; x < rSize; x++)
                    {
                        for (y = 0; y < rSize; y++)
                        {
                            //Sum of "b" and of "b^2"
                            indexX          = rx * rSize + x;
                            indexY          = ry * rSize + y;
                            sumOfRY        += imgDataY[indexX, indexY];
                            sumOfRI        += imgDataI[indexX, indexY];
                            sumOfRQ        += imgDataQ[indexX, indexY];
                            sumOfRYSquared += imgDataY[indexX, indexY] * imgDataY[indexX, indexY];
                            sumOfRISquared += imgDataI[indexX, indexY] * imgDataI[indexX, indexY];
                            sumOfRQSquared += imgDataQ[indexX, indexY] * imgDataQ[indexX, indexY];
                        }
                    }

                    //For Each Di
                    for (dx = 0; dx < maxDxPos; dx++)
                    {
                        for (dy = 0; dy < maxDyPos; dy++)
                        {
                            tempSYDenominator = n * sumsOfDYSquared[dx, dy] - sumsOfDY[dx, dy] * sumsOfDY[dx, dy];
                            tempSIDenominator = n * sumsOfDISquared[dx, dy] - sumsOfDI[dx, dy] * sumsOfDI[dx, dy];
                            tempSQDenominator = n * sumsOfDQSquared[dx, dy] - sumsOfDQ[dx, dy] * sumsOfDQ[dx, dy];

                            //For Each possible transformations
                            for (i = 0; i < transformationsCount; i++)
                            {
                                sumOfDAndRY = 0;
                                sumOfDAndRI = 0;
                                sumOfDAndRQ = 0;
                                tempPixelY  = tempDiY[dx, dy][i];
                                tempPixelI  = tempDiI[dx, dy][i];
                                tempPixelQ  = tempDiQ[dx, dy][i];

                                //Compute sums necessary for calculating RMS metric
                                for (x = 0; x < rSize; x++)
                                {
                                    indexX = rx * rSize + x;

                                    for (y = 0; y < rSize; y++)
                                    {
                                        //Calculate indexX and indexY
                                        indexY = ry * rSize + y;

                                        //Sum of "ri*di"
                                        sumOfDAndRY += tempPixelY[x, y] * imgDataY[indexX, indexY];
                                        sumOfDAndRI += tempPixelI[x, y] * imgDataI[indexX, indexY];
                                        sumOfDAndRQ += tempPixelQ[x, y] * imgDataQ[indexX, indexY];
                                    }
                                }

                                //Compute contrast, brightness and RMS metric
                                sY = CalculateS(tempSYDenominator, n, sumOfDAndRY, sumsOfDY[dx, dy], sumOfRY, out sYInt);
                                sI = CalculateS(tempSIDenominator, n, sumOfDAndRI, sumsOfDI[dx, dy], sumOfRI, out sIInt);
                                sQ = CalculateS(tempSQDenominator, n, sumOfDAndRQ, sumsOfDQ[dx, dy], sumOfRQ, out sQInt);

                                oY = CalculateO(n, sY, sumOfRY, sumsOfDY[dx, dy], Y_LEVELS, out oYInt);
                                oI = CalculateO(n, sI, sumOfRI, sumsOfDI[dx, dy], I_LEVELS, out oIInt);
                                oQ = CalculateO(n, sQ, sumOfRQ, sumsOfDQ[dx, dy], Q_LEVELS, out oQInt);

                                tempRmsY = Math.Sqrt((sumOfRYSquared + sY * (sY * sumsOfDYSquared[dx, dy] - 2 * sumOfDAndRY + 2 * oY * sumsOfDY[dx, dy]) + oY * (n * oY - 2 * sumOfRY)) / n);
                                tempRmsI = Math.Sqrt((sumOfRISquared + sI * (sI * sumsOfDISquared[dx, dy] - 2 * sumOfDAndRI + 2 * oI * sumsOfDI[dx, dy]) + oI * (n * oI - 2 * sumOfRI)) / n);
                                tempRmsQ = Math.Sqrt((sumOfRQSquared + sQ * (sQ * sumsOfDQSquared[dx, dy] - 2 * sumOfDAndRQ + 2 * oQ * sumsOfDQ[dx, dy]) + oQ * (n * oQ - 2 * sumOfRQ)) / n);

                                if (Double.IsNaN(tempRmsY))
                                {
                                    throw new Exception("SQRT IS NAN!");
                                }
                                if (Double.IsNaN(tempRmsI))
                                {
                                    throw new Exception("SQRT IS NAN!");
                                }
                                if (Double.IsNaN(tempRmsQ))
                                {
                                    throw new Exception("SQRT IS NAN!");
                                }

                                //For each component, keep best transformation
                                if (!Double.IsNaN(tempRmsY) && finalRmsY > tempRmsY)
                                {
                                    finalRmsY      = tempRmsY;
                                    bestRmsYIndexX = dx;
                                    bestRmsYIndexY = dy;
                                    bestRmsYTransformationIndex = i;
                                    bestSY = sYInt;
                                    bestOY = oYInt;
                                }

                                if (!Double.IsNaN(tempRmsI) && finalRmsI > tempRmsI)
                                {
                                    finalRmsI      = tempRmsI;
                                    bestRmsIIndexX = dx;
                                    bestRmsIIndexY = dy;
                                    bestRmsITransformationIndex = i;
                                    bestSI = sIInt;
                                    bestOI = oIInt;
                                }

                                if (!Double.IsNaN(tempRmsQ) && finalRmsQ > tempRmsQ)
                                {
                                    finalRmsQ      = tempRmsQ;
                                    bestRmsQIndexX = dx;
                                    bestRmsQIndexY = dy;
                                    bestRmsQTransformationIndex = i;
                                    bestSQ = sQInt;
                                    bestOQ = oQInt;
                                }
                            }
                        }
                    }

                    //Save Di retained for Ri
                    transformations[ry * rXCount + rx, 0] = new FractalCompressionResult.FractalTransformation(bestRmsYIndexX, bestRmsYIndexY, Convert.ToByte(bestOY), Convert.ToByte(bestSY), Convert.ToByte(bestRmsYTransformationIndex));
                    transformations[ry * rXCount + rx, 1] = new FractalCompressionResult.FractalTransformation(bestRmsIIndexX, bestRmsIIndexY, Convert.ToByte(bestOI), Convert.ToByte(bestSI), Convert.ToByte(bestRmsITransformationIndex));
                    transformations[ry * rXCount + rx, 2] = new FractalCompressionResult.FractalTransformation(bestRmsQIndexX, bestRmsQIndexY, Convert.ToByte(bestOQ), Convert.ToByte(bestSQ), Convert.ToByte(bestRmsQTransformationIndex));
                });
            }

            return(new FractalCompressionResult(transformations, Convert.ToByte(Math.Ceiling(Math.Log(Math.Max(maxDxPos, maxDyPos), 2))), Convert.ToInt16(rXCount)));
        }