我已经通过将两个重缩放功能应用于FFT卷积输出来测试它们。
第一个是从this link收集的。
public static void RescaleComplex(Complex[,] convolve)
{
int imageWidth = convolve.GetLength(0);
int imageHeight = convolve.GetLength(1);
double maxAmp = 0.0;
for (int i = 0; i < imageWidth; i++)
{
for (int j = 0; j < imageHeight; j++)
{
maxAmp = Math.Max(maxAmp, convolve[i, j].Magnitude);
}
}
double scale = 1.0 / maxAmp;
for (int i = 0; i < imageWidth; i++)
{
for (int j = 0; j < imageHeight; j++)
{
convolve[i, j] = new Complex(convolve[i, j].Real * scale,
convolve[i, j].Imaginary * scale);
}
}
}
这里问题是对比度不正确。
第二个是从this link收集的。
public static void RescaleComplex(Complex[,] convolve)
{
int imageWidth = convolve.GetLength(0);
int imageHeight = convolve.GetLength(1);
double scale = imageWidth * imageHeight;
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
double re = Math.Max(0.0, Math.Min(convolve[i, j].Real * scale, 1.0));
double im = Math.Max(0.0, Math.Min(convolve[i, j].Imaginary * scale, 1.0));
convolve[i, j] = new Complex(re, im);
}
}
}
此处输出为全白。
因此,您可以看到其中两个版本给出了一个正确的输出而另一个给出了不正确的输出。
如何解决这个难题?
。
注意。 Matrix是以下内核:
0 -1 0
-1 5 -1
0 -1 0
源代码。 这是我的FFT卷积函数。
private static Complex[,] ConvolutionFft(Complex[,] image, Complex[,] kernel)
{
Complex[,] imageCopy = (Complex[,])image.Clone();
Complex[,] kernelCopy = (Complex[,])kernel.Clone();
Complex[,] convolve = null;
int imageWidth = imageCopy.GetLength(0);
int imageHeight = imageCopy.GetLength(1);
int kernelWidth = kernelCopy.GetLength(0);
int kernelHeight = kernelCopy.GetLength(1);
if (imageWidth == kernelWidth && imageHeight == kernelHeight)
{
Complex[,] fftConvolved = new Complex[imageWidth, imageHeight];
Complex[,] fftImage = FourierTransform.ForwardFFT(imageCopy);
Complex[,] fftKernel = FourierTransform.ForwardFFT(kernelCopy);
for (int j = 0; j < imageHeight; j++)
{
for (int i = 0; i < imageWidth; i++)
{
fftConvolved[i, j] = fftImage[i, j] * fftKernel[i, j];
}
}
convolve = FourierTransform.InverseFFT(fftConvolved);
RescaleComplex(convolve);
convolve = FourierShifter.ShiftFft(convolve);
}
else
{
throw new Exception("Padded image and kernel dimensions must be same.");
}
return convolve;
}
答案 0 :(得分:2)
这并不是真正的难题。这只是显示范围有限和您的期望的问题,这在两种情况下是不同的。
(顶部):这是一个归一化的内核(其元素总计为1)。它不会改变图像的对比度。但是由于其中包含负值,它可以生成超出原始范围的值。
(底部):这不是规范化的内核。它将更改输出的对比度。
例如,使用内核
0, -1, 0
-1, 6, -1
0, -1, 0
(注意中间的6)。总计为2。图像对比度将加倍。也就是说,在输入全为0的区域中,输出也为0,但是在输入全为1的区域中,输出将为2。
通常,如果不打算改变图像对比度,则对卷积滤波器进行归一化。如果应用此类过滤器,则无需重新缩放输出以进行显示(尽管您可能希望裁剪超出范围的值(如果出现)。但是,超出范围的值可能是相关的,在这种情况下,您需要重新缩放输出以匹配显示范围。
在您的情况2(图像内核)中,您可以对内核进行规范化,以避免重新缩放输出。但这通常不是解决方案。一些过滤器的总和为0(例如Sobel内核或Laplace内核,这两个都是基于去除DC分量的导数)。这些无法归一化,您将始终必须重新缩放输出图像以进行显示(尽管您不必重新缩放其输出以进行分析,因为它们的输出值具有在重新缩放后会被破坏的物理含义)。 / p>
这就是说,卷积有时意在生成与输入图像具有相同对比度(在大约相同范围内)的输出图像,有时不是。您需要知道要为输出使用有意义的过滤器,并能够在期望图像在特定范围内的屏幕上显示输出。
编辑:解释您的数据所发生的情况。
第一个数字:在此处进行缩放,以便可以看到整个图像强度范围。从逻辑上讲,您不会得到任何饱和像素。但是由于矩阵内核增强了高频,所以输出图像的值超出了原始范围。重新缩放以适合显示器范围内的整个范围会降低图像的对比度。
第二个数字:您正在按N = imageWidth * imageHeight
重新缩放频域卷积结果。这样会产生正确的输出。需要应用此缩放比例表示您的正向FFT缩放了1/N
,而反向FFT则没有缩放。
对于IFFT(FFT(img))==img
,有必要用1/N
缩放FFT或IFFT。通常,是按比例缩放的IFFT。原因是卷积没有任何进一步的缩放就可以按预期进行。要看到这一点,请想象一个图像,其中所有像素都具有相同的值。 FFT(img)
在所有地方都为零,除了0频率分量(DC分量)为sum(img)
。归一化内核的总和为1,因此其DC分量为sum(kernel)==1
。将这两者相乘,我们再一次获得与输入类似的频谱,其直流分量为sum(img)
。其逆变换将等于img
。这正是我们对这种卷积的期望。
现在,使用另一种标准化形式(即您有权使用的FFT使用的一种形式)。 FFT(img)
的DC分量将为sum(img)/N
。内核的DC组件将为1/N
。将这两个乘积,得到sum(img)/(N*N)
的DC分量。其逆变换将等于img/N
。因此,您需要乘以N
以获得预期结果。这正是在归一化的“矩阵内核”的频域卷积中看到的。
如上所述,“图像内核”未标准化。 FFT(kernel)
的DC分量为sum(img)/N
,乘以FFT(img)
的DC分量为sum(img)*sum(img)/(N*N)
,因此逆变换的对比度乘以{{1} }乘以sum(img)/N
仍会给您带来N
太大的因素。如果要标准化内核,则将其除以sum(img)
,这将使您的输出达到预期范围。