[CV] Gaussian Filter

31044 ๋‹จ์–ด CVGaussianFilterCV

์š”์ฆ˜ CV์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ •๋ฆฌํ•ด๋‘์ง€ ์•Š์œผ๋ฉด ์žŠ์–ด๋ฒ„๋ฆด ๊ฒƒ ๊ฐ™์•„์„œ ๋ฒจ๋กœ๊ทธ์— ์ •๋ฆฌํ•ด๋‘๋ ค๊ณ  ํ•œ๋‹ค.. ํ•œ๋‹ฌ์— ๋‘๊ฐœ ์ •๋„ ์ด์ƒ ํ•˜๋Š”๊ฒŒ ๋ชฉํ‘œ์ธ๋ฐ ์ž˜ ๋˜๋ฉด ์ข‹๊ฒ ๋‹ค

1. ํ•„ํ„ฐ

ํ•„ํ„ฐ๋Š” CV์—์„œ ๊ต‰์žฅํžˆ ์ค‘์š”ํ•œ ์š”์†Œ๋‹ค. ์ฃผ๋กœ convolution์„ ์ด์šฉํ•˜๋Š”๋ฐ, ์ด๋ฏธ์ง€์— ๋‹ค์–‘ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•œ๋‹ค๋“ ์ง€, ์ด๋ฏธ์ง€๋ฅผ ๋ธ”๋Ÿฌ ์ฒ˜๋ฆฌํ•œ๋‹ค๋“ ์ง€, ๋“ฑ๋“ฑ ๋‹ค์–‘ํ•œ ์˜ต์…˜์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•„ํ„ฐ๋ผ๊ณ ๋„ ๋ถ€๋ฅด์ง€๋งŒ, ์ปค๋„์ด๋ผ๊ณ ๋„ ํ•œ๋‹ค.

2. ๊ฐ€์šฐ์‹œ์•ˆ ํ•„ํ„ฐ (Gaussian Filter)

์ด๋ฒˆ์—๋Š” ๊ฐ€์šฐ์‹œ์•ˆ ํ•„ํ„ฐ์— ๋Œ€ํ•ด์„œ ๋ฐฐ์› ๋Š”๋ฐ, ์ฃผ๋กœ ์ด๋ฏธ์ง€๋ฅผ smoothing ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ ๊ฐ™๋‹ค. lena ์ด๋ฏธ์ง€์— ๋Œ€ํ•ด์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๊ฐ’๋“ค์„ ๋ณ€๊ฒฝํ•ด๊ฐ€๋ฉด์„œ ๊ฒฐ๊ณผ๋ฅผ ๋„์ถœํ•˜๋Š” ๊ฑธ ์‹œ๋„ํ•ด๋ดค๋‹ค.

opencv ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋ฌผ๋ก  ๊ฐ€์šฐ์‹œ์•ˆ ํ•„ํ„ฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•˜์ง€๋งŒ, ์ง์ ‘ ํ•ด๋ณด๋Š”๊ฒŒ ๋ชฉํ‘œ๋ผ! ์‹์„ ์ดํ•ดํ•˜๊ณ  ์ฝ”๋”ฉ์œผ๋กœ ํ‘œํ˜„ํ•ด๋ดค๋‹ค.

2-1. 1D Gaussian Filter

๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํ˜•ํƒœ์˜ ๊ฐ€์šฐ์‹œ์•ˆ ํ•„ํ„ฐ์ด๋‹ค (๊ทธ๋ž˜ํ”„๋Š” ฮผ=0,ฯƒ=1\mu = 0, \sigma =1

G(x)=12ฯ€ฯƒeโˆ’x22ฯƒ2G(x) = \dfrac 1 {\sqrt{2\pi}\sigma} e^{-\frac {x^2} {2\sigma^2}}

๋ชจ๋“  ํ•„ํ„ฐ์—๋Š” ์‚ฌ์ด์ฆˆ์™€ ์‹œ๊ทธ๋งˆ ๊ฐ’์ด ์กด์žฌํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ xx๋Š” ์ปค๋„์˜ ์ค‘์‹ฌ์—์„œ ๋–จ์–ด์ง„ ๊ฑฐ๋ฆฌ์ด๊ณ , ฯƒ\sigma ๋Š” ์œ ์ €๊ฐ€ ์ง์ ‘ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์ด๋‹ค. ์ด ๊ฐ’์— ๋”ฐ๋ผ์„œ ๋…ธ์ด์ฆˆ ์ฒ˜๋ฆฌ์˜ ์ •๋„๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค.

์‚ฌ์šฉํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ชฉ๋ก

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์— ๊ฐ’์œผ๋กœ ์ €์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€์ ์ธ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
์ˆ˜์‹์œผ๋กœ ๋‚˜ํƒ€๋‚ด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค!

G(x,y)=12ฯ€ฯƒ2eโˆ’x2+y22ฯƒ2G(x,y) = \dfrac 1 {{2\pi}\sigma^2} e^{-\frac {x^2+y^2} {2\sigma^2}}

์•ž์—์„œ 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 ์‹คํ–‰์‹œ๊ฐ„ ๋น„๊ต)

์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ