フーリエデジタル画像処理

まえがき



デジタル写真またはその他のビットマップ画像は、2次元平面の輝度レベルのセンサーによって記録された数字の配列です。 数学的な観点から、薄いレンズは焦点面に配置された画像のフーリエ変換を実行することを知っているため、古典的な光学システムによる画像処理の類似物である画像処理アルゴリズムを作成することができます。



そのようなアルゴリズムの式は次のようになります。

  1. Z = FFT(X)-直接2次元フーリエ変換
  2. Z ′= T(Z)-画像のフーリエ変換への関数または透明度の適用
  3. Y = BFT(Z ′)は逆2次元フーリエ変換


フーリエ変換を計算するには、高速離散フーリエ変換アルゴリズムが使用されます。 レンズの光学系は連続スペクトルの引数の連続範囲でフーリエ変換を実行しますが、デジタルデータ処理に切り替えると、フーリエ変換式は離散フーリエ変換式に置き換えることができます。



実装例





実装されたアルゴリズムは、オープンソースライブラリFFTToolsの一部です。 インターネットアドレス: github.com/dprotopopov/FFTTools



画像ぼかしアルゴリズム



光学システムでは、焦点面にあるダイアフラムはスクリーンの単純な穴です。 ダイアフラムを通過する光の結果として、高周波(短波長)は障害物を通過し、低周波(長波長)はスクリーンによって遮断されます。 したがって、結果として得られる画像の鮮明さが増します。 画面の穴を画面の障害物に置き換えると、長い波長の周波数から形成されるため、結果はぼやけた画像になります。



アルゴリズム:

  1. X(N1、N2)を画像ピクセルの輝度の配列とします。
  2. Px =配列Xの平均(rms)ピクセル輝度を計算する
  3. 計算配列Z = FT(X)-直接2次元離散フーリエ変換
  4. 配列Z ′= T(Z)を計算します。ここで、Tは、高5周波数に対応する引数行列の指定された内部領域にある行と列のゼロ化です(すなわち、高周波数に対応するフーリエ展開係数のゼロ化)。
  5. 計算配列Y = RFT(Z ′)-逆2次元離散フーリエ変換
  6. Py =配列Yのピクセルの平均(rms)輝度を計算する
  7. 平均輝度レベルPx / Pyに従って配列Y(N1、N2)を正規化する


画像鮮鋭化アルゴリズム



光学システムでは、焦点面にあるダイアフラムはスクリーンの単純な穴です。 ダイアフラムを通過する光の結果として、高周波(短波長)は障害物を通過し、低周波(長波長)はスクリーンによって遮断されます。 したがって、結果として得られる画像の鮮明さが増します。



アルゴリズム:

  1. X(N1、N2)を画像ピクセルの輝度の配列とします。
  2. Px =配列Xの平均(rms)ピクセル輝度を計算する
  3. 計算配列Z = FT(X)-直接2次元離散フーリエ変換
  4. 保存値L = Z(0,0)-元の画像のピクセルの平均輝度に対応
  5. 配列Z ′= T(Z)を計算します。ここで、Tは低6に対応する引数行列の指定された外部領域にある行と列のゼロ化です。周波数(低周波数に対応するフーリエ展開係数のゼロ化)
  6. 値Z '(0,0)= Lを復元-元の画像のピクセルの平均輝度に対応
  7. 計算配列Y = RFT(Z ′)-逆2次元離散フーリエ変換
  8. Py =配列Yのピクセルの平均(rms)輝度を計算する
  9. 平均輝度レベルPx / Pyに従って配列Y(N1、N2)を正規化する


画像スケーリングアルゴリズム



光学システムでは、システムの焦点面の光束は元の画像のフーリエ変換です。 光学系の出力で得られる画像のサイズは、レンズと接眼レンズの焦点距離の比によって決まります。



アルゴリズム:

  1. X(N1、N2)を画像ピクセルの輝度の配列とします。
  2. Px =配列Xの平均(rms)ピクセル輝度を計算する
  3. 計算配列Z = FT(X)-直接2次元離散フーリエ変換
  4. 配列Z ′= T(Z)を計算します。ここで、Tは高周波に対応する行列のゼロ行と列を追加するか、高周波に対応する行列の行と列を削除して必要な最終画像サイズを取得します。
  5. 計算配列Y = RFT(Z ′)-逆2次元離散フーリエ変換
  6. Py =配列Yのピクセルの平均(rms)輝度を計算する
  7. 平均輝度レベルPx / Pyに従って配列Y(M1、M2)を正規化する


使用したソフトウェア



画像ぼかしアルゴリズム



アルゴリズムコード
/// <summary> /// Clear internal region of array /// </summary> /// <param name="data">Array of values</param> /// <param name="size">Internal blind region size</param> private static void Blind(Complex[,,] data, Size size) { int n0 = data.GetLength(0); int n1 = data.GetLength(1); int n2 = data.GetLength(2); int s0 = Math.Max(0, (n0 - size.Height)/2); int s1 = Math.Max(0, (n1 - size.Width)/2); int e0 = Math.Min((n0 + size.Height)/2, n0); int e1 = Math.Min((n1 + size.Width)/2, n1); for (int i = s0; i < e0; i++) { Array.Clear(data, i*n1*n2, n1*n2); } for (int i = 0; i < s0; i++) { Array.Clear(data, i*n1*n2 + s1*n2, (e1 - s1)*n2); } for (int i = e0; i < n0; i++) { Array.Clear(data, i*n1*n2 + s1*n2, (e1 - s1)*n2); } } /// <summary> /// Blur bitmap with the Fastest Fourier Transform /// </summary> /// <returns>Blured bitmap</returns> public Bitmap Blur(Bitmap bitmap) { using (var image = new Image<Bgr, double>(bitmap)) { int length = image.Data.Length; int n0 = image.Data.GetLength(0); int n1 = image.Data.GetLength(1); int n2 = image.Data.GetLength(2); var doubles = new double[length]; Buffer.BlockCopy(image.Data, 0, doubles, 0, length*sizeof (double)); double power = Math.Sqrt(doubles.Average(x => x*x)); var input = new fftw_complexarray(doubles.Select(x => new Complex(x, 0)).ToArray()); var output = new fftw_complexarray(length); fftw_plan.dft_3d(n0, n1, n2, input, output, fftw_direction.Forward, fftw_flags.Estimate).Execute(); Complex[] complex = output.GetData_Complex(); var data = new Complex[n0, n1, n2]; var buffer = new double[length*2]; GCHandle complexHandle = GCHandle.Alloc(complex, GCHandleType.Pinned); GCHandle dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); IntPtr complexPtr = complexHandle.AddrOfPinnedObject(); IntPtr dataPtr = dataHandle.AddrOfPinnedObject(); Marshal.Copy(complexPtr, buffer, 0, buffer.Length); Marshal.Copy(buffer, 0, dataPtr, buffer.Length); Blind(data, _blinderSize); Marshal.Copy(dataPtr, buffer, 0, buffer.Length); Marshal.Copy(buffer, 0, complexPtr, buffer.Length); complexHandle.Free(); dataHandle.Free(); input.SetData(complex); fftw_plan.dft_3d(n0, n1, n2, input, output, fftw_direction.Backward, fftw_flags.Estimate).Execute(); double[] array2 = output.GetData_Complex().Select(x => x.Magnitude).ToArray(); double power2 = Math.Sqrt(array2.Average(x => x*x)); doubles = array2.Select(x => x*power/power2).ToArray(); Buffer.BlockCopy(doubles, 0, image.Data, 0, length*sizeof (double)); return image.Bitmap; } }
      
      







画像鮮鋭化アルゴリズム



アルゴリズムコード
  /// <summary> /// Clear external region of array /// </summary> /// <param name="data">Array of values</param> /// <param name="size">External blind region size</param> private static void Blind(Complex[,,] data, Size size) { int n0 = data.GetLength(0); int n1 = data.GetLength(1); int n2 = data.GetLength(2); int s0 = Math.Max(0, (n0 - size.Height)/2); int s1 = Math.Max(0, (n1 - size.Width)/2); int e0 = Math.Min((n0 + size.Height)/2, n0); int e1 = Math.Min((n1 + size.Width)/2, n1); for (int i = 0; i < s0; i++) { Array.Clear(data, i*n1*n2, s1*n2); Array.Clear(data, i*n1*n2 + e1*n2, (n1 - e1)*n2); } for (int i = e0; i < n0; i++) { Array.Clear(data, i*n1*n2, s1*n2); Array.Clear(data, i*n1*n2 + e1*n2, (n1 - e1)*n2); } } /// <summary> /// Sharp bitmap with the Fastest Fourier Transform /// </summary> /// <returns>Sharped bitmap</returns> public Bitmap Sharp(Bitmap bitmap) { using (var image = new Image<Bgr, double>(bitmap)) { int length = image.Data.Length; int n0 = image.Data.GetLength(0); int n1 = image.Data.GetLength(1); int n2 = image.Data.GetLength(2); var doubles = new double[length]; Buffer.BlockCopy(image.Data, 0, doubles, 0, length*sizeof (double)); double power = Math.Sqrt(doubles.Average(x => x*x)); var input = new fftw_complexarray(doubles.Select(x => new Complex(x, 0)).ToArray()); var output = new fftw_complexarray(length); fftw_plan.dft_3d(n0, n1, n2, input, output, fftw_direction.Forward, fftw_flags.Estimate).Execute(); Complex[] complex = output.GetData_Complex(); Complex level = complex[0]; var data = new Complex[n0, n1, n2]; var buffer = new double[length*2]; GCHandle complexHandle = GCHandle.Alloc(complex, GCHandleType.Pinned); GCHandle dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); IntPtr complexPtr = complexHandle.AddrOfPinnedObject(); IntPtr dataPtr = dataHandle.AddrOfPinnedObject(); Marshal.Copy(complexPtr, buffer, 0, buffer.Length); Marshal.Copy(buffer, 0, dataPtr, buffer.Length); Blind(data, _blinderSize); Marshal.Copy(dataPtr, buffer, 0, buffer.Length); Marshal.Copy(buffer, 0, complexPtr, buffer.Length); complexHandle.Free(); dataHandle.Free(); complex[0] = level; input.SetData(complex); fftw_plan.dft_3d(n0, n1, n2, input, output, fftw_direction.Backward, fftw_flags.Estimate).Execute(); double[] array2 = output.GetData_Complex().Select(x => x.Magnitude).ToArray(); double power2 = Math.Sqrt(array2.Average(x => x*x)); doubles = array2.Select(x => x*power/power2).ToArray(); Buffer.BlockCopy(doubles, 0, image.Data, 0, length*sizeof (double)); return image.Bitmap; } }
      
      







画像スケーリングアルゴリズム



アルゴリズムコード
  /// <summary> /// Copy arrays /// </summary> /// <param name="input">Input array</param> /// <param name="output">Output array</param> private static void Copy(Complex[,,] input, Complex[,,] output) { int n0 = input.GetLength(0); int n1 = input.GetLength(1); int n2 = input.GetLength(2); int m0 = output.GetLength(0); int m1 = output.GetLength(1); int m2 = output.GetLength(2); int ex0 = Math.Min(n0, m0)/2; int ex1 = Math.Min(n1, m1)/2; int ex2 = Math.Min(n2, m2); Debug.Assert(n2 == m2); for (int k = 0; k < ex2; k++) { for (int i = 0; i <= ex0; i++) { for (int j = 0; j <= ex1; j++) { int ni = n0 - i - 1; int nj = n1 - j - 1; int mi = m0 - i - 1; int mj = m1 - j - 1; output[i, j, k] = input[i, j, k]; output[mi, j, k] = input[ni, j, k]; output[i, mj, k] = input[i, nj, k]; output[mi, mj, k] = input[ni, nj, k]; } } } } /// <summary> /// Resize bitmap with the Fastest Fourier Transform /// </summary> /// <returns>Resized bitmap</returns> public Bitmap Stretch(Bitmap bitmap) { using (var image = new Image<Bgr, double>(bitmap)) { int length = image.Data.Length; int n0 = image.Data.GetLength(0); int n1 = image.Data.GetLength(1); int n2 = image.Data.GetLength(2); var doubles = new double[length]; Buffer.BlockCopy(image.Data, 0, doubles, 0, length*sizeof (double)); double power = Math.Sqrt(doubles.Average(x => x*x)); var input = new fftw_complexarray(doubles.Select(x => new Complex(x, 0)).ToArray()); var output = new fftw_complexarray(length); fftw_plan.dft_3d(n0, n1, n2, input, output, fftw_direction.Forward, fftw_flags.Estimate).Execute(); Complex[] complex = output.GetData_Complex(); using (var image2 = new Image<Bgr, double>(_newSize)) { int length2 = image2.Data.Length; int m0 = image2.Data.GetLength(0); int m1 = image2.Data.GetLength(1); int m2 = image2.Data.GetLength(2); var complex2 = new Complex[length2]; var data = new Complex[n0, n1, n2]; var data2 = new Complex[m0, m1, m2]; var buffer = new double[length*2]; GCHandle complexHandle = GCHandle.Alloc(complex, GCHandleType.Pinned); GCHandle dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned); IntPtr complexPtr = complexHandle.AddrOfPinnedObject(); IntPtr dataPtr = dataHandle.AddrOfPinnedObject(); Marshal.Copy(complexPtr, buffer, 0, buffer.Length); Marshal.Copy(buffer, 0, dataPtr, buffer.Length); complexHandle.Free(); dataHandle.Free(); Copy(data, data2); buffer = new double[length2*2]; complexHandle = GCHandle.Alloc(complex2, GCHandleType.Pinned); dataHandle = GCHandle.Alloc(data2, GCHandleType.Pinned); complexPtr = complexHandle.AddrOfPinnedObject(); dataPtr = dataHandle.AddrOfPinnedObject(); Marshal.Copy(dataPtr, buffer, 0, buffer.Length); Marshal.Copy(buffer, 0, complexPtr, buffer.Length); complexHandle.Free(); dataHandle.Free(); var input2 = new fftw_complexarray(complex2); var output2 = new fftw_complexarray(length2); fftw_plan.dft_3d(m0, m1, m2, input2, output2, fftw_direction.Backward, fftw_flags.Estimate).Execute(); double[] array2 = output2.GetData_Complex().Select(x => x.Magnitude).ToArray(); double power2 = Math.Sqrt(array2.Average(x => x*x)); double[] doubles2 = array2.Select(x => x*power/power2).ToArray(); Buffer.BlockCopy(doubles2, 0, image2.Data, 0, length2*sizeof (double)); return image2.Bitmap; } } }
      
      









プログラムのスクリーンショット



画像のぼかし
画像





画像スケーリング
画像





文学



  1. A.L. ドミトリエフ。 情報処理の光学的方法。 学習ガイド。 SPb。 SPYUGUITMO 2005.46 p。
  2. A.A. Akayev、S.A. Mayorov「情報処理の光学的方法」M .: 1988
  3. J.グッドマン「フーリエ光学入門」M。:世界1970



All Articles