/// <summary>
 /// Заполняет пиксели сегментируемого изображения L*a*b данными
 /// </summary>
 /// <param name="rgbData">RGB-данные изображения в виде матрицы значений r, g и b</param>
 /// <param name="segmentedImage">Сегментируемое изображение</param>
 private static void FillSegmentedImageIntensityFeatures(int[][] rgbData, ref SegmentedImage segmentedImage)
 {
     double[][] labData = new double[segmentedImage.Height * segmentedImage.Width][];
     for (int i = 0; i < segmentedImage.Height * segmentedImage.Width; i++)
         labData[i] = new double[3];
     ColorSpacesProcessing.TransformRGBtoLab(rgbData, ref labData);
     for (int i = 0; i < segmentedImage.Regions.Count; i++)
     {
         for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
         {
             int pixelX = segmentedImage.Regions[i].RegionPixels[j].Id[0];
             int pixelY = segmentedImage.Regions[i].RegionPixels[j].Id[1];
             segmentedImage.Regions[i].RegionPixels[j].IntensityFeatures = labData[(pixelX * segmentedImage.Width) + pixelY];
         }
     }
 }
        public static int requiredSegmentsCount = 5; // требуемое количество регионов

        public static SegmentedImage PerformSegmentation(Bitmap image)
        {
            int imageWidth = image.Width;
            int imageHeight = image.Height;
            int imageSize = imageWidth * imageHeight;
            int[][] imageColorData = new int[imageSize][];
            for (int i = 0; i < imageSize; i++)
                imageColorData[i] = new int[3];

            // заполнение массива цветов изображения - загрузка изображения
            ImageProcessing.LoadImage(image, ref imageColorData, ref imageHeight, ref imageWidth);

            // подбор начального размера сегмента, исходя из расчета максимум defaultSegmentsCount начальных сегментов на изображении
            int segmentSize = defaultSegmentSize < ((int)Math.Sqrt(imageHeight * imageWidth / ((double)defaultSegmentsCount))) ?
                ((int)Math.Ceiling(Math.Sqrt(imageHeight * imageWidth / ((double)defaultSegmentsCount)))) : defaultSegmentSize;

            // создание начального сегментированного изображения
            SegmentedImage segmentedImage = new SegmentedImage(imageColorData, imageHeight, imageWidth, segmentSize);

            // заполнение L*a*b данных для каждого пикселя сегмента
            FillSegmentedImageIntensityFeatures(imageColorData, ref segmentedImage);

            // заполние текстурных характеристик для каждого пикселя изображения
            FillSegmentedImageTextureFeatures(imageColorData, ref segmentedImage);

            // выполнение условной фильтрации и заполние характеристик интенсивности пикселей после фильтрации
            PerformConditionalIntencityFiltering(ref segmentedImage);

            // выполние рассчета всех параметров регионов после заполнения всех параметров пикселей
            for (int i = 0; i < segmentedImage.Regions.Count; i++)
                segmentedImage.Regions[i].CalculateParameters();

            // классификация пикселей на основе KMCC алгоритма
            KMCCClassification(regularizationParameter, ref segmentedImage);

            // объединение полученных регионов
            RegionsClassification(regularizationParameter, requiredSegmentsCount, ref segmentedImage);

            // Подсчет разброса точек регионов для сегментированного изображения
            segmentedImage.CalculateDispersion(requiredSegmentsCount);

            return segmentedImage;
        }
        /// <summary>
        /// Заполняет пиксели сегментируемого изображения данными текстурных характеристик
        /// </summary>
        /// <param name="rgbData">RGB-данные изображения в виде матрицы значений r, g и b</param>
        /// <param name="segmentedImage">Сегментируемое изображение</param>
        private static void FillSegmentedImageTextureFeatures(int[][] rgbData, ref SegmentedImage segmentedImage)
        {
            // матрица текстурных характеристик
            // строки - элементы входной матрицы, соответствующие пикселям изображения
            // столбцы - значения тексурных характеристик для каждого из цветов: (r g b) x TextureFeaturesProcessing.numOfFeatures
            double[][] textureFeatures = new double[segmentedImage.Height * segmentedImage.Width][];
            for (int i = 0; i < segmentedImage.Height * segmentedImage.Width; i++)
                textureFeatures[i] = new double[TextureFeaturesProcessing.numOfFeatures * TextureFeaturesProcessing.colorsCount];

            int iteration = 0; // текущая итерация получения текстурных характеристик
            for (int i = 0; i < TextureFeaturesProcessing.colorsCount; i++)
            {
                int[][] colorData; // массив отдельных цветовых компонентов изображения
                switch (i)
                {
                    case 0:
                        colorData = ImageProcessing.getRedMatrix(rgbData, segmentedImage.Height, segmentedImage.Width);
                        break;
                    case 1:
                        colorData = ImageProcessing.getGreenMatrix(rgbData, segmentedImage.Height, segmentedImage.Width);
                        break;
                    default:
                        colorData = ImageProcessing.getBlueMatrix(rgbData, segmentedImage.Height, segmentedImage.Width);
                        break;
                }
                for (int j = 0; j < TextureFeaturesProcessing.numOfFeatures; j++)
                {
                    double[][] textureFeatureMatrix = TextureFeaturesProcessing.LavsFiltering(colorData, TextureFeaturesProcessing.filters[j],
                        segmentedImage.Height, segmentedImage.Width, TextureFeaturesProcessing.filterSize, TextureFeaturesProcessing.filterSize);

                    // заполнение элементов массива текстурных характеристик для полученных значений текстурных характеристик
                    for (int x = 0; x < segmentedImage.Height; x++)
                    {
                        for (int y = 0; y < segmentedImage.Width; y++)
                        {
                            textureFeatures[(x * segmentedImage.Width) + y][iteration] = textureFeatureMatrix[x][y];
                        }
                    }

                    iteration++;
                }
            }

            // запоминаем полученные текстурные характеристики в каждом пикселе
            for (int i = 0; i < segmentedImage.Regions.Count; i++)
            {
                for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
                {
                    int pixelX = segmentedImage.Regions[i].RegionPixels[j].Id[0];
                    int pixelY = segmentedImage.Regions[i].RegionPixels[j].Id[1];
                    segmentedImage.Regions[i].RegionPixels[j].TextureFeatures = textureFeatures[(pixelX * segmentedImage.Width) + pixelY];
                }
            }
        }
        /// <summary>
        /// Объединяет близкие по параметрам регионы для получения заданного количества регионов
        /// </summary>
        /// <param name="regularizationParameter">Регуляризационный параметр ламбда, используемый при вычислении геометрического расстояния</param>
        /// <param name="segmentsCount">Требуемое количество регионов</param>
        /// <param name="segmentedImage">Сегментируемое изображение</param>
        private static void RegionsClassification(double regularizationParameter, int segmentsCount, ref SegmentedImage segmentedImage)
        {
            // цикл объединения регионов продолжается до тех пор, пока не будет получено нужное количество регионов
            while (segmentedImage.Regions.Count > segmentsCount)
            {
                // проходимся по всем регионам
                for (int i = 0; i < segmentedImage.Regions.Count; i++)
                {
                    // Если нужное количество регионов уже получено
                    if (segmentedImage.Regions.Count <= segmentsCount)
                        break;

                    // массив для хранения расстояний между текущим регионом и всеми другими регионами
                    double[] distances = new double[segmentedImage.Regions.Count];
                    for (int j = 0; j < distances.Length; j++)
                        distances[j] = 0.0;

                    // сравниваем текущий регион со всеми остальными
                    for (int j = 0; j < segmentedImage.Regions.Count; j++)
                    {
                        if (i == j)
                            continue; // пропускаем сравнение региона самого с собой

                        // подсчет квадратов разностей элементов векторов условной интенсивности
                        double sum = 0.0;
                        for (int k = 0; k < segmentedImage.Regions[j].AverageConditionalIntensityFeature.Length; k++)
                        {
                            sum += (segmentedImage.Regions[i].AverageConditionalIntensityFeature[k] -
                                segmentedImage.Regions[j].AverageConditionalIntensityFeature[k]) *
                                (segmentedImage.Regions[i].AverageConditionalIntensityFeature[k] -
                                segmentedImage.Regions[j].AverageConditionalIntensityFeature[k]);
                        }
                        distances[j] += Math.Sqrt(sum);

                        // подсчет квадратов разностей элементов векторов текстурных характеристик
                        sum = 0.0;
                        for (int k = 0; k < segmentedImage.Regions[j].AverageTextureFeature.Length; k++)
                        {
                            sum += (segmentedImage.Regions[i].AverageTextureFeature[k] -
                                segmentedImage.Regions[j].AverageTextureFeature[k]) *
                                (segmentedImage.Regions[i].AverageTextureFeature[k] -
                                segmentedImage.Regions[j].AverageTextureFeature[k]);
                        }
                        distances[j] += Math.Sqrt(sum);

                        // подсчет квадатов разностей элементов вектора геометрической позиции сравниваемых регионов
                        sum = 0.0;
                        for (int k = 0; k < segmentedImage.Regions[j].SpacialSenterId.Length; k++)
                        {
                            sum += (segmentedImage.Regions[i].SpacialSenterId[k] -
                                segmentedImage.Regions[j].SpacialSenterId[k]) *
                                (segmentedImage.Regions[i].SpacialSenterId[k] -
                                segmentedImage.Regions[j].SpacialSenterId[k]);
                        }
                        // подсчет средней площади для всех регионов
                        double averageArea = 0.0;
                        for (int k = 0; k < segmentedImage.Regions.Count; k++)
                            averageArea += segmentedImage.Regions[k].Area;
                        averageArea /= segmentedImage.Regions.Count;
                        distances[j] += regularizationParameter * (averageArea / segmentedImage.Regions[j].Area) * Math.Sqrt(sum);
                    }

                    // определение минимального расстояния и перемещение пикселей регионов
                    double minDistance = distances[0] != 0.0 ? distances[0] : distances[1];
                    for (int j = 0; j < distances.Length; j++)
                        if (distances[j] != 0.0 && distances[j] < minDistance) // исключаем сравнение региона с самим собой
                            minDistance = distances[j];
                    for (int j = 0; j < distances.Length; j++)
                    {
                        if (distances[j] == minDistance)
                        {
                            // перемещаем пиксели из j-го региона в i-ый регион
                            segmentedImage.Regions[i].AddPixelsWithParametersRecalculation(
                                segmentedImage.Regions[j].RemovePixels());

                            // удаляем пустой регион
                            segmentedImage.Regions.RemoveAt(j);

                            // если удаленные регион был до i-го региона в списке регионов, то индексы сместились, и,
                            // чтобы не пропускать регионы, делаем:
                            if (j < i)
                                i--;

                            break;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Выполняет сегментацию изображения по алгоритму KMCC
        /// </summary>
        /// <param name="regularizationParameter">Регуляризационный параметр ламбда, используемый при вычислении геометрического расстояния</param>
        /// <param name="segmentedImage">Сегментируемое изображение</param>
        private static void KMCCClassification(double regularizationParameter, ref SegmentedImage segmentedImage)
        {
            bool isNeedInteration = true;
            int iterationsCount = 1;
            while (isNeedInteration && iterationsCount > 0)
            {
                iterationsCount--;

                // настраиваем параметры перед очередной итерацией алгоритма
                isNeedInteration = false;
                for (int i = 0; i < segmentedImage.Regions.Count; i++)
                    for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
                        segmentedImage.Regions[i].RegionPixels[j].IsChecked = false;

                // проходимся по всем пикселям изображения в двойном цикле по пикселям регионов
                for (int i = 0; i < segmentedImage.Regions.Count; i++)
                {
                    for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
                    {
                        if (segmentedImage.Regions[i].RegionPixels[j].IsChecked)
                            continue; // пропускаем пиксели, которые уже были проверены на данной итерации

                        segmentedImage.Regions[i].RegionPixels[j].IsChecked = true; // помечаем пиксель, как проверенный на текущей итерации

                        // массив для хранения расстояний по KMCC между текущим пикселем и всеми регионами
                        double[] distances = new double[segmentedImage.Regions.Count];
                        for (int k = 0; k < segmentedImage.Regions.Count; k++)
                        {
                            // подсчет квадратов разностей элементов векторов условной интенсивности
                            double sum = 0.0;
                            for (int z = 0; z < segmentedImage.Regions[k].RegionPixels[0].ConditionalIntensityFeatures.Length; z++)
                            {
                                // находится квадрат разности элементов вектора условной интенсивности текущего пикселя и текущего региона
                                sum += (segmentedImage.Regions[i].RegionPixels[j].ConditionalIntensityFeatures[z] -
                                    segmentedImage.Regions[k].AverageConditionalIntensityFeature[z]) *
                                    (segmentedImage.Regions[i].RegionPixels[j].ConditionalIntensityFeatures[z] -
                                    segmentedImage.Regions[k].AverageConditionalIntensityFeature[z]);
                            }
                            // Евклидово расстояние между векторами условной интенсивности текущего пикселя и текущего региона прибавляется к общему расстоянию
                            distances[k] += Math.Sqrt(sum);

                            // подсчет квадратов разностей элементов векторов текстурных характеристик
                            sum = 0.0;
                            for (int z = 0; z < segmentedImage.Regions[k].RegionPixels[0].TextureFeatures.Length; z++)
                            {
                                // находится квадрат разности элементов вектора текстурной характеристики текущего пикселя и текущего региона
                                sum += (segmentedImage.Regions[i].RegionPixels[j].TextureFeatures[z] -
                                    segmentedImage.Regions[k].AverageTextureFeature[z]) *
                                    (segmentedImage.Regions[i].RegionPixels[j].TextureFeatures[z] -
                                    segmentedImage.Regions[k].AverageTextureFeature[z]);
                            }
                            // Евклидово расстояние между векторами текстурной характеристики текущего пикселя и текущего региона прибавляется к общему расстоянию
                            distances[k] += Math.Sqrt(sum);

                            // подсчет квадатов разностей элементов вектора геометрической позиции текущего пикселя и центра текущего региона
                            sum = 0.0;
                            for (int z = 0; z < segmentedImage.Regions[k].RegionPixels[0].Id.Length; z++)
                            {
                                // находится квадрат разности элементов вектора геометрической позиции текущего пикселя и центра текущего региона
                                sum += (segmentedImage.Regions[i].RegionPixels[j].Id[z] -
                                    segmentedImage.Regions[k].SpacialSenterId[z]) *
                                    (segmentedImage.Regions[i].RegionPixels[j].Id[z] -
                                    segmentedImage.Regions[k].SpacialSenterId[z]);
                            }
                            // подсчет средней площади для всех регионов
                            double averageArea = 0.0;
                            for (int z = 0; z < segmentedImage.Regions.Count; z++)
                                averageArea += segmentedImage.Regions[z].Area;
                            averageArea /= segmentedImage.Regions.Count;
                            // Евклидово расстояние между векторами геометрического положения текущего пикселя и центра текущего региона прибавляется к общему расстоянию
                            distances[k] += regularizationParameter * (averageArea / segmentedImage.Regions[k].Area) * Math.Sqrt(sum);
                        }

                        // определение минимального расстояния и перемещение пикселя
                        double minDistance = distances.Min();
                        for (int k = 0; k < distances.Length; k++)
                        {
                            if (distances[k] == minDistance)
                            {
                                if (k != i) // пиксель находится не в своем регионе
                                {
                                    // удаляем пиксель из текущего региона (регион i) и добавляем его в самый близкий для него регион (регион k)
                                    segmentedImage.Regions[k].AddPixelWithParametersRecalculation(
                                        segmentedImage.Regions[i].RemovePixelWithParametersRecalculation(
                                        segmentedImage.Regions[i].RegionPixels[j].Id));

                                    if (segmentedImage.Regions[i].RegionPixels.Count != 0) // если в регионе еще есть пиксели
                                    {
                                        if (j != segmentedImage.Regions[i].RegionPixels.Count) // если текущий удаленный пиксель не был последним рассматриваемым в регионе
                                        {
                                            // значит удаленный пиксель был внутри региона, в силу особенностей удаления элемента из List
                                            // чтобы не пропускать пиксели делаем:
                                            j--;
                                        }
                                    }

                                    isNeedInteration = true;
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Производит условную фильтрацию L*a*b данных изображения и записывает эти данные в пиксели изображения
        /// </summary>
        /// <param name="segmentedImage">Сегментируемое изображение</param>
        public static void PerformConditionalIntencityFiltering(ref SegmentedImage segmentedImage)
        {
            // извлечение текстурных характеристик пикселей в один массив
            double[][] textureFeatures = new double[segmentedImage.Height * segmentedImage.Width][];
            for (int i = 0; i < segmentedImage.Height * segmentedImage.Width; i++)
                textureFeatures[i] = new double[TextureFeaturesProcessing.numOfFeatures * TextureFeaturesProcessing.colorsCount];

            // извлечение характеристик интенсивности пикселей в один массив
            double[][] intencityFeatures = new double[segmentedImage.Height * segmentedImage.Width][];
            for (int i = 0; i < segmentedImage.Height * segmentedImage.Width; i++)
                intencityFeatures[i] = new double[segmentedImage.Regions[0].RegionPixels[0].IntensityFeatures.Length];

            for (int i = 0; i < segmentedImage.Regions.Count; i++)
            {
                // TODO: Извечь в массивы TextureFeatures и IntencityFeatures
                for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
                {
                    int pixelX = segmentedImage.Regions[i].RegionPixels[j].Id[0];
                    int pixelY = segmentedImage.Regions[i].RegionPixels[j].Id[1];
                    textureFeatures[(pixelX * segmentedImage.Width) + pixelY] = segmentedImage.Regions[i].RegionPixels[j].TextureFeatures;
                    intencityFeatures[(pixelX * segmentedImage.Width) + pixelY] = segmentedImage.Regions[i].RegionPixels[j].IntensityFeatures;
                }
            }

            // получаем пороговое значение текстурных характеристик изображения
            double textureFeatureThreshold = ConditionalFiltering.GetTextureFeatureThreshold(textureFeatures,
                segmentedImage.Height, segmentedImage.Width);

            // условная фильтрация
            for (int i = 0; i < segmentedImage.Regions.Count; i++)
            {
                for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
                {
                    // если норма вектора текстурной характеристики пикселя меньше, чем пороговое значение
                    if (ConditionalFiltering.VectorNorm(segmentedImage.Regions[i].RegionPixels[j].TextureFeatures) < textureFeatureThreshold)
                        segmentedImage.Regions[i].RegionPixels[j].ConditionalIntensityFeatures =
                            segmentedImage.Regions[i].RegionPixels[j].IntensityFeatures;
                    else
                    {
                        // считаем покомпонентную сумму векторов интенсивности для текущего региона
                        double[] intencitySum = new double[segmentedImage.Regions[0].RegionPixels[0].IntensityFeatures.Length];
                        for (int k = 0; k < segmentedImage.Regions[i].RegionPixels.Count; k++)
                            for (int z = 0; z < segmentedImage.Regions[i].RegionPixels[k].IntensityFeatures.Length; z++)
                                intencitySum[z] += segmentedImage.Regions[i].RegionPixels[k].IntensityFeatures[z];

                        // делим каджую сумму на количество пикселей в регионе
                        for (int z = 0; z < intencitySum.Length; z++)
                            intencitySum[z] /= segmentedImage.Regions[i].RegionPixels.Count;

                        segmentedImage.Regions[i].RegionPixels[j].ConditionalIntensityFeatures = intencitySum;
                    }
                }
            }
        }
        /// <summary>
        /// Делит регионы по пространственному признаку (если регион состоит из не соединенных фрагментов) на несколько регионов
        /// </summary>
        /// <param name="segmentedImage">Сегментированное изображение</param>
        public static void SplitRegions(ref SegmentedImage segmentedImage)
        {
            // Производим расчет усредненных по региону цветов пикселей
            segmentedImage.AverageRegionPixelsColor();

            // Составляем топографически ориентированную карту цветов пикселей, определяющую принадлежность пикселя к региону
            int[][] classMap = new int[segmentedImage.Height][];
            for (int i = 0; i < segmentedImage.Height; i++)
                classMap[i] = new int[segmentedImage.Width];

            for (int i = 0; i < segmentedImage.Regions.Count; i++)
                for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
                    classMap[segmentedImage.Regions[i].RegionPixels[j].Id[0]][segmentedImage.Regions[i].RegionPixels[j].Id[1]] =
                        segmentedImage.Regions[i].RegionPixels[j].SegmentsGrayColor;

            // Составляем топографически ориентированный массив пикселей
            Pixel[][] imagePixels = new Pixel[segmentedImage.Height][];
            for (int i = 0; i < segmentedImage.Height; i++)
                imagePixels[i] = new Pixel[segmentedImage.Width];

            for (int i = 0; i < segmentedImage.Regions.Count; i++)
            {
                for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
                {
                    Pixel currentPixel = segmentedImage.Regions[i].RegionPixels[j];
                    int x = currentPixel.Id[0];
                    int y = currentPixel.Id[1];

                    imagePixels[x][y] = new Pixel(currentPixel.Id, currentPixel.RgbData, segmentedImage.Width);
                    imagePixels[x][y].GlobalNumber = currentPixel.GlobalNumber;
                    imagePixels[x][y].Region = null; // необходимо для операции разделения регионов
                    imagePixels[x][y].SegmentsRgbData = currentPixel.SegmentsRgbData;
                    imagePixels[x][y].SegmentsGrayColor = currentPixel.SegmentsGrayColor;
                    imagePixels[x][y].IntensityFeatures = currentPixel.IntensityFeatures;
                    imagePixels[x][y].ConditionalIntensityFeatures = currentPixel.ConditionalIntensityFeatures;
                    imagePixels[x][y].TextureFeatures = currentPixel.TextureFeatures;
                    imagePixels[x][y].isNeighboring = currentPixel.isNeighboring;
                    imagePixels[x][y].Type = Pixel.PixelType.inner;
                    if ((x == 0) || (x == segmentedImage.Height - 1))
                        imagePixels[x][y].Type = Pixel.PixelType.border;
                    if ((y == 0) || (y == segmentedImage.Width - 1))
                        imagePixels[x][y].Type = Pixel.PixelType.border;
                }
            }
            List<Region> tempRegions = new List<Region>();

            // фомирование новых регионов
            for (int i = 0; i < segmentedImage.Height; i++)
            {
                for (int j = 0; j < segmentedImage.Width; j++)
                {
                    if (imagePixels[i][j].Region == null)
                    {
                        Region region = new Region();
                        tempRegions.Add(region);
                        region.RegionPixels.Add(imagePixels[i][j]);
                        imagePixels[i][j].Region = region;
                    }

                    // рассматриваем 8-ми связную окресность текущего пикселя
                    for (int fi = -1; fi <= 1; fi++)
                    {
                        for (int fj = -1; fj <= 1; fj++)
                        {
                            if ((fi + i >= 0) && (fi + i < segmentedImage.Height) &&
                                (fj + j >= 0) && (fj + j < segmentedImage.Width))
                            {
                                int indexX = fi + i;
                                int indexY = fj + j;
                                Pixel neighbourPixel = imagePixels[indexX][indexY];

                                if (classMap[i][j] != classMap[indexX][indexY])
                                    imagePixels[i][j].Type = Pixel.PixelType.border;

                                if (neighbourPixel.Region == null)
                                {
                                    if (classMap[i][j] == classMap[indexX][indexY])
                                    {
                                        imagePixels[indexX][indexY].Region = imagePixels[i][j].Region;
                                        imagePixels[i][j].Region.RegionPixels.Add(imagePixels[indexX][indexY]);
                                    }
                                }
                                else if (neighbourPixel.Region != imagePixels[i][j].Region)
                                {
                                    if (classMap[i][j] == classMap[indexX][indexY])
                                    {
                                        // Перемещаем пиксели из региона пикселя окрестности в регион текущего пикселя
                                        Region regionFROM = imagePixels[indexX][indexY].Region;
                                        Region regionTO = imagePixels[i][j].Region;
                                        for (int k = 0; k < regionFROM.RegionPixels.Count; k++)
                                        {
                                            Pixel pixel = regionFROM.RegionPixels[k];
                                            pixel.Region = regionTO;
                                            regionTO.RegionPixels.Add(pixel);
                                        }
                                        regionFROM.RegionPixels.Clear();
                                    }
                                }
                            }
                        }
                    }
                }
            }

            List<Region> newRegions = new List<Region>();
            for (int i = 0; i < tempRegions.Count; i++)
            {
                if (tempRegions[i].RegionPixels.Count > 0)
                    newRegions.Add(tempRegions[i]);
            }

            segmentedImage.Regions = newRegions;
        }
        //private static void KMCCClassification(double regularizationParameter, ref SegmentedImage segmentedImage)
        //{
        //    // Составляем топографически ориентированный массив пикселей
        //    Pixel[][] imagePixels = new Pixel[segmentedImage.Height][];
        //    for (int i = 0; i < segmentedImage.Height; i++)
        //        imagePixels[i] = new Pixel[segmentedImage.Width];

        //    for (int i = 0; i < segmentedImage.Regions.Count; i++)
        //    {
        //        for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
        //        {
        //            Pixel currentPixel = segmentedImage.Regions[i].RegionPixels[j];
        //            int x = currentPixel.Id[0];
        //            int y = currentPixel.Id[1];

        //            imagePixels[x][y] = currentPixel;
        //        }
        //    }

        //    bool isNeedInteration = true;
        //    int iterationsCount = 1;
        //    while (isNeedInteration && iterationsCount > 0)
        //    {
        //        iterationsCount--;

        //        // настраиваем параметры перед очередной итерацией алгоритма
        //        isNeedInteration = false;
        //        for (int i = 0; i < segmentedImage.Regions.Count; i++)
        //            for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
        //                segmentedImage.Regions[i].RegionPixels[j].IsChecked = false;

        //        //проходимся по всем пикселям изображения в двойном цикле по пикселям регионов
        //        for (int i = 0; i < segmentedImage.Regions.Count; i++)
        //        {
        //            for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
        //            {
        //                Pixel currentPixel = segmentedImage.Regions[i].RegionPixels[j];

        //                if (currentPixel.IsChecked)
        //                    continue; // пропускаем пиксели, которые уже были проверены на данной итерации

        //                currentPixel.IsChecked = true;

        //                // подсчет средней площади для всех регионов
        //                double averageArea = 0.0;
        //                for (int z = 0; z < segmentedImage.Regions.Count; z++)
        //                    averageArea += segmentedImage.Regions[z].Area;
        //                averageArea /= segmentedImage.Regions.Count;

        //                // нахождение расстояния пикселя до региона, в котором он находится сейчас
        //                double selfDistance = 0.0;
        //                // подсчет квадратов разностей элементов векторов условной интенсивности
        //                double selfSum = 0.0;
        //                for (int z = 0; z < currentPixel.ConditionalIntensityFeatures.Length; z++)
        //                {
        //                    // находится квадрат разности элементов вектора условной интенсивности текущего пикселя и текущего региона
        //                    selfSum += (currentPixel.ConditionalIntensityFeatures[z] -
        //                        currentPixel.Region.AverageConditionalIntensityFeature[z]) *
        //                        (currentPixel.ConditionalIntensityFeatures[z] -
        //                        currentPixel.Region.AverageConditionalIntensityFeature[z]);
        //                }
        //                // Евклидово расстояние между векторами условной интенсивности текущего пикселя и текущего региона прибавляется к общему расстоянию
        //                selfDistance += Math.Sqrt(selfSum);

        //                // подсчет квадратов разностей элементов векторов текстурных характеристик
        //                selfSum = 0.0;
        //                for (int z = 0; z < currentPixel.TextureFeatures.Length; z++)
        //                {
        //                    // находится квадрат разности элементов вектора текстурной характеристики текущего пикселя и текущего региона
        //                    selfSum += (currentPixel.TextureFeatures[z] -
        //                        currentPixel.Region.AverageTextureFeature[z]) *
        //                        (currentPixel.TextureFeatures[z] -
        //                        currentPixel.Region.AverageTextureFeature[z]);
        //                }
        //                // Евклидово расстояние между векторами текстурной характеристики текущего пикселя и текущего региона прибавляется к общему расстоянию
        //                selfDistance += Math.Sqrt(selfSum);

        //                // подсчет квадатов разностей элементов вектора геометрической позиции текущего пикселя и центра текущего региона
        //                selfSum = 0.0;
        //                for (int z = 0; z < currentPixel.Id.Length; z++)
        //                {
        //                    // находится квадрат разности элементов вектора геометрической позиции текущего пикселя и центра текущего региона
        //                    selfSum += (currentPixel.Id[z] - currentPixel.Region.SpacialSenterId[z]) *
        //                        (currentPixel.Id[z] - currentPixel.Region.SpacialSenterId[z]);
        //                }
        //                // Евклидово расстояние между векторами геометрического положения текущего пикселя и центра текущего региона прибавляется к общему расстоянию
        //                selfDistance += regularizationParameter * (averageArea / currentPixel.Region.Area) * Math.Sqrt(selfSum);

        //                // массив для хранения расстояний от текущего пикселя до соседних регионов
        //                double[] distances = new double[currentPixel.Region.Neighbors.Count];
        //                // Нахождение расстояния от текущего пикселя до соседних регионов
        //                for (int k = 0; k < currentPixel.Region.Neighbors.Count; k++)
        //                {
        //                    Region currentRegion = currentPixel.Region.Neighbors[k];

        //                    // подсчет квадратов разностей элементов векторов условной интенсивности
        //                    double sum = 0.0;
        //                    for (int z = 0; z < currentPixel.ConditionalIntensityFeatures.Length; z++)
        //                    {
        //                        // находится квадрат разности элементов вектора условной интенсивности текущего пикселя и текущего региона
        //                        sum += (currentPixel.ConditionalIntensityFeatures[z] -
        //                            currentRegion.AverageConditionalIntensityFeature[z]) *
        //                            (currentPixel.ConditionalIntensityFeatures[z] -
        //                            currentRegion.AverageConditionalIntensityFeature[z]);
        //                    }
        //                    // Евклидово расстояние между векторами условной интенсивности текущего пикселя и текущего региона прибавляется к общему расстоянию
        //                    distances[k] += Math.Sqrt(sum);

        //                    // подсчет квадратов разностей элементов векторов текстурных характеристик
        //                    sum = 0.0;
        //                    for (int z = 0; z < currentPixel.TextureFeatures.Length; z++)
        //                    {
        //                        // находится квадрат разности элементов вектора текстурной характеристики текущего пикселя и текущего региона
        //                        sum += (currentPixel.TextureFeatures[z] -
        //                            currentRegion.AverageTextureFeature[z]) *
        //                            (currentPixel.TextureFeatures[z] -
        //                            currentRegion.AverageTextureFeature[z]);
        //                    }
        //                    // Евклидово расстояние между векторами текстурной характеристики текущего пикселя и текущего региона прибавляется к общему расстоянию
        //                    distances[k] += Math.Sqrt(sum);

        //                    // подсчет квадатов разностей элементов вектора геометрической позиции текущего пикселя и центра текущего региона
        //                    sum = 0.0;
        //                    for (int z = 0; z < currentPixel.Id.Length; z++)
        //                    {
        //                        // находится квадрат разности элементов вектора геометрической позиции текущего пикселя и центра текущего региона
        //                        sum += (currentPixel.Id[z] - currentRegion.SpacialSenterId[z]) *
        //                            (currentPixel.Id[z] - currentRegion.SpacialSenterId[z]);
        //                    }
        //                    // Евклидово расстояние между векторами геометрического положения текущего пикселя и центра текущего региона прибавляется к общему расстоянию
        //                    distances[k] += regularizationParameter * (averageArea / currentRegion.Area) * Math.Sqrt(sum);
        //                }

        //                // определение минимального расстояния и перемещение пикселя
        //                double minDistance = distances.Min();
        //                // Выполняем миграцию пикселя тогда, когда до соседнего региона расстояние меньше, чем до собственного
        //                if (minDistance < selfDistance)
        //                {
        //                    for (int k = 0; k < distances.Length; k++)
        //                    {
        //                        if (distances[k] == minDistance)
        //                        {
        //                            // удаляем пиксель из текущего региона и добавляем его в самый близкий для него соседний регион (регион k)
        //                            currentPixel.Region.Neighbors[k].AddPixelWithParametersRecalculation(
        //                                currentPixel.Region.RemovePixelWithParametersRecalculation(currentPixel.Id));

        //                            List<Region> alreadyCheckedRegions = new List<Region>(); // Регионы, все соседи которого перепроверены
        //                            // рассматриваем 8-ми связную окрестность перемещенного пикселя
        //                            for (int x = currentPixel.Id[0] - 1; x <= currentPixel.Id[0] + 1; x++)
        //                            {
        //                                for (int y = currentPixel.Id[1] - 1; y <= currentPixel.Id[1] + 1; y++)
        //                                {
        //                                    // пропускаем пиксель, если такого нет
        //                                    if (x < 0 || x >= segmentedImage.Height || y < 0 || y >= segmentedImage.Width)
        //                                        continue;

        //                                    Pixel firstNeighboringPixel = imagePixels[x][y];

        //                                    // для каждого пикселя из окрестности проводим определение соседства регионов
        //                                    // для каждого из них также рассматриваем 8-ми связную окрестность
        //                                    bool isNeedCheck = false;
        //                                    for (int xx = x - 1; xx <= x + 1; xx++)
        //                                    {
        //                                        for (int yy = y - 1; yy <= y + 1; yy++)
        //                                        {
        //                                            // пропускаем пиксель, если такого нет
        //                                            if (xx < 0 || xx >= segmentedImage.Height || yy < 0 || yy >= segmentedImage.Width)
        //                                                continue;

        //                                            Pixel secondNeighboringPixel = imagePixels[xx][yy];

        //                                            if (firstNeighboringPixel.Region != secondNeighboringPixel.Region)
        //                                            {
        //                                                firstNeighboringPixel.isNeighboring = true;

        //                                                if (firstNeighboringPixel.Region.Neighbors.IndexOf(secondNeighboringPixel.Region) == -1)
        //                                                    firstNeighboringPixel.Region.Neighbors.Add(secondNeighboringPixel.Region);

        //                                                if (secondNeighboringPixel.Region.Neighbors.IndexOf(firstNeighboringPixel.Region) == -1)
        //                                                    secondNeighboringPixel.Region.Neighbors.Add(firstNeighboringPixel.Region);
        //                                            }
        //                                            else
        //                                                // TODO: Возможно, среди регионов firstNeighboringPixel.Region есть регион, с которым firstNeighboringPixel.Region уже не граничит
        //                                                isNeedCheck = true;
        //                                        }
        //                                    }

        //                                    // Если хотя бы один соседний пиксель принадлежит тому же региону и этот регион еще не проверялся, то нужно проверить соседство с соседями
        //                                    if (isNeedCheck && (alreadyCheckedRegions.IndexOf(firstNeighboringPixel.Region) == -1))
        //                                    {
        //                                        List<Region> checkRegions = new List<Region>();
        //                                        for (int z = 0; z < firstNeighboringPixel.Region.RegionPixels.Count; z++)
        //                                        {
        //                                            int pixelX = firstNeighboringPixel.Region.RegionPixels[z].Id[0];
        //                                            int pixelY = firstNeighboringPixel.Region.RegionPixels[z].Id[1];

        //                                            // Рассматриваем 8-ми связную окрестность пикселя
        //                                            for (int xx = pixelX - 1; xx <= pixelX + 1; xx++)
        //                                            {
        //                                                for (int yy = pixelY - 1; yy <= pixelY + 1; yy++)
        //                                                {
        //                                                    // пропускаем пиксель, если такого нет
        //                                                    if (xx < 0 || xx >= segmentedImage.Height || yy < 0 || yy >= segmentedImage.Width)
        //                                                        continue;

        //                                                    // Если в списке соседей такой регион есть
        //                                                    if (firstNeighboringPixel.Region.Neighbors.IndexOf(imagePixels[xx][yy].Region) != -1)
        //                                                    {
        //                                                        // Если в контрольном списке соседей такого региона нет
        //                                                        if (checkRegions.IndexOf(imagePixels[xx][yy].Region) == -1)
        //                                                            checkRegions.Add(imagePixels[xx][yy].Region);
        //                                                    }
        //                                                }
        //                                            }
        //                                        }

        //                                        // Если какой-то регион физическо исчез из соседей, но в списке остался,
        //                                        // то он будет присутствовать в списке соседей, но его не будет в контрольном списке соседей
        //                                        for (int z = 0; z < firstNeighboringPixel.Region.Neighbors.Count; z++)
        //                                        {
        //                                            if (checkRegions.IndexOf(firstNeighboringPixel.Region.Neighbors[z]) == -1)
        //                                                firstNeighboringPixel.Region.Neighbors.RemoveAt(z);
        //                                        }

        //                                        alreadyCheckedRegions.Add(firstNeighboringPixel.Region);
        //                                    }
        //                                }
        //                            }

        //                            if (segmentedImage.Regions[i].RegionPixels.Count != 0) // если в регионе еще есть пиксели
        //                            {
        //                                if (j != segmentedImage.Regions[i].RegionPixels.Count - 1) // если текущий удаленный пиксель не был последним рассматриваемым в регионе
        //                                {
        //                                    // значит удаленный пиксель был внутри региона, в силу особенностей удаления элемента из List
        //                                    // чтобы не пропускать пиксели делаем:
        //                                    j--;
        //                                }
        //                            }

        //                            isNeedInteration = true;
        //                            break;
        //                        }
        //                    }
        //                }
        //            }
        //        }
        //    }
        //}

        /// <summary>
        /// Объединяет близкие по параметрам регионы для получения заданного количества регионов
        /// </summary>
        /// <param name="regularizationParameter">Регуляризационный параметр ламбда, используемый при вычислении геометрического расстояния</param>
        /// <param name="segmentsCount">Требуемое количество регионов</param>
        /// <param name="segmentedImage">Сегментируемое изображение</param>
        //private static void RegionsClassification(double regularizationParameter, int segmentsCount, ref SegmentedImage smg)
        //{
        //    SegmentedImage segmentedImage = smg;
        //    // цикл объединения регионов продолжается до тех пор, пока не будет получено нужное количество регионов
        //    while (segmentedImage.Regions.Count > segmentsCount)
        //    {
        //        // проходимся по всем регионам
        //        for (int i = 0; i < segmentedImage.Regions.Count; i++)
        //        {
        //            // Если нужное количество регионов уже получено
        //            if (segmentedImage.Regions.Count <= segmentsCount)
        //                break;

        //            // массив для хранения расстояний между текущим регионом и всеми другими регионами
        //            double[] distances = new double[segmentedImage.Regions.Count];
        //            for (int j = 0; j < distances.Length; j++)
        //                distances[j] = 0.0;

        //            // сравниваем текущий регион со всеми остальными
        //            //for (int j = 0; j < segmentedImage.Regions.Count; j++)
        //            Parallel.For(0, segmentedImage.Regions.Count, j =>
        //            {
        //                //if (i == j)
        //                //    continue; // пропускаем сравнение региона самого с собой

        //                // подсчет квадратов разностей элементов векторов условной интенсивности
        //                double sum = 0.0;
        //                for (int k = 0; k < segmentedImage.Regions[j].AverageConditionalIntensityFeature.Length; k++)
        //                {
        //                    sum += (segmentedImage.Regions[i].AverageConditionalIntensityFeature[k] -
        //                        segmentedImage.Regions[j].AverageConditionalIntensityFeature[k]) *
        //                        (segmentedImage.Regions[i].AverageConditionalIntensityFeature[k] -
        //                        segmentedImage.Regions[j].AverageConditionalIntensityFeature[k]);
        //                }
        //                distances[j] += Math.Sqrt(sum);

        //                // подсчет квадратов разностей элементов векторов текстурных характеристик
        //                sum = 0.0;
        //                for (int k = 0; k < segmentedImage.Regions[j].AverageTextureFeature.Length; k++)
        //                {
        //                    sum += (segmentedImage.Regions[i].AverageTextureFeature[k] -
        //                        segmentedImage.Regions[j].AverageTextureFeature[k]) *
        //                        (segmentedImage.Regions[i].AverageTextureFeature[k] -
        //                        segmentedImage.Regions[j].AverageTextureFeature[k]);
        //                }
        //                distances[j] += Math.Sqrt(sum);

        //                // подсчет квадатов разностей элементов вектора геометрической позиции сравниваемых регионов
        //                sum = 0.0;
        //                for (int k = 0; k < segmentedImage.Regions[j].SpacialSenterId.Length; k++)
        //                {
        //                    sum += (segmentedImage.Regions[i].SpacialSenterId[k] -
        //                        segmentedImage.Regions[j].SpacialSenterId[k]) *
        //                        (segmentedImage.Regions[i].SpacialSenterId[k] -
        //                        segmentedImage.Regions[j].SpacialSenterId[k]);
        //                }
        //                // подсчет средней площади для всех регионов
        //                double averageArea = 0.0;
        //                for (int k = 0; k < segmentedImage.Regions.Count; k++)
        //                    averageArea += segmentedImage.Regions[k].Area;
        //                averageArea /= segmentedImage.Regions.Count;
        //                distances[j] += regularizationParameter * (averageArea / segmentedImage.Regions[j].Area) * Math.Sqrt(sum);
        //            });

        //            // определение минимального расстояния и перемещение пикселей регионов
        //            double minDistance = distances[0] != 0.0 ? distances[0] : distances[1];
        //            for (int j = 0; j < distances.Length; j++)
        //                if (distances[j] != 0.0 && distances[j] < minDistance) // исключаем сравнение региона с самим собой
        //                    minDistance = distances[j];
        //            for (int j = 0; j < distances.Length; j++)
        //            {
        //                if (distances[j] == minDistance)
        //                {
        //                    // перемещаем пиксели из j-го региона в i-ый регион
        //                    segmentedImage.Regions[i].AddPixelsWithParametersRecalculation(
        //                        segmentedImage.Regions[j].RemovePixels());

        //                    // удаляем пустой регион
        //                    segmentedImage.Regions.RemoveAt(j);

        //                    // если удаленные регион был до i-го региона в списке регионов, то индексы сместились, и,
        //                    // чтобы не пропускать регионы, делаем:
        //                    if (j < i)
        //                        i--;

        //                    break;
        //                }
        //            }
        //        }
        //        smg = segmentedImage;
        //    }
        //}

        private static void RegionsClassification(double regularizationParameter, int segmentsCount, ref SegmentedImage smg)
        {
            SegmentedImage segmentedImage = smg;
            // цикл объединения регионов продолжается до тех пор, пока не будет получено нужное количество регионов
            while (segmentedImage.Regions.Count > segmentsCount)
            {
                // матрица расстояний между регионами (каждого с каждым, по главной диагонали нули)
                double[][] distances = new double[segmentedImage.Regions.Count][];
                for (int i = 0; i < segmentedImage.Regions.Count; i++)
                    distances[i] = new double[segmentedImage.Regions.Count];

                // сравниваем текущий регион i (строка матрицы distances) со всеми остальными j-ми (столбцы матрицы distances)
                //for (int i = 0; i < segmentedImage.Regions.Count; i++)
                Parallel.For(0, segmentedImage.Regions.Count, i =>
                {
                    for (int j = 0; j < segmentedImage.Regions.Count; j++)
                    {
                        // подсчет квадратов разностей элементов векторов условной интенсивности
                        double sum = 0.0;
                        for (int k = 0; k < segmentedImage.Regions[i].AverageConditionalIntensityFeature.Length; k++)
                        {
                            sum += (segmentedImage.Regions[i].AverageConditionalIntensityFeature[k] -
                                segmentedImage.Regions[j].AverageConditionalIntensityFeature[k]) *
                                (segmentedImage.Regions[i].AverageConditionalIntensityFeature[k] -
                                segmentedImage.Regions[j].AverageConditionalIntensityFeature[k]);
                        }
                        distances[i][j] += Math.Sqrt(sum);

                        // подсчет квадратов разностей элементов векторов текстурных характеристик
                        sum = 0.0;
                        for (int k = 0; k < segmentedImage.Regions[i].AverageTextureFeature.Length; k++)
                        {
                            sum += (segmentedImage.Regions[i].AverageTextureFeature[k] -
                                segmentedImage.Regions[j].AverageTextureFeature[k]) *
                                (segmentedImage.Regions[i].AverageTextureFeature[k] -
                                segmentedImage.Regions[j].AverageTextureFeature[k]);
                        }
                        distances[i][j] += Math.Sqrt(sum);

                        // подсчет квадатов разностей элементов вектора геометрической позиции сравниваемых регионов
                        sum = 0.0;
                        for (int k = 0; k < segmentedImage.Regions[i].SpacialSenterId.Length; k++)
                        {
                            sum += (segmentedImage.Regions[i].SpacialSenterId[k] -
                                segmentedImage.Regions[j].SpacialSenterId[k]) *
                                (segmentedImage.Regions[i].SpacialSenterId[k] -
                                segmentedImage.Regions[j].SpacialSenterId[k]);
                        }
                        // подсчет средней площади для всех регионов
                        double averageArea = 0.0;
                        for (int k = 0; k < segmentedImage.Regions.Count; k++)
                            averageArea += segmentedImage.Regions[k].Area;
                        averageArea /= segmentedImage.Regions.Count;
                        distances[i][j] += regularizationParameter * (averageArea / segmentedImage.Regions[j].Area) * Math.Sqrt(sum);
                    }
                });

                int regionsNumberToMerge = segmentedImage.Regions.Count - segmentsCount;
                // 3 списка, позици на которых синхронизированы
                List<int> mergingRegions = new List<int>(); // регионы, которые будут объединяться (соответствуют строкам матрицы distances)
                List<int> regionsToMerge = new List<int>(); // регионы, с которыми будут объединяться (соответствуют столбцам матрицы distances)
                List<double> minDistances = new List<double>(); // минимальны расстояния (пересечение соответствующих строки и столбца матрицы distances)
                for (int i = 0; i < segmentedImage.Regions.Count; i++)
                {
                    // флаг, показывающий, добавлен ли уже текущий рассматриваемый регион в список кандидатов на объединение
                    bool isAlreadyInLists = false;

                    // выходим, если больше регионов нельзя объединить
                    if (regionsNumberToMerge <= 0)
                        break;

                    double minDistance = distances[i][0] != 0.0 ? distances[i][0] : distances[i][1];
                    int minDistanceCol = distances[i][0] != 0.0 ? 0 : 1;
                    for (int j = 0; j < segmentedImage.Regions.Count; j++)
                    {
                        if (distances[i][j] != 0.0 && distances[i][j] < minDistance)
                        {
                            minDistance = distances[i][j];
                            minDistanceCol = j;
                        }
                    }

                    // проверяем, есть ли текущий объедингяющийся регион в списках регионов, с которыми уже хотят объединиться
                    if (regionsToMerge.Contains(i))
                    {
                        int position = regionsToMerge.IndexOf(i);
                        // проверяем равны ли минимумы для этих двух объединений
                        if (minDistance == minDistances[position]) // если равны, то переходим к проверке другого региона
                            continue;
                        else // если не равны, то минимумы для этих двух объединений сравниваются
                        {
                            if (minDistance < minDistances[position])
                            {
                                // заменяем кандидата на объединение на более подходящего кандидата
                                minDistances.RemoveAt(position);
                                mergingRegions.RemoveAt(position);
                                regionsToMerge.RemoveAt(position);

                                minDistances.Add(minDistance);
                                mergingRegions.Add(i);
                                regionsToMerge.Add(minDistanceCol);

                                isAlreadyInLists = true;
                            }
                            else
                                continue; // если регион, который уже хотел с ним объединиться ранее побеждает, то объединение определено и переходим дальше
                        }
                    }

                    // проверяем, если ли среди объединяющихся регионов регион, с которым хочет объединиться текущий регион
                    if (mergingRegions.Contains(minDistanceCol))
                    {
                        int position = mergingRegions.IndexOf(minDistanceCol);
                        if (minDistance < minDistances[position])
                        {
                            // заменяем кандидата на объединение на более подходящего кандидата
                            minDistances.RemoveAt(position);
                            mergingRegions.RemoveAt(position);
                            regionsToMerge.RemoveAt(position);

                            if (!isAlreadyInLists)
                            {
                                minDistances.Add(minDistance);
                                mergingRegions.Add(i);
                                regionsToMerge.Add(minDistanceCol);

                                isAlreadyInLists = true;
                            }
                        }
                        else
                        {
                            if (isAlreadyInLists)
                            {
                                int pos = mergingRegions.IndexOf(i);
                                minDistances.RemoveAt(pos);
                                mergingRegions.RemoveAt(pos);
                                regionsToMerge.RemoveAt(pos);

                                isAlreadyInLists = false;
                            }

                            continue;
                        }
                    }

                    // проверяем, был ли такой такой регион уже в кандитатах на объединение в списке regionsToMerge
                    if (regionsToMerge.Contains(minDistanceCol))
                    {
                        int position = regionsToMerge.IndexOf(minDistanceCol);
                        // оставляем только минимальное значение
                        if (minDistance < minDistances[position])
                        {
                            // заменяем кандидата на объединение на более подходящего кандидата
                            minDistances.RemoveAt(position);
                            mergingRegions.RemoveAt(position);
                            regionsToMerge.RemoveAt(position);

                            if (!isAlreadyInLists)
                            {
                                minDistances.Add(minDistance);
                                mergingRegions.Add(i);
                                regionsToMerge.Add(minDistanceCol);

                                isAlreadyInLists = true;
                            }
                        }
                        else
                        {
                            if (isAlreadyInLists)
                            {
                                int pos = mergingRegions.IndexOf(i);
                                minDistances.RemoveAt(pos);
                                mergingRegions.RemoveAt(pos);
                                regionsToMerge.RemoveAt(pos);

                                isAlreadyInLists = false;
                            }

                            continue;
                        }
                    }
                    else
                    {
                        // добавляем регион к списку кандидатов на объединение
                        minDistances.Add(minDistance);
                        mergingRegions.Add(i);
                        regionsToMerge.Add(minDistanceCol);

                        isAlreadyInLists = true;

                        regionsNumberToMerge--;
                    }
                }

                Parallel.For(0, mergingRegions.Count, i =>
                {
                    // перемещаем пиксели из региона с номером regionsToMerge[i] в регион с номером mergingRegions[i]
                    segmentedImage.Regions[mergingRegions[i]].AddPixelsWithParametersRecalculation(
                    segmentedImage.Regions[regionsToMerge[i]].RemovePixels());
                });

                // удаление пустых регионов
                regionsToMerge.Sort(delegate (int a, int b)
                {
                    return b.CompareTo(a);
                });
                for (int i = 0; i < regionsToMerge.Count; i++)
                    segmentedImage.Regions.RemoveAt(regionsToMerge[i]);

                // определение минимального расстояния и перемещение пикселей регионов
                //double minDistance = distances[0] != 0.0 ? distances[0] : distances[1];
                //for (int j = 0; j < distances.Length; j++)
                //    if (distances[j] != 0.0 && distances[j] < minDistance) // исключаем сравнение региона с самим собой
                //        minDistance = distances[j];
                //for (int j = 0; j < distances.Length; j++)
                //{
                //    if (distances[j] == minDistance)
                //    {
                //        // перемещаем пиксели из j-го региона в i-ый регион
                //        segmentedImage.Regions[i].AddPixelsWithParametersRecalculation(
                //            segmentedImage.Regions[j].RemovePixels());

                //        // удаляем пустой регион
                //        segmentedImage.Regions.RemoveAt(j);

                //        // если удаленные регион был до i-го региона в списке регионов, то индексы сместились, и,
                //        // чтобы не пропускать регионы, делаем:
                //        if (j < i)
                //            i--;

                //        break;
                //    }
                //}
                smg = segmentedImage;
            }
        }
        public static int requiredSegmentsCount = 30; // требуемое количество регионов

        public static SegmentedImage PerformSegmentation(Bitmap image, int defaultSegmentsNumber, int requiredSegmentsNumber)
        {
            defaultSegmentsCount = defaultSegmentsNumber;
            requiredSegmentsCount = requiredSegmentsNumber;

            int imageWidth = image.Width;
            int imageHeight = image.Height;
            int imageSize = imageWidth * imageHeight;
            int[][] imageColorData = new int[imageSize][];
            for (int i = 0; i < imageSize; i++)
                imageColorData[i] = new int[3];

            //Logger logger = new Logger("../../Log/log_performance.txt");
            //logger.WriteLog("Начало алгоритма сегментации\r\n");
            //DateTime start = DateTime.Now;

            //logger.WriteLog("Загрузка изображения: ");
            //DateTime loadImageTime = DateTime.Now;
            // заполнение массива цветов изображения - загрузка изображения
            ImageProcessing.LoadImage(image, ref imageColorData, ref imageHeight, ref imageWidth);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - loadImageTime));

            // подбор начального размера сегмента, исходя из расчета максимум defaultSegmentsCount начальных сегментов на изображении
            int segmentSize = defaultSegmentSize < ((int)Math.Sqrt(imageHeight * imageWidth / ((double)defaultSegmentsCount))) ?
                ((int)Math.Ceiling(Math.Sqrt(imageHeight * imageWidth / ((double)defaultSegmentsCount)))) : defaultSegmentSize;

            //logger.WriteLog("Создание начального сегментированного изображения: ");
            //DateTime defaultSegmentsTime = DateTime.Now;
            // создание начального сегментированного изображения
            SegmentedImage segmentedImage = new SegmentedImage(imageColorData, imageHeight, imageWidth, segmentSize);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - defaultSegmentsTime));

            //logger.WriteLog("Получение и заполнение Lab-данных: ");
            //DateTime labDataTime = DateTime.Now;
            // заполнение L*a*b данных для каждого пикселя сегмента
            FillSegmentedImageIntensityFeatures(imageColorData, ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - labDataTime));

            //logger.WriteLog("Получение и заполнение текстурных характеристик: ");
            //DateTime textureFeaturesTime = DateTime.Now;
            // заполние текстурных характеристик для каждого пикселя изображения
            FillSegmentedImageTextureFeatures(imageColorData, ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - textureFeaturesTime));

            //logger.WriteLog("Выполнение условной фильтрации: ");
            //DateTime conditionalFilteringTime = DateTime.Now;
            // выполнение условной фильтрации и заполние характеристик интенсивности пикселей после фильтрации
            PerformConditionalIntencityFiltering(ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - conditionalFilteringTime));

            //logger.WriteLog("Выполнение перерасчета параметров регионов: ");
            //DateTime calcRegionParamsTime1 = DateTime.Now;
            // выполние рассчета всех параметров регионов после заполнения всех параметров пикселей
            Parallel.For(0, segmentedImage.Regions.Count, i =>
            {
                segmentedImage.Regions[i].CalculateParameters();
            });
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - calcRegionParamsTime1));

            // Раскоментрпорвать только при использовании альтернативного метода KMCCClassification
            //logger.WriteLog("Первое определение соседства регионов: ");
            //DateTime firstNeighborsTime = DateTime.Now;
            //// определение соседства регионов
            //CalculateNeighborhood(ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - firstNeighborsTime));

            //logger.WriteLog("Выполнение этапа миграции пикселей: ");
            //DateTime pixelMigrationTime = DateTime.Now;
            // классификация пикселей на основе KMCC алгоритма
            KMCCClassification(regularizationParameter, ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - pixelMigrationTime));

            //logger.WriteLog("Выполнение этапа объединения регионов: ");
            //DateTime regionsMergingTime = DateTime.Now;
            // объединение полученных регионов
            RegionsClassification(regularizationParameter, requiredSegmentsCount, ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - regionsMergingTime));

            // постобработка результатов сегментации

            //logger.WriteLog("Пространственное разделение регионов: ");
            //DateTime regionsSplitingTime = DateTime.Now;
            // отделение пространственно разделенных частей регионов
            SplitRegions(ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - regionsSplitingTime));

            //logger.WriteLog("Выполнение перерасчета параметров регионов: ");
            //DateTime calcRegionParamsTime2 = DateTime.Now;
            // Пересчитываем все параметры регионов, т.к. количество регионов изменилось
            Parallel.For(0, segmentedImage.Regions.Count, i =>
            {
                segmentedImage.Regions[i].CalculateParameters();
            });
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - calcRegionParamsTime2));

            //logger.WriteLog("Второе определение соседства регионов: ");
            //DateTime SecondNeighborsTime = DateTime.Now;
            // определение соседства регионов
            CalculateNeighborhood(ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - SecondNeighborsTime));

            //logger.WriteLog("Удаление маленьких регионов: ");
            //DateTime removingSmallRegions = DateTime.Now;
            // сливаем маленькие регионы с соседними регионами
            RemoveSmallRegions(ref segmentedImage);
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - removingSmallRegions));

            //logger.WriteLog("Завершение сегментации, все время сегментации: ");
            //logger.WriteLog(string.Format("{0}\r\n", DateTime.Now - start));

            return segmentedImage;
        }
        /// <summary>
        /// Удаляет регионы, размером меньше, чем lowThresholdForRegionSize часть изображения путем слияния с наиболее близкими регионами
        /// </summary>
        /// <param name="segmentedImage">Сегментируемое изображение</param>
        public static void RemoveSmallRegions(ref SegmentedImage segmentedImage)
        {
            // сортируем регионы по количеству пикселей в них - по возрастанию
            segmentedImage.Regions.Sort(delegate (Region a, Region b)
            {
                return a.RegionPixels.Count.CompareTo(b.RegionPixels.Count);
            });

            int minSegmentSize = (int)Math.Round(lowThresholdForRegionSize * segmentedImage.Width * segmentedImage.Height);

            for (int i = 0; i < segmentedImage.Regions.Count; i++)
            {
                // выходим из цикла после обработки всех маленьких сегментов
                if (segmentedImage.Regions[i].RegionPixels.Count > minSegmentSize)
                    break;

                // массив для хранения расстояний между текущим регионом и его соседними регионами
                double[] distances = new double[segmentedImage.Regions[i].Neighbors.Count];
                for (int j = 0; j < distances.Length; j++)
                    distances[j] = 0.0;

                // сравниваем текущий регион со всеми соседними
                for (int j = 0; j < segmentedImage.Regions[i].Neighbors.Count; j++)
                //Parallel.For(0, segmentedImage.Regions[i].Neighbors.Count, j =>
                {
                    Region neighbor = segmentedImage.Regions[i].Neighbors[j];

                    double sum = 0.0;
                    // подсчет квадратов разностей элементов векторов условной интенсивности
                    for (int k = 0; k < neighbor.AverageConditionalIntensityFeature.Length; k++)
                    {
                        sum += (segmentedImage.Regions[i].AverageConditionalIntensityFeature[k] -
                            neighbor.AverageConditionalIntensityFeature[k]) *
                            (segmentedImage.Regions[i].AverageConditionalIntensityFeature[k] -
                            neighbor.AverageConditionalIntensityFeature[k]);
                    }
                    distances[j] += Math.Sqrt(sum);

                    // подсчет квадратов разностей элементов векторов текстурных характеристик
                    sum = 0.0;
                    for (int k = 0; k < neighbor.AverageTextureFeature.Length; k++)
                    {
                        sum += (segmentedImage.Regions[i].AverageTextureFeature[k] -
                            neighbor.AverageTextureFeature[k]) *
                            (segmentedImage.Regions[i].AverageTextureFeature[k] -
                            neighbor.AverageTextureFeature[k]);
                    }
                    distances[j] += Math.Sqrt(sum);

                    // подсчет квадатов разностей элементов вектора геометрической позиции сравниваемых регионов
                    sum = 0.0;
                    for (int k = 0; k < neighbor.SpacialSenterId.Length; k++)
                    {
                        sum += (segmentedImage.Regions[i].SpacialSenterId[k] -
                            neighbor.SpacialSenterId[k]) *
                            (segmentedImage.Regions[i].SpacialSenterId[k] -
                            neighbor.SpacialSenterId[k]);
                    }
                    // подсчет средней площади для всех оцениваемых регионов
                    double averageArea = segmentedImage.Regions[i].Area;
                    for (int k = 0; k < segmentedImage.Regions[i].Neighbors.Count; k++)
                        averageArea += segmentedImage.Regions[i].Neighbors[k].Area;
                    averageArea /= segmentedImage.Regions[i].Neighbors.Count + 1;
                    distances[j] += regularizationParameter * (averageArea / neighbor.Area) * Math.Sqrt(sum);
                }

                // определение минимального расстояния и перемещение пикселей регионов
                double minDistance = distances[0] != 0.0 ? distances[0] : distances[1];
                for (int j = 0; j < distances.Length; j++)
                    if (distances[j] != 0.0 && distances[j] < minDistance)
                        minDistance = distances[j];
                for (int j = 0; j < distances.Length; j++)
                {
                    if (distances[j] == minDistance)
                    {
                        // самый близкий к текущему маленькому региону поглощает текущий регион
                        segmentedImage.Regions[i].Neighbors[j].AddPixelsWithParametersRecalculation(
                            segmentedImage.Regions[i].RemovePixels());

                        // регион, который поглотил текущий регион, должен знать о своих новых соседях, доставшихся ему от старого региона
                        for (int k = 0; k < segmentedImage.Regions[i].Neighbors.Count; k++)
                        {
                            if (segmentedImage.Regions[i].Neighbors[j].Neighbors.IndexOf(segmentedImage.Regions[i].Neighbors[k]) == -1)
                                segmentedImage.Regions[i].Neighbors[j].Neighbors.Add(segmentedImage.Regions[i].Neighbors[k]);
                        }

                        // соседи текущего региона должны должны знать о новом регионе
                        for (int k = 0; k < segmentedImage.Regions[i].Neighbors.Count; k++)
                        {
                            // если среди соседей мы наткнулись на тот регион, который только что поглотил текущий регион, пропускаем его
                            if (segmentedImage.Regions[i].Neighbors[k] == segmentedImage.Regions[i].Neighbors[j])
                                continue;

                            // для всех остальных
                            // добавляем им в качестве соседа регион, который только что поглотил текущий регион
                            if (segmentedImage.Regions[i].Neighbors[k].Neighbors.IndexOf(segmentedImage.Regions[i].Neighbors[j]) == -1)
                                segmentedImage.Regions[i].Neighbors[k].Neighbors.Add(segmentedImage.Regions[i].Neighbors[j]);
                        }

                        // все соседи текущего поглощенного региона должны дружно забыть о нем, как о соседе
                        for (int k = 0; k < segmentedImage.Regions[i].Neighbors.Count; k++)
                        {
                            int oldRegionIndex = segmentedImage.Regions[i].Neighbors[k].Neighbors.IndexOf(segmentedImage.Regions[i]);
                            if (oldRegionIndex != -1)
                                segmentedImage.Regions[i].Neighbors[k].Neighbors.RemoveAt(oldRegionIndex);
                        }

                        // удаляем пустой текущий регион
                        segmentedImage.Regions.RemoveAt(i);

                        // поскольку мы текущий регион удалили, то, чтобы не пропускать следующие регионы, делаем
                        i--;

                        break;
                    }
                }
            }
        }
        /// <summary>
        /// Заполняет списки id соседних регионов для каждого региона, помечает пиксели, как граничные или неграничные
        /// </summary>
        /// <param name="segmentedImage">Сегментируемое изображение</param>
        private static void CalculateNeighborhood(ref SegmentedImage segmentedImage)
        {
            // Составляем топографически ориентированный массив пикселей
            Pixel[][] imagePixels = new Pixel[segmentedImage.Height][];
            for (int i = 0; i < segmentedImage.Height; i++)
                imagePixels[i] = new Pixel[segmentedImage.Width];

            for (int i = 0; i < segmentedImage.Regions.Count; i++)
            {
                segmentedImage.Regions[i].Neighbors.Clear();
                for (int j = 0; j < segmentedImage.Regions[i].RegionPixels.Count; j++)
                {
                    Pixel currentPixel = segmentedImage.Regions[i].RegionPixels[j];
                    currentPixel.isNeighboring = false;
                    int x = currentPixel.Id[0];
                    int y = currentPixel.Id[1];

                    imagePixels[x][y] = currentPixel;
                }
            }

            for (int i = 0; i < segmentedImage.Height; i++)
            {
                for (int j = 0; j < segmentedImage.Width; j++)
                {
                    Pixel currentPixel = imagePixels[i][j];
                    // Рассматриваем 8-ми связную окрестность текущего пикселя
                    for (int x = i - 1; x <= i + 1; x++)
                    {
                        for (int y = j - 1; y <= j + 1; y++)
                        {
                            // пропускаем пиксель, если такого нет
                            if (x < 0 || x >= segmentedImage.Height || y < 0 || y >= segmentedImage.Width)
                                continue;

                            Pixel neighborPixel = imagePixels[x][y];

                            // Если соседний пиксель относится к другому региону
                            if (currentPixel.Region != neighborPixel.Region)
                            {
                                currentPixel.isNeighboring = true;

                                // Если такой регион еще не занесен в списки соседних регионов текущего региона
                                if (currentPixel.Region.Neighbors.IndexOf(neighborPixel.Region) == -1)
                                    currentPixel.Region.Neighbors.Add(neighborPixel.Region);
                            }
                        }
                    }
                }
            }
        }