将高斯模糊应用于频域图像



我在频域中对图像应用高斯模糊有很多困难。由于未知的原因(可能我没有做错什么),我收到了有线图像而不是模糊图像。

这是我一步一步做的:

  1. 加载图像
  2. 将图像拆分为单独的通道。

    private static Bitmap[] separateColorChannels(Bitmap source, int channelCount)
    {
        if (channelCount != 3 && channelCount != 4)
        {
            throw new NotSupportedException("Bitmap[] FFTServices.separateColorChannels(Bitmap, int): Only 3 and 4 channels are supported.");
        }
        Bitmap[] result = new Bitmap[channelCount];
        LockBitmap[] locks = new LockBitmap[channelCount];
        LockBitmap sourceLock = new LockBitmap(source);
        sourceLock.LockBits();
        for (int i = 0; i < channelCount; ++i)
        {
            result[i] = new Bitmap(source.Width, source.Height, PixelFormat.Format8bppIndexed);
            locks[i] = new LockBitmap(result[i]);
            locks[i].LockBits();
        }
        for (int x = 0; x < source.Width; x++)
        {
            for (int y = 0; y < source.Height; y++)
            {
                switch (channelCount)
                {
                    case 3:
                        locks[0].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).R));
                        locks[1].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).G));
                        locks[2].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).B));
                        break;
                    case 4:
                        locks[0].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).A));
                        locks[1].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).R));
                        locks[2].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).G));
                        locks[3].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).B));
                        break;
                    default:
                        break;
                }
            }
        }
        for (int i = 0; i < channelCount; ++i)
        {
            locks[i].UnlockBits();
        }
        sourceLock.UnlockBits();
    }
    
  3. 将每个通道转换为复杂的图像(使用AForge.NET).

    public static AForge.Imaging.ComplexImage[] convertColorChannelsToComplex(Emgu.CV.Image<Emgu.CV.Structure.Gray, Byte>[] channels)
    {
        AForge.Imaging.ComplexImage[] result = new AForge.Imaging.ComplexImage[channels.Length];
        for (int i = 0; i < channels.Length; ++i)
        {
            result[i] = AForge.Imaging.ComplexImage.FromBitmap(channels[i].Bitmap);
        }
        return result;
    }
    
  4. 应用高斯模糊。

    1. 首先,我创建内核(出于测试目的,内核大小等于图像大小,只有它的中心部分是用高斯函数计算的,内核的其余部分等于re=1 im=0)。

      private ComplexImage makeGaussKernel(int side, double min, double max, double step, double std)
      {
          // get value at top left corner
          double _0x0 = gauss2d(min, min, std);
          // top left corner should be 1, so making scaler for rest of the values
          double scaler = 1 / _0x0;
          int pow2 = SizeServices.getNextNearestPowerOf2(side);
          Bitmap bitmap = new Bitmap(pow2, pow2, PixelFormat.Format8bppIndexed);
          var result = AForge.Imaging.ComplexImage.FromBitmap(bitmap);
          // For test purposes my kernel is size of image, so first, filling with 1 only.
          for (int i = 0; i < result.Data.GetLength(0); ++i)
          {
              for (int j = 0; j < result.Data.GetLength(0); ++j)
              {
                  result.Data[i, j].Re = 1;
                  result.Data[i, j].Im = 0;
              }
          }
          // The real kernel's size.
          int count = (int)((Math.Abs(max) + Math.Abs(min)) / step);
          double h = min;
          // Calculating kernel's values and storing them somewhere in the center of kernel.
          for (int i = result.Data.GetLength(0) / 2 - count / 2; i < result.Data.GetLength(0) / 2 + count / 2; ++i)
          {
              double w = min;
              for (int j = result.Data.GetLength(1) / 2 - count / 2; j < result.Data.GetLength(1) / 2 + count / 2; ++j)
              {
                  result.Data[i, j].Re = (scaler * gauss2d(w, h, std)) * 255;
                  w += step;
              }
              h += step;
          }
          return result;
      }
      // The gauss function
      private double gauss2d(double x, double y, double std)
      {
          return ((1.0 / (2 * Math.PI * std * std)) * Math.Exp(-((x * x + y * y) / (2 * std * std))));
      }
      
    2. 将FFT应用于每个通道和内核。

    3. 将每个通道的中心部分乘以内核。

      void applyFilter(/*shortened*/)
      {
          // Image's size is 512x512 that's why 512 is hardcoded here
          // min = -2.0; max = 2.0; step = 0.33; std = 11
          ComplexImage filter = makeGaussKernel(512, min, max, step, std);
          // Applies FFT (with AForge.NET) to every channel and filter
          applyFFT(complexImage);
          applyFFT(filter);
          for (int i = 0; i < 3; ++i)
          {
              applyGauss(complexImage[i], filter, side);
          }
          // Applies IFFT to every channel
          applyIFFT(complexImage);
      }
      private void applyGauss(ComplexImage complexImage, ComplexImage filter, int side)
      {
          int width = complexImage.Data.GetLength(1);
          int height = complexImage.Data.GetLength(0);
          for(int i = 0; i < height; ++i)
          {
              for(int j = 0; j < width; ++j)
              {
                  complexImage.Data[i, j] = AForge.Math.Complex.Multiply(complexImage.Data[i, j], filter.Data[i, j]);
              }
          }
      }
      
  5. 将IFFT应用于每个通道
  6. 将每个通道转换回位图(使用AForge.NET).

    public static System.Drawing.Bitmap[] convertComplexColorChannelsToBitmap(AForge.Imaging.ComplexImage[] channels)
    {
        System.Drawing.Bitmap[] result = new System.Drawing.Bitmap[channels.Length];
        for (int i = 0; i < channels.Length; ++i)
        {
            result[i] = channels[i].ToBitmap();
        }
        return result;
    }
    
  7. 将位图合并为单个位图

    public static Bitmap mergeColorChannels(Bitmap[] channels)
    {
        Bitmap result = null;
        switch (channels.Length)
        {
            case 1:
                return channels[0];
            case 3:
                result = new Bitmap(channels[0].Width, channels[0].Height, PixelFormat.Format24bppRgb);
                break;
            case 4:
                result = new Bitmap(channels[0].Width, channels[0].Height, PixelFormat.Format32bppArgb);
                break;
            default:
                throw new NotSupportedException("Bitmap FFTServices.mergeColorChannels(Bitmap[]): Only 1, 3 and 4 channels are supported.");
        }
        LockBitmap resultLock = new LockBitmap(result);
        resultLock.LockBits();
        LockBitmap red = new LockBitmap(channels[0]);
        LockBitmap green = new LockBitmap(channels[1]);
        LockBitmap blue = new LockBitmap(channels[2]);
        red.LockBits();
        green.LockBits();
        blue.LockBits();
        for (int y = 0; y < result.Height; y++)
        {
            for (int x = 0; x < result.Width; x++)
            {
                resultLock.SetPixel(x, y, Color.FromArgb((int)red.GetPixel(x, y).R, (int)green.GetPixel(x, y).G, (int)blue.GetPixel(x, y).B));
            }
        }
        red.UnlockBits();
        green.UnlockBits();
        blue.UnlockBits();
        resultLock.UnlockBits();
        return result;
    }
    

结果我得到了移动,红色模糊版本的图像:链接。

@edit-通过对代码的几处更改更新了问题。

我在DSP stackexchange上得到了一些帮助。。。还有一些作弊行为,但确实有效。主要的问题是内核生成和FFT的应用。同样重要的是,AForge.NET在转换为ComplexImage时将图像像素除以255,在从ComplexImage转换为位图时乘以255(感谢Olli Niemialo@DSP SE)。

我是如何解决这个问题的:

  1. 我发现了FFT后内核应该是什么样子(见下文)
  2. 查找了该图像的颜色
  3. x=-2时的计算gauss2d;y=-2;std=1
  4. 计算预分频器以从pt.3中计算的值接收颜色值(参见wolfram)
  5. 从pt.4生成具有缩放值的内核

然而,我不能在生成的滤波器上使用FFT,因为生成的滤波器看起来已经像FFT之后的滤波器了。它是有效的-输出图像是模糊的,没有伪影,所以我认为这还不错。

图片(我不能发布超过2个链接,图片大得离谱):

  • 输入图像
  • 生成的滤波器(无FFT!)
    • 以下函数的参数:
    • std=1.0
    • 大小=8.0
    • 宽度=高度=512
  • 结果图像

最终代码:

private ComplexImage makeGaussKernel(double size, double std, int imgWidth, int imgHeight)
{
    double scale = 2000.0;
    double hsize = size / 2.0;
    Bitmap bmp = new Bitmap(imgWidth, imgHeight, PixelFormat.Format8bppIndexed);
    LockBitmap lbmp = new LockBitmap(bmp);
    lbmp.LockBits();
    double y = -hsize;
    double yStep = hsize / (lbmp.Height / 2.0);
    double xStep = hsize / (lbmp.Width / 2.0);
    for (int i = 0; i < lbmp.Height; ++i)
    {
        double x = -hsize;
        for (int j = 0; j < lbmp.Width; ++j)
        {
            double g = gauss2d(x, y, std) * scale;
            g = g < 0.0 ? 0.0 : g;
            g = g > 255.0 ? 255.0 : g;
            lbmp.SetPixel(j, i, Color.FromArgb((int)g));
            x += xStep;
        }
        y += yStep;
    }
    lbmp.UnlockBits();
    return ComplexImage.FromBitmap(bmp);
}
private double gauss2d(double x, double y, double std)
{
    return (1.0 / (2 * Math.PI * std * std)) * Math.Exp(-(((x * x) + (y * y)) / (2 * std * std)));
}
private void applyGaussToImage(ComplexImage complexImage, ComplexImage filter)
{
    for (int i = 0; i < complexImage.Height; ++i)
    {
        for (int j = 0; j < complexImage.Width; ++j)
        {
            complexImage.Data[i, j] = AForge.Math.Complex.Multiply(complexImage.Data[i, j], filter.Data[i, j]);
        }
    }
}

相关内容

  • 没有找到相关文章

最新更新