ガウスぼかしを周波数領域の画像に適用することに問題があります。理由は不明ですが (おそらく何か間違ったことはしていません)、ぼやけた画像ではなく有線の画像を受け取ります。
私が段階的に行うことは次のとおりです。
- 画像を読み込みます。
画像を別々のチャネルに分割します。
private static Bitmap[] separateColorChannels(Bitmap source, int channelCount) { if (channelCount != 3 && channelCount != 4) { throw new NotSupportedException("Bitmap[] FFTServices.separateColorChannels(Bitmap, int): Only 3 and 4 channels are supported."); } Bitmap[] result = new Bitmap[channelCount]; LockBitmap[] locks = new LockBitmap[channelCount]; LockBitmap sourceLock = new LockBitmap(source); sourceLock.LockBits(); for (int i = 0; i < channelCount; ++i) { result[i] = new Bitmap(source.Width, source.Height, PixelFormat.Format8bppIndexed); locks[i] = new LockBitmap(result[i]); locks[i].LockBits(); } for (int x = 0; x < source.Width; x++) { for (int y = 0; y < source.Height; y++) { switch (channelCount) { case 3: locks[0].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).R)); locks[1].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).G)); locks[2].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).B)); break; case 4: locks[0].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).A)); locks[1].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).R)); locks[2].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).G)); locks[3].SetPixel(x, y, Color.FromArgb(sourceLock.GetPixel(x, y).B)); break; default: break; } } } for (int i = 0; i < channelCount; ++i) { locks[i].UnlockBits(); } sourceLock.UnlockBits(); }
すべてのチャンネルを複雑な画像に変換します (AForge.NET を使用)。
public static AForge.Imaging.ComplexImage[] convertColorChannelsToComplex(Emgu.CV.Image<Emgu.CV.Structure.Gray, Byte>[] channels) { AForge.Imaging.ComplexImage[] result = new AForge.Imaging.ComplexImage[channels.Length]; for (int i = 0; i < channels.Length; ++i) { result[i] = AForge.Imaging.ComplexImage.FromBitmap(channels[i].Bitmap); } return result; }
ガウスぼかしを適用します。
最初にカーネルを作成します(テスト目的で、カーネルのサイズは画像サイズと同じです。中央部分のみがガウス関数で計算され、残りのカーネルはre = 1 im = 0に等しくなります)。
private ComplexImage makeGaussKernel(int side, double min, double max, double step, double std) { // get value at top left corner double _0x0 = gauss2d(min, min, std); // top left corner should be 1, so making scaler for rest of the values double scaler = 1 / _0x0; int pow2 = SizeServices.getNextNearestPowerOf2(side); Bitmap bitmap = new Bitmap(pow2, pow2, PixelFormat.Format8bppIndexed); var result = AForge.Imaging.ComplexImage.FromBitmap(bitmap); // For test purposes my kernel is size of image, so first, filling with 1 only. for (int i = 0; i < result.Data.GetLength(0); ++i) { for (int j = 0; j < result.Data.GetLength(0); ++j) { result.Data[i, j].Re = 1; result.Data[i, j].Im = 0; } } // The real kernel's size. int count = (int)((Math.Abs(max) + Math.Abs(min)) / step); double h = min; // Calculating kernel's values and storing them somewhere in the center of kernel. for (int i = result.Data.GetLength(0) / 2 - count / 2; i < result.Data.GetLength(0) / 2 + count / 2; ++i) { double w = min; for (int j = result.Data.GetLength(1) / 2 - count / 2; j < result.Data.GetLength(1) / 2 + count / 2; ++j) { result.Data[i, j].Re = (scaler * gauss2d(w, h, std)) * 255; w += step; } h += step; } return result; } // The gauss function private double gauss2d(double x, double y, double std) { return ((1.0 / (2 * Math.PI * std * std)) * Math.Exp(-((x * x + y * y) / (2 * std * std)))); }
すべてのチャネルとカーネルに FFT を適用します。
すべてのチャネルの中央部分にカーネルを掛けます。
void applyFilter(/*shortened*/) { // Image's size is 512x512 that's why 512 is hardcoded here // min = -2.0; max = 2.0; step = 0.33; std = 11 ComplexImage filter = makeGaussKernel(512, min, max, step, std); // Applies FFT (with AForge.NET) to every channel and filter applyFFT(complexImage); applyFFT(filter); for (int i = 0; i < 3; ++i) { applyGauss(complexImage[i], filter, side); } // Applies IFFT to every channel applyIFFT(complexImage); } private void applyGauss(ComplexImage complexImage, ComplexImage filter, int side) { int width = complexImage.Data.GetLength(1); int height = complexImage.Data.GetLength(0); for(int i = 0; i < height; ++i) { for(int j = 0; j < width; ++j) { complexImage.Data[i, j] = AForge.Math.Complex.Multiply(complexImage.Data[i, j], filter.Data[i, j]); } } }
- すべてのチャネルに IFFT を適用します。
すべてのチャネルをビットマップに戻します (AForge.NET を使用)。
public static System.Drawing.Bitmap[] convertComplexColorChannelsToBitmap(AForge.Imaging.ComplexImage[] channels) { System.Drawing.Bitmap[] result = new System.Drawing.Bitmap[channels.Length]; for (int i = 0; i < channels.Length; ++i) { result[i] = channels[i].ToBitmap(); } return result; }
ビットマップを単一のビットマップにマージ
public static Bitmap mergeColorChannels(Bitmap[] channels) { Bitmap result = null; switch (channels.Length) { case 1: return channels[0]; case 3: result = new Bitmap(channels[0].Width, channels[0].Height, PixelFormat.Format24bppRgb); break; case 4: result = new Bitmap(channels[0].Width, channels[0].Height, PixelFormat.Format32bppArgb); break; default: throw new NotSupportedException("Bitmap FFTServices.mergeColorChannels(Bitmap[]): Only 1, 3 and 4 channels are supported."); } LockBitmap resultLock = new LockBitmap(result); resultLock.LockBits(); LockBitmap red = new LockBitmap(channels[0]); LockBitmap green = new LockBitmap(channels[1]); LockBitmap blue = new LockBitmap(channels[2]); red.LockBits(); green.LockBits(); blue.LockBits(); for (int y = 0; y < result.Height; y++) { for (int x = 0; x < result.Width; x++) { resultLock.SetPixel(x, y, Color.FromArgb((int)red.GetPixel(x, y).R, (int)green.GetPixel(x, y).G, (int)blue.GetPixel(x, y).B)); } } red.UnlockBits(); green.UnlockBits(); blue.UnlockBits(); resultLock.UnlockBits(); return result; }
その結果、画像の赤い色のぼやけたバージョンがシフトされました: link。
@edit - コードにいくつかの変更を加えて質問を更新しました。