/// <summary>
        /// Выполняет эффективную сегментацию изображения.
        /// </summary>
        /// <param name="inputBitmap">Изображение, которое будет сегментировано.</param>
        /// <param name="outputFolder">Путь к папке, в которую нужно сохранить изображение.</param>
        /// <param name="segmentOut">Массив представителей для каждого пикселя.</param>
        /// <returns>Сегментированное изображение.</returns>
        public Bitmap ExecuteEfficientSegmentation(Bitmap inputBitmap, string outputFolder, out int[] segmentOut)
        {
            Bitmap outputBitmap = new Bitmap(inputBitmap.Width, inputBitmap.Height);

            using (Graphics graphics = Graphics.FromImage(outputBitmap))                      //рисует на поверхности smoothedImage
            {
                graphics.DrawImage(inputBitmap, 0, 0, inputBitmap.Width, inputBitmap.Height); //рисует на поверхности изображение inputBitmap
            }

            //обернуть изображение в lockBitmap и далее пользоваться им
            LockBitmap lockBitmap = new LockBitmap(outputBitmap);

            lockBitmap = new GaussianBlur(Sigma).SmoothImage(lockBitmap);

            int edgesCount; //число ребер в графе

            Edge[]      edges       = BuildGraph(lockBitmap, out edgesCount);
            DisjointSet disjointSet = SegmentGraph(edges, edgesCount, lockBitmap.Width * lockBitmap.Height);

            JointSmallSegments(disjointSet, edges, edgesCount);

            segmentOut = Colorize(lockBitmap, disjointSet);

            return(lockBitmap.Source);
        }
        /// <summary>
        /// Строит на основе графа лес минимальных остовных деревьев, каждое из которых представляет сегмент изображения.
        /// </summary>
        /// <param name="edges">Набор ребер, представляющих исходный граф.</param>
        /// <param name="edgesCount">Число ребер в графе.</param>
        /// <param name="numVerticies">Число вершин в графе.</param>
        /// <returns>Система непересекающихся множеств, каждое из которых представляет собой сегмент изображения.</returns>
        private DisjointSet SegmentGraph(Edge[] edges, int edgesCount, int numVerticies)
        {
            QuickSort(edges, 0, edgesCount - 1);

            DisjointSet disjointSet = new DisjointSet(numVerticies);

            //значение пороговой функции изначально дано для каждой вершины
            double[] thresholdValues = new double[numVerticies];
            for (int i = 0; i < numVerticies; i++)
            {
                thresholdValues[i] = ThresholdFunction(1);
            }

            for (int i = 0; i < edgesCount; i++)
            {
                //найти представителей сегментов, которые соединяются этим ребром
                int a = disjointSet.Find(edges[i].A);
                int b = disjointSet.Find(edges[i].B);

                if (a != b)
                {
                    if (edges[i].W <= thresholdValues[a] && edges[i].W <= thresholdValues[b])
                    {
                        disjointSet.Joint(a, b);
                        a = disjointSet.Find(a);
                        thresholdValues[a] = edges[i].W + ThresholdFunction(disjointSet.Size(a));
                    }
                }
            }

            return(disjointSet);
        }
 /// <summary>
 /// Объединяет маленькие сегменты.
 /// </summary>
 /// <param name="disjointSet">Система непересекающихся множеств, представляющая сегменты.</param>
 /// <param name="edges">Массив всех ребер изображения.</param>
 /// <param name="count">Число всех ребер изображения.</param>
 private void JointSmallSegments(DisjointSet disjointSet, Edge[] edges, int edgesCount)
 {
     for (int i = 0; i < edgesCount; i++)
     {
         int a = disjointSet.Find(edges[i].A);
         int b = disjointSet.Find(edges[i].B);
         if (a != b && (disjointSet.Size(a) < MinSize || disjointSet.Size(b) < MinSize))
         {
             disjointSet.Joint(a, b);
         }
     }
 }
        /// <summary>
        /// Раскрашивает изображение.
        /// </summary>
        /// <param name="lockBitmap">Изображение, которое будет раскрашено.</param>
        /// <param name="disjointSet">Система непересекающихся множеств, описывающая сегменты.</param>
        /// <returns>Массив, в котором каждому пикселю поставлен в соответствие представитель множества, которому он принадлежит.</returns>
        private int[] Colorize(LockBitmap lockBitmap, DisjointSet disjointSet)
        {
            lockBitmap.LockBits();
            int width  = lockBitmap.Width;
            int height = lockBitmap.Height;

            int[] segmentOut = new int[width * height]; //массив представителей для каждого пикселя (обход - построчно)


            //создадим массив случайных цветов для каждого пикселя
            //но на практике понадобятся не все цвета, а только цвет представителя каждого сегмента
            Random random = new Random();

            Color[] colors = new Color[width * height];
            for (int i = 0; i < colors.Length; i++)
            {
                colors[i] = Color.FromArgb(random.Next(0, 256), random.Next(0, 256), random.Next(0, 256));
            }

            int temp;

            for (int y = 0; y < height - 1; y++)
            {
                int YtimesWidth = y * (width - 1);
                temp = y;
                for (int x = 0; x < width - 1; x++)
                {
                    int parent = disjointSet.Find(YtimesWidth + x);
                    lockBitmap.SetPixel(x, y, colors[parent]);
                    segmentOut[temp] = parent;
                    temp            += height;
                }
            }

            lockBitmap.UnlockBits();
            return(segmentOut);
        }