2011-12-28 2 views
3
    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)); 
       } 
      } 
     } 

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

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

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

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

+0

Похоже, что существует сильная отрицательная реакция на .GetPixel(). Я собираюсь изучить ваши предложения о том, чтобы отбросить это и работать непосредственно с данными растрового изображения. Спасибо за ваши ответы. – Wesley

ответ

3

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

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

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

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

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

+0

Спасибо. Я переработаю это, чтобы использовать вашу первую рекомендацию. Если я увижу такое значительное улучшение, я могу отбросить весь этот многопоточный подход и двигаться дальше. – Wesley

+0

О, вы увидите много улучшений! C -: = Удачи, и получайте удовольствие! –

+0

Ну, я не видел улучшения. После некоторого возиться, я понял, что это фактически хранилище Dictionary <>, которое я использовал. Я предполагаю, что добавление ~ 4 миллионов записей в словарь будет довольно медленным, учитывая, что он должен проверять каждую запись для повторяющихся значений индекса для каждого .Add(). После перерыва в словаре я вернулся к .GetPixel() временно из любопытства, и это оказалось значительно медленнее. Ваш метод плюс отсутствие словаря делает этот процесс ощутимо мгновенным, а не 5-минутным ожиданием. Спасибо за помощь. – Wesley

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

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

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

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

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

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

+0

Это не проблема памяти, это проблема с процессором. Я просто запускал это в одном потоке, и это заняло 5 минут, потому что мой процессор работал только на 25% (4 ядра). Я не буду использовать .GetPixel(). Спасибо – Wesley

+1

GetPixel(), вероятно, составляет большую часть этих 5 минут. – dthorpe

Смежные вопросы