基于卷积的图像处理是执行模糊,边缘检测,去模糊等操作的常用技术。基本前提是生成一个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的别名。 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
语句有效地取代了以任何方式填充图像的需要,尽管它们是未经优化的黑客。
我想将至少其中一个for循环转换为Parallel.For
循环,但由于workPixel不是线程安全的,因此它不会产生功能结果。采取什么样的正确步骤?
答案 0 :(得分:1)
您对每个像素执行完全相同的操作,因此您可以并行化两个第一个循环(通过图像的循环)。您只需要在int sourcePxTarget = i * img.Width + j;
之后移动workPixel然后成为线程安全的局部变量。
就个人而言,我将图像划分为波段(与线程一样多的波段),并为每个线程分配一个波段。
有更快的方法来进行此类卷积:使用FFT。当为每行和每列执行FFT时,库已经实现了并行化。