2016-08-24 1 views
5

난, 3 × 3 커널을 사용하여 그레이 스케일 이미지를 선명하게하기 위해 몇몇 루틴을 작성한는 FFT는 컨볼 루션 - 3 × 3 커널

-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

  • 요 u는 here in this link에서 전체 솔루션을 다운로드 할 수 있습니다.
+0

안녕 수 당신이 'Convolution.Convolve (x, y)'는 무엇을할까요? 어쨌든 4 중 공간에서의 회선은 곱셈이다. 따라서 용어로 용어를 복잡하게 곱하면 cPaddedLena와 cPaddedMask의 4 변형 변환 (4 배 공간에서 동일한 차원을 갖도록 패딩되어야 함)을 수행하고 반전 4 배 변환을 수행하면 연산이 작동합니다 –

답변

4

주요 문제는 커널을 부호없는 바이트 값으로 구성된 이미지로 해석하는 것으로 보입니다. 결과적으로 -1 값 효과적으로 즉시 "컨볼 루션 커널"이미지 흰색 영역에서 관찰 될 수있는 커널

255 255 255 
255 9 255 
255 255 255 

로 콘볼 루션을 연산 255로 변환된다. 결과적으로 생성 된 커널은 저역 통과 필터의 커널이므로 해당하는 흐림 효과가 생성됩니다.

아마도 이것을 처리하는 가장 좋은 방법은 커널을 이미지 대신 부호있는 값의 매트릭스로 읽는 것입니다.

커널을 여전히 이미지로 사용하려면 이미지를 부호있는 값으로 다시 변환해야합니다.당신은 다음과 SharpenFilter.ApplyWithPadding에 이미지를 변환 할 수있을 것

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; 

:

당신이 서명 값으로 바이트를지도 어디이 결과를 달성 생각할 수있는 가장 간단한 방법은 ImageDataConverter.ToInteger(Bitmap)의 수정 된 버전을 생성하는 것입니다
Complex[,] cPaddedMask = ImageDataConverter.Unwrap(maskClone); 

이는 당신에게 다음과 같은 결과를 제공해야합니다

Dynamic scaling lena

Whil 이것은 이미지의 선명도를 향상 시키므로 이미지가 원본보다 훨씬 더 어둡다는 것을 즉각 알아야합니다. 이는 최소 및 최대 값에 따라 동적으로 이미지의 크기를 재조정하는 Convolution.Rescale 기능 때문입니다. 최대 동적 범위로 이미지를 표시하는 것이 편리 할 수 ​​있지만 표준 컨볼 루션보다 전체적인 비율이 다를 수 있습니다.

//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

(당신의 FFT 구현의 확장 기준)이 표준 확장을 달성하기 위해, 다음과 같은 구현을 사용할 수 있습니다

마지막으로, 필터링 작업의 경우 일반적으로 결과가 원래 이미지 크기와 일치 할 것으로 예상됩니다 (꼬리가 포함 된 회선과 달리). 와 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

관련 문제