2015-04-07 25 views
4

我已經在頻域中將高斯模糊應用於圖像。 由於未知的原因(可能我沒有錯)我收到有線圖像,而不是模糊的。在頻域中對圖像應用高斯模糊

有什麼我一步一步做:

  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. 首先我創建內核(用於測試目的的內核大小等於圖像大小,壽它唯一的中心部分與高斯函數來計算,內核其餘等於重新= 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; 
    } 
    

結果我有位移的圖像中,紅色的模糊版本:link

@edit - 通過對代碼進行了多處更改,更新了問題。

+0

如果你使用全1的內核發生什麼呢? –

+0

@MobyDisk結果是一樣的... – user2475983

+0

結果是否與原始圖像相同,或者與奇怪的圖像相同?如果所有1的內核給出奇怪的圖像,那麼你知道錯誤不在你的模糊代碼中。所以它必須在FFT/IFFT中,或者在分離通道和重新組合它們的過程中,或者在使用aforge.net生成複雜圖像的過程中。另一方面,如果所有1的內核都返回原始圖像,那麼問題在於模糊/內核。這種思路似乎有幫助嗎? –

回答

0

我在DSP stackexchange上找到了一些幫助,還有一些作弊,但它工作。主要問題是內核生成並對其應用FFT。同樣重要的是,在將ComplexImage轉換爲位圖的過程中,AForge.NET在轉換爲ComplexImage的過程中將圖像像素分隔255,並且在轉換期間將255乘以255(感謝Olli Niemitalo @ DSP SE)。

我如何解決這樣的:

  1. 我發現內核應該怎麼看起來像FFT(見下文)後。
  2. 查找該圖像的顏色。
  3. x = -2的計算gauss2d; y = -2; std = 1。
  4. 計算預分頻器從pt中計算出的值中接收顏色值。 3(見wolfram)。
  5. 生成的內核與縮放值與來自pt。 4.

但是我不能在生成的濾波器上使用FFT,因爲生成的濾波器看起來像FFT之後的濾波器。它的作品 - 輸出圖像模糊,沒有人爲因素,所以我認爲這不算太壞。

的圖像(I不能發佈超過2個鏈接,以及圖像是farily大):

最終代碼:

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