2016-08-24 117 views
5

我已經寫了一些程序使用一個3x3內核來銳化灰度圖像,FFT卷積 - 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()可以正常工作。但是,如果我使用內核,例如3 x 3內核,則不起作用。

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

輸出:

enter image description here


源碼:

enter image description here

+0

嗨你能指定了什麼'Convolution.Convolve(x,y)'呢?無論如何,四空間中的卷積是一個乘法。因此,如果你逐項複合術語cPaddedLena和cPaddedMask的四載波變換(它們應該被填充以在四個載波空間中具有相同的維度),並且採用反向傅立葉變換,則該操作將起作用 –

回答

4

主要問題似乎是將內核解釋爲由無符號字節值組成的圖像。其結果是-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); 

這應該然後給你以下結果:

Dynamic scaling lena

得到控制而這可以提高圖像的清晰度,您應該立即注意到圖像比原始圖像暗得多。這是由於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); 
      } 
     } 
    } 

這應該然後給你一個更合適的亮度水平的圖像:

Standard scaling

最後,對於濾波操作,通常會期望結果匹配原始圖像大小(不同於包含尾部的卷積)。與裁剪的結果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); 

應該給你:

sharpened image

爲了便於直觀比較,這裏是原始圖像再次:

original image