在C#中并行化基于卷积/内核的图像处理

时间:2016-06-22 15:28:53

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

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

内核将与图像进行卷积,图像可归结为扫描整个图像并执行相邻像素的总和,其中kernel对邻居进行加权。

就算法而言, Kernel 类似于:

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

对于下面的代码段,它具有以下属性:

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语句有效地取代了以任何方式填充图像的需要,尽管它们是未经优化的黑客。

我想将至少其中一个for循环转换为Parallel.For循环,但由于workPixel不是线程安全的,因此它不会产生功能结果。采取什么样的正确步骤?

1 个答案:

答案 0 :(得分:1)

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

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

有更快的方法来进行此类卷积:使用FFT。当为每行和每列执行FFT时,库已经实现了并行化。