2

我正在尝试使用相同的旧 GDI 技术使用 C# 进行一些图像处理,使用嵌套的 for 循环遍历每个像素,然后在该(位图)图像上使用 GetPixel 和 SetPixel 方法。

我已经用指针方法(使用不安全的上下文)得到了相同的结果,但我现在正在尝试使用老式的 Get/Set-Pixel 方法来玩我的位图......

    Bitmap ToGrayscale(Bitmap source)
    {
        for (int y = 0; y < source.Height;y++ )
        {
            for (int x = 0; x < source.Width; x++)
            {
                Color current = source.GetPixel(x, y);
                int avg = (current.R + current.B + current.G) / 3;
                Color output = Color.FromArgb(avg, avg, avg);
                source.SetPixel(x, y, output);
            }
        }
        return source;
    }

考虑到上面代码的性能......它需要太多才能完成,同时强调用户等待他的 1800x1600 图像完成处理。

所以我认为我可以使用我们使用的与 HLSL 一起使用的技术,为每个像素运行一个单独的函数(像素着色器引擎(正如我所认为的那样)在 GPU 上复制返回 float4(颜色)数千次的函数来执行并行处理)。

因此,我尝试为每个像素运行一个单独的任务(函数),将这些任务变量放入一个列表中,并将 List.ToArray() 的“等待”。但是我没有这样做,因为每个新任务“等待”在下一个任务运行之前完成。

我想为每个像素调用一个新任务来运行它:

                Color current = source.GetPixel(x, y);
                int avg = (current.R + current.B + current.G) / 3;
                Color output = Color.FromArgb(avg, avg, avg);
                source.SetPixel(x, y, output);

在一天结束的时候,我得到了一个异步非阻塞代码,但不是并行的......有什么建议吗?

4

3 回答 3

3

GetPixel并且SetPixel可能是这里的主要瓶颈。

我建议不要尝试并行化,而是Bitmap.LockBits更有效地处理解析。

话虽如此,您可以通过以下方式并行化当前版本:

Bitmap ToGrayscale(Bitmap source)
{
    Parallel.For(0, source.Height, y =>
    {
        for (int x = 0; x < source.Width; x++)
        {
            Color current = source.GetPixel(x, y);
            int avg = (current.R + current.B + current.G) / 3;
            Color output = Color.FromArgb(avg, avg, avg);
            source.SetPixel(x, y, output);
        }
    });

    return source;
}

但是,Bitmap该类不是线程安全的,因此这可能会导致问题。

更好的方法是使用LockBits,然后直接并行处理原始数据(如上所述)。请注意,我只是(故意)并行化外循环,因为这将防止内核与工作项过度饱和。

于 2013-08-22T00:30:42.210 回答
1

并行化工作的一个好方法不是按像素分派任务,而是分派与处理器内核一样多的线程。
你还说你可以通过指针来操作你的像素,所以如果你走那条路,这里有另一个重要的建议:让每个线程处理相邻的像素
一个有效的场景是线程 1 使用前 25% 的像素,线程 2 使用接下来的 25%,依此类推,直到线程 4。
以上对于避免False Sharing非常重要,在这种情况下,您正在有效地关闭缓存的服务,使你的算法慢了很多。 除此之外,您可能可以使用您的显卡,但这完全超出了我的范围。

编辑:正如 Panagiotis 在评论中所指出的,任务可能与线程无关,因此您必须谨慎对待将使用什么 API 来并行化您的工作以及如何进行。

于 2013-08-22T01:12:28.427 回答
1

使用任务只会在 CPU 上设置多个线程 - 它不会使用图形处理器。另外,我很确定您正在使用的 Bitmap 对象不是线程安全的,因此无论如何您都无法使用多个线程来访问它们。

如果您要做的只是将图像转换为灰度,我会先查看内置功能。一般来说,.NET 框架中内置的东西可以使用较低级别的“不安全”代码来比其他方式更有效地做事,而不会不安全。尝试如何:将图像转换为灰度

如果您真的想使用多个线程进行自定义位图处理,我认为您必须制作一个字节数组,以多线程方式对其进行修改,然后从字节数组的末尾创建一个位图对象。查看https://stackoverflow.com/a/15290190/1453269以获取有关如何执行此操作的一些指示。

于 2013-08-22T00:43:03.830 回答