/// <summary> /// Бьет изображение на части и запускает для кождого <see cref="ImagePrData()"/> на отдельных потоках. Ждет пока они завершаться. /// </summary> private void ImagePr() { //Как говорится, лучше перестраховаться Init(); //Отмечается метка начала процесса. var startSeconds = Milliseconds / 1000; //Вывод информации о начале процесса. if (_outputDebugInfo) { Console.WriteLine("================================="); Console.WriteLine("Vectorization Started"); Console.WriteLine(" 1. Preparing Image"); } //Рабочее изображение. Bitmap Bmp = _proceedBitmap; //Ширина одной части изображения. int Width = _proceedBitmap.Width / _num; //Части изображения. Bitmap[] bitmaps = new Bitmap[_num]; int i = 0, gli = 0, currbmp = 0; bitmaps[0] = new Bitmap(Width, _proceedBitmap.Height, PixelFormat.Format24bppRgb); //Процесс резделения изображения на _Num частей. if (_num != 1) { while (i != Bmp.Width) { i++; gli++; if (i % Width == 0 && i + currbmp * Width != Bmp.Width && currbmp + 1 != bitmaps.Length) { currbmp += 1; bitmaps[currbmp] = new Bitmap(Width, _proceedBitmap.Height, PixelFormat.Format24bppRgb); i = 0; } if (i > Width - 1) { break; } if (i + currbmp * Width > Bmp.Width) { break; } Color[] hpixels = new Color[Bmp.Height]; try { for (int ii = 0; ii <= Bmp.Height - 1; ii++) { hpixels[ii] = Bmp.GetPixel(gli, ii); } for (int ii = 0; ii <= Bmp.Height - 1; ii++) { bitmaps[currbmp].SetPixel(i, ii, hpixels[ii]); } hpixels = null; } catch { } } } else { bitmaps[0] = Bmp; } //Массив потоков выполнения. Thread[] streams = new Thread[_num]; //Каждому потоку задается метод. //К его ID привязывается: статус выполнения, метка о том, закончил ли поток, изображения для этого потока. for (int ii = 0; ii <= _num - 1; ii++) { streams[ii] = new Thread(ImagePrData); _proList.Add(streams[ii].ManagedThreadId, 0); _endList.Add(streams[ii].ManagedThreadId, false); _imgMap.Add(streams[ii].ManagedThreadId, bitmaps[ii]); } //Каждый поток запускается. for (int ii = 0; ii <= _num - 1; ii++) { streams[ii].Start(); } int MaxValue = _num * 5; int LastValue = 0; //Вывод информации о выполении новой операции. if (_outputDebugInfo) { Console.WriteLine(" 2. Processing"); } while (true) { //Собирает общий статус-каунт всех потоков. for (int ii = 0; ii <= _num - 1; ii++) { if (streams[ii].ThreadState == ThreadState.Running) { _numPr += _proList[streams[ii].ManagedThreadId]; } } //Если значение изменилось, то... if (LastValue != _numPr) { //... вызвать ивент изменения. if (_numPr > MaxValue) { MaxValue = _numPr; } VectChanged(new PrStausChangedParameters(MaxValue, _numPr)); LastValue = _numPr; } Thread.Sleep(500); _numPr = 0; //Если все потоки завершили свою работу, то... bool f = true; for (int ii = 0; ii <= _num - 1; ii++) { if (!_endList.Values.ToArray()[ii]) { f = false; break; } } //... прервать цикл. if (f) { break; } } //Для всех частей. for (int ii = 0; ii <= _num - 1; ii++) { //Каждый результат склеивается с предыдущим, с небольшим смщением. var tmVect = _imgVect[bitmaps[ii]]; for (int a = 0; a <= tmVect.Length - 1; a++) { for (int b = 0; b <= tmVect[a].Length - 1; b++) { tmVect[a][b].BasePoint = new VPoint(tmVect[a][b].BasePoint.X, tmVect[a][b].BasePoint.Y + ii * Width - ii, tmVect[a][b].BasePoint.Color); } } Helper.ConcatArrays(ref _rawData, ref tmVect); tmVect = null; } _endList = null; _proList = null; _imgMap = null; _imgVect = null; if (_outputDebugInfo) { Console.WriteLine(string.Format("DONE in {0} second(s) or {1:0.##} minute(s)", (Milliseconds / 1000), (Milliseconds / 60000))); Console.WriteLine("================================="); } _cNum = Milliseconds; }
/// <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); } }