(notify me if you need more source code to see.)
。
public static double[,]linearconvolutionspatial(double[,]image,double[,]mask)
{
int maskwidth=mask.getlength(0);
int maskheight=mask.getlength(1);
double[,]paddedimage=imagepadder.pad(图像,遮罩宽度);
double[,]conv=卷积。卷积空间(paddedimage,mask);
int cropsize=(maskwidth/2);
double[,]裁切=imagecropper.crop(conv,cropsize);
返回conv;
}
静态双[,]卷积空间(双[,]paddedimage1,双[,]mask1)
{
int imagewidth=paddedimage1.getlength(0);
int imageheight=paddedimage1.getlength(1);
int maskwidth=mask1.getlength(0);
int maskheight=mask1.getlength(1);
int convwidth=图像宽度-((maskwidth/2)*2);
int convheight=imageheight-((maskheight/2)*2);
double[,]卷积=new double[转换宽度,转换高度];
对于(int y=0;y<convheight;y++)
{
对于(int x=0;x<convwidth;x++)
{
int开始x=x;
int starty=y;
卷积[x,y]=和(paddedimage1,mask1,startx,starty);
}
}
重新缩放(卷积);
返回卷积;
}
静态双和(double[,]paddedimage1,double[,]mask1,int startx,int starty)
{
双和=0;
int maskwidth=mask1.getlength(0);
int maskheight=mask1.getlength(1);
对于(int y=starty;y<(starty+maskheight);y++)
{
对于(int x=startx;x<(startx+maskwidth);x++)
{
双img=paddedimage1[x,y];
double msk=mask1[x-startx,y-starty];
sum=sum+(img*msk);
}
}
返还金额;
}
静态空隙重新缩放(双[,]卷积)
{
int imagewidth=卷积。获取长度(0);
int imageheight=卷积。获取长度(1);
双最大值=0.0;
对于(int j=0;j<imageheight;j++)
{
对于(int i=0;i<imagewidth;i++)
{
最大值=数学最大值(最大值,卷积[I,J]);
}
}
双刻度=1.0/maxamp;
对于(int j=0;j<imageheight;j++)
{
对于(int i=0;i<imagewidth;i++)
{
双D=卷积[I,J]*尺度;
卷积[i,j]=d;
}
}
}
公共静态位图卷积频率域(位图图像1、位图内核1)
{
位图结果=空;
位图图像=(bitmap)image1.clone();
位图内核=(bitmap)kernel1.clone();
//线性卷积:和。
//循环卷积: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(paddedbkernel);
//调用复杂函数
复数[,]卷积=卷积(cpximage,cpxkernel);
结果=imagedataconverter.tobitmap(卷积);
结果=imagecropper.crop(结果,(kernel.width/2)+1);
返回结果;
}
卷积空间域.
图像首先转换为二维double
数组,然后进行卷积。图像和内核大小相同。图像在卷积之前被填充,在卷积之后被相应地剪切。
与基于快速傅立叶变换的卷积相比,该卷积的输出很奇怪,而且不正确的.
我如何解决这个问题?
注意,我从Matlab获得了以下图像输出,与我的c_fft输出相匹配:
.
更新-1:跟随@本·沃伊特的评论,我改变了Rescale()
要替换的函数255.0
具有1
从而大大提高了产量。但是,输出与FFT输出(正确的输出)不匹配。
.
更新-2:跟随@克鲁恩戈在评论中,我通过缝合填充图像,然后进行空间卷积。结果如下:
所以,输出比前一个更糟。但是,这与linked answer这意味着循环卷积不是解决方案。
.
更新-3:我用过Sum()
功能建议人@克鲁恩戈的答案。结果是改进了**Update-1**
以下内容:
但是,它仍然不是100%类似于FFT版本。
.
更新-4:以下@克鲁恩戈在评论中,我将两个结果相减,以看出不同之处:
,请
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;
}