FFT Convolution - 3x3 kernel

前端 未结 1 1727
北海茫月
北海茫月 2021-01-13 10:36

I have written some routines to sharpen a Grayscale image using a 3x3 kernel,

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

The following code is wo

1条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-01-13 10:56

    The main issue appears to be with the interpretation of the kernel as an image consisting of unsigned byte values. As a result the -1 values are converted to 255 effectively computing a convolution with the kernel

    255 255 255
    255  9  255
    255 255 255
    

    This can be immediately observed from white area in the "Convolution Kernel" image. The resulting kernel is thus that of a low-pass filter, producing a corresponding blurring effect.

    Probably the best way to handle this would be to read the kernel as a matrix of signed values instead of as an image.

    If you still prefer to handle the kernel as an image, you would need to convert the image back to signed values. The simplest way I can think of achieving this result would be to create a modified version of ImageDataConverter.ToInteger(Bitmap) where you map the bytes to signed values:

    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;
    

    You would then be able to convert your image in SharpenFilter.ApplyWithPadding with:

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

    This should then give you the following result:

    While this improves on the sharpness of the image, you should immediately notice that the image is much darker than the original. This is due to the Convolution.Rescale function which dynamically rescales the image according to it's minimum and maximum value. This can be convenient to show the image with maximum dynamic range, but could result in a different overall scaling than a standard convolution. To achieve this standard scaling (based on the scaling of your FFT implementation), you could use the following implementation:

        //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);
                }
            }
        }
    

    This should then give you an image with a more appropriate brightness level:

    Finally, for a filtering operation one would typically expect the result to match the original image size (unlike a convolution which includes the tails). Cropping the result in SharpenFilter.ApplyWithPadding with:

    ...
    // -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);
    

    should give you:

    For easier visual comparison, here is the original image again:

    0 讨论(0)
提交回复
热议问题