【问题标题】:Parallelizing Convolution / Kernel based image processing in C#在 C# 中并行化基于卷积/内核的图像处理
【发布时间】:2016-10-24 15:41:24
【问题描述】:

基于卷积的图像处理是执行模糊、边缘检测、去模糊等操作的常用技术。基本前提是生成一个kernel,它是一些二维数组,通常是正方形。

内核将与图像进行卷积,归结为将其扫过图像并对相邻像素求和,其中kernel 对相邻像素进行加权。

就算法而言,Kernel 看起来像:

double[3, 3]{ 
        { 0 , -1 , 0 },
        { -1 , 4 , -1 },
        { 0 , -1 , 0 }};

对于下面的代码sn-p,它具有以下属性:

filterWidth = 3
filterRadius = (filterWidth - 1) / 2; // (=1)

有一些变量可以使用:

img:
    Class with members:
        ushort[] Pixels
        int Width
        int Height

Pixels 只是 img.Pixels 的别名。 finalPixelsPixels 的克隆,其值已被过滤器修改。

现在,执行此操作的循环:

double workPixel = 0;
for (int i = 0; i < img.Height; i++)
            {
                // IMAGE rows
                for (int j = 0; j < img.Width; j++)
                {
                    // IMAGE columns
                    // target pixel in original (and final) image
                    int sourcePxTarget = i * img.Width + j;
                    for (int k = -filterRadius; k <= filterRadius; k++)
                    {
                        // FILTER rows
                        for (int l = -filterRadius; l <= filterRadius; l++)
                        {
                            // FILTER columns
                            int sourcePxActive = sourcePxTarget +
                                k * img.Width + l;
                            if (sourcePxActive < 0)
                            {
                                workPixel += 0;
                            }
                            else if (sourcePxActive >= Pixels.Length)
                            {
                                workPixel += 0;
                            }
                            else
                            {
                                workPixel += Pixels[sourcePxActive] *
                                    filter.Values[k + filterRadius, l + filterRadius];
                            }
                        }
                    }

                    // apply filter factor and bias
                    workPixel = workPixel * filterFactor + filterBias;

                    // filter bad values
                    if (workPixel > 65535)
                        workPixel = 65535;
                    else if (workPixel < 0)
                        workPixel = 0;

                    finalPixels[sourcePxTarget] = (ushort)workPixel;
                    workPixel = 0;
                }
            }

此算法不通用,适用于 16 位灰度图像。 Pixelsushort[] 的一维数组,没有隔行扫描或任何其他修改。隐藏在循环中的 if if elseelse 语句有效地取代了以任何方式填充图像的需要,尽管它们是未经优化的 hack。

我想将这些 for 循环中的至少一个转换为 Parallel.For 循环,但由于 workPixel 不是线程安全的,它不会产生函数结果。在这里采取哪些正确步骤?

【问题讨论】:

  • 您对每个像素执行完全相同的操作,因此您可以并行化两个第一个循环(通过图像的循环)。就我个人而言,我将图像分成带状(与线程一样多的带),并为每个线程分配一个带。
  • 对,问题是workPixel 被用作内部两个循环内的局部变量。它不是线程安全的,所以如果你并行化第一个或第二个循环,你会得到一个损坏的输出,因为多个 Parallel.For 循环同时修改了 workPixel,打乱了值。
  • 只需将 workPixel 移到 "int sourcePxTarget = i * img.Width + j;" 之后然后就是成为局部变量ThreadSafe
  • 就是这样,谢谢!我将保留这个问题,因为我不确定这是最优化的方法。
  • 我已经发布了一个答案,以及您可能感兴趣的细节。

标签: c# image-processing parallel-processing convolution


【解决方案1】:

您对每个像素执行完全相同的操作,因此您可以并行化前两个循环(通过图像的循环)。你只需要在int sourcePxTarget = i * img.Width + j;之后移动workPixel,然后它就变成了一个线程安全的局部变量。

就我个人而言,我将图像分成带(与线程一样多的带),并为每个线程分配一个带。

有一种更快的方法可以进行此类卷积:使用 FFT。由于对每一行和每一列都执行 FFT,因此库已经实现了并行化。

【讨论】:

  • 感谢您的帮助!此应用程序中应用的过滤器很小(最大尺寸为 15x15px),因此 FFT 方法不会提供性能优势,我不相信。
  • 这取决于库语言和优化。对于 15x15 内核,它会给您带来真正的改进,但如果您谈论的是 15x15 图像,那么多线程也不会改变任何东西。
  • 图像大小范围从大约 1MP 到 5MP - 这取决于相机。当然,这只会随着时间的推移而增加。在我机器的 CPU 上,我可以在 0.1 秒内对 1MP 图像执行卷积,这对于现在来说已经足够了。当这段代码成为性能限制时,我们最终希望将其移植到 GPU 上,它每秒可以处理数千 MP,而不是几十个。
猜你喜欢
  • 1970-01-01
  • 2020-08-13
  • 2013-11-27
  • 1970-01-01
  • 2019-02-26
  • 1970-01-01
  • 2017-09-07
  • 1970-01-01
  • 2012-12-15
相关资源
最近更新 更多