【发布时间】: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 的别名。 finalPixels 是 Pixels 的克隆,其值已被过滤器修改。
现在,执行此操作的循环:
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 位灰度图像。 Pixels 是 ushort[] 的一维数组,没有隔行扫描或任何其他修改。隐藏在循环中的 if if else 和 else 语句有效地取代了以任何方式填充图像的需要,尽管它们是未经优化的 hack。
我想将这些 for 循环中的至少一个转换为 Parallel.For 循环,但由于 workPixel 不是线程安全的,它不会产生函数结果。在这里采取哪些正确步骤?
【问题讨论】:
-
您对每个像素执行完全相同的操作,因此您可以并行化两个第一个循环(通过图像的循环)。就我个人而言,我将图像分成带状(与线程一样多的带),并为每个线程分配一个带。
-
对,问题是
workPixel被用作内部两个循环内的局部变量。它不是线程安全的,所以如果你并行化第一个或第二个循环,你会得到一个损坏的输出,因为多个 Parallel.For 循环同时修改了 workPixel,打乱了值。 -
只需将 workPixel 移到 "int sourcePxTarget = i * img.Width + j;" 之后然后就是成为局部变量ThreadSafe
-
就是这样,谢谢!我将保留这个问题,因为我不确定这是最优化的方法。
-
我已经发布了一个答案,以及您可能感兴趣的细节。
标签: c# image-processing parallel-processing convolution