앎을 경계하기

Machine Learning/For CV

3. 밝기 변환과 공간필터링(3)

양갱맨 2020. 11. 9. 15:07

3.4 공간 필터링의 기초

Filter 라는 용어는 주파수 도메인 처리에서 가져왔고, Filtering은 특정 주파수 성분들을 통과시키거나 거부하는 것이다. 낮은 주파수를 통과시키면 저역통과 필터라고 한다. 이는 영상을 부드럽게(흐릿하게) 만든다. 공간 필터는 선형, 비선형 필터링에 사용 가능하다.

3.4.1 공간 필터링의 메커니즘

필터링은 이웃의 중심의 좌표와 같은 좌표를 가지며 필터링 연산의 결과를 값으로 하는 새로운 화소를 만든다.

필터 센터부분이 입력 영상의 각 화소를 거쳐 필터링 된 영상이 생성된다.

수행되는 연산이 선형적이면 그 필터는 선형 공간 필터이며, 아니라면 비선형 공간 필터가 된다.

선형 공간 필터링의 예시이다.

영상 임의의 점 (x,y)에 필터의 중앙 계수인 w(0,0)가 맞춰진다.

x, y 값이 바뀌면서 필터가 모든 영상의 화소와 계산되도록 한다.

그러나, 영상의 가장자리 부분들이 필터의 중심과 겹쳐지지 않는다는 문제가 발생한다. 이를 해결하기 위해 0으로 패딩하여 모든 화소에 중심이 겹쳐질 수 있도록 zero padding 기법을 사용한다.

3.4.2 공간 코릴레이션과 컨볼루션

선형 공간 필터링에서 중요한 개념 두가지가 Correlation과 Convolution이다.

  • Correlation은 필터를 영상 위로 이동시키면서 각 위치에서 곱의 합을 구한다.
  • Convolution은 필터가 180도 회전하고서 이동하면서 곱의 합을 구한다.
import numpy as np
import scipy.signal as signal

# 하나만 1이고 나머지는 모두 0인 함수를 이산 단위 임펄스라고 한다.
origin = np.zeros((5,5))
origin[2][2] = 1
'''
origin
array([[0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 1., 0., 0.],
       [0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0.]])
'''

filter = np.array(range(1,10)).reshape((3,3))
'''
filter
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
'''

#padding 적용
padded = np.pad(origin, 2)
'''
padded
array([[0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 1., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.]])
'''

correlate_result = signal.correlate2d(padded,filter,'same')
'''
correlate_result
array([[0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 9., 8., 7., 0., 0., 0.],
       [0., 0., 0., 6., 5., 4., 0., 0., 0.],
       [0., 0., 0., 3., 2., 1., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.]])
'''

convolution_result = signal.convolve2d(padded,filter,'same')
'''
convolution_result
array([[0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 1., 2., 3., 0., 0., 0.],
       [0., 0., 0., 4., 5., 6., 0., 0., 0.],
       [0., 0., 0., 7., 8., 9., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.],
       [0., 0., 0., 0., 0., 0., 0., 0., 0.]])
'''

convolution 연산을 거쳐 나온 결과가 필터를 180도 회전 후 연산된 결과라는 것을 확인할 수 있다.

correlation은 영상 간 일치하는 유사성을 구할 때 사용될 수 있다.

필터가 대칭이라면, 코릴레이션과 컨볼루션의 결과는 같다.

3.4.3 선형 필터링의 벡터 표현

선형 필터링은 다음과 같이 표현할 수 있다

R=w1z1+w2z2+...+wmnzmn=k=1mnwkzk=wTzR=w_1z_1+w_2z_2+...+w_{mn}z_{mn}\\=\sum_{k=1}^{mn}w_kz_k\\=\bold{w}^T\bold{z}

correlation을 사용한다면, 위 식 그대로 사용하면 되고 convolution을 사용한다면, 필터인 w를 180도 회전시켜서 사용하면 된다.

3.4.4 공간 필터 마스크 만들기

m×n 선형 공간 필터를 만들기 위해 mn개의 계수를 정해야 한다. 만약 영상의 화소를 평균 밝기로 바꾼다면 (x,y)위치에서 평균값은 (x,y)를 중심으로 하는 3×3 이웃의 9개 밝기 값들을 9로 나눈 것이다. 이것은 계수가 1/9인 상황과 같다.

비선형 필터를 만들려면 필터 크기, 포함되는 영상 화소들에 수행될 연산을 정해야한다.

예를 들어, 비선형 연산인 max 연산은 영상에서 임의의 점 (x,y)를중심으로하는 5×5 max 필터를 통해 최대 밝기 값을 (x,y)의 화소 값으로 설정한다.

선형함수로 처리하지 못하는 기능들을 비선형함수를 통해 처리할 수 있다.

3.5 스무딩 공간 필터

smoothing filter는 blurring과 noise reduction에 사용된다. 블러는 큰 사이즈의 객체 추출 전 작은 디테일들을 제거하거나 곡선의 작은 끊김을 잇는 것 같은 preprocessing 작업에 사용된다. 노이즈 축소는 선형 필터에 의한 블러링이나 비선형 필터링으로 가능하다.

3.5.1 스무딩 선형 필터

스무딩 선형 공간 필터의 출력은 단순 필터를 거치는 영상 화소들의 평균이다. 이 스무딩 선형 필터는 평균화 필터라고도 하고 저역통과 필터라고도 한다.

스무딩 필터는 영상의 각 화소를 이웃 화소들과의 평균으로 교체하여 sharp transition을 감소시킨다. 스무딩이 가장 많이 활용되는 응용은 노이즈 축소이다. 엣지 부분에서도 밝기의 sharp transition을 낮춤으로써 엣지 영역이 흐려지는 경우도 생긴다.

from google.colab.patches import cv2_imshow
import numpy as np
import scipy.signal as signal
import cv2

noise = cv2.imread('/content/cat.jpg')
noise = cv2.resize(noise, (320,320))
cv2_imshow(noise)

def smooth(src, k_size=3, depth = 3):
    res = np.zeros(src.shape)
    kernel = np.ones((k_size,k_size),dtype=np.int8)
    for i in range(depth):
        res_ = (signal.correlate2d(src[:,:,i],kernel,mode='same'))
        res[:,:,i] = res_*(1/(k_size**2))
    return res

cv2_imshow(smooth(noise, 3))
print(smooth(noise, 3))

res = np.concatenate((noise, np.full((320,10,3),255)), axis=1)
cv2_imshow(np.concatenate((res,smooth(noise,3)),1))
출처 - OpenCV:Image Filtering

m×n 필터는 1/mn 정규화 상수를 갖는다. 모든 계수가 같은 평균 필터를 box filter라고 한다.

res2 = cv2.boxFilter(noise,0,(3,3),normalize=True)
cv2_imshow(res2)

계수가 모두 같은 box filter 말고 가장자리에서 중심 화소로 갈 수록 계수가 커지도록 weighted average를 적용할 수 있다.

이 weighted average를 적용하게 되면 중앙 화소를 다른 화소들보다 더 가중치를 두어 적용된다.

kernel = [[1,2,1],[2,4,2],[1,2,1]]
res = np.zeros(noise.shape)
for i in range(noise.shape[-1]):
    res[:,:,i] = (signal.correlate2d(noise[:,:,i], kernel, 'same'))/16

cv2_imshow(res)

16으로 나눠주는 이유는 필터마스크의 합을 1로 만들어주기 위함이다. 마스크 합이 1보다 크면 영상이 전체적으로 밝고, 작으면 어둡게 나타난다.

3.5.2 order-statistics filters

order-statistics filter는 출력이 필터에 의해 둘러싸여있는 영상 영역에 담긴 화소들을 정렬하고 정렬된 순서에 따라 결정된 값으로 중앙 화소의 값을 교체하는 비선형 공간 필터다. 가장 잘 알려져 있는 필터는 median filter다. 중간값 필터는 이름처럼 필터 영역에 겹쳐지는 화소 영역들의 값을 정렬한 뒤 중간값으로 중앙 화소 값을 교체한다.

import random

def salt_and_pepper(image, p):
    output = np.zeros(image.shape,np.uint8) 
    thres = 1 - p 

    for i in range(image.shape[0]): 
        for j in range(image.shape[1]): 
            rdn = random.random()
            if rdn < p: 
                output[i][j][:] = 0 
            elif rdn > thres: 
                output[i][j][:] = 255 
            else: 
                output[i][j][:] = image[i][j][:]
    return output

noise_ = salt_and_pepper(noise, 0.2)
res3 = cv2.medianBlur(np.float32(noise_), 3)
res5 = cv2.medianBlur(np.float32(noise_), 5)
cv2_imshow(noise_)
cv2_imshow(res3)
cv2_imshow(res5)
왼쪽부터 salt-and-pepper 추가된 영상, 중간값 필터(k=3), 중간값 필터(k=5)

median filter가 많이 사용되는 이유는 노이즈 축소 성능도 좋고 선형 스무딩 필터보다 덜 블러링 시키기 때문이다. 특히, median filter는 흑백색의 점들이 뿌려진 salt-and-pepper noise(impulse noise)에 대해 특히 효과적이다.

백분위 수 중 50번째를 사용하면 중간값 필터이고, 100번째는 max 필터가 된다. 0은 당연히 min 필터이다.