2016-07-20 8 views
1

Я использую следующий код для преобразования Bitmap в Complex и наоборот.Потеря данных во время обратного БПФ изображения

Несмотря на то, что они были скопированы непосредственно с Accord.NET framework, при тестировании этих статических методов я обнаружил, что повторное использование этих статических методов вызывает «потерю данных». В результате конечный результат/результат становится искаженным.

public partial class ImageDataConverter 
{ 
    #region private static Complex[,] FromBitmapData(BitmapData bmpData) 
    private static Complex[,] ToComplex(BitmapData bmpData) 
    { 
     Complex[,] comp = null; 

     if (bmpData.PixelFormat == PixelFormat.Format8bppIndexed) 
     { 
      int width = bmpData.Width; 
      int height = bmpData.Height; 
      int offset = bmpData.Stride - (width * 1);//1 === 1 byte per pixel. 

      if ((!Tools.IsPowerOf2(width)) || (!Tools.IsPowerOf2(height))) 
      { 
       throw new Exception("Imager width and height should be n of 2."); 
      } 

      comp = new Complex[width, height]; 

      unsafe 
      { 
       byte* src = (byte*)bmpData.Scan0.ToPointer(); 

       for (int y = 0; y < height; y++) 
       { 
        for (int x = 0; x < width; x++, src++) 
        { 
         comp[y, x] = new Complex((float)*src/255, 
                comp[y, x].Imaginary); 
        } 
        src += offset; 
       } 
      } 
     } 
     else 
     { 
      throw new Exception("EightBppIndexedImageRequired"); 
     } 

     return comp; 
    } 
    #endregion 

    public static Complex[,] ToComplex(Bitmap bmp) 
    { 
     Complex[,] comp = null; 

     if (bmp.PixelFormat == PixelFormat.Format8bppIndexed) 
     { 
      BitmapData bmpData = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height), 
               ImageLockMode.ReadOnly, 
               PixelFormat.Format8bppIndexed); 
      try 
      { 
       comp = ToComplex(bmpData); 
      } 
      finally 
      { 
       bmp.UnlockBits(bmpData); 
      } 
     } 
     else 
     { 
      throw new Exception("EightBppIndexedImageRequired"); 
     } 

     return comp; 
    } 

    public static Bitmap ToBitmap(Complex[,] image, bool fourierTransformed) 
    { 
     int width = image.GetLength(0); 
     int height = image.GetLength(1); 

     Bitmap bmp = Imager.CreateGrayscaleImage(width, height); 

     BitmapData bmpData = bmp.LockBits(
      new Rectangle(0, 0, width, height), 
      ImageLockMode.ReadWrite, 
      PixelFormat.Format8bppIndexed); 

     int offset = bmpData.Stride - width; 
     double scale = (fourierTransformed) ? Math.Sqrt(width * height) : 1; 

     unsafe 
     { 
      byte* address = (byte*)bmpData.Scan0.ToPointer(); 

      for (int y = 0; y < height; y++) 
      { 
       for (int x = 0; x < width; x++, address++) 
       { 
        double min = System.Math.Min(255, image[y, x].Magnitude * scale * 255); 

        *address = (byte)System.Math.Max(0, min); 
       } 
       address += offset; 
      } 
     } 

     bmp.UnlockBits(bmpData); 

     return bmp; 
    } 
} 

(The DotNetFiddle link of the complete source code)

(ImageDataConverter)

Выход:

enter image description here

Как вы можете видеть, FFT работает правильно, но я-FFT ISN «т.

Это связано с тем, что битмап к сложному и наоборот не работает должным образом.

Что можно сделать, чтобы исправить функции ToComplex() и ToBitmap(), чтобы они не потеряли данные?

+0

Скрипка не содержит класс «ImageDataConverter». Можем ли мы посмотреть? Я подозреваю, что проблема с динамическим диапазоном данных после преобразования. – rayryeng

+1

Надеюсь, вы знаете, что результат FFT сложный ... ваш результат FFT выглядит как скаляр в градациях серого, который не является сложным, поэтому вы, скорее всего, выбрасываете мнимую часть или используете спектр мощности вместо сложного домена, так или иначе из таких данных, вы не можете восстановить оригинальные образ. Не говоря уже о смене ... Взгляните на [Что должно быть вводом и выводом для преобразования изображения FFT?] (Http://stackoverflow.com/a/26734979/2521214) – Spektre

+1

@Spektre Тем более изучите, как выглядит класс 'ImageDataConverter'. Это я считаю виновником сомнительных результатов. Глядя на скрипку, код FFT кажется прекрасным, но код для обработки преобразования при отображении данных изображения отсутствует. Кстати, отличная ссылка. – rayryeng

ответ

4

Я не кодирую код C#, так что справляйтесь с этим ответом с крайними предрассудками!

Просто от быстрого взгляда я заметил несколько проблем:

  1. ToComplex()

    конвертирует BMP в 2D комплексной матрицы. При преобразовании вы уезжаете мнимая часть без изменений, но в начале той же функции, у вас есть:

    Complex[,] complex2D = null; 
    complex2D = new Complex[width, height]; 
    

    Так мнимые части либо не определено или равно нулю, зависит от сложного конструктора класса. Это означает, что вам не хватает половины данных, необходимых для реконструкции !!! Вы должны восстановить исходную комплексную матрицу из двух изображений, один для реального и второй для мнимой части результата.

  2. ToBitmap()

    Вы спасаете величину, которая я думаю sqrt(Re*Re + Im*Im) поэтому спектр мощности не оригинальные комплексные значения, и поэтому вы не можете восстановить обратно ... Вы должны хранить Re, Im в 2 отдельных изображений.

  3. 8bit на пиксель

    Это не так много, и может привести к значительным ошибкам округления после FFT/IFFT так восстановления может быть действительно искажается.

[Edit1] Устранение

Есть больше возможностей для исправления этого, например:

  1. использования плавающих комплексной матрицы для вычислений и битовой карты только для визуализации.

    Это самый безопасный способ, потому что вы избегаете дополнительных округлений округления. Такой подход имеет наилучшую точность. Но вам нужно переписать свои алгоритмы DIP/CV для поддержки сложных матриц областей, а не растровых изображений, которые требуют не малой работы.

  2. переписать свои преобразования, чтобы поддержать реальное и мнимой часть изображения

    Вашего преобразование действительно плохо, как не хранит/восстановить вещественные и мнимые части, как должно, а также он не учитывает отрицательные значения (по крайней мере, я не вижу его, вместо этого они обрезаются до нуля, что НЕПРАВИЛЬНО). Я хотел бы переписать преобразование этого:

    // conversion scales 
    float Re_ofset=256.0,Re_scale=512.0/255.0; 
    float Im_ofset=256.0,Im_scale=512.0/255.0; 
    
    private static Complex[,] ToComplex(BitmapData bmpRe,BitmapData bmpIm) 
    { 
    //... 
    byte* srcRe = (byte*)bmpRe.Scan0.ToPointer(); 
    byte* srcIm = (byte*)bmpIm.Scan0.ToPointer(); 
    complex c = new Complex(0.0,0.0); 
    // for each line 
    for (int y = 0; y < height; y++) 
        { 
        // for each pixel 
        for (int x = 0; x < width; x++, src++) 
        { 
        complex2D[y, x] = c; 
        c.Real  = (float)*(srcRe*Re_scale)-Re_ofset; 
        c.Imaginary = (float)*(srcIm*Im_scale)-Im_ofset; 
        } 
        src += offset; 
        }   
    //... 
    } 
    public static Bitmap ToBitmapRe(Complex[,] complex2D) 
    { 
    //... 
    float Re = (complex2D[y, x].Real+Re_ofset)/Re_scale; 
    Re = min(Re,255.0); 
    Re = max(Re, 0.0); 
    *address = (byte)Re; 
    //... 
    } 
    public static Bitmap ToBitmapIm(Complex[,] complex2D) 
    { 
    //... 
    float Im = (complex2D[y, x].Imaginary+Im_ofset)/Im_scale; 
    Re = min(Im,255.0); 
    Re = max(Im, 0.0); 
    *address = (byte)Im; 
    //... 
    } 
    

    Где:

    Re_ofset = min(complex2D[,].Real); 
    Im_ofset = min(complex2D[,].Imaginary); 
    Re_scale = (max(complex2D[,].Real )-min(complex2D[,].Real ))/255.0; 
    Im_scale = (max(complex2D[,].Imaginary)-min(complex2D[,].Imaginary))/255.0; 
    

    или покрывают больший интервал затем комплексные значения матрицы.

    Вы также можете кодировать как реальные, так и мнимые части для одного изображения, например, первая половина изображения может быть реальной, а следующая - мнимой частью. В этом случае вам не нужно вообще менять заголовки функций и имена, но вам нужно будет обрабатывать изображения как 2 соединенных квадрата, каждый из которых имеет различный смысл ...

    Вы также можете использовать изображения RGB, где R = Real, B = Imaginary или любая другая кодировка, которая вас устраивает.

[Edit2] некоторые примеры, чтобы сделать мои пункты более четко

  1. пример подход # 1

    изображение находится в форме с плавающей точкой 2D комплексной матрицей и изображения создаются только для визуализации. Таким образом, существует небольшая ошибка округления. Значения не нормируются, поэтому диапазон составляет <0.0,255.0> на пиксель/ячейку, но после преобразований и масштабирования он может сильно измениться.

    approach #1

    Как вы можете видеть, что я добавил масштабирование, так что все пиксели умножаются на 315, чтобы действительно увидеть что-нибудь, потому что FFT выходные значения невелики за исключением нескольких клеток. Но только для визуализации сложная матрица не изменяется.

  2. пример подхода # 2

    Ну, как я уже говорил, вы не обрабатывать отрицательные значения, нормализовать значения в диапазоне <0,1> и обратно за счет масштабирования и округления и с использованием только 8 бит на пиксель для хранения суб результаты. Я попытался имитировать это с помощью моего кода, и вот что я получил (используя сложный домен вместо неправильно используемого спектра мощности, как и вы). Здесь C++ источник только в качестве примера шаблона, как вы не имеете функции и классы за ним:

    transform t; 
    cplx_2D c; 
    rgb2i(bmp0); 
    c.ld(bmp0,bmp0); 
    null_im(c); 
    c.mul(1.0/255.0); 
    
    c.mul(255.0); c.st(bmp0,bmp1); c.ld(bmp0,bmp1); i2iii(bmp0); i2iii(bmp1); c.mul(1.0/255.0); 
    bmp0->SaveToFile("_out0_Re.bmp"); 
    bmp1->SaveToFile("_out0_Im.bmp"); 
    
    t. DFFT(c,c); 
    c.wrap(); 
    
    c.mul(255.0); c.st(bmp0,bmp1); c.ld(bmp0,bmp1); i2iii(bmp0); i2iii(bmp1); c.mul(1.0/255.0); 
    bmp0->SaveToFile("_out1_Re.bmp"); 
    bmp1->SaveToFile("_out1_Im.bmp"); 
    
    c.wrap(); 
    t.iDFFT(c,c); 
    
    c.mul(255.0); c.st(bmp0,bmp1); c.ld(bmp0,bmp1); i2iii(bmp0); i2iii(bmp1); c.mul(1.0/255.0); 
    bmp0->SaveToFile("_out2_Re.bmp"); 
    bmp1->SaveToFile("_out2_Im.bmp"); 
    

    А вот результаты суб:

    approach #2 8bit

    Как вы можете видеть после DFFT и оберните изображение очень темным, и большинство значений округлены. Таким образом, результат после распаковки и IDFFT действительно чистый.

    Вот некоторые пояснения к коду:

    • c.st(bmpre,bmpim) так же, как ваш ToBitmap
    • c.ld(bmpre,bmpim) так же, как ваш ToComplex
    • c.mul(scale) умножает комплексную матрицу c по scale
    • rgb2i преобразует RGB в интенсивность оттенков серого <0,255>
    • i2iii преобразует интенсивность полутонового ра в оттенках серого RGB изображения
+0

Прежде всего, код 90% скопирован с сайта accord.net. во-вторых, эти функции работают хорошо, индивидуально. – anonymous

+0

@ anonymous Я не сомневаюсь, что они работают так, как должны, но они не делают то, что вы хотите/должны их делать.Вы делаете IFFT на данных, которые не являются прямым результатом FFT, поэтому вы не можете ожидать, что результатом будет исходное изображение или даже что-нибудь близко к нему. – Spektre

+0

Да. Несмотря на то, что 1 и 2 были скопированы непосредственно из рамки accord.net, они не работают должным образом. Это настоящая BS и разочарование. – anonymous

0

Я не очень хорошо в этих головоломках, но двойной проверке этого разделении.

comp[y, x] = new Complex((float)*src/255, comp[y, x].Imaginary); 

Вы можете потерять точность, как описано здесь Complex class definition в Примечания раздела. Возможно, это произойдет в вашем случае. Надеюсь, это поможет.