[CV] Gaussian Filter
์์ฆ CV์ ๋ํด์ ๊ณต๋ถํ๊ณ ์๋๋ฐ, ์ ๋ฆฌํด๋์ง ์์ผ๋ฉด ์์ด๋ฒ๋ฆด ๊ฒ ๊ฐ์์ ๋ฒจ๋ก๊ทธ์ ์ ๋ฆฌํด๋๋ ค๊ณ ํ๋ค.. ํ๋ฌ์ ๋๊ฐ ์ ๋ ์ด์ ํ๋๊ฒ ๋ชฉํ์ธ๋ฐ ์ ๋๋ฉด ์ข๊ฒ ๋ค
1. ํํฐ
ํํฐ๋ CV์์ ๊ต์ฅํ ์ค์ํ ์์๋ค. ์ฃผ๋ก convolution์ ์ด์ฉํ๋๋ฐ, ์ด๋ฏธ์ง์ ๋ค์ํ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๊ฒ ๋๋ค. ํฌ๊ธฐ๋ฅผ ์กฐ์ ํ๋ค๋ ์ง, ์ด๋ฏธ์ง๋ฅผ ๋ธ๋ฌ ์ฒ๋ฆฌํ๋ค๋ ์ง, ๋ฑ๋ฑ ๋ค์ํ ์ต์ ์ ๊ฐ์ง๊ณ ์์ด์ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋ค. ํํฐ๋ผ๊ณ ๋ ๋ถ๋ฅด์ง๋ง, ์ปค๋์ด๋ผ๊ณ ๋ ํ๋ค.
2. ๊ฐ์ฐ์์ ํํฐ (Gaussian Filter)
์ด๋ฒ์๋ ๊ฐ์ฐ์์ ํํฐ์ ๋ํด์ ๋ฐฐ์ ๋๋ฐ, ์ฃผ๋ก ์ด๋ฏธ์ง๋ฅผ smoothing ํ๋๋ฐ ์ฌ์ฉ๋๋ ๊ฒ ๊ฐ๋ค. lena ์ด๋ฏธ์ง์ ๋ํด์ ์ฌ๋ฌ๊ฐ์ง ๊ฐ๋ค์ ๋ณ๊ฒฝํด๊ฐ๋ฉด์ ๊ฒฐ๊ณผ๋ฅผ ๋์ถํ๋ ๊ฑธ ์๋ํด๋ดค๋ค.
opencv ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฌผ๋ก ๊ฐ์ฐ์์ ํํฐ๋ฅผ ๋ง๋ค ์ ์๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด ์กด์ฌํ์ง๋ง, ์ง์ ํด๋ณด๋๊ฒ ๋ชฉํ๋ผ! ์์ ์ดํดํ๊ณ ์ฝ๋ฉ์ผ๋ก ํํํด๋ดค๋ค.
2-1. 1D Gaussian Filter

๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ํํ์ ๊ฐ์ฐ์์ ํํฐ์ด๋ค (๊ทธ๋ํ๋
๋ชจ๋ ํํฐ์๋ ์ฌ์ด์ฆ์ ์๊ทธ๋ง ๊ฐ์ด ์กด์ฌํ๋ค. ์ฌ๊ธฐ์ ๋ ์ปค๋์ ์ค์ฌ์์ ๋จ์ด์ง ๊ฑฐ๋ฆฌ์ด๊ณ , ๋ ์ ์ ๊ฐ ์ง์ ๋ณ๊ฒฝํ ์ ์๋ ๊ฐ์ด๋ค. ์ด ๊ฐ์ ๋ฐ๋ผ์ ๋ ธ์ด์ฆ ์ฒ๋ฆฌ์ ์ ๋๊ฐ ๋ฌ๋ผ์ง๋ค.
์ฌ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ชฉ๋ก
import scipy import cv2 import numpy as np import matplotlib.pyplot as plt import math
1D Gaussian Filter ์์ฑํ๊ธฐ
def genGausKernel1D(width, sigma): arr = np.arange((width//2)*(-1), (width//2)+1) #์ค์ฌ์์๋ถํฐ์ ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ์ฐํจ, arr = np.array([float(x) for x in arr]) #float ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์ง ์์ผ๋ฉด ๊ฐ์ด ๋ฌ๋ผ์ง ์ ์์ด์ ์ฒ๋ฆฌํด์ฃผ์๋ค kernel_1d= np.exp((-arr*arr)/(2*sigma**2)) #์์์ ๋ดค๋ ์์์ ๊ทธ๋๋ก ์ ์ฉํ๋ค. ๋ฌ๋ผ์ง ์ ์, ์์ ์์๋ก ๋๋ ์ฃผ๋ ๋ถ๋ถ์ ์๋ตํ๊ฑด๋ฐ #์ด์ฐจํผ ๋์ค์ ์ปค๋ ์ ์ฒด ๊ฐ์ ํฉ์ 1๋ก ๋ง๋ค๊ธฐ ์ํด์ ์ปค๋ ์ ์ฒด์ ํฉ์ผ๋ก ๋๋ ์ฃผ๊ธฐ ๋๋ฌธ์ #์์๋ถ๋ถ์ด ํ์ ์๋ค๊ณ ์๊ฐํด์ ๋๋์ง ์์๋ค kernel_1d /= kernel_1d.sum() #kernel์ ๋ชจ๋ element์ ํฉ์ด 1์ด์ด์ผํ๊ธฐ ๋๋ฌธ์ ์ ์ฒด ์์ ํฉ์ผ๋ก ๋๋ ์ค๋ค. #๋น์จ์ด ์ค์ํ๋ค๋ ๋ป์ผ๋ก ํด์ํ๋ค. return np.array([kernel_1d])
opencv ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํด ํ๋ฒ์ ์์ฑ!
kernel_1d = cv2.getGaussianKernel(5, 3) # getGaussiankernel(width, sigma)์ ํํ์
๋ฌผ๋ก ๊ฐ๋จํ๊ฒ ํ์๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํจ์๋ฅผ ์ฐ๋๊ฒ ๋ ์ข์ง๋ง, ์ดํดํ ๊ฑธ ํ์ธํ๊ณ ์ถ์ด์ ์ง์ ๊ตฌํํด๋ดค๋ค.

2-2. 2D Gaussian Filter
1D Gaussian filter์ ํฌ๊ฒ ๋ค๋ฅด์ง ์๋ค.
point-spread ํ์์ ๊ทธ๋ํ๋ผ๊ณ ํ๋๋ฐ, ๋งจ ์ ์ ์์ ํผ์ง๋ ๋ชจ์์ผ๋ก ์กด์ฌํ๋ค๋ ๋ป์ด๋ค. convolution์ผ๋ก ์ป์ ์ ์๊ณ , array์ ๊ฐ์ผ๋ก ์ ์ฅ๋๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ์ ์ธ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค.
์์์ผ๋ก ๋ํ๋ด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค!
์์์ 1D Gaussian Filter ์๊ธฐ๋ฅผ ํ์๋๋ฐ, ๊ทธ๊ฑธ ์ด์ฉํด์ 2D Gaussian Filter๋ฅผ ๊ตฌํ ์๋ ์๋ค. ๊ฐ๋จํ๊ฒ ์ค๋ช ํ์๋ฉด ๋ฒกํฐ์์ ๋งคํธ๋ฆญ์ค๋ฅผ ์ป๋ ๋ฐฉ์์ ์ด์ฉํ๋ค.
TMI : ๋ฒกํฐ์ ๋งคํธ๋ฆญ์ค- ๊ฐ๋กํ ๋ฒกํฐ * ์ธ๋กํ ๋ฒกํฐ (์ค์นผ๋ผ ํํ)
: ์ด๊ฑธ ๋ฒกํฐ์ ๋ด์ ์ด๋ผ๊ณ ํ๋ค
- ์ธ๋กํ ๋ฒกํฐ * ๊ฐ๋กํ ๋ฒกํฐ (๋งคํธ๋ฆญ์ค ํํ)
: ์ด๊ฑธ ๋ฒกํฐ์ ์ธ์ ์ด๋ผ๊ณ ํจ
1D Gaussian Filter ์ด์ฉํ๊ธฐ
width = 11 sigma = 3 kernel_x = genGausKernel1D(width, sigma) kernel_y = kernel_x.T # .T๋ transpose์ธ๋ฐ, ๋ฒกํฐ๋ฅผ ๋ค์ง์ด์ ๊ฐ๋กํ์ ์ธ๋ก๋ก, ์ธ๋กํ์ ๊ฐ๋ก๋ก ํํํ๋ค๋ ๋ป kernel_2d = np.outer(kernel_x, kernel_y) #outer๋ ์ธ์ ์ด๋ผ๋ ๋ป! inner๋ ๋ด์
2D gaussian filter ์์ฑํ๋ ์๋ก์ด ํจ์ ์์ฑ
def genGaussianKernel(width, sigma): array = np.arange((width//2)*(-1), (width//2)+1) #์ค์ฌ์์๋ถํฐ์ ๊ฑฐ๋ฆฌ ๊ณ์ฐ arr = np.zeros((width, width)) #x^2+y^2 ๋ถ๋ถ์ ๋ฏธ๋ฆฌ ๊ณ์ฐํด๋ ๋งคํธ๋ฆญ์ค initialize for x in range(width): for y in range(width): arr[x,y] = array[x]**2+array[y]**2 #์ค์ฌ์์๋ถํฐ์ ๊ฑฐ๋ฆฌ๋ฅผ ์ ๊ณฑํฉ์ผ๋ก ๊ณ์ฐ kernel_2d = np.zeros((width, width)) #์ปค๋์ ๊ฐ์ ์ ์ฅํ ๋งคํธ๋ฆญ์ค ์์ฑ for x in range(width): for y in range(width): kernel_2d[x,y] = np.exp(-arr[x,y]/(2*sigma**2)) #์์์ ๋ง๊ฒ ๊ฐ ์ ์ฅ(์ญ์๋ ์์ ๋ถ๋ถ์ ์๋ต) kernel_2d /= kernel_2d.sum() #์ ์ฒด ๊ฐ์ ํฉ์ผ๋ก ๋๋์ด ํํฐ ์ ์ฒด์ ํฉ์ด 1์ด ๋๋๋ก ํจ return kernel_2d
์์ฑ๋ ํํฐ ํ์ธ
์ด๋ฏธ์ง ๋ก๋ฉ
lena = cv2.imread('SourceImages/lena.bmp', 0) # 0 represents gray-scale #(1 for unchanged, -1 for color - default) lena_noise = cv2.imread('SourceImages/lena_noise.bmp', 0)
์์ ํจ์๋ฅผ ์ด์ฉํ์ฌ ์ปค๋(ํํฐ) ์์ฑ
kernel_1 = genGaussianKernel(5,1) # 5 by 5 kernel with sigma of 1 kernel_2 = genGaussianKernel(11,3) # 11 by 11 kernel with sigma of 3
์ด๋ฏธ์ง ๋ณํ
res_lena_kernel1 = cv2.filter2D(lena, -1, kernel_1) # cv2.filter2D(src, ddepth, kernel) # src : input # ddepth = -1 : return type same as the source # kernel (= mask) res_lena_kernel2 = cv2.filter2D(lena, -1, kernel_2) res_lena_noise_kernel1 = cv2.filter2D(lena_noise, -1, kernel_1) res_lena_noise_kernel2 = cv2.filter2D(lena_noise, -1, kernel_2)

์ปค๋์์ ์๊ทธ๋ง ๊ฐ์ ๋ณ๊ฒฝํ๊ณ , ์ปค๋ ์ฌ์ด์ฆ๋ฅผ ๋ณ๊ฒฝํ๋ฉด์ ์ด๋ฏธ์ง๊ฐ ์ด๋ป๊ฒ ๋ณํ๋์ง ๋ณผ ์ ์์๋ค!
์์ฑ๋ ๋ ํํฐ๋ฅผ ๋น๊ต
์ฌ๋ฐ๋๊ฑธ ๋ ์๋ํด๋ดค๋๋ฐ, filter๋ฅผ ๋๊ฐ ์ด์ด์๋ ์ ์ฉํ ์ ์๋ค๋ ๊ฒ์ด๋ค.
width = 11 sigma = 3 kernel_x = genGausKernel1D(width, sigma) kernel_y = kernel_x.T kernel_2d = np.outer(kernel_x, kernel_y)
res_lena_noise_kernel1d_x = cv2.filter2D(lena_noise, -1, kernel_x) res_lena_noise_kernel1d_xy = cv2.filter2D(res_lena_noise_kernel1d_x, -1, kernel_y) #๋๋ฒ ์ฐ์ํด์ ์ปค๋์ ์ ์ฉํ๊ณ ์ถ์๋ ์ฐ๋ ๋ฐฉ๋ฒ! res_lena_noise_kernel2d = cv2.filter2D(lena_noise, -1, kernel_2d)
์์ ์ฌ์ง์ ์์๋๋ก ์๋ ์ด๋ฏธ์ง, kernel_x ๋ง ์ ์ฉํ ์ด๋ฏธ์ง, ๊ฑฐ๊ธฐ์ kernel_y๋ฅผ ์ถ๊ฐ๋ก ์ ์ฉํ ์ด๋ฏธ์ง, ๊ทธ๋ฆฌ๊ณ 2D kernel์ ์ ์ฉํ ์ด๋ฏธ์ง ์ด๋ ๊ฒ ๋ค๊ฐ๋ฅผ ๋ํ๋ธ ๊ฒ์ด๋ค!
๋ง์ง๋ง ๋๊ฐ์ ๊ฒฐ๊ณผ๊ฐ์ด ๊ฝค ๋น์ทํ๊ณ , ์ฐจ์ด๋ฅผ ๊ทธ๋ํ๋ก ๊ทธ๋ ค๋ดค์๋ ํฌ์ง ์์์ ๋ง์กฑ์ค๋ฌ์ ๋ค.
์ฝ๋ ์์ฒด์ ์๊ฐ๋ณต์ก๋๋ ์ข Redundantํ ๋ถ๋ถ๋ค์ด ์ ๊ฒฝ์ฐ์ด์ง๋ง, ๋ค์์ ๋ฆฌํฉํ ๋ง ์๋ํด๋ณด๋ ๊ฑธ๋ก ํ๊ณ , ์ค๋์ ์ฌ๊ธฐ์ ๋!
์ ๊ทธ๋ฆฌ๊ณ 1D์ 2D ํํฐ์ ๋ํด์ ๋ชจ๋ ๊ตฌํํด๋ดค๋๋ฐ, ๋ด๊ฐ ์ง์ ์คํํด๋ณธ๊ฑด ์๋์ง๋ง, ๊ตฌ๊ธ๋งํ๋ค๋ณด๋ 1Dํํฐ๋ฅผ ์ฌ์ฉํ์๋๊ฐ ๋ฏธ์ธํ๊ฒ(๋ฌผ๋ก ์ฒ๋ฆฌ๋์ด ๋ง์์ง๋ฉด ์ฐจ์ด๊ฐ ์ฌํด์ง๊ฒ ์ง๋ง) ๋น ๋ฅด๋ค. ์ด๊ฑด ๊ฐ์ธ์ ์ฝ๋ฉ ์คํ์ผ์ด๋ ์๊ฐ๋ณต์ก๋์ ๋ฐ๋ผ ๋ฌ๋ผ์ง์ ์๋ ๋ถ๋ถ์ธ๊ฒ ๊ฐ๋ค. ๋ธ๋ก๊ทธ ์ฐธ๊ณ
REF
https://homepages.inf.ed.ac.uk/rbf/HIPR2/gsmooth.htm
https://docs.opencv.org/4.x/
https://toitoitoi79.tistory.com/90 (1D์ 2D ์คํ์๊ฐ ๋น๊ต)
Author And Source
์ด ๋ฌธ์ ์ ๊ดํ์ฌ([CV] Gaussian Filter), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค https://velog.io/@zzaerynn_/CV-Gaussian-Filter์ ์ ๊ท์: ์์์ ์ ๋ณด๊ฐ ์์์ URL์ ํฌํจ๋์ด ์์ผ๋ฉฐ ์ ์๊ถ์ ์์์ ์์ ์ ๋๋ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ
์ธ ๋ฐ๊ฒฌ์ ์ ๋
(Collection and Share based on the CC Protocol.)
์ข์ ์นํ์ด์ง ์ฆ๊ฒจ์ฐพ๊ธฐ
๊ฐ๋ฐ์ ์ฐ์ ์ฌ์ดํธ ์์ง
๊ฐ๋ฐ์๊ฐ ์์์ผ ํ ํ์ ์ฌ์ดํธ 100์ ์ถ์ฒ ์ฐ๋ฆฌ๋ ๋น์ ์ ์ํด 100๊ฐ์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์ ํ์ต ์ฌ์ดํธ๋ฅผ ์ ๋ฆฌํ์ต๋๋ค