C#线程图像处理

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 8bpp灰度高度图中获取像素数据,并构建具有相应高度值的hex地图。 我将hexes存储在Dictionary集合中。 总而言之,该系列中有大约400万个咒语。

为了有效地做到这一点,我将图像分解为256 x 256块并将该图像传递给另一个线程,该线程将解析它并将该六角形添加到集合中。 这一切都是乱七八糟的。 我现在有64个图像块,而左上角有(0,0),而不是一个图像的左上角为(0,0)。 但是我使用像素位置作为字典的索引。 当第二个线程尝试使用索引(0,0)添加另一个值时,这会崩溃。

如何缓解此问题? 我已经考虑过构建一个只有一个图像成员和一个块号成员的类并将其传递给线程,这样我就可以根据线程处理的块来调整像素位置,但这似乎不是最佳的。

(我意识到我使用的字典不是线程安全的。我已经解决了这个问题。)

我可以推荐一些东西吗?

  1. 忘记image.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. 如果你坚持产生许multithreading,不要产生比CPU核心更多的线程。 我不认为你的机器上有64个核心,对吗?

  4. 如果你坚持要产生许multithreading,你当然需要将每个图块的位置传递给线程,以便在重建大图片时准确知道该图块的放置位置。 这不是最优的,这是必要的。

为什么你认为将大图像分解成更小的块会更有效? 大图像是否太大而无法放入系统内存? 400万像素x 8bpp(每像素1个字节)= 4兆字节。 这是20年前的大量记忆。 今天它变化很大。

创建多个256×256子图像将需要将像素数据复制到内存中的新图像,以及每个新图像的图像头/描述符开销,以及每个扫描线的对齐填充。 您的内存使用量将增加一倍以上,这可能会导致性能问题(虚拟交换)本身。

您还为每个图像块启动了一个新线程。 分配线程非常昂贵,并且可能比您希望线程完成的工作花费更多时间。 至少考虑使用ThreadPool.QueueUserWorkItem来使用已经可用的系统工作线程。 使用.NET 4.0的Task类会更好,IMO。

忘了.GetPixel()。 它比像素存储器访问慢一千倍。

如果要在多个CPU内核之间分配处理图像像素,请考虑将每个扫描线或一组扫描线处理到不同的任务或工作线程。

  1. 我认为,在类中的图像数据中封装块位置并不是那么糟糕。 我看不到任何其他选择。
  2. 作为优化,如果您对不安全操作没有任何限制,则可以从image.Scan0获取像素指针。
  3. 为每个块创建一个新的图像并不是一个非常聪明的主意。 将感兴趣的区域传递给线程。
  4. 如果您可以使用.NET Framework 4,请将Parallel.ForEach用于此类用法。 如果您无法使用它,则可以使用线程池。 我猜您的计算机没有(2048 x 2048)/(256 x 256)= 64核CPU。
  5. 线程终止后更新全局hex映射可以显着提高性能。 由于Dictionary不是线程安全的,在一个线程内锁定全局hex映射内部循环并不是一个好主意。