我有源图像和结果图像。我知道,在源图像上使用了某些卷积矩阵来得到结果。是否可以计算出这个卷积矩阵?或者至少得到一个非常相似的卷积矩阵。
我有源图像和结果图像。我知道,在源图像上使用了某些卷积矩阵来得到结果。是否可以计算出这个卷积矩阵?或者至少得到一个非常相似的卷积矩阵。
from scipy import misc
from numpy import fft
orig = misc.imread('lena256.png')
blur = misc.imread('lena256blur.png')
orig_f = fft.rfft2(orig)
blur_f = fft.rfft2(blur)
kernel_f = blur_f / orig_f # do the deconvolution
kernel = fft.irfft2(kernel_f) # inverse Fourier transform
kernel = fft.fftshift(kernel) # shift origin to center of image
kernel /= kernel.max() # normalize gray levels
misc.imsave('kernel.png', kernel) # save reconstructed kernel
下方展示了卷积核的256×256像素重建图以及其中心7×7像素区域的放大图:
将重建后的图像与原始卷积核进行比较,可以发现它们非常相似;实际上,在重建图像上应用介于0.5到0.68之间的任何截止值都会恢复原始卷积核。 重建图像中峰值周围的微弱波纹是由于舍入和边缘效应引起的噪声。
我不确定造成重建图像中出现十字形伪影的原因是什么(但我肯定有更多这方面经验的人可以告诉您),但是我的猜测是它与图像边缘有关。 当我在GIMP中卷积原始图像时,我要求它通过扩展图像来处理边缘(实际上是复制最外层的像素),而FFT去卷积则假设图像边缘环绕。 这可能会在重建图像中引入沿x和y轴的虚假相关性。
结果 = 源 * 核
上面使用FFT的答案是正确的,但在存在噪声的情况下,你不能真正使用基于FFT的反卷积。正确的方法是使用Richardson-Lucy反卷积(参见https://en.wikipedia.org/wiki/Richardson%E2%80%93Lucy_deconvolution)。
实现起来非常简单。此答案还提供了一个示例Matlab实现:Would Richardson–Lucy deconvolution work for recovering the latent kernel?
template<class ty>
void circshift(ty *out, const ty *in, int xdim, int ydim, int xshift, int yshift)
{
for (int i =0; i < xdim; i++)
{
int ii = (i + xshift) % xdim;
for (int j = 0; j < ydim; j++)
{
int jj = (j + yshift) % ydim;
out[ii * ydim + jj] = in[i * ydim + j];
}
}
}
int width = 256;
int height = 256;
int index = 0;
MyStringAnsi imageName1 = "C://ka4ag.png";
MyStringAnsi imageName2 = "C://KyPu2.png";
double * in1 = new double[width * height];
fftw_complex * out1 = new fftw_complex[width * height];
double * in2 = new double[width * height];
fftw_complex * out2 = new fftw_complex[width * height];
MyUtils::MyImage * im1 = MyUtils::MyImage::Load(imageName1, MyUtils::MyImage::PNG);
MyUtils::MyImage * im2 = MyUtils::MyImage::Load(imageName2, MyUtils::MyImage::PNG);
for (int i = 0; i < width * height; i++)
{
in1[i] = ((im1->Get(i).r / (255.0 * 0.5)) - 1.0);
in2[i] = ((im2->Get(i).r / (255.0 * 0.5)) - 1.0);
}
fftw_plan dft_plan1 = fftw_plan_dft_r2c_2d(width, height, in1, out1, FFTW_ESTIMATE);
fftw_execute(dft_plan1);
fftw_destroy_plan(dft_plan1);
fftw_plan dft_plan2 = fftw_plan_dft_r2c_2d(width, height, in2, out2, FFTW_ESTIMATE);
fftw_execute(dft_plan2);
fftw_destroy_plan(dft_plan2);
fftw_complex * kernel = new fftw_complex[width * height];
for (int i = 0; i < width * height; i++)
{
std::complex<double> c1(out1[i][0], out1[i][1]);
std::complex<double> c2(out2[i][0], out2[i][1]);
std::complex<double> div = c2 / c1;
kernel[i][0] = div.real();
kernel[i][1] = div.imag();
}
double * kernelOut = new double[width * height];
fftw_plan dft_planOut = fftw_plan_dft_c2r_2d(width, height, kernel, kernelOut, FFTW_ESTIMATE);
fftw_execute(dft_planOut);
fftw_destroy_plan(dft_planOut);
double * kernelShift = new double[width * height];
circshift(kernelShift, kernelOut, width, height, (width/2), (height/2));
double maxKernel = kernelShift[0];
for (int i = 0; i < width * height; i++)
{
if (maxKernel < kernelShift[i]) maxKernel = kernelShift[i];
}
for (int i = 0; i < width * height; i++)
{
kernelShift[i] /= maxKernel;
}
uint8 * res = new uint8[width * height];
for (int i = 0; i < width * height; i++)
{
res[i] = static_cast<uint8>((kernelShift[i]+ 1.0) * (255.0 * 0.5));
}
//now in res is similar result as in @Ilmari Karonen, but shifted by +128
代码没有内存管理,因此您必须清理您的内存!
/
只是逐元素除法(即乘以倒数)。如果需要,我认为我可以稍后提供带有GSL的C代码(但现在不行,我还没有清醒到那个程度)。 - Ilmari Karonen