본문 바로가기

개발자/Computer Vision

영상처리[주파수 관련+푸리에변환]

반응형

- 주파수 영역 처리 -

* 푸리에 변환을 적용하여 공간 영역에서 주파수 영역으로 변환

* 주파수 영역에서 특정한 주파수 성분을 제거하거나 증폭

* 주파수 영역에서 공간 영역으로 역변환

 

 

- 공간 주파수 -

* 화소값의 변화를 파형의 형태로 그린 것(=화소 값들의 변화율)

* 밝기의 변화 정도에 따라서 고주파 영역/저주파 영역으로 분류

 

- 저주파 공간 영역 -

* 화소 밝기가 거의 변화가 없거나 점진적으로 변화하는 것

* 영상에서 배경 부분이나 객체의 내부에 많다.

 

- 고주파 영역 -

* 화소 밝기가 급변하는 것

* 경계부분이나 객체의 모서리 부분

 

- 주파수 별로 분리 -

* 경계부분에 많은 고주파 성분이 제거된 영상 -> 경계가 흐려진 영상

* 고주파 성분만 취한 영상 -> 경계나 모서리만 나오는 영상(에지)

 

- 푸리에 변환 -

* 시간에 따라 변화하는 함수를 분해하여 그 안에 들어있는 주파수 성분을 끄집어내는 변환

* 비사인파를 사인파로 분해하거나 합하여 비사인파를 만들 수 있다.

 

- 이산 푸리에 변환(DFT) -

* 디지털 신호에 적용

* 복소수의 실수부와 허수부를 벡터로 가주하여 벡터의 크기 계산(주파수 스펙트럼)

* 실수부와 허수부 원소의 기울기 계산(주파수 위상)

 

- 주파수 스펙트럼 -

* 저주파영역이 모서리 부분에 위치. 고주파 부분이 중심부에 위치

* 사각형의 각 모서리를 중심으로 원형의 밴드를 형성하여 주파수 영역에 분포

* 보기 쉽게하기 위해 셔플링 적용(마주보는 대각선 방향으로 서로 바꿔준다.)

 

- 주파수 영역 변환 -

* 저주파 영역과 고주파 영역분리(특정 주파수 영역 강화, 약화, 제거 가능)

 

- 저주파 통과 필터링 -

* DFT 변환 영역에서 저주파 영역의 계수들은 통과, 고주파 영역의 계수 차단

 

- 고주파 통과 필터링 -

* 고주파 영역의 계수들을 통과시키고 저주파 영역의 계수들 차단

 

- 대역 통과 필터 -

* 특정한 대역에서 급격하게 값을 제거하기 때문에 결과 영상의 화질 저하

* 객체의 경계 부분 주위로 잔물결 같은 무늬 나타남

 

- 가우시안 필터 -

* 필터 원소의 구성을 가우시안 함수의 수식 분포를 갖게 함으로써 차단 주파수 부분을 점진적으로 구성

 

- 버터워쓰 필터 -

* 차단 주파수 반지름 위치와 지수의 승수인 n 값에 따라서 차단 필터의 반지름과 포물선의 곡률 결정


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

푸리에 변환 공식

 

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

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

 

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

위키백과, 우리 모두의 백과사전. 사인파의 진폭이 다양한 방식으로 표현되어 있다. (1)은 일반적인 첨두치peak 진폭을, (2)는 최대치와 최저치 사이의 차이를, (3)은 제곱평균제곱근을, (4)는 주기

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()



출처: https://marisara.tistory.com/entry/파이썬-openCV-25-주파수-푸리에-변환Fourier-transform [마리사라의 본진]

반응형