//создание доменных блоков
        public Bitmap[,,] CreateAllDomainBlocks(Bitmap image)
        {
            int offsetX = 0;
            int offsetY = 0;

            Bitmap[,,] domainBlocks = new Bitmap[image.Width / Data.DomainBlockSize, image.Height / Data.DomainBlockSize, numberOfRotations];
            Bitmap reducedImage = ResizeImage(image, image.Width / Data.Factor, image.Height / Data.Factor);
            var    bbReduced    = new BufferedBitmap(reducedImage);

            //создаем множество доменных блоков
            for (offsetX = 0; offsetX *Data.RankBlockSize != bbReduced.Width; offsetX++)
            {
                for (offsetY = 0; offsetY *Data.RankBlockSize != bbReduced.Height; offsetY++)
                {
                    //вычленяем доменный блок из сжатой картинки
                    domainBlocks[offsetX, offsetY, 0] = SelectBlockFromImage(bbReduced, offsetX, offsetY, Data.RankBlockSize);

                    //для текущего доменного блока создаем все возможные варианты поворотов
                    for (int i = 1; i < numberOfRotations; i++)
                    {
                        domainBlocks[offsetX, offsetY, i] = new Bitmap(domainBlocks[offsetX, offsetY, i - 1]);
                        if (i != 4)
                        {
                            domainBlocks[offsetX, offsetY, i].RotateFlip(RotateFlipType.Rotate90FlipNone);
                        }
                        else
                        {
                            domainBlocks[offsetX, offsetY, i].RotateFlip(RotateFlipType.Rotate90FlipX);
                        }
                    }
                }
            }
            bbReduced.Unlock();
            return(domainBlocks);
        }
        //подсчет величины корреляции
        public static double[] GetAlpha(Bitmap rankBlock, Bitmap domainBlock, double[] meanRankBlock, double[] meanDomainBlock)
        {
            BufferedBitmap bbRankBlock   = new BufferedBitmap(rankBlock);
            BufferedBitmap bbDomainBlock = new BufferedBitmap(domainBlock);

            double[] alpha = new double[Data.ColorFlag];
            double   temp  = 0;

            for (int k = 0; k < Data.ColorFlag; k++)
            {
                alpha[k] = 0;
                for (int i = 0; i < rankBlock.Height; i++)
                {
                    for (int j = 0; j < rankBlock.Width; j++)
                    {
                        temp = (bbDomainBlock.GetColorOfPixel(i, j, k) - meanDomainBlock[k]) *
                               (bbRankBlock.GetColorOfPixel(i, j, k) - meanRankBlock[k]);
                        alpha[k] += temp;
                    }
                }
            }
            bbRankBlock.Unlock();
            bbDomainBlock.Unlock();
            return(alpha);
        }
        //разбиение изображения на области
        public Bitmap[,] SplitImage(Bitmap image, out int numberOfParts)
        {
            numberOfParts   = Data.FastCompression ? 2 : 1;
            Bitmap[,] parts = new Bitmap[numberOfParts, numberOfParts];
            BufferedBitmap bb = new BufferedBitmap(image);

            for (int offsetX = 0; offsetX *(image.Width / numberOfParts) != image.Width; offsetX++)
            {
                for (int offsetY = 0; offsetY *(image.Height / numberOfParts) != image.Height; offsetY++)
                {
                    parts[offsetX, offsetY] = SelectBlockFromImage(bb, offsetX, offsetY, image.Width / numberOfParts);
                }
            }
            bb.Unlock();
            return(parts);
        }
        //создание случайного изображения
        public Bitmap CreateRandomImage(int width, int height)
        {
            BufferedBitmap bb = new BufferedBitmap(new Bitmap(width, height));
            Random         r  = new Random();
            byte           color;

            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    color = (byte)r.Next(0, 255);
                    bb.SetPixel(i, j, Color.FromArgb(color, color, color));
                }
            }
            bb.Unlock();
            return(bb.Bitmap);
        }
        //вычленение блока из изображения
        public Bitmap SelectBlockFromImage(BufferedBitmap bb, int offsetX, int offsetY, int blockSize)
        {
            int x, y;
            var bbTemp = new BufferedBitmap(new Bitmap(blockSize, blockSize));

            x = offsetX * blockSize;
            for (int xDomain = 0; xDomain < blockSize; xDomain++)
            {
                y = offsetY * blockSize;
                for (int yDomain = 0; yDomain < blockSize; yDomain++)
                {
                    bbTemp.SetPixel(xDomain, yDomain, bb.GetPixel(x, y));
                    y++;
                }
                x++;
            }

            bbTemp.Unlock();
            return(bbTemp.Bitmap);
        }
        //подсчет дисперсии значений пикселов
        public static double[] GetBeta(Bitmap domainBlock, double[] mean)
        {
            BufferedBitmap bbDomainBlock = new BufferedBitmap(domainBlock);

            double[] beta = new double[Data.ColorFlag];

            for (int k = 0; k < Data.ColorFlag; k++)
            {
                beta[k] = 0;
                for (int i = 0; i < domainBlock.Height; i++)
                {
                    for (int j = 0; j < domainBlock.Width; j++)
                    {
                        beta[k] += Math.Pow(bbDomainBlock.GetColorOfPixel(i, j, k) - mean[k], 2);
                    }
                }
            }
            bbDomainBlock.Unlock();
            return(beta);
        }
        //подсчет среднего значения пикселов в блоке
        public static double[] Mean(Bitmap block)
        {
            BufferedBitmap bbBlock = new BufferedBitmap(block);

            double[] mean = new double[Data.ColorFlag];

            for (int k = 0; k < Data.ColorFlag; k++)
            {
                mean[k] = 0;
                for (int i = 0; i < bbBlock.Height; i++)
                {
                    for (int j = 0; j < bbBlock.Width; j++)
                    {
                        mean[k] += bbBlock.GetColorOfPixel(i, j, k);
                    }
                }
                mean[k] = mean[k] / (Data.RankBlockSize * Data.RankBlockSize);
            }
            bbBlock.Unlock();
            return(mean);
        }
        //подсчет значения метрики
        public static double[] GetMetric(Bitmap rankBlock, Bitmap domainBlock, double[] contrast, double[] brightness)
        {
            BufferedBitmap bbRankBlock   = new BufferedBitmap(rankBlock);
            BufferedBitmap bbDomainBlock = new BufferedBitmap(domainBlock);

            double[] metric = new double[Data.ColorFlag];

            for (int k = 0; k < Data.ColorFlag; k++)
            {
                metric[k] = 0;
                for (int i = 0; i < rankBlock.Height; i++)
                {
                    for (int j = 0; j < rankBlock.Width; j++)
                    {
                        metric[k] += Math.Pow(contrast[k] * bbDomainBlock.GetColorOfPixel(i, j, k)
                                              + brightness[k] - bbRankBlock.GetColorOfPixel(i, j, k), 2);
                    }
                }
            }
            bbRankBlock.Unlock();
            bbDomainBlock.Unlock();
            return(metric);
        }
        //кодирование изображения
        public void Compress(Bitmap image, IProgress <int> progress = null, CancellationToken?token = null)
        {
            if (progress == null)
            {
                progress = new Progress <int>();
            }
            if (token == null)
            {
                token = new CancellationToken();
            }
            Stopwatch sw = new Stopwatch();

            sw.Start();
            Bitmap[,] parts = SplitImage(image, out numberOfParts);
            Bitmap[,,] domainBlocks;
            int partOffsetX = image.Width / Data.RankBlockSize / numberOfParts;
            int partOffsetY = image.Height / Data.RankBlockSize / numberOfParts;
            var bbOriginal  = new BufferedBitmap(image);

            if (bbOriginal.IsGrey())
            {
                Data.ColorFlag = 1;
            }
            else
            {
                Data.ColorFlag = 3;
            }
            bbOriginal.Unlock();

            double[] contrast   = new double[Data.ColorFlag];
            double[] brightness = new double[Data.ColorFlag];
            double[] metric     = new double[Data.ColorFlag];
            double[] min        = new double[Data.ColorFlag];
            double[,,,] compressedData = new double[Data.ColorFlag, bbOriginal.Width / Data.RankBlockSize,
                                                    bbOriginal.Height / Data.RankBlockSize, numberOfParameters];
            double controlValue = (bbOriginal.Width / Data.RankBlockSize) * (bbOriginal.Height / Data.RankBlockSize) / 100.0;
            double count        = 0;
            int    value        = 1;
            double mse          = 0;

            for (int partX = 0; partX < numberOfParts; partX++)
            {
                for (int partY = 0; partY < numberOfParts; partY++)
                {
                    domainBlocks = CreateAllDomainBlocks(parts[partX, partY]);
                    bbOriginal   = new BufferedBitmap(parts[partX, partY]);

                    for (int i = 0; i *Data.RankBlockSize != bbOriginal.Width; i++)
                    {
                        for (int j = 0; j *Data.RankBlockSize != bbOriginal.Height; j++)
                        {
                            if ((bool)token?.IsCancellationRequested)
                            {
                                progress.Report(0);
                                MessageBox.Show("Операция прервана");
                                return;
                            }

                            int    indexX    = i + partX * partOffsetX;
                            int    indexY    = j + partY * partOffsetY;
                            Bitmap rankBlock = SelectBlockFromImage(bbOriginal, i, j, Data.RankBlockSize);
                            if (min[0] != 0)
                            {
                                for (int k = 0; k < Data.ColorFlag; k++)
                                {
                                    mse += min[k];
                                }
                            }
                            for (int k = 0; k < Data.ColorFlag; k++)
                            {
                                min[k] = double.PositiveInfinity;
                            }
                            var meanRankBlock = Metric.Mean(rankBlock);

                            for (int p = 0; p < parts[partX, partY].Width / Data.DomainBlockSize; p++)
                            {
                                for (int q = 0; q < parts[partX, partY].Height / Data.DomainBlockSize; q++)
                                {
                                    var meanDomainBlock = Metric.Mean(domainBlocks[p, q, 0]);

                                    for (int rotation = 0; rotation < numberOfRotations; rotation++)
                                    {
                                        contrast   = Metric.GetContrast(rankBlock, domainBlocks[p, q, rotation], meanRankBlock, meanDomainBlock);
                                        brightness = Metric.GetBrightness(contrast, meanRankBlock, meanDomainBlock);
                                        metric     = Metric.GetMetric(rankBlock, domainBlocks[p, q, rotation], contrast, brightness);

                                        for (int m = 0; m < Data.ColorFlag; m++)
                                        {
                                            if (metric[m] < min[m])
                                            {
                                                min[m] = metric[m];
                                                double[] mas = new double[] { p + partX * partOffsetX / Data.Factor, q + partY * partOffsetY / Data.Factor, rotation, contrast[m], brightness[m] };

                                                for (int k = 0; k < mas.Length; k++)
                                                {
                                                    compressedData[m, indexX, indexY, k] = mas[k];
                                                }
                                            }
                                        }
                                        //if (Data.FastCompression && min[0] <= 100)
                                        //break;
                                    }
                                    //if (Data.FastCompression && min[0] <= 100)
                                    //break;
                                }
                                count += 1.0 / domainBlocks.GetLength(1);
                                if (count >= controlValue)
                                {
                                    if (value > 100)
                                    {
                                        value = 100;
                                    }
                                    progress.Report(value);
                                    value++;
                                    count -= controlValue;
                                    count  = Math.Round(count, 2);
                                }
                                //if (Data.FastCompression && min[0] <= 100)
                                //break;
                            }
                        }
                    }
                }
            }
            mse = mse / ((image.Width / Data.RankBlockSize) * (image.Height / Data.RankBlockSize));
            bbOriginal.Unlock();
            double psnr = 10 * Math.Log10(image.Width * image.Height * Math.Pow(byte.MaxValue, 2) * Data.ColorFlag / mse);

            WriteData(compressedData, image.Width, image.Height);
            sw.Stop();
            MessageBox.Show("Сжатие завершено\r\nВремя: " + (sw.ElapsedMilliseconds / 1000.0).ToString());
            progress.Report(0);
        }
        //декодирование изображения
        public Bitmap[] Decompress(string path, int numberOfIterations, bool imageFlag)
        {
            Stopwatch sw = new Stopwatch();

            sw.Restart();
            int width, height;
            var compressedData = ReadData(path, out width, out height);

            Bitmap[] images = new Bitmap[numberOfIterations];

            if (!imageFlag)
            {
                try
                {
                    OpenFileDialog opf = new OpenFileDialog();
                    if (opf.ShowDialog() == DialogResult.OK)
                    {
                        images[0] = ResizeImage(new Bitmap(opf.FileName), width, height);
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("Файл не является картинкой");
                }
            }
            else
            {
                images[0] = CreateRandomImage(width, height);
            }

            Bitmap image      = new Bitmap(images[0]);
            Bitmap reduced    = new Bitmap(ResizeImage(image, width / 2, height / 2));
            Bitmap restored   = new Bitmap(image);
            Bitmap temp       = new Bitmap(image);
            var    bbOriginal = new BufferedBitmap(image);
            var    bbReduced  = new BufferedBitmap(reduced);

            for (int nb = 1; nb < numberOfIterations; nb++)
            {
                for (int i = 0; i < compressedData.GetLength(0); i++)
                {
                    for (int x = 0; x < compressedData.GetLength(1); x++)
                    {
                        for (int y = 0; y < compressedData.GetLength(2); y++)
                        {
                            var offsetX       = (int)compressedData[i, x, y, 0];
                            var offsetY       = (int)compressedData[i, x, y, 1];
                            var indexRotation = (int)compressedData[i, x, y, 2];
                            var contrast      = compressedData[i, x, y, 3];
                            var brightness    = compressedData[i, x, y, 4];

                            var domainBlock = SelectBlockFromImage(bbReduced, offsetX, offsetY, Data.RankBlockSize);
                            domainBlock.RotateFlip(rotate[indexRotation]);
                            var bbDomainBlock = new BufferedBitmap(domainBlock);

                            for (int domainX = 0; domainX < Data.RankBlockSize; domainX++)
                            {
                                for (int domainY = 0; domainY < Data.RankBlockSize; domainY++)
                                {
                                    double pixel = bbDomainBlock.GetColorOfPixel(domainX, domainY, i);
                                    pixel = contrast * pixel + brightness;
                                    pixel = Math.Round(pixel);
                                    if (pixel < byte.MinValue)
                                    {
                                        pixel = 0;
                                    }
                                    if (pixel > byte.MaxValue)
                                    {
                                        pixel = 255;
                                    }

                                    if (i == 0)
                                    {
                                        bbOriginal.SetPixel(x * Data.RankBlockSize + domainX, y * Data.RankBlockSize + domainY,
                                                            Color.FromArgb((int)pixel, (int)pixel, (int)pixel));
                                        continue;
                                    }
                                    var color = temp.GetPixel(x * Data.RankBlockSize + domainX, y * Data.RankBlockSize + domainY);
                                    if (i == 1)
                                    {
                                        bbOriginal.SetPixel(x * Data.RankBlockSize + domainX, y * Data.RankBlockSize + domainY,
                                                            Color.FromArgb((int)pixel, (int)pixel, color.B));
                                        continue;
                                    }
                                    if (i == 2)
                                    {
                                        bbOriginal.SetPixel(x * Data.RankBlockSize + domainX, y * Data.RankBlockSize + domainY,
                                                            Color.FromArgb((int)pixel, color.G, color.B));
                                        continue;
                                    }
                                }
                            }
                        }
                    }
                    bbOriginal.Unlock();

                    image      = bbOriginal.Bitmap;
                    temp       = new Bitmap(image);
                    bbOriginal = new BufferedBitmap(image);
                }
                bbOriginal.Unlock();
                bbReduced.Unlock();

                image      = bbOriginal.Bitmap;
                reduced    = new Bitmap(ResizeImage(image, width / 2, height / 2));
                images[nb] = new Bitmap(image);

                var a = new Bitmap(image);
                a.Save(nb + ".jpg");

                bbOriginal = new BufferedBitmap(image);
                bbReduced  = new BufferedBitmap(reduced);
            }
            image.Save(Path.GetDirectoryName(Data.Path) + "//" + Path.GetFileNameWithoutExtension(Data.Path) + "-restored.png");
            sw.Stop();
            MessageBox.Show("Восстановление завершено\r\nВремя: " + (sw.ElapsedMilliseconds / 1000.0).ToString());
            return(images);
        }