C # Резьбовая обработка изображений - PullRequest
3 голосов
/ 28 декабря 2011
                    for (int x = 0; x < blockCountX; x++)
                    {
                        for (int y = 0; y < blockCountY; y++)
                        {
                            //get blocks from image to new image and send to threaded processor
                            imageBlocks[x, y] = image.Clone(clipRectangle, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
                            System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(ThreadedFromHeightMap));
                            t.Start(imageBlocks[x,y]);
                            clipRectangle.Offset(0, IMAGEBLOCKSIZE);
                        }
                        clipRectangle.Offset(IMAGEBLOCKSIZE, clipRectangle.Location.Y * -1);
                    }
                    break;
            }
        }
        private void ThreadedFromHeightMap(object Image)
        {
            Bitmap image = (Bitmap)Image;
            int width = image.Width;
            int height = image.Height;

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    map.Hexes.Add(new Point(x, y), new Hex(image.GetPixel(x, y).B));
                    //tempHexes.Enqueue(new Hex(image.GetPixel(x, y).B));
                }
            }
        }

Я пытаюсь взять данные пикселей из карты высот в оттенках серого 2048 x 2048 пикселей на дюйм и строю шестнадцатеричную карту с соответствующими значениями высоты. Я храню гексы в коллекции словарей. В общей сложности в коллекции ~ 4 миллиона гексов.

Чтобы сделать это эффективно, я разбиваю изображение на 256 x 256 фрагментов и передаю это изображение другому потоку, который будет анализировать его и добавлять гексы в коллекцию. Это все, что сходит с ума. Вместо одного изображения с (0,0) в верхнем левом углу у меня теперь есть 64 фрагмента изображения, которые имеют (0,0) в качестве верхнего левого угла. Однако я использую расположение пикселей в качестве индекса для словаря. Это происходит сбой, когда второй поток пытается добавить другое значение с индексом (0,0).

Как мне решить эту проблему? Я подумал о создании класса, который просто имеет элемент изображения и элемент номера чанка и передает его потоку, чтобы я мог отрегулировать положение пикселя в зависимости от того, над каким фрагментом работает поток, но это кажется неоптимальным.

(Я понимаю, что используемый мной словарь не безопасен для потоков. С тех пор я исправил это.)

Ответы [ 3 ]

3 голосов
/ 28 декабря 2011

Могу ли я порекомендовать несколько вещей?

  1. Забудьте об изображении. GetPixel (), который ужасно медленный; работать непосредственно с данными растрового изображения, и производительность вашего алгоритма улучшится настолько, что вам не потребуется запускать параллельные потоки для повышения его эффективности. См. MSDN: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx

  2. Если вы настаиваете на параллельных потоках, используйте пул потоков вместо порождения 64 потоков. (См. MSDN: http://msdn.microsoft.com/en-us/library/3dasc8as(v=vs.80).aspx)

  3. Если вы настаиваете на создании множества потоков, НЕ порождайте больше потоков, чем ядра вашего ЦП. Я не думаю, что у вас есть 64 ядра на вашей машине, не так ли?

  4. Если вы настаиваете на создании множества потоков, вам, конечно, нужно будет передать местоположение каждой плитки в поток, чтобы вы точно знали, где эта плитка должна быть размещена, когда вы восстанавливаете большую картинку. Это не менее чем оптимально, это необходимо.

2 голосов
/ 28 декабря 2011

Почему вы считаете, что разбиение большого изображения на более мелкие фрагменты будет более эффективным?Не слишком ли большое изображение, чтобы поместиться в системную память?4 миллиона пикселей x 8bpp (1 байт на пиксель) = 4 мегабайта.Это было много памяти 20 лет назад.Сегодня это чурбановое изменение.

Создание нескольких подизображений 256x256 потребует копирования данных пикселей в новые изображения в памяти, а также заголовка / дескриптора изображения для каждого нового изображения, а также выравнивания для каждой строки развертки.Вы более чем удвоите свое использование памяти, что само по себе может создать проблемы с производительностью (виртуальный обмен).

Вы также раскручиваете новый поток для каждого блока изображения.Выделение потока очень дорого, и может занять больше времени, чем работа, которую вы хотите сделать потоком.Рассмотрите хотя бы использование ThreadPool.QueueUserWorkItem для использования уже доступных системных рабочих потоков.Использование Task класса .NET 4.0 было бы еще лучше, IMO.

Забудьте .GetPixel ().Это в тысячу раз медленнее, чем доступ к памяти пикселей.

Если вы хотите распределить обработку пикселей изображения по нескольким ядрам ЦП, рассмотрите возможность обработки каждой строки развертки или группы строк развертки для другой задачи или рабочего потока.

0 голосов
/ 28 декабря 2011
  1. Я думаю, инкапсуляция местоположения чанка среди данных изображения в классе не так уж и плоха.Я не вижу никакой другой опции.
  2. В качестве оптимизации вы можете получить указатель в пикселях с image.Scan0, если у вас нет ограничений на небезопасные операции.
  3. Создание нового изображениядля каждого блока не очень умная идея.Передайте область интереса в поток.
  4. Если вы можете использовать .NET Framework 4, используйте Parallel.ForEach для такого использования.Если вы не можете использовать его, вы можете использовать пулы потоков.Я предполагаю, что ваш компьютер не имеет (2048 x 2048) / (256 x 256) = 64-ядерный процессор.
  5. Обновление глобальной шестнадцатеричной карты после завершения потока может значительно повысить производительность.Так как Словарь не является поточно-ориентированным, блокировка внутреннего цикла шестнадцатеричной карты в потоке не очень хорошая идея.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...