해당 포스트는 "열혈강의 영상처리 프로그래밍" 책의 내용을 요약한 것이다.



※ 주파수 영역

영상에서 주파수가 높다는 것은 영상 밝기값 변화가 자주 일어나는 것이고 주파수가 낮다는 것은 밝기값 변화가 비교적 드물게 일어난다는 것을 의미한다. 밑에 있는 그림을 보면 윗 부분은 밝기값 변화가 거의 없다. 그래서 저주파 영역이다. 밑 부분은 밝기값 변화가 자주 일어나다. 그래서 고주파 영역이다. 아래 그림의 저주파 영역과 고주파 영역의 단면을 그래프로 나타내면 아래 그래프와 같이 나타낼 수 있다. x축은 단면이기 때문에 영역의 x 위치가 되고 y 축은 밝기값이 된다. 저주파 영역의 경우 밝기값 변화가 거의 없으므로 그래프가 일정한 선을 그리며 떨림이 거의 없다. 고주파 영역의 경우 밝기값 변화가 많으므로 그래프가 크게 요동을 치고 떨림이 많이 있다. 이러한 x위치에 따른 밝기값 그래프를 푸리에 변환과 같은 기법을 이용해 다양한 주기의 사인함수와 코사인 함수와 같은 정현파의 조합으로 분석하는 것을 주파수 영역으로 변환한다고 한다. 변환 결과로 x축은 주파수, y축은 각 주파수 값에 대한 신호 성분의 세기를 나타내는 그래프가 나오게 된다. 해당 그래프는 아래에 있다. 좀 더 쉽게 주파수 영역 변환에 대해서 말하자면 아래의 주파수 변환 전 그래프, x축이 영역의 x위치고 y축이 밝기 값인 그래프 즉, 기존 신호가 여러 주파수로 이루어져 있고 이를 사인과 코사인으로 된 함수들 각각의 배수 합 즉, 가중치 합으로 나타낼 수 있다. 각 사인과 코사인 함수는 하나의 주파수이고 이들의 가중치가 x축이 주파수, y축이 각 주파수 값에 대한 신호 성분 세기 그래프에서 y축인 주파수 값에 대한 신호 성분 세기가 되는 것이다.  


 



 
           

그림1



영상에서 주파수의 의미를 대략 알아보았다. 영상 신호를 주파수 영역으로 변환하면 회선연산이 편리해진다. 우리는 앞에서 가우스 필터를 이용한 회선 연산으로 영상 잡음을 제거 했는데 가우스 필터의 마스크 크기가 증가할수록 연산 시간이 마스크 면적에 비례하여 증가했었다. 주파수 영역으로 변환된 신호에서 회선 연산은 주파수 성분 사이의 곱셈 연산으로 수행되 마스크의 크기에 상관 없이 연산 시간이 일정하게 유지된다. 따라서 영상을 주파수 영역으로 변환하면 여러 가지 마스크를 이용한 필터링 연산을 더욱 효율적으로 수행할 수 있다. 

또한 영상을 주파수 영역으로 변환하게 되면 영상의 메모리 용량을 줄이는 압축 작업도 할 수 있다. 위에서 주파수 영역으로 변환한 그래프를 보면 영상의 대부분이 저주파 성분으로 되어 있다. 따라서 미미하게 존재하는 고주파 성분을 영상 저장 과정에서 생력하면 시각적으로 식별할 수 있는 영상의 품질 저하를 최소화하면서 저장에 사용하는 메모리 용량도 최소화 할 수 있다. 이렇게 영상의 일부 성분을 제거하여 압축하는 것을 손실 압축이라고 하는데 주파수 영역 변환을 이용한 방법은 손실되는 정보에 비하여 메모리 용량 감소가 뛰어나다는 장점이 있다.



※ 주파수 변환

이번에는 주파수 영역으로 변환하는 법을 알아보자. 먼저 변환 전 공간 영역(영상의 x위치)의 원본 신호를 g(t)라고 하자. t는 위의 x에 대한 밝기값 그래프에서 영상의 가로 좌표를 말하고 g(t)는 각 위치 픽셀의 밝기값에 해당한다. 영상의 주파수 영역 변환이란 신호를 구성하는 각 주파수 성분들의 크기를 구하는 것이다.  원본 신호 g(t)를 서로 다른 주파수를 가지는 기본 신호, 즉 기저 함수들의 선형 조합으로 나타내는 것이다. 기저함수를 s(t)라 할 때 원본 신호 g(t)를 구성하는 선형 조합의 계수가 곧 주파수 성분의 크기에 해당한다. 보통 기저함수 s(t)는 사인, 코사인 함수로 되어 있다. 다음 그림을 보자.

위 그림은 원본 신호 g(t)를 기저함수 여러 개의 코사인, 사인 함수들의 선형 조합으로 나타내는 것을 도식화했다. 각 사인, 코사인 함수 앞에 곱해진 값들이 계수 즉, 주파수 성분의 크기에 해당한다. 계수 [0.5, 0.4, -0.2, -0.3, 0.1, -0.1] 을 구하는 것이 원본 신호 g(t)를 주파수 변환한 결과가 된다. 따라서 영상에서 주파수로 변환하는 것은 고유 주파수를 가진 기저 함수들에 대한 계수를 찾는 것이고, 반대로 주파수 변환의 역변환은 주어진 계수와 기저함수를 이용하여 원본 신호를 찾는 것이다. 다음 식은 주파수 변환을 표현한 식이다.



G(f)가 기저 함수 s(t)의 계수에 해당하는 값으로 주파수 함수라고 한다. 위 식과 같이 원본 신호 g(t)는 가능한 모든 주파수 f에 대하여 f에 해당하는 기저 함수와 그 계수의 선형 조합으로 구한다. 따라서 적분 형태로 표현된다. 주파수 영역으로 변환하는 것은 주어진 기저 함수에 대하여 계수를 나타내는 주파수 함수인 G(f)를 구하는 것이다. 제일 위 그림에서 밑에 있는 그래프의 x축은 주파수 값 f 가 되고 y축은 각 주파수 성분의 계수 G(f)가 된다.  



※ 푸리에 변환, 이산 푸리에 변환(DFT)

주파수 변환을 하기 위해서는 기저함수가 필요한 데 일반적으로 사인, 코사인 함수를 사용한다. 이런 사인, 코사인 함수를 사용하여 변환하는 대표적인 방법이 푸리에 변환이다. 다음 수식은 기저함수를 나타낸다.


기저 함수 s(t)는 위와 같이 사인과 코사인 함수로 나타낼 수 있다. 두 번 째 등식은 오일러 공식에 의해서 나온 것으로 푸리에 변환에서는 기저함수를 사인 코사인을 변환 시킨 자연 지수 함수를 사용한다. 다음은 자연 지수 함수를 이용한 원본 신호 g(t)를 구하는 공식이다.


위 식은 주파수 함수 G(f)로부터 원본 신호 g(t)를 얻는 것으로 푸리에 변환의역변환에 해당한다. 원본 신호로부터 주파수 함수를 얻는 것은 위 식에서 다음과 같이 유도할 수 있다.



위 두 식은 t가 실수 값으로 주어지는 연속적인 1차원 신호에 대한 푸리에 변환과 역변환이다. 따라서 이를 영상에 적용하려면 디지털 신호를 위해 고안된 이산 푸리에 변환(DFT)를 사용해야한다. 또한 1차원 푸리에 변환을 영상은 가로 세로가 있기 때문에 가로와 세로 방향에 대해 각각 차례로 수행해야 한다. 이산 푸리에 변환은 픽셀 값과 같이 정의역이 정수 값 신호를 위한 것으로 적분을 유한 개의 원소 합 형태로 표현한다. 다음은 이산 푸리에 변환의 공식이다.


g[n]은 정의역이 정수 n으로 주어지는 원본 디지털 신호(밝기값)를 나타낸다. G(k)는 주파수 k에 대한 함수(주파수 성분 크기)를 나타낸다. 다음은 주파수 역변환을 통하여 주파수 함수 G(k)로부터 원본 신호 g[n]을 얻는 공식이다.



2차 평면 영상에 대한 푸리에 변환에서 영상의 가로와 세로 좌표가 n,m에 대응하고 해당 위치의 픽셀 값을 g[n,m]이라고 하자. 그리고 주파수의 가로, 세로 좌표가 k,l에 대응하고 해당 위치의 주파수 함수값을 G(k,l)이라고 하자. 다음은 2차원 평면에서 이산 푸리에 변환과 역변환 공식이다.




2차원 평면 상에서 이산 푸리에 변환와 역변환은 1차 푸리에 변환과 역변환을 영상 가로방향으로 한 후에 결과를 세로 방향으로 다시 1차 푸리에 변환, 역변환을 한 것이다. 따라서 2차원 평면 상 이산 푸리에 변환과 역변환을 코드로 구현할 때는 1차원 변환을 수행하는 코드만 있으면 된다. 그리고 변환과 역변환 공식이 기저 함수가 사용하는 지수의 부호만 다를 뿐 나머지는 같다는 것이다. 따라서 변환과 역변환에 같은 코드를 사용하면서 변환의 방향에 따라 전체 결과를 신호의 길이에 나누는지와 지수의 부호만 바꾸도록 구현하면 편리하다.

1차원 이산 푸리에 변환에서 각 원소의 계산은 위에서 언급한 오일러 공식을 사용하여 쉽게 구할 수 있다. 이 때 원본 신호 g[n]도 허수부를 가지는 복소수를 두고 식을 유도하는 데 이는 영상 등의 실제 신호는 보통 실수부만 가지지만 주파수 함수는 허수부도 가지므로 역변환까지 다룰 수 있는 코드를 유도하기 위해서이다. 

g[n]의 실수부를 Re{g[n]}, 허수부를 Im{g[n]} 이라고 하면, 주파수 함수에서 k번째 원소의 실수부 Re{G(k)}와 허수부 Im{G(k)}는 오일러 공식을 사용하여 다음 식과 같이 구할 수 있다.



 

역변환은 각 삼각 함수에서 각도의 부호만 바꾸어 다음과 같이 구할 수 있다.


다음은 1차원 이산 푸리에 연산의 코드이다. 



int isign = 1;    // 역변환일 때는 -1

double sumRe = 0;

double sumIm = 0;

for ( int n=0; n<N; n++)

{

  double theta = isign*(2*PI/N)*k*n;

  sumRe += (srcRe[n]*cos(theta) - srcIm[n]*sin(theta));

  sumIm += (srcIm[n]*cos(theta) + srcRe[n]*sin(theta));

}

dstRe[k] = sumRe;

dstIm[k] = sumIm;


if(isign == -1)

{

  dstRe[k] /= N;

  dstIm[k] /= N;

}



위 코드는 1차원 원본 신호나 주파수 함수 srcRe[n]과 허수부 srcIm[n] 배열이 주어졌을 때 이들의 주파수 변환이나 역변환에서 k번째 원소의 실수부 dstRe[k]와 허수부 dstIm[k]를 구하는 코드이다. 이산 푸리에 변환 코드는 간단하지만 신호 길이의 제곱에 비례해 시간이 오래 걸리는 문제가 있다. 만약 2차원 영상을 변환할 때는 가로와 세로 길이가 N, M이라면 N제곱 * M제곱만큼 연산을 수행해야 한다. 따라서 실제 주파수 변환을 사용하는 프로그램에서는 이산 푸리에 변환은 잘 사용하지 않고 더욱 효율적인 푸리에 변환 기법인 고속 푸리에 변환, FFT 기법을 많이 사용한다.


+ Recent posts