/// <summary> /// 2й по важности метод класса. Разбивает массив точек на контура, в зависимости от того, с какими областями\объектами они граничат. В связке с <see cref="ImagePrData"/> фактически полностью векторизирует изображение. /// </summary> /// <param name="arr">Массив для выделения контуров</param> private VPointEx[][] SortPointArray(VPointEx[] arr) { VPointEx[][] result = new VPointEx[0][]; while (arr.Length > 0) { var a = arr[0].BordWith; var tm = new VPointEx[0]; Helper.InsertToArray(ref tm, arr[0]); Helper.DeleteFromArray(1, ref arr); var needtodelete = new int[0]; for (int i = 0; i <= arr.Length - 1; i++) { if (arr[i].BordWith == a) { Helper.InsertToArray(ref tm, arr[i]); Helper.InsertToArray(ref needtodelete, i + 1); } } if (arr.Length != 0) { if (needtodelete != null) { Array.Sort(needtodelete); for (int ii = needtodelete.Length - 1; ii >= 0; ii--) { Helper.DeleteFromArray(needtodelete[ii], ref arr); } } } Helper.InsertToArray(ref result, tm); } return(result); }
/// <summary> /// Сортировка контуров так, чтобы расстояние между ними было минимальное, и перо совершало найменшее количество перемещений. /// </summary> /// <param name="Arr"></param> private void CSort(ref VPointEx[][] Arr) { var res = new VPointEx[0][]; var checked_ = new VPointEx[0]; var first = Arr[0]; checked_ = first; var uncheckedmask = new bool[Arr.Length]; for (int ii = 0; ii <= Arr.Length; ii++) { var mostshort = 99999.9; int num = 0; for (int i = 0; i <= Arr.Length - 1; i++) { if (!uncheckedmask[i]) { var dist = Distance(checked_[checked_.Length - 1].BasePoint, Arr[i][0].BasePoint); if (dist < mostshort) { mostshort = dist; num = i; } } } checked_ = Arr[num]; uncheckedmask[num] = true; Helper.InsertToArray(ref res, checked_); } Arr = res; }
/// <summary> /// Загружает вектор формата .PCV. /// </summary> /// <param name="filename">Имя файла для загрузки</param> private void LoadVectPCV(string filename) { VPointEx[][] contours = new VPointEx[0][]; string s = new string(GetBytesAndRemoveHeader(filename).Select(pp => (char)pp).ToArray()); //TODO: Это по прежнему не совместимо с PCV старого образца, с VectArch. //Нид сделать что-то по типу удаления того "хедера" и читать его. var data = s.Split(';'); var head = new VectHeader(); var header_ = data[0].Split(','); head.Width = float.Parse(header_[1], CultureInfo.InvariantCulture); head.Height = float.Parse(header_[2], CultureInfo.InvariantCulture); switch (header_[3].ToLower().Trim()) { case ("rastr"): head.VectType = VectType.Rastr; break; case ("func"): head.VectType = VectType.Func; break; case ("curve"): head.VectType = VectType.Curve; break; case ("svgvector"): head.VectType = VectType.SvgVector; break; default: throw new ArgumentException("Указан неверный тип вектора"); } //try { head.CountOfCont = int.Parse(header_[4]); } catch { throw new ArgumentException("Указано неверное число контуров вектора"); } var p = data[1].Split('?'); head.CountOfCont = p.Length; contours = new VPointEx[p.Length][]; for (int i = 0; i <= p.Length - 1; i++) { var data1 = p[i]; var data2 = data1.Split(':'); var countofpoints = data2.Length; contours[i] = new VPointEx[countofpoints - 1]; for (int ii = 0; ii <= data2.Length - 2; ii++) { var Coordinates = data2[ii].Split(','); var xs = Coordinates[0]; var ys = Coordinates[1]; contours[i][ii] = new VPointEx(float.Parse(xs, CultureInfo.InvariantCulture), float.Parse(ys, CultureInfo.InvariantCulture), 0, Color.Black); } } Helper.DeleteFromArray(contours.Length, ref contours); Header = head; RawData = contours; }
/// <summary> /// Находит разницу двух точек и возвращает результаты типа <see cref="Int16"/>, как массив 4х байтов. /// </summary> /// <param name="pn1">Первая точка.</param> /// <param name="pn2">Вторая точка.</param> private byte[] DeltaPoints(VPointEx p1, VPointEx p2) { var dx = BitConverter.GetBytes((Int16)(-p1.BasePoint.X + p2.BasePoint.X)); var dy = BitConverter.GetBytes((Int16)(-p1.BasePoint.Y + p2.BasePoint.Y)); var bytes = new byte[4]; Buffer.BlockCopy(dx, 0, bytes, 0, 2); Buffer.BlockCopy(dy, 0, bytes, 2, 2); return(bytes); }
/// <summary> /// Загружает вектор формата .PRRES. /// </summary> /// <param name="FileName">Имя файла для загрузки.</param> private void LoadVectPrres(string FileName) { VPointEx[][] contours;// = new VPointEx[0][]; var s = File.ReadAllText(FileName); var main = s.Split('$'); var header = main[0].Split(';'); VectHeader headerr = new VectHeader(); if (header[0] == "prres") { headerr.Width = int.Parse(header[1]); headerr.Height = int.Parse(header[2]); headerr.CountOfCont = int.Parse(header[6]); headerr.VectType = VectType.Rastr; //TODO sdelat` normalnoe opredelenie tipa vectora contours = new VPointEx[headerr.CountOfCont][]; for (int i = 0; i <= headerr.CountOfCont - 1; i++) { var data1 = main[i + 1].Split('?'); var countofpoints = data1[0]; contours[i] = new VPointEx[int.Parse(countofpoints)]; var data2 = data1[1].Split(';'); for (int j = 0; j <= data2.Length - 2; j++) { var coordinates = data2[j].Split(','); var xs = coordinates[0]; Helper.Delete(ref xs, 1, 1); var ys = coordinates[1]; Helper.Delete(ref ys, ys.Length, 1); contours[i][j] = new VPointEx(int.Parse(xs), int.Parse(ys), 0, Color.Black); } } } else { return; } Header = headerr; RawData = contours; }
/// <summary> /// Основной метод класса. Выделяет те самые массивы точек, которые методом <see cref="SortPointArray(VPointEx[])"/> бьются на контура. Сортирует их и чистит от хлама. Возвращает и берет данные с соответсвущих словарей. Многопоточная операция. /// </summary> private void ImagePrData() { // == ШАГ 1: ОБЪЯВЛЕНИЕ ЛОКАЛЬНЫХ ПЕРЕМЕННЫХ == //Основной счетчик. Обозначает уникальный номер области или объекта. int AreaMark = 0; //Результирующий массив контуров. var Contours = new VPointEx[0][]; //Изначальное биннарное представление изображения. Если пиксель равен (0,0,0), то digMap на этом индексе будет равен 1. var digMap = new int[0][]; //Само изображение данного потока. var ProceedImage = _imgMap[Thread.CurrentThread.ManagedThreadId]; //Массив, с обозначенными на нем областями. var WhiteMap = new int[0][]; //Массив цветов изображения. Color[][] BufferImage = new Color[0][]; // == ШАГ 2: ПОДГОТОВКА == //Вывод информации о выполении новой операции. if (_outputDebugInfo) { Console.WriteLine(string.Format(_initSay, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.ThreadState, Thread.CurrentThread.Priority, (Milliseconds / 1000))); Console.WriteLine(string.Format(_stateSay, Thread.CurrentThread.ManagedThreadId, "Init", (Milliseconds / 1000))); } _proList[Thread.CurrentThread.ManagedThreadId]++; //Задаем размер BufferImage. Helper.ResizeArray(ref BufferImage, ProceedImage.Height, ProceedImage.Width); //Задаем размер digMap. Helper.ResizeArray(ref digMap, BufferImage.Length, BufferImage[0].Length); //Заполняем BufferImage цветами из ProceedImage. for (int i = 0; i <= ProceedImage.Width - 1; i++) { for (int ii = 0; ii <= ProceedImage.Height - 1; ii++) { BufferImage[ii][i] = ProceedImage.GetPixel(i, ii); } } //Заполняем digMap по вышеуказанному правилу. for (int i = 0; i <= BufferImage.Length - 2; i++) { for (int ii = 0; ii <= BufferImage[0].Length - 2; ii++) { if (BufferImage[i][ii].R == 0 && BufferImage[i][ii].G == 0 && BufferImage[i][ii].B == 0) { digMap[i][ii] = 1; } } } //Добавляем digMap по 2 пустых ячейки с каждой стороны. Surround(ref digMap); Surround(ref digMap); //Создаем копию digMap. WhiteMap = digMap; // == ШАГ 3: ПОИСК ОБЛАСТЕЙ == //Вывод информации о выполении новой операции. if (_outputDebugInfo) { Console.WriteLine(string.Format(_stateSay, Thread.CurrentThread.ManagedThreadId, "Searching For Areas", (Milliseconds / 1000))); } _proList[Thread.CurrentThread.ManagedThreadId]++; //Присваиваем 3, потому что 0,1 и 2 зарезервированы. AreaMark = 3; //Для каждого пикселя выполняем. for (int i = 1; i <= BufferImage.Length - 2; i++) { for (int ii = 1; ii <= BufferImage[0].Length - 2; ii++) { //Если, проверяеммый пиксель пустой (что есть условием существования области) if (WhiteMap[i][ii] == 0) { //Обозначиваем этот пиксель уникальным для области номером. WhiteMap[i][ii] = AreaMark; //Следующая операция выполняется для присваивания всем пикселям данной области, одного и того же номера. //Некий аналог рекурсивного метода поиска объектов. while (true) { //Указывает на то, были ли произведены какие-либо изменения. bool changedSomething = false; //Для каждого пикселя выполняем. for (int i2 = 1; i2 <= BufferImage.Length - 2; i2++) { for (int ii2 = 1; ii2 <= BufferImage[0].Length - 2; ii2++) { //Если есть пиксель, рядом с пикселем, помеченным нашим текущем AreaMark, то... if (WhiteMap[i2][ii2] == 0 && NearK(WhiteMap, i2, ii2, AreaMark)) { //...и этот пиксель помечаем, как AreaMark. WhiteMap[i2][ii2] = AreaMark; //Помечаем, что изменили что-то. changedSomething = true; } } } if (!changedSomething) { break; } } AreaMark++; } } } // == ШАГ 4: ПОИСК ОБЪЕКТОВ == //Вывод информации о выполении новой операции. if (_outputDebugInfo) { Console.WriteLine(string.Format(_stateSay, Thread.CurrentThread.ManagedThreadId, "Searching For Objects", (Milliseconds / 1000))); } _proList[Thread.CurrentThread.ManagedThreadId]++; //Создаем копию digMap. var MaskArray = digMap; //Для отделения областей от объектов, добавляем к счетчику еще 2. var ObjectMark = AreaMark + 2; //Для каждого пикселя выполняем. for (int i = 1; i <= BufferImage.Length - 2; i++) { for (int ii = 1; ii <= BufferImage[0].Length - 2; ii++) { //Если, проверяеммый пиксель не пустой (что есть условием существования объекта) if (MaskArray[i][ii] == 1) { //tempPointArray - временный массив несортированых точек. VPointEx[] tempPointArray = new VPointEx[0]; //Обозначиваем этот пиксель уникальным для объетка номером. MaskArray[i][ii] = ObjectMark; //Если, этот пиксель граничит с каким-то пикселем из области (т.е. его значение <= AreaMark)... for (int d = 3; d <= AreaMark - 1; d++) { if (NearK(WhiteMap, i, ii, d)) { //..., то добавляем его в наш массив. Helper.InsertToArray(ref tempPointArray, new VPointEx(i, ii, d, Color.Empty)); } } //Далее выполняется аналог предыдущей операции, с некоторым изменениям. while (true) { //Указывает на то, были ли произведены какие-либо изменения. bool changedSomething = false; //Для каждого пикселя выполняем. for (int i2 = 1; i2 <= BufferImage.Length - 2; i2++) { for (int ii2 = 1; ii2 <= BufferImage[0].Length - 2; ii2++) { //Если есть пиксель, рядом с пикселем, помеченным нашим текущем ObjectMark, то... if (MaskArray[i2][ii2] == 1 && NearK(MaskArray, i2, ii2, ObjectMark)) { //...и этот пиксель помечаем, как ObjectMark. MaskArray[i2][ii2] = ObjectMark; //Помечаем, что изменили что-то. changedSomething = true; //В этом случаем, мы выполняем то же, что и с первым пекселем. //Если, этот пиксель граничит с каким-то пикселем из области (т.е. его значение <= AreaMark)... for (int d = 3; d <= AreaMark - 1; d++) { if (NearK(WhiteMap, i2, ii2, d)) { //..., то добавляем его в наш массив. Helper.InsertToArray(ref tempPointArray, new VPointEx(i2, ii2, d, Color.Empty)); break; } } } } } if (!changedSomething) { break; } } ObjectMark++; //Далее сортируем наш массив. var tmp = SortPointArray(tempPointArray); //Сливаем его с нашим Contours. Helper.ConcatArrays(ref Contours, ref tmp); tmp = null; tempPointArray = null; } } } // == ШАГ 5: ОБРАБОТКА РЕЗУЛЬТАТОВ == // ШАГ 5.1: Сортировка точек. //Вывод информации о выполении новой операции. if (_outputDebugInfo) { Console.WriteLine(string.Format(_stateSay, Thread.CurrentThread.ManagedThreadId, "Sorting Points", (Milliseconds / 1000))); } _proList[Thread.CurrentThread.ManagedThreadId]++; //Для каждого контура... for (int ii = 0; ii <= Contours.Length - 1; ii++) { VPoint[] sortedArray = new VPoint[0]; //Добавляем в новый массив необходимые точки. //Это необходимо из-за различия типов VPoint и VPointEx. for (int i = 0; i <= Contours[ii].Length - 1; i++) { Helper.InsertToArray(ref sortedArray, Contours[ii][i].BasePoint); } //Сортируем наш массив. PointSort(ref sortedArray); //Выполняем обратную операцию пресваивания. for (int i = 0; i <= Contours[ii].Length - 1; i++) { Contours[ii][i].BasePoint = sortedArray[i]; } sortedArray = null; } // ШАГ 5.2: Сортировка контуров и сохранения результата. //Вывод информации о выполении новой операции. if (_outputDebugInfo) { Console.WriteLine(string.Format(_stateSay, Thread.CurrentThread.ManagedThreadId, "Sorting Contours", (Milliseconds / 1000))); } _proList[Thread.CurrentThread.ManagedThreadId]++; //Сортировка всех контуров. CSort(ref Contours); //Сохраняем результат. _imgVect.Add(ProceedImage, Contours); //Отмечаем что процесс завершенн. _endList[Thread.CurrentThread.ManagedThreadId] = true; //Вывод информации о завершении процесса. if (_outputDebugInfo) { Console.WriteLine("Stream Ended. ID: " + Thread.CurrentThread.ManagedThreadId); } }