OpenCV 푸리에 변환 - OpenCV pulie byeonhwan

이미지 프로세싱 & 컴퓨터 비전 

OpenCV-Python 강좌 29편 : 푸리에 변환 응용하기

필요환경: 파이썬 3.6.x, OpenCV 3.2.0+contrib-cp36 버전

이번 강좌에서는 28편에서 다루었던 푸리에 변환을 어떻게 이미지에 응용하는지 살펴보도록 하겠습니다.

이 강좌를 읽기 전에 28편 강좌를 반드시 읽고 오셔야 합니다.

푸리에 변환은 이미지를 주파수 영역으로 전환하여 이미지 프로세싱 작업을 수행할 수 있게 해주는 도구이고, 주파수 영역에서 작업이 끝나면 역푸리에변환(Inversion Fourier Transform; IFT)을 수행하여 원래 이미지 영역으로 되돌려서 이미지 프로세싱 결과를 확인할 수 있습니다.

OpenCV-Python 강좌 12편에서 이미지 필터링을 소개할 때 LPF(Low Pass Filter), HPF(High Pass Filter)등에 대해 살펴보긴 했는데 기억이 나실런지 몰겠군요. 기억이 나지 않는다면,, 아래 링크로 잠시 다녀오셔도 됩니다

☞ OpenCV-Python 강좌 12편: 이미지 필터링 바로가기

LPF는 낮은 주파수 대역만 통과시키는 필터이고, HPF는 높은 주파수 대역만 통과시키는 필터입니다.

이미지에서 LPF를 사용하여 이미지를 프로세싱한 결과는 낮은 주파수 대역만 남아 있는 이미지가 될 것이므로 블러(blur) 효과를 가진 이미지가 되며, HPF를 사용하게 되면 높은 주파수 대역만 남아 있는 이미지이므로 사물의 경계나 노이즈 등만 남아 있는 이미지가 됩니다.

푸리에 변환을 통해 주파수 영역으로 옮긴 이미지로 주파수 작업을 수행하면 이전 강좌에서 보인 다양한 필터링 작업이 수행가능합니다.

푸리에 변환으로 이미지 작업하기

이미지를 푸리에 변환하여 주파수 영역으로 전환하고, 주파수 영역 이미지 정중앙에 60x60 크기의 정사각형 영역에 해당하는 값을 모두 0으로 만들어 보겠습니다.

주파수 영역 이미지의 정중앙부분은 주파수 대역이 낮은 값들이 위치하고 있습니다. 이 부분에 대한 설명은 이전 강좌에서 했습니다. 주파수 영역 이미지의 정중앙 부분을 0으로 만든다는 것은 주파수가 낮은 대역 부분은 차단시킨다는 의미이고, 이는 곧 주파수 대역이 높은 부분만 통과시키겠다는 것이므로, HPF를 적용한 것과 같은 결과가 도출됩니다.

AoA 설현 이미지를 주파수 영역으로 전환하고, 주파수 영역의 이미지 정중앙에 60x60 크기의 빨간 사각형내에 있는 값을 모두 0으로 바꾼 후, 역푸리에 변환을 이용해 원래 이미지 영역으로 전환하게 되면, 원본 이미지에서 주파수가 낮은 부분이 없는 새로운 이미지가 됩니다.

코드를 보시죠~

코드 윗부분은 앞에서 다룬 것이므로 넘어가고, 추가된 부분만 살펴보겠습니다.

>>> fshift[crow-30:crow+30, ccol-30:ccol+30] = 0

주파수 영역의 이미지 정중앙의 60 x 60 크기 영역에 있는 값을 모두 0으로 만듭니다.

>>> f_ishift = np.fft.ifftshift(fshift)

역쉬프트 함수 np.fft.ifftshift()를 이용해 재배열된 주파수 값들의 위치을 본래대로 되돌립니다.

>>> img_back = np.fft.ifft2(f_ishift)

>>> img_back = np.abs(img_back) 

np.fft.ifft2() 함수를 이용해 역푸리에 변환을 하여 원래 이미지 영역으로 전환합니다. 그런 후 모든 값에 절대값을 취합니다.

이것이 다입니다!

이 코드를 실행해서 원본 이미지를 제외한 결과를 보면 아래와 같습니다.

푸리에 변환을 이용하면 이전 강좌에 배웠던 다양한 이미지 필터 효과를 구현할 수 있습니다.

마지막으로 이전 강좌에서 배웠던 각종 필터들에 대한 특성을 푸리에 변환으로 살펴보고 마무리하도록 하겠습니다. 

본 강좌에서 배웠던 이미지 필터링에 사용되는 커널 종류는 다음과 같은 것이 있었죠~

  • Averaging Filter
  • Gaussian Filter
  • Scharr Filter
  • sobel_x, sobel_y Filter
  • laplacian filter

여기까지 충실하게 따라오신 분들은 위 5가지 필터를 한번씩 다 경험하신 분들입니다.

위 이미지 필터들의 3x3 커널 모양은 다음과 같습니다.

Gaussian 필터 커널은 생략했습니다. 생각보다 숫자가 복잡해서리~~

아래의 코드는 다양한 필터에 대한 푸리에 변환 결과를 화면에 출력하는 코드와 결과 화면입니다.

결과 그림에서 흰색 부분이 통과되는 주파수 영역이고, 검정색 부분은 필터링 되는 주파수 영역입니다.  mean 필터와 gaussian 필터는 중앙 부분인 주파수가 낮은 영역만 통과시키는 LPF 입니다.

laplacian 필터는 mean 필터와는 정반대인 걸 알 수 있습니다. 주파수가 높은 부분만 통과시키는 HPF 이군요..

sobel_x, sobel_y, scharr_x 의 주파수 영역 이미지를 보면 어떤 영역이 필터링 되고 패스되는지 알 수 있을 겁니다.

이번 강좌는 여기까지입니다~~

​이미지 프로세싱  & 컴퓨터 비전

OpenCV-Python 강좌 28편 : 푸리에 변환(Fourier Transform) 이해하기

필요환경: 파이썬 3.6.x, OpenCV 3.2.0+contrib-cp36 버전

이번 강좌에서는 OpenCV를 이용해 다음과 같이 이미지에서 푸리에 변환을 활용하는 방법에 대해 알아보겠습니다.

  • 이미지의 푸리에 변환 구하는 방법
  • Numpy를 이용해 FFT(Fast Fourier Transform) 활용하기
  • 푸리에 변환 응용하기

푸리에 변환(Fourier Transform)

푸리에 변환이란 주파수(frequency)를 분석하는데 가장 많이 활용되는 도구입니다. 이미지에서 왠 주파수냐고 궁금해 할 수 있겠습니다. 일단 푸리에 변환부터 가볍게 알아보죠.

푸리에 변환의 핵심은 이겁니다. 주기를 가진 함수(주기함수)는 주파수가 다른 삼각함수의 합으로 표현 가능하다. 이를 다시 말하면 주기는 무한대까지 가능하므로 "모든 함수는 주파수가 다른 삼각함수의 합으로 표현 가능하다"라는 말이 됩니다.

삼각함수

에서  가 주파수를 나타냅니다.  푸리에 변환에 대한 상세한 내용은 이 포스팅의 목적이 아닙니다만, 조금만 더 설명하자면 아래의 애니메이션으로 설명 가능합니다. (아래 그림의 출처는 위키피디아입니다.)

위 애니메이션에서 빨간색으로 표시된 파형은 시간이 독립변수인 시간영역(time domain)의 함수입니다. 다시말하면, 빨간색 표시 파형은 일정한 주기 f를 가진 시간변수 t에 대한 그래프인 것이죠.  

다양한 주파수를 가진 사인곡선이 중첩되어 최종 결과로 나온 파형이 빨간색으로 표시된 것입니다. 이와 같이 다양한 주파수를 f1, f2, f3,,,이라 하죠.

이 빨간색 파형을 푸리에 변환하면 주파수가 독립변수가 되는 주파수 영역(frequecy domain)의 함수로 표현되는데, 이 함수는 파란색으로 표시된 것입니다.

푸리에 변환의 결과로 나온 파란색으로 표시된 그래프를 보면 빨간색 파형을 구성하는 시간영역의 각 삼각함수의 주파수 f1, f2, f3,,,,,에서 피크로 표시됩니다. 주파수 이외 부분에서는 값이 모두 0으로 되죠.

이러다가는 푸리에 변환에 대한 수학교재가 될 듯 하니 이쯤에서 마무리하고, 이미지를 위한 푸리에 변환으로 돌아가야 겠습니다.

주파수 개념을 이미지에 적용하면 어떻게 될까요...

짧은 시간에 값이 빨리 변하면 주파수가 높다고 하고, 그렇지 않으면 주파수가 낮다고 합니다. 이미지에서 이런 주파수 개념을 적용하면 픽셀값의 변화가 얼마나 빨리 진행되는가와 관련 있습니다.

이미지는 보통 2차원 평면이므로, 픽셀값의 변화도 x축, y축 두 방향의 변화를 모두 고려해야 겠지요.

이미지에서 픽셀값의 변화가 큰 경우는 이미지의 경계부근이나 이미지와 관련없는 노이즈부분에서 발생합니다.

2D 이산 푸리에 변환(2D Discrete Fourier Transform; 2D-DFT)을 이미지에 적용하면 이미지를 주파수 영역으로 변환해줍니다. DFT를 계산하기 위해서는 고속 푸리에 변환(Fast Fourier Transform; FFT)을 이용합니다.

자, 그럼 실제 예시를 통해 이해해봅니다.

Numpy를 활용한 푸리에 변환

>>> f = np.fft.fft2(img)

이미지의 2D DFT를 계산합니다.

>>> fshift = np.fft.fftshift(f)

np.fft.fft2(img) 를 수행하여 얻어진 푸리에 변환 결과는 주파수가 0인 컴포넌트를 좌상단에 위치킵니다. np.fft.fftshift(f) 는 주파수가 0인 부분을 정중앙에 위치시키고 재벼열해주는 함수입니다.

이해를 돕기 위해 아래의 예를 보시죠.

>>> import numpy as np

>>> f = np.fft.fftfreq(10, 0.1)

>>> f

[0. 1. 2. 3. 4. -5. -4. -3. -2. -1.]

>>> fshift = np.fft.fftshift(f)

>>> fshift

[-5. -4. -3. -2. -1. 0. 1. 2. 3. 4.]

원래 주파수 배열 [ 0.  1.  2.  3.  4. -5. -4. -3. -2. -1.] 을 np.fft.fftshift(f)로 [-5. -4. -3. -2. -1.  0.  1.  2.  3.  4.] 형태로 변형했습니다.

>>> m_spectrum = 20*np.log(np.abs(fshift))

magnitude spectrum을 구합니다. 수학식에 대한 자세한 내용은 넘기겠습니다.

결과를 matplotlib을 이용해 화면에 디스플레이 하면 아래와 같습니다. 

 만약 np.fft.fftshift() 함수를 적용하지 않고 magnitude spectrum을 화면에 그려보면 아래와 같습니다.

OpenCV를 활용한 푸리에 변환

numpy를 이용한 푸리에 변환과 다른 부분만 살펴보면,

>>> dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT)

cv2.dft() 함수는 이미지의 2D DFT을 계산합니다. 이미지를 이 함수의 인자로 입력할 때는 반드시 np.float32로 랩핑하여야 합니다. 그리고 이 함수는 복소수 형태(실수부, 허수부)로 결과를 리턴하는 것이 numpy를 이용할 때와 차이점입니다.

>>> m_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))

cv2.magnitude(x, y)는 2차원 벡터의 크기를 계산합니다. 이 함수의 인자로 벡터의 x성분, y성분을 각각 입력합니다.

결과는 Numpy를 이용한 것과 동일하게 나옵니다.

이번 강좌는 여기까지입니다~ (조금 어려울수 있으나 푸리에 변환이라는 것에 대한 약간의 이해만 하시면 되겠네요.)​