2016-08-24 4 views
5

Ich habe einige Routinen geschrieben wirdFFT Convolution - 3x3 kernel

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

Der folgende Code in Fall arbeitet der Nicht-FFT (räumliche-Domäne) Faltung und ein Graustufenbild unter Verwendung eines 3x3-Kernel zu schärfen, aber nicht in FFT-basierten (Frequenz-Domain) Faltung arbeiten.

Das Ausgabebild scheint verschwommen zu sein.

I haben mehrere Probleme:

(1) Diese Routine wird nicht in der Lage ist, gewünschte Ergebnis zu erzeugen. Es friert auch die Anwendung ein.

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) Diese Routine gibt gutes Ergebnis. Aber so langsam wie die Hölle.

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) Das Folgende ist mein GUI-Code. SharpenFilter.ApplyWithPadding() funktioniert ordnungsgemäß, wenn ich ein Bild als Maske verwende. Aber, funktioniert nicht, wenn ich einen, sagen wir, 3 x 3 Kernel benutze.

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

Ausgang:

enter image description here


Source Code:

enter image description here

+0

Hallo können Sie spezifiziert was 'Convolution.Convolve (x, y)' tut? In jedem Fall ist die Faltung im Fourierraum eine Multiplikation. Wenn Sie Term-für-Term die Fourrier-Transformation von cPaddedLena und cPaddedMask komplex multiplizieren (sie sollten so gepolt sein, dass sie die gleiche Dimension im Fourier-Raum haben) und die invertierte Fourrier-Transformation verwenden, wird die Operation funktionieren –

Antwort

4

Das Hauptproblem scheint mit der Auslegung des Kerns als ein Bild zu sein, das aus unsigned Bytewerte. in dem „Convolution Kernel“ Bild Als Ergebnis werden die -1 Werte 255 effektiv umgewandelt, um eine Faltung mit dem Kernel-Berechnung

255 255 255 
255 9 255 
255 255 255 

Dies kann sofort von den weißen Bereich zu beachten. Der resultierende Kern ist somit der eines Tiefpassfilters, der einen entsprechenden Unschärfeeffekt erzeugt.

Wahrscheinlich wäre der beste Weg, dies zu tun, den Kernel als eine Matrix von vorzeichenbehafteten Werten anstatt als Bild zu lesen.

Wenn Sie den Kernel lieber als Bild behandeln möchten, müssen Sie das Bild wieder in vorzeichenbehaftete Werte konvertieren.Die einfachste Art, wie ich zu erreichen, um dieses Ergebnis zu denken wäre eine modifizierte Version von ImageDataConverter.ToInteger(Bitmap) zu schaffen, in dem Sie die Bytes signierte Werte zuzuordnen:

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; 

Sie würden dann in der Lage sein, Ihr Bild zu konvertieren in SharpenFilter.ApplyWithPadding mit:

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

Dies sollte Ihnen dann das folgende Ergebnis:

Dynamic scaling lena

Whil Wenn Sie die Schärfe des Bildes verbessern, sollten Sie sofort bemerken, dass das Bild viel dunkler als das Original ist. Dies liegt an der Convolution.Rescale-Funktion, die das Bild dynamisch entsprechend seinem minimalen und maximalen Wert skaliert. Dies kann nützlich sein, um das Bild mit maximalem Dynamikbereich anzuzeigen, könnte aber zu einer anderen Gesamtskalierung führen als eine Standardfaltung. Um diese Standard-Skalierung zu erreichen (basierend auf der Skalierung Ihrer FFT-Implementierung), könnten Sie die folgende Implementierung verwenden:

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

Dies sollte man dann mit einer geeigneteren Helligkeitsstufe ein Bild geben:

Standard scaling

Schließlich würde man für eine Filteroperation normalerweise erwarten, dass das Ergebnis der ursprünglichen Bildgröße entspricht (im Gegensatz zu einer Faltung, die die Schwänze enthält). Cropping das Ergebnis in SharpenFilter.ApplyWithPadding mit:

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

sollte Ihnen:

sharpened image

Zur leichteren visuellen Vergleich, hier ist das Originalbild wieder:

original image