FFT卷积-3x3内核

用户名

我已经编写了一些例程来使用3x3内核来锐化灰度图像,

-1 -1 -1 
-1  9 -1 
-1 -1 -1

以下代码在非FFT(空间域)卷积的情况下工作良好,但在基于FFT(频域)卷积的情况下不起作用。

输出图像似乎模糊。

我有几个问题:

(1)此例程无法生成所需的结果。它还会冻结该应用程序。

    public static Bitmap ApplyWithPadding(Bitmap image, Bitmap mask)
    {
        if(image.PixelFormat == PixelFormat.Format8bppIndexed)
        {
            Bitmap imageClone = (Bitmap)image.Clone();
            Bitmap maskClone = (Bitmap)mask.Clone();

            /////////////////////////////////////////////////////////////////
            Complex[,] cPaddedLena = ImageDataConverter.ToComplex(imageClone);
            Complex[,] cPaddedMask = ImageDataConverter.ToComplex(maskClone);

            Complex[,] cConvolved = Convolution.Convolve(cPaddedLena, cPaddedMask);

            return ImageDataConverter.ToBitmap(cConvolved);
        }
        else
        {
            throw new Exception("not a grascale");
        }
    }

(2)此例程产生了良好的结果。但是,慢到地狱。

    public static Bitmap Apply(Bitmap sourceBitmap)
    {
        Sharpen filter = new Sharpen();

        BitmapData sourceData = sourceBitmap.LockBits(new Rectangle(0, 0,
                                 sourceBitmap.Width, sourceBitmap.Height),
                                 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        byte[] pixelBuffer = new byte[sourceData.Stride * sourceData.Height];
        byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height];

        Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, pixelBuffer.Length);

        sourceBitmap.UnlockBits(sourceData);

        double blue = 0.0;
        double green = 0.0;
        double red = 0.0;

        int filterWidth = filter.FilterMatrix.GetLength(1);
        int filterHeight = filter.FilterMatrix.GetLength(0);

        int filterOffset = (filterWidth - 1) / 2;
        int calcOffset = 0;

        int byteOffset = 0;

        for (int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
        {
            for (int offsetX = filterOffset; offsetX <
                sourceBitmap.Width - filterOffset; offsetX++)
            {
                blue = 0;
                green = 0;
                red = 0;

                byteOffset = offsetY *
                             sourceData.Stride +
                             offsetX * 4;

                for (int filterY = -filterOffset;
                    filterY <= filterOffset; filterY++)
                {
                    for (int filterX = -filterOffset;
                        filterX <= filterOffset; filterX++)
                    {

                        calcOffset = byteOffset +
                                     (filterX * 4) +
                                     (filterY * sourceData.Stride);

                        blue += (double)(pixelBuffer[calcOffset]) *
                                filter.FilterMatrix[filterY + filterOffset,
                                                    filterX + filterOffset];

                        green += (double)(pixelBuffer[calcOffset + 1]) *
                                 filter.FilterMatrix[filterY + filterOffset,
                                                    filterX + filterOffset];

                        red += (double)(pixelBuffer[calcOffset + 2]) *
                               filter.FilterMatrix[filterY + filterOffset,
                                                  filterX + filterOffset];
                    }
                }

                blue = filter.Factor * blue + filter.Bias;
                green = filter.Factor * green + filter.Bias;
                red = filter.Factor * red + filter.Bias;

                if (blue > 255)
                { blue = 255; }
                else if (blue < 0)
                { blue = 0; }

                if (green > 255)
                { green = 255; }
                else if (green < 0)
                { green = 0; }

                if (red > 255)
                { red = 255; }
                else if (red < 0)
                { red = 0; }

                resultBuffer[byteOffset] = (byte)(blue);
                resultBuffer[byteOffset + 1] = (byte)(green);
                resultBuffer[byteOffset + 2] = (byte)(red);
                resultBuffer[byteOffset + 3] = 255;
            }
        }

        Bitmap resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);

        BitmapData resultData = resultBitmap.LockBits(new Rectangle(0, 0,
                                 resultBitmap.Width, resultBitmap.Height),
                                 ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

        Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length);
        resultBitmap.UnlockBits(resultData);

        return resultBitmap;
    }

(3)以下是我的GUI代码。SharpenFilter.ApplyWithPadding()如果我将图像用作遮罩,则可以正常工作。但是,如果我使用3x3内核,则不起作用

    string path = @"E:\lena.png";
    string path2 = @"E:\mask.png";

    Bitmap _inputImage;
    Bitmap _maskImage;

    private void LoadImages_Click(object sender, EventArgs e)
    {
        _inputImage = Grayscale.ToGrayscale(Bitmap.FromFile(path) as Bitmap);

        /*
        _maskImage = Grayscale.ToGrayscale(Bitmap.FromFile(path2) as Bitmap);
        */

        SharpenFilter filter = new SharpenFilter();
        double[,] mask = new double[,]  { { -1, -1, -1, }, 
                                        { -1,  9, -1, }, 
                                        { -1, -1, -1, }, };
        _maskImage = ImageDataConverter.ToBitmap(mask);

        inputImagePictureBox.Image = _inputImage;
        maskPictureBox.Image = _maskImage;
    }

    Bitmap _paddedImage;
    Bitmap _paddedMask;
    private void padButton_Click(object sender, EventArgs e)
    {
        Bitmap lena = Grayscale.ToGrayscale(_inputImage);
        Bitmap mask = Grayscale.ToGrayscale(_maskImage);

        ////Not working...
        //int maxWidth = (int)Math.Max(lena.Width, mask.Width);
        //int maxHeight = (int)Math.Max(lena.Height, mask.Height);

        ////This is working correctly in case if I use a png image as a mask.
        int maxWidth = (int)Tools.ToNextPow2(Convert.ToUInt32(lena.Width + mask.Width));
        int maxHeight = (int)Tools.ToNextPow2(Convert.ToUInt32(lena.Height + mask.Height));

        _paddedImage = ImagePadder.Pad(lena, maxWidth, maxHeight);
        _paddedMask = ImagePadder.Pad(mask, maxWidth, maxHeight);

        paddedImagePictureBox.Image = _paddedImage;
        paddedMaskPictureBox.Image = _paddedMask;
    }

    private void filterButton_Click(object sender, EventArgs e)
    {
        // Not working properly.
        // Freezes the application. 
        Bitmap sharp = SharpenFilter.ApplyWithPadding(_paddedImage, _paddedMask);

        ////Works well. But, very slow.
        //Bitmap sharp = SharpenFilter.Apply(_paddedImage);

        filteredPictureBox.Image = sharp as Bitmap;
    }

输出:

在此处输入图片说明


源代码 :

在此处输入图片说明

侦探眼

主要问题似乎在于将内核解释为由无符号字节值组成的映像。结果,这些-1值被转换为255有效地计算与内核的卷积

255 255 255
255  9  255
255 255 255

可以从“卷积内核”图像中的白色区域立即观察到这一点。因此,所得的核是低通滤波器的核,从而产生相应的模糊效果。

解决此问题的最佳方法可能是将内核读取为带符号值的矩阵,而不是图像。

如果您仍然希望将内核作为映像处理,则需要将映像转换回带符号的值。我可以想到的最简单的方法是创建一个ImageDataConverter.ToInteger(Bitmap)将字节映射到带符号的值的修改版本

public static Complex[,] Unwrap(Bitmap bitmap)
{
  int Width = bitmap.Width;
  int Height = bitmap.Height;

  Complex[,] array2D = new Complex[bitmap.Width, bitmap.Height];
  ...

        else// If there is only one channel:
        {
          iii = (int)(*address);
          if (iii >= 128)
          {
            iii -= 256;
          }
        }
        Complex tempComp = new Complex((double)iii, 0.0);
        array2D[x, y] = tempComp;

然后,您可以使用以下方法转换图像SharpenFilter.ApplyWithPadding

Complex[,] cPaddedMask =  ImageDataConverter.Unwrap(maskClone);

然后,这应该给您以下结果:

动态缩放镜头

虽然这可以提高图像的清晰度,但是您应该立即注意到图像比原始图像要暗得多。这是由于该Convolution.Rescale功能可以根据图像的最小值和最大值动态地重新缩放图像。这可以方便地显示具有最大动态范围的图像,但可能导致与标准卷积不同的整体缩放比例。要实现此标准缩放(基于FFT实现的缩放),可以使用以下实现:

    //Rescale values between 0 and 255.
    private static void Rescale(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, Math.Min(convolve[i, j].Real * scale, 255.0));
                double im = Math.Max(0, Math.Min(convolve[i, j].Imaginary * scale, 255.0));
                convolve[i, j] = new Complex(re, im);
            }
        }
    }

然后,这应该为您提供具有更适当亮度级别的图像:

标准缩放

最后,对于滤波操作,通常会期望结果与原始图像大小匹配(与包括尾部的卷积不同)。通过以下方式裁剪结果SharpenFilter.ApplyWithPadding

...
// -3 terms are due to kernel size
// +5 vertical offset term is due to vertical reflection & offset in SetPixel
Rectangle rect = new Rectangle((cPaddedLena.GetLength(0) / 2 - 3) / 2,
                               (cPaddedLena.GetLength(1) / 2 - 3) / 2 + 5, 
                               cPaddedLena.GetLength(0) / 2,
                               cPaddedLena.GetLength(1) / 2);
return ImageDataConverter.ToBitmap(cConvolved).Clone(rect, PixelFormat.Format8bppIndexed);

应该给你:

图像锐化

为了便于视觉比较,以下是原始图像:

原始图片

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

卷积神经网络中不同内核大小(1x1、3x3、5x5)之间有什么区别?

来自分类Dev

如何向量化3x3 2D卷积?

来自分类Dev

在C ++中使用3X3低通滤波器对.pgm图像进行卷积

来自分类Dev

如何正确应用带有卷积层的 3x3 过滤器?

来自分类Dev

CIImage(IOS):在单色滤镜之后添加3x3卷积以某种方式恢复颜色

来自分类Dev

FFT卷积不比规范卷积计算快

来自分类Dev

在python中将具有三个内核(x,y,z)的3D阵列卷积

来自分类Dev

使用fft进行系统输出的线性卷积

来自分类Dev

使用 FFT 的卷积速度有多快

来自分类Dev

与未填充内核的卷积

来自分类Dev

为什么使用FFT时卷积结果会发生偏移

来自分类Dev

如何将FFT用于一维反卷积?

来自分类Dev

为什么在使用FFT时我的卷积结果会移位

来自分类Dev

尝试使用 FFT 卷积时不支持 cuDNN 状态

来自分类Dev

torch.rfft-基于fft的卷积创建的输出与空间卷积不同

来自分类Dev

2D圆形卷积Vs卷积FFT [Matlab / Octave / Python]

来自分类Dev

以3x3模式排列屏幕

来自分类Dev

Swift中的3x3阵列

来自分类Dev

生成3x3魔方

来自分类Dev

共享图例3x3 ggplots

来自分类Dev

使用1D FFT与2D FFT的可分卷积

来自分类Dev

确定3D内核的可分离性(1D内核能否获得3D卷积的结果?)

来自分类Dev

使用偶数大小的内核进行图像卷积

来自分类Dev

创建一个python卷积内核

来自分类Dev

用于没有 Astropy 的卷积的梯形内核

来自分类Dev

如何让 tensorflow 在具有 1 x 2 内核的 2 x 2 矩阵上进行卷积?

来自分类Dev

使用符号的矩阵3x3的逆

来自分类Dev

HTML Layout issue - 3x3 table with centered image

来自分类Dev

HTML布局问题 - 与居中的图像3x3的表格

Related 相关文章

热门标签

归档