c#线程图像处理

本文关键字:图像处理 线程 | 更新日期: 2023-09-27 18:18:50

                    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灰度高度图中获取像素数据,并构建具有相应高度值的十六进制地图。我把六边形存储在字典集合里。收藏中总共有大约400万个六边形。

为了有效地做到这一点,我将图像分解成256 x 256块,并将该图像传递给另一个线程,该线程将解析它并将十六进制添加到集合中。这里是一切失控的地方。我现在有64个图像块,它们的左上角为(0,0),而不是一个图像的左上角为(0,0)。然而,我使用像素位置作为字典的索引。当第二个线程试图添加另一个索引为(0,0)的值时,会崩溃。

我如何缓解这个问题?我想过建立一个类,只是有一个图像成员和一个块数成员,并传递给线程,所以我可以调整像素位置取决于什么块的线程正在工作,但这似乎不太理想。

(我意识到我使用的字典不是线程安全的。

c#线程图像处理

我可以推荐一些东西吗?

  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. 如果你坚持要产生很多线程,不要产生比CPU内核更多的线程。我想你的机器上没有64核吧?

  4. 如果你坚持要生成许多线程,你当然需要将每个贴图的位置传递给线程,这样当你重建大画面时,你就确切地知道该贴图应该放在哪里。

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

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

您还为每个图像块旋转一个新线程。分配线程的开销非常大,并且可能比您希望线程完成的工作花费更多的时间。至少考虑使用ThreadPool.QueueUserWorkItem来利用已经可用的系统工作线程。在我看来,使用。net 4.0的Task类会更好。

忘记.GetPixel()。它比像素内存访问慢一千倍。

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

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