/// <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> /// <returns>Разница между пикселями.</returns> private double Difference(LockBitmap lockBitmap, int x1, int y1, int x2, int y2) { double diffR = Math.Pow((int)lockBitmap.GetPixel(x1, y1).R - lockBitmap.GetPixel(x2, y2).R, 2); double diffG = Math.Pow((int)lockBitmap.GetPixel(x1, y1).G - lockBitmap.GetPixel(x2, y2).G, 2); double diffB = Math.Pow((int)lockBitmap.GetPixel(x1, y1).B - lockBitmap.GetPixel(x2, y2).B, 2); return(Math.Sqrt(diffR + diffG + diffB)); }
/// <summary> /// Осуществляет размытие данного изображения. /// </summary> /// <param name="inputImage">Изображение, которое будет размыто.</param> /// <returns>Размытое изображение.</returns> public LockBitmap SmoothImage(LockBitmap inputImage) { double[] mask = CreateConvolutionVector(); NormalizeVector(mask); //дважды применяем маску, чтобы осуществить размытие как по горизонтали, так и по вертикали LockBitmap smoothedImage = ApplyMask(inputImage, mask); smoothedImage = ApplyMask(smoothedImage, mask); return(smoothedImage); }
/// <summary> /// Строит граф, в котором ребра соединяют все соседние пиксели (в том числе и по диагонали). /// </summary> /// <param name="lockBitmap">Изображение, для которого строится граф.</param> /// <param name="count">Число ребер в выходном массиве ребер.</param> /// <returns>Массив ребер графа.</returns> private Edge[] BuildGraph(LockBitmap lockBitmap, out int count) { lockBitmap.LockBits(); int width = lockBitmap.Width - 1; int height = lockBitmap.Height - 1; //от каждого пикселя строится 4 ребра: вправо, вниз, вправо-вниз, вправо-вверх. Edge[] edges = new Edge[width * height * 4]; count = 0; for (int y = 0; y < height; y++) { int YtimesWidth = y * width; //вершины нумеруются построчно for (int x = 0; x < width; x++) { if (x < width - 1) { edges[count].A = YtimesWidth + x; edges[count].B = YtimesWidth + (x + 1); edges[count].W = Difference(lockBitmap, x, y, x + 1, y); count++; } if (y < width - 1) { edges[count].A = YtimesWidth + x; edges[count].B = YtimesWidth + width + x; edges[count].W = Difference(lockBitmap, x, y, x, y + 1); count++; } if (x < width - 1 && y < height - 1) { edges[count].A = YtimesWidth + x; edges[count].B = YtimesWidth + width + (x + 1); edges[count].W = Difference(lockBitmap, x, y, x + 1, y + 1); count++; } if (x < width - 1 && y > 0) { edges[count].A = YtimesWidth + x; edges[count].B = YtimesWidth - width + x + 1; edges[count].W = Difference(lockBitmap, x, y, x + 1, y - 1); count++; } } } lockBitmap.UnlockBits(); return(edges); }
/// <summary> /// Применяет вектор скручивания к исходному изображению и на его основе формирует новое изображение. /// </summary> /// <param name="inputBitmap">Исходное изображение.</param> /// <param name="mask">Вектор скручивания.</param> /// <returns>Новое повернутое изображение, созданное на основе исходного с примененим вектора скручивания.</returns> private LockBitmap ApplyMask(LockBitmap inputBitmap, double[] mask) { inputBitmap.LockBits(); Bitmap outputBitmap = new Bitmap(inputBitmap.Height, inputBitmap.Width); LockBitmap lockBitmap = new LockBitmap(outputBitmap); lockBitmap.LockBits(); //проходим строчку за строчкой, для каждого элемента вычисляя определенную сумму for (int y = 0; y < inputBitmap.Height; y++) { for (int x = 0; x < inputBitmap.Width; x++) { Color color = inputBitmap.GetPixel(x, y); double sumR = mask[0] * color.R; double sumG = mask[0] * color.G; double sumB = mask[0] * color.B; for (int i = 1; i < mask.Length; i++) { //просмотр соседних пикселей на расстоянии i от заданного координатам (x,y) Color leftColor = inputBitmap.GetPixel(Math.Max(x - i, 0), y); Color rightColor = inputBitmap.GetPixel(Math.Min(x + i, inputBitmap.Width - 1), y); sumR += mask[i] * (leftColor.R + rightColor.R); sumG += mask[i] * (leftColor.G + rightColor.G); sumB += mask[i] * (leftColor.B + rightColor.B); } lockBitmap.SetPixel(y, x, Color.FromArgb((int)sumR, (int)sumG, (int)sumB)); } } lockBitmap.UnlockBits(); inputBitmap.UnlockBits(); return(lockBitmap); }
/// <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); }