파이썬 강의/openCV

파이썬 openCV 25. 주파수 : 푸리에 변환(Fourier transform)

마리사라 2020. 12. 19. 17:31
반응형

파이썬 openCV 25번째 강의는 푸리에 변환(Fourier transform)입니다. 이번 강의부터 영상을 주파수 단위로 조작하는 방법을 알려드릴 예정입니다.


0. 푸리에 변환(Fourier transform)?

여태까지 그래 왔듯이 처음 보는 단어는 항상 수학과 관련이 있습니다.

 

 

푸리에 변환 공식

 

푸리에 변환은 적분을 이용한 공식으로 어떠한 함수를 주파수로 변환하는 공식입니다. 사실상 푸리에 변환을 강의하기 위해서는 전문적인 교수가 시간을 들여서 강의해야 하므로, 푸리에 변환은 무언가를 주파수로 변환하는 공식이라고만 알아 두시고, 자세한 내용은 아래 위키백과를 참조하시기 바랍니다.

ko.wikipedia.org/wiki/%ED%91%B8%EB%A6%AC%EC%97%90_%EB%B3%80%ED%99%98

푸리에 변환 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 푸리에 변환(Fourier transform, FT)은 시간에 대한 함수(혹은 신호)를 함수를 구성하고 있는 주파수 성분으로 분해하는 작업이다. 음악에서, 악보에 코드를 나타낼

ko.wikipedia.org


1. openCV에서의 푸리에 변환(Fourier transform)

영상을 주파수로 변환한다고 하면 이상하게 느껴지실 분들이 많으실겁니다. 주파수는 단위 시간에 몇 번의 변화가 일어났는지를 표현하는 방법입니다. 이를 화소의 밝기로 적용하면 영상에서 화소 밝기의 변화가 얼마나 빨리 변화하는가에 따라서 고주파와 저주파로 분류할 수 있습니다.

 

이렇게 영상을 위에서 나오는 푸리에 변환 공식을 사용하여 주파수로 변환할 수 있지만, 너무 힘든 과정을 거쳐야 합니다. 그렇기에 openCV에서는 자체적으로 푸리에 변환에 대한 함수를 지원하고 있습니다.

img = cv2.imread('lenna.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
height, width = gray.shape

푸리에 변환은 밝기의 변화를 주파수로 변환하는 것 입니다. 그래서 영상은 그레이 스케일로 변환하도록 합니다.

 

dft = cv2.dft(np.float32(gray), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
out = 20*np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))

이제부터 푸리에 변환을 하도록 합니다. 이때 영상을 푸리에 변환시키는 것을 DFT라고 합니다.

 

openCV에 있는 dft함수는 실수로 변환된 영상을 입력받습니다. 그래서 영상을 입력할 때에는 실수로 변환해 주어야 합니다. 또한 dft를 할 때, flags를 입력해 주어야 하는데, dft 변환한 값을 얻을 때는 DFT_COMPLEX_OUTPUT을 입력해 줍니다.

 

그다음에 있는 np.fft.fftshift는 dpf로 변환된 주파수를 재배열해주는 함수입니다. 주파수가 0인 부분을 정 중앙에 위치시키고, 주파수가 커질수록 가장자리에 위치시킵니다.

 

마지막으로 우리가 볼 수 있는 영상으로 만들기 위해서 dft_shift 된 값들을 2차원 백터 값을 계산해주는 magnitude함수에 넣습니다. 이 값들은 매우 큰 값들이 나오기 때문에 log를 취해주어 출력할 수 있는 영상으로 만들어 줍니다.

 

 

여기까지가 기본적인 푸리에 변환의 방법입니다. 그런데 푸리에 변환으로 영상을 주파수로 만들 수 있지만, 같은 방식으로 주파수를 영상으로 만들 수도 있습니다. 그것을 역 푸리에 변환(Inverse Fourier Transform)이라고 합니다.

 

 

inverse_shift = np.fft.fftshift(dft_shift)
inverse_dft = cv2.dft(inverse_shift, flags=cv2.DFT_INVERSE)
out2 = cv2.magnitude(inverse_dft[:, :, 0], inverse_dft[:, :, 1])

역 푸리에 변환을 하기 위해서는 재배열된 주파수를 다시 원래 배열로 재배열해야 합니다. 그래서 fftshift 된 dft_shift를 다시 fftshift에 넣어 변환해 줍니다.

 

이렇게 변환된 주파수를 다시 dft함수에 넣어줍니다. 하지만 이번에 flags는 주파수를 역으로 영상으로 만들어주는 DFT_INVERSE를 입력해 줍니다.

 

마지막으로 INVERSE된 값들을 magnitude 함수에 넣어서 영상으로 변환해 줍니다.

 

 

마지막으로 결과 영상을 보겠습니다. 주파수 영상은 cv2로 출력하게 되면 정상적인 영상이 나오지 않아서, pyplot을 사용하도록 합니다.

 

 

좌 : 원본영상 / 중 : 주파수 변환 영상 / 우 : 주파수 역변환 영상

 

주파수로 변환된 영상을 보시면 가운데가 밝은 것을 보실 수 있습니다. 이는 영상이 저주파가 많다는 뜻이며, 밝기의 변화가 급격하게 일어나지 않는다는 뜻입니다.

 

또한 역변환된 영상은 원본 영상과 똑같은 영상이 나오는 것을 보실 수 있습니다.


2. 마치며

일반적인 푸리에 변환은 시간에 대한 연속성이 고려되지 않았습니다. 그래서 연속적인 영상은 변환하기 힘들다는 단점은 있습니다.

 

def fourier():
    img = cv2.imread('lenna.png')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    height, width = gray.shape

    dft = cv2.dft(np.float32(gray), flags=cv2.DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)
    out = 20*np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))

    inverse_shift = np.fft.fftshift(dft_shift)
    inverse_dft = cv2.dft(inverse_shift, flags=cv2.DFT_INVERSE)
    out2 = cv2.magnitude(inverse_dft[:, :, 0], inverse_dft[:, :, 1])

    plt.subplot(131)
    plt.imshow(gray, cmap='gray')
    plt.title('original')
    plt.subplot(132)
    plt.imshow(out, cmap='gray')
    plt.title('dft')
    plt.subplot(133)
    plt.imshow(out2, cmap='gray')
    plt.title('inverse')

    plt.show()
반응형