使用C#5.0进行并行图像处理

时间:2013-08-22 00:29:00

标签: c# image-processing asynchronous bitmap async-await

我正在尝试使用相同的旧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的技术,为每个像素运行一个单独的函数(Pixel Shader引擎(因为我被拍))复制函数在GPU上返回float4(Color)数千次并行处理。

所以我尝试为每个像素运行一个单独的Task(函数),将这些Task变量放入List并将List.ToArray()放入'await'。但是我没有做到这一点,因为每个新的任务'等待'在下一个任务运行之前完成。

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

                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);

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

3 个答案:

答案 0 :(得分:3)

GetPixelSetPixel可能是此处的主要瓶颈。

我建议使用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,然后直接并行处理原始数据(如上所述)。请注意,我只是并行化外循环(故意),因为这样可以防止核心过度饱和。

答案 1 :(得分:1)

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

如果您要做的只是将图像转换为灰度,我会首先看一下内置功能。通常,.NET框架中内置的东西可以使用较低级别的“不安全”代码来比其他方式更有效地执行操作,而不会造成不安全。试试How to: Convert an Image to Greyscale

如果您真的想要使用多个线程进行自定义位图处理,我认为您必须创建一个字节数组,以多线程方式修改它,然后在字节数组的末尾创建一个位图对象。请查看https://stackoverflow.com/a/15290190/1453269,了解如何做到这一点。

答案 2 :(得分:1)

并行化工作的一个好方法不是按像素调度任务,而是调度与处理器内核一样多的线程
你还说你可以通过指针操纵你的像素,所以如果你走这条路线,这里有另一个重要的建议:让每个线程都能在相邻的像素上工作
一个有效的场景是线程1使用前25%像素,线程2使用下一个25%,依此类推,直到线程4 以上非常非常重要,以避免False Sharing,您实际上会解除缓存的服务,使您的算法慢很多。
除此之外,你可以使用你的显卡,但这完全不属于我的联盟。

编辑:正如Panagiotis在评论中所指出的,任务可能与某个主题无关,因此您必须谨慎使用哪些API来并行化您的工作以及如何执行该工作。