주파수 영역처리 C# 소스 코드(푸리에변환)
주파수 영역처리(푸리에변환) C# 소스 코드
- 메뉴를 다음과 같이 추가한다.
- [주파수_FFT변환] 메뉴를 더블클릭한 후 private void 주파수FFT변환ToolStripMenuItem_Click() 함수를 추가한다.
123456789101112131415161718192021private void 주파수FFT변환ToolStripMenuItem_Click(object sender, EventArgs e){Bitmap bmp = f_OpenBitmapFile();if (bmp == null) return;byte[] data = f_getDataFromImage(bmp);///////// Start Image Processing ////////////////if (bmp.PixelFormat != PixelFormat.Format8bppIndexed){MessageBox.Show("8bit 색상 모델만 실행 가능합니다.");return;}complex_num[] fft = f_FFT_2D(data, bmp.Width, bmp.Height);byte[] newdata = f_FFT_toImage(fft, bmp.Width, bmp.Height);///////// End Image Processing ////////////////Bitmap bmpResult = f_makeImageFromData(bmp.Width, bmp.Height, bmp.PixelFormat, newdata);f_drawImage(bmp, bmpResult);}
- 복소수를 저장할 클래스를 선언한다.
12345class complex_num{public double re;public double im;}
- reverse_bit_order() 함수를 생성한다.
최하위 비트부터 최상위비트 순으로 비트 순서를 거꾸로 변경하는 함수
예) 10101100 => 00110101
123456789101112private int reverse_bit_order(int index, int log2N){int b, r, k;r = 0;for (k = 0; k < log2N; k++){b = (index & (1 << k)) >> k;r = (r << 1) | b;}return r;}
- shuffle_data() 함수를 생성한다.
복소수 x의 앞 뒤 값들을 서로 바꾼다.
1234567891011121314151617private void shuffle_data(complex_num[] x, int N, int log2N){int i;complex_num[] tmp;tmp = new complex_num[N];for (i = 0; i < N; i++){tmp[i] = new complex_num();tmp[i].re = x[reverse_bit_order(i, log2N)].re;tmp[i].im = x[reverse_bit_order(i, log2N)].im;}for (i = 0; i < N; i++){x[i].re = tmp[i].re;x[i].im = tmp[i].im;}}
- butterfly_computation() 함수를 생성한다.
나비 흐름도 게산
12345678910111213141516171819202122232425262728293031323334353637private void butterfly_computation(complex_num[] x, int N, int log2N){int k, l, i, j;int groupSize; // 그룹 크기int start; // 그룹의 시작 위치complex_num[] W; // 값을 저장하는 기억 장소complex_num temp = new complex_num();W = new complex_num[N / 2];for (k = 1; k <= log2N; k++){ // 각 단계에 대하여groupSize = (int)Math.Pow((float)2, (int)k); // groupSize =for (i = 0; i < groupSize / 2; i++){ // ,,...,값 계산W[i] = new complex_num();W[i].re = Math.Cos(i * 2.0 * Math.PI / (double)groupSize);W[i].im = -Math.Sin(i * 2.0 * Math.PI / (double)groupSize);}start = 0;for (l = 0; l < N / groupSize; l++){ // 각 그룹에 대하여// 그룹 내의 각 나비 흐름도에 대하여for (i = start; i < start + groupSize / 2; i++){j = i + groupSize / 2;temp.re = W[i - start].re * x[j].re - W[i - start].im * x[j].im; // 계산temp.im = W[i - start].im * x[j].re + W[i - start].re * x[j].im;x[j].re = x[i].re - temp.re; // 계산x[j].im = x[i].im - temp.im;x[i].re = x[i].re + temp.re; // 계산x[i].im = x[i].im + temp.im;}start = start + groupSize;}}}
- f_FFT_1D() 함수를 생성한다.
1차원 푸리에 변환 함수 생성
12345private void f_FFT_1D(complex_num[] x, int N, int log2N){shuffle_data(x, N, log2N);butterfly_computation(x, N, log2N);}
- f_FFT_2D() 함수를 생성한다.
2차원 푸리에 변환 함수 생성
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778private complex_num[] f_FFT_2D(byte[] inputImg, int width, int height){complex_num[] rows, cols; /* 1차원 FFT를 위한 데이터 배열 */int log2N; /* FFT 단계수 */int num;// FFT 변환 결과를 저장하기 위한 기억 장소 할당complex_num[] fft_result = new complex_num[inputImg.Length];for (int i = 0; i < fft_result.Length; i++){fft_result[i] = new complex_num();}// 영상의 폭에 대한 log2N을 계산 => 폭이 256이면 lon2(256) 즉 8이 나옴num = width;log2N = 0;while (num >= 2){num >>= 1;log2N++;}rows = new complex_num[width];for (int i = 0; i < rows.Length; i++){rows[i] = new complex_num();}for (int y = 0; y < height; y++) // 영상의 각 행에 대하여 FFT 수행{for (int x = 0; x < width; x++) // 한 행을 data 배열에 복사{rows[x].re = inputImg[y * width + x]; // 실수부는 영상의 픽셀 값으로 설정rows[x].im = 0; // 허수부 값을 0으로 설정}f_FFT_1D(rows, width, log2N); // 1차원 FFT 수행for (int x = 0; x < width; x++) // 결과 저장{fft_result[y * width + x].re = rows[x].re;fft_result[y * width + x].im = rows[x].im;}}// 영상의 높이에 대한 log2N 계산 => 높이가 256이면 lon2(256) 즉 8이 나옴num = height;log2N = 0;while (num >= 2){num >>= 1;log2N++;}// 기억장소 할당cols = new complex_num[height];for (int i = 0; i < cols.Length; i++){cols[i] = new complex_num();}for (int x = 0; x < width; x++){for (int y = 0; y < height; y++) // 행에 대한 FFT 결과의 한 열을 복사{cols[y].re = fft_result[y * width + x].re;cols[y].im = fft_result[y * width + x].im;}f_FFT_1D(cols, height, log2N); // 1차원 FFT 수행for (int y = 0; y < height; y++) // 결과 저장{fft_result[y * width + x].re = cols[y].re;fft_result[y * width + x].im = cols[y].im;}}return fft_result;}
- f_FFT_toImage() 함수를 생성한다.
푸리에 변환 후 결과를 2차원 영상으로 변환하는 함수
123456789101112131415161718192021222324252627282930313233343536373839404142434445private byte[] f_FFT_toImage(complex_num[] fft_result, int width, int height){byte[] resultImg = new byte[fft_result.Length];// FFT 결과값을 영상으로 보여주기 위해 변환for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){int val = 20 * (int)Math.Log(Math.Abs(Math.Sqrt(fft_result[y * width + x].re * fft_result[y * width + x].re + fft_result[y * width + x].im * fft_result[y * width + x].im)));if (val > 255) val = 255;if (val < 0) val = 0;resultImg[y * width + x] = (byte)val;}}// FFT 결과 이미지의 중심점을 기준으로 이미지를 4등분하고// 각 사분면의 내용을 상하 대칭 및 좌우 대칭시킴byte[] tmpImg = new byte[width * height];for (int i = 0; i < height; i += height / 2){for (int j = 0; j < width; j += width / 2){for (int row = 0; row < height / 2; row++){for (int col = 0; col < width / 2; col++){tmpImg[((height / 2 - 1) - row + i) * width + (width / 2 - 1) - col + j] =resultImg[(i + row) * width + j + col];}}}}for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){resultImg[y * width + x] = tmpImg[y * width + x];}}return resultImg;}
- [주파수_FFT역변환] 메뉴를 더블클릭한 후 private void 주파수FFT역변환ToolStripMenuItem_Click() 함수를 추가한다.
1234567891011121314151617181920private void 주파수FFT역변환ToolStripMenuItem_Click(object sender, EventArgs e){Bitmap bmp = f_OpenBitmapFile();if (bmp == null) return;byte[] data = f_getDataFromImage(bmp);///////// Start Image Processing ////////////////if (bmp.PixelFormat != PixelFormat.Format8bppIndexed){MessageBox.Show("8bit 색상 모델만 실행 가능합니다.");return;}complex_num[] fft = f_FFT_2D(data, bmp.Width, bmp.Height);byte[] newdata = f_Inverse_FFT_2D(fft, bmp.Width, bmp.Height);///////// End Image Processing ////////////////Bitmap bmpResult = f_makeImageFromData(bmp.Width, bmp.Height, bmp.PixelFormat, newdata);f_drawImage(bmp, bmpResult);}
- inverse_butterfly_computation() 함수를 생성한다.
나비 흐름도 역 계산함수
1234567891011121314151617181920212223242526272829303132333435363738394041424344private void inverse_butterfly_computation(complex_num[] x, int N, int log2N){int groupSize; // 그룹 크기int start; // 그룹의 시작 위치complex_num[] W; // 값을 저장하는 기억 장소W = new complex_num[N / 2];for (int i = 0; i < W.Length; i++){W[i] = new complex_num();}complex_num temp = new complex_num();for (int k = 1; k <= log2N; k++){ // 각 단계에 대하여groupSize = (int)Math.Pow((float)2, (int)k); // groupSize =for (int i = 0; i < groupSize / 2; i++){ // ,,...,값 계산W[i].re = Math.Cos(i * 2.0 * Math.PI / (double)groupSize);W[i].im = Math.Sin(i * 2.0 * Math.PI / (double)groupSize);}start = 0;for (int m = 0; m < N / groupSize; m++){ // 각 그룹에 대하여// 그룹 내의 각 나비 흐름도에 대하여for (int i = start; i < start + groupSize / 2; i++){int j = i + groupSize / 2;temp.re = W[i - start].re * x[j].re - W[i - start].im * x[j].im; // 계산temp.im = W[i - start].im * x[j].re + W[i - start].re * x[j].im;x[j].re = x[i].re - temp.re; // 계산x[j].im = x[i].im - temp.im;x[i].re = x[i].re + temp.re; // 계산x[i].im = x[i].im + temp.im;}start = start + groupSize;}}for (int i = 0; i < N; i++){x[i].re = x[i].re / N;x[i].im = x[i].im / N;}}
- f_Inverse_FFT_1D() 함수를 생성한다.
1차원 푸리에 역변환 함수
12345private void f_Inverse_FFT_1D(complex_num[] x, int N, int log2N){shuffle_data(x, N, log2N);inverse_butterfly_computation(x, N, log2N);}
- f_Inverse_FFT_2D() 함수를 생성한다.
2차원 푸리에 역변환 함수
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384private byte[] f_Inverse_FFT_2D(complex_num[] fft_result, int width, int height){complex_num[] rows, cols; /* 1차원 FFT를 위한 데이터 배열 */int log2N; /* FFT 단계수 */int num;complex_num[] ifft_result = new complex_num[fft_result.Length];for (int i = 0; i < ifft_result.Length; i++){ifft_result[i] = new complex_num();}/* 영상의 폭에 대한 log2N을 계산 */num = width;log2N = 0;while (num >= 2){num >>= 1;log2N++;}/* 1차원 FFT를 위한 기억장소 할당 */rows = new complex_num[width];for (int i = 0; i < rows.Length; i++){rows[i] = new complex_num();}for (int y = 0; y < height; y++) // 영상의 각 행에 대하여 IFFT 수행{for (int x = 0; x < width; x++) // 한 행을 data 배열에 복사{rows[x].re = fft_result[y * width + x].re;rows[x].im = fft_result[y * width + x].im;}f_Inverse_FFT_1D(rows, width, log2N); // 1차원 IFFT 수행for (int x = 0; x < width; x++) // 결과 저장{ifft_result[y * width + x].re = rows[x].re;ifft_result[y * width + x].im = rows[x].im;}}// 영상의 높이에 대한 log2N 계산num = height;log2N = 0;while (num >= 2){num >>= 1;log2N++;}// 기억장소 할당cols = new complex_num[height];for (int i = 0; i < cols.Length; i++){cols[i] = new complex_num();}for (int x = 0; x < width; x++){for (int y = 0; y < height; y++) // 행에 대한 IFFT 결과의 한 열을 복사{cols[y].re = ifft_result[y * width + x].re;cols[y].im = ifft_result[y * width + x].im;}f_Inverse_FFT_1D(cols, height, log2N); // 1차원 IFFT 수행for (int y = 0; y < height; y++) // 결과 저장{ifft_result[y * width + x].re = cols[y].re;ifft_result[y * width + x].im = cols[y].im;}}byte[] resultImg = new byte[fft_result.Length];for (int y = 0; y < height; y++){for (int x = 0; x < width; x++){resultImg[y * width + x] = (byte)ifft_result[y * width + x].re;}}return resultImg;}
- [주파수_저주파통과필터] 메뉴를 더블클릭한 후 private void 주파수저주파통과필터ToolStripMenuItem_Click() 함수를 추가한다.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546private void 주파수저주파통과필터ToolStripMenuItem_Click(object sender, EventArgs e){Bitmap bmp = f_OpenBitmapFile();if (bmp == null) return;byte[] data = f_getDataFromImage(bmp);///////// Start Image Processing ////////////////if (bmp.PixelFormat != PixelFormat.Format8bppIndexed){MessageBox.Show("8bit 색상 모델만 실행 가능합니다.");return;}int imageWidth = bmp.Width;int imageHeight = bmp.Height;double B;int x, y, u, v;double D0 = 32.0;double N = 2.0;complex_num[] fft_result = f_FFT_2D(data, bmp.Width, bmp.Height); // FFT 수행// 영상 스펙트럼에 필터를 곱함for (y = 0; y < imageHeight; y++){for (x = 0; x < imageWidth; x++){u = x;v = y;if (u > imageWidth / 2) u = imageWidth - u;if (v > imageHeight / 2) v = imageHeight - v;B = 1.0 / (1.0 + Math.Pow(Math.Sqrt((double)(u * u + v * v)) / D0, 2 * N));fft_result[y * imageWidth + x].re = fft_result[y * imageWidth + x].re * B;fft_result[y * imageWidth + x].im = fft_result[y * imageWidth + x].im * B;}}byte[] newdata = f_Inverse_FFT_2D(fft_result, imageWidth, imageHeight);///////// End Image Processing ////////////////Bitmap bmpResult = f_makeImageFromData(bmp.Width, bmp.Height, bmp.PixelFormat, newdata);f_drawImage(bmp, bmpResult);}
- [주파수_고주파통과필터] 메뉴를 더블클릭한 후 private void 주파수고주파통과필터ToolStripMenuItem_Click() 함수를 추가한다.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546private void 주파수고주파통과필터ToolStripMenuItem_Click(object sender, EventArgs e){Bitmap bmp = f_OpenBitmapFile();if (bmp == null) return;byte[] data = f_getDataFromImage(bmp);///////// Start Image Processing ////////////////if (bmp.PixelFormat != PixelFormat.Format8bppIndexed){MessageBox.Show("8bit 색상 모델만 실행 가능합니다.");return;}int imageWidth = bmp.Width;int imageHeight = bmp.Height;double B;int x, y, u, v;double D0 = 64.0;double N = 2.0;complex_num[] fft_result = f_FFT_2D(data, bmp.Width, bmp.Height); // FFT 수행// 영상 스펙트럼에 필터를 곱함for (y = 0; y < imageHeight; y++){for (x = 0; x < imageWidth; x++){u = x;v = y;if (u > imageWidth / 2) u = imageWidth - u;if (v > imageHeight / 2) v = imageHeight - v;B = 1.0 / (1.0 + Math.Pow(D0 / Math.Sqrt((double)(u * u + v * v)), 2 * N));fft_result[y * imageWidth + x].re = fft_result[y * imageWidth + x].re * B;fft_result[y * imageWidth + x].im = fft_result[y * imageWidth + x].im * B;}}byte[] newdata = f_Inverse_FFT_2D(fft_result, imageWidth, imageHeight);///////// End Image Processing ////////////////Bitmap bmpResult = f_makeImageFromData(bmp.Width, bmp.Height, bmp.PixelFormat, newdata);f_drawImage(bmp, bmpResult);}
- [주파수_잡음제거] 메뉴를 더블클릭한 후 private void 주파수잡음제거ToolStripMenuItem_Click() 함수를 추가한다.
12345678910111213141516171819202122232425262728private void 주파수잡음제거ToolStripMenuItem_Click(object sender, EventArgs e){Bitmap bmp = f_OpenBitmapFile();if (bmp == null) return;byte[] data = f_getDataFromImage(bmp);///////// Start Image Processing ////////////////if (bmp.PixelFormat != PixelFormat.Format8bppIndexed){MessageBox.Show("8bit 색상 모델만 실행 가능합니다.");return;}int imageWidth = bmp.Width;int imageHeight = bmp.Height;complex_num[] fft_result = f_FFT_2D(data, imageWidth, imageHeight); // FFT 수행fft_result[0 * imageWidth + 64].re = 0.0;fft_result[0 * imageWidth + 64].im = 0.0;fft_result[0 * imageWidth + 192].re = 0.0;fft_result[0 * imageWidth + 192].im = 0.0;byte[] newdata = f_Inverse_FFT_2D(fft_result, imageWidth, imageHeight);///////// End Image Processing ////////////////Bitmap bmpResult = f_makeImageFromData(bmp.Width, bmp.Height, bmp.PixelFormat, newdata);f_drawImage(bmp, bmpResult);}
- 소스코드 다운로드