代码之家  ›  专栏  ›  技术社区  ›  user366312

空间域图像卷积

  •  13
  • user366312  · 技术社区  · 6 年前

    我正在尝试复制 此链接 >a>using linear->strong>covolcition in spatial domain->strong>的结果。

    图像首先被转换为2d double array,然后进行卷积。图像和内核大小相同。图像在卷积之前被填充,在卷积之后被相应地剪切。

    与基于FFT的卷积相比,输出很奇怪且不正确。

    如何解决问题?

    注意,我从Matlab获得了以下图像输出,与我的c_fft输出相匹配:

    update-1: following@ ben voigt s comment,i changed the rescale() function to replace 255.0 with 1 and such the output is improved substantially.但是,输出与FFT输出不匹配(这是正确的输出)。

    update-2: following@ cris luengo s comment,i have padded the image by stitching and then performed space covolcition.结果如下:

    所以,输出比前一个更差。但是,这与链接答案的第二个输出相似。

    update-3 :i have used the sum() function proposed by@ cris luengo s answer.结果是一个更改进版本的

    但是,它仍然不是100%类似于fft版本。

    update-4 :following@ cris luengo s comment,i have subtracted the two outcomes to see the difference:
    ,

    1.空间负频域
    2.第二步。频率减去空间域

    看起来,差异很大,这意味着空间卷积没有正确完成。

    源代码:

    (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数组,然后进行卷积。图像和内核大小相同。图像在卷积之前被填充,在卷积之后被相应地剪切。

    enter image description here

    与基于快速傅立叶变换的卷积相比,该卷积的输出很奇怪,而且不正确的.

    我如何解决这个问题?

    注意,我从Matlab获得了以下图像输出,与我的c_fft输出相匹配:

    enter image description here

    .

    更新-1:跟随@本·沃伊特的评论,我改变了Rescale()要替换的函数255.0具有1从而大大提高了产量。但是,输出与FFT输出(正确的输出)不匹配。
    enter image description here

    .

    更新-2:跟随@克鲁恩戈在评论中,我通过缝合填充图像,然后进行空间卷积。结果如下:
    enter image description here

    所以,输出比前一个更糟。但是,这与linked answer这意味着循环卷积不是解决方案。

    .

    更新-3:我用过Sum()功能建议人@克鲁恩戈的答案。结果是改进了**Update-1**以下内容:
    enter image description here

    但是,它仍然不是100%类似于FFT版本。

    .

    更新-4:以下@克鲁恩戈在评论中,我将两个结果相减,以看出不同之处:
    enter image description here,请enter image description here

    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;
        } 
    
    2 回复  |  直到 6 年前
        1
  •  5
  •   Cris Luengo    6 年前

    你的电流输出看起来更像自相关函数,而不是莉娜和她自己的卷积。我想问题可能出在你身上 Sum 功能。

    如果你看一下 convolution sum ,您将看到内核(或映像,无关紧要)被镜像:

    sum_m( f[n-m] g[m] )
    

    对于一个函数, m 用加号显示,另一个用减号显示。

    您需要修改 总和 函数读取 mask1 图像顺序正确:

    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[maskWidth - x + startX - 1, maskHeight - y + startY - 1];
                sum = sum + (img * msk);
            }
        }
    
        return sum;
    }
    

    另一种选择是传递 蒙面1 到这个函数。

        2
  •  3
  •   user366312    6 年前

    我从中找到了解决方案 this link .主要线索是介绍 offset 还有一个 factor .

    • 因素 是内核中所有值的总和。
    • 抵消 是用于进一步修复输出的任意值。

    .

    @ 克鲁恩戈 他的回答也提出了一个有效的观点。

    .

    给定链接中提供了以下源代码:

        private void SafeImageConvolution(Bitmap image, ConvMatrix fmat) 
        { 
            //Avoid division by 0 
            if (fmat.Factor == 0) 
                return; 
    
            Bitmap srcImage = (Bitmap)image.Clone(); 
    
            int x, y, filterx, filtery; 
            int s = fmat.Size / 2; 
            int r, g, b; 
            Color tempPix; 
    
            for (y = s; y < srcImage.Height - s; y++) 
            { 
                for (x = s; x < srcImage.Width - s; x++) 
                { 
                    r = g = b = 0; 
    
                    // Convolution 
                    for (filtery = 0; filtery < fmat.Size; filtery++) 
                    { 
                        for (filterx = 0; filterx < fmat.Size; filterx++) 
                        { 
                            tempPix = srcImage.GetPixel(x + filterx - s, y + filtery - s); 
    
                            r += fmat.Matrix[filtery, filterx] * tempPix.R; 
                            g += fmat.Matrix[filtery, filterx] * tempPix.G; 
                            b += fmat.Matrix[filtery, filterx] * tempPix.B; 
                        } 
                    } 
    
                    r = Math.Min(Math.Max((r / fmat.Factor) + fmat.Offset, 0), 255); 
                    g = Math.Min(Math.Max((g / fmat.Factor) + fmat.Offset, 0), 255); 
                    b = Math.Min(Math.Max((b / fmat.Factor) + fmat.Offset, 0), 255); 
    
                    image.SetPixel(x, y, Color.FromArgb(r, g, b)); 
                } 
            } 
        }