我正在尝试使用线性空间域卷积复制此链接的输出结果。
首先将图像转换为2D double
数组,然后进行卷积。 图像和卷积核的大小相同。 在卷积之前对图像进行填充,然后在卷积后相应地裁剪。
与基于FFT的卷积相比,输出结果很奇怪且不正确。
我该如何解决这个问题?
请注意,我从Matlab获得了以下与C# FFT输出匹配的图像输出:
.
更新1:根据@Ben Voigt的评论,我将Rescale()
函数替换为1
而不是255.0
,因此输出效果有了很大的改善。但是,输出结果仍与FFT输出(正确的结果)不匹配。
.
更新2:根据@Cris Luengo的评论,我通过拼接填充了图像,然后执行了空间卷积。 结果如下:
因此,输出结果比之前更糟糕。 但是,这与链接答案的第二个输出类似,这意味着循环卷积不是解决方案。
.
更新3:我使用@Cris Luengo答案中提出的Sum()
函数。 结果是**更新1**
的更改版本:
但是,它仍然与FFT版本不完全相同。
.
更新4:根据@Cris Luengo的评论,我对两个输出结果进行了减法以查看差异:
,
1. 空间域减频率域
2. 频率域减空间域
<
public static double[,] LinearConvolutionSpatial(double[,] image, double[,] mask)
{
int maskWidth = mask.GetLength(0);
int maskHeight = mask.GetLength(1);
double[,] paddedImage = ImagePadder.Pad(image, maskWidth);
double[,] conv = Convolution.ConvolutionSpatial(paddedImage, mask);
int cropSize = (maskWidth/2);
double[,] cropped = ImageCropper.Crop(conv, cropSize);
return conv;
}
static double[,] ConvolutionSpatial(double[,] paddedImage1, double[,] mask1)
{
int imageWidth = paddedImage1.GetLength(0);
int imageHeight = paddedImage1.GetLength(1);
int maskWidth = mask1.GetLength(0);
int maskHeight = mask1.GetLength(1);
int convWidth = imageWidth - ((maskWidth / 2) * 2);
int convHeight = imageHeight - ((maskHeight / 2) * 2);
double[,] convolve = new double[convWidth, convHeight];
for (int y = 0; y < convHeight; y++)
{
for (int x = 0; x < convWidth; x++)
{
int startX = x;
int startY = y;
convolve[x, y] = Sum(paddedImage1, mask1, startX, startY);
}
}
Rescale(convolve);
return convolve;
}
static double Sum(double[,] paddedImage1, double[,] mask1, int startX, int startY)
{
double sum = 0;
int maskWidth = mask1.GetLength(0);
int maskHeight = mask1.GetLength(1);
for (int y = startY; y < (startY + maskHeight); y++)
{
for (int x = startX; x < (startX + maskWidth); x++)
{
double img = paddedImage1[x, y];
double msk = mask1[x - startX, y - startY];
sum = sum + (img * msk);
}
}
return sum;
}
static void Rescale(double[,] convolve)
{
int imageWidth = convolve.GetLength(0);
int imageHeight = convolve.GetLength(1);
double maxAmp = 0.0;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
maxAmp = Math.Max(maxAmp, convolve[i, j]);
}
}
double scale = 1.0 / maxAmp;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
double d = convolve[i, j] * scale;
convolve[i, j] = d;
}
}
}
public static Bitmap ConvolveInFrequencyDomain(Bitmap image1, Bitmap kernel1)
{
Bitmap outcome = null;
Bitmap image = (Bitmap)image1.Clone();
Bitmap kernel = (Bitmap)kernel1.Clone();
//linear convolution: sum.
//circular convolution: max
uint paddedWidth = Tools.ToNextPow2((uint)(image.Width + kernel.Width));
uint paddedHeight = Tools.ToNextPow2((uint)(image.Height + kernel.Height));
Bitmap paddedImage = ImagePadder.Pad(image, (int)paddedWidth, (int)paddedHeight);
Bitmap paddedKernel = ImagePadder.Pad(kernel, (int)paddedWidth, (int)paddedHeight);
Complex[,] cpxImage = ImageDataConverter.ToComplex(paddedImage);
Complex[,] cpxKernel = ImageDataConverter.ToComplex(paddedKernel);
// call the complex function
Complex[,] convolve = Convolve(cpxImage, cpxKernel);
outcome = ImageDataConverter.ToBitmap(convolve);
outcome = ImageCropper.Crop(outcome, (kernel.Width/2)+1);
return outcome;
}
string str = string.Empty;
是做什么用的?你没有使用str
,这可能会拖慢速度?-- 无论如何,在空间域中使用大内核进行卷积本质上是非常昂贵的。这就是为什么FFT方法如此重要的原因。 - Cris Luengostring.Empty
仅用于偶尔的调试目的。已删除。 - user366312convWidth
是卷积/输出图像的大小。这里没有显示填充操作。填充在ConvolveTd()
函数之前和之外完成。 - user366312