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

时间:2015-04-07 17:38:14

标签: c# emgucv gaussian aforge frequency-domain

我已经在频域中将高斯模糊应用于图像。 由于未知原因(可能我没有错)我收到有线图像而不是模糊的图像。

我的目标是一步一步:

  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)。 / p>

      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;
    }
    
  8. 因此,我已经移动了红色模糊的图像版本:link

    @edit - 通过对代码进行了一些更改来更新问题。

1 个答案:

答案 0 :(得分:0)

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

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

  1. 我发现在FFT之后内核应该是什么样子(见下文)。
  2. 查看该图像的颜色。
  3. 计算的gauss2d为x = -2; y = -2; std = 1。
  4. 计算预分频器以从以pt计算的值接收颜色值。 3(见wolfram)。
  5. 使用来自pt的perscaler生成具有缩放值的内核。 4。
  6. 但是我不能在生成的滤波器上使用FFT,因为生成的滤波器看起来像FFT后的滤波器。它的工作原理 - 输出图像模糊,没有伪影,所以我认为这不是太糟糕。

    图像(我不能发布超过2个链接,图像非常大):

    最终代码:

    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]);
            }
        }
    }
    
相关问题