2016-08-24 4 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

Whil e это улучшает резкость изображения, вы должны сразу заметить, что изображение намного темнее оригинала. Это связано с функцией 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

Наконец, для операции фильтрации обычно ожидалось, что результат будет соответствовать исходному размеру изображения (в отличие от свертки, которая включает в себя хвосты). Обрезка результата в 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

Смежные вопросы