이미지 필터링
Smoothing to Remove Image Noise
이미지의 노이즈를 줄이기 위한 필터가 여러개 존재한다
Box Filter
- Linear filter
- 노이즈를 제거하는 가장 간단한 방법 중 하나이며 아이디어는 필터 중간 픽셀값의 이웃 픽셀들의 평균값으로 필터 중간 픽셀값을 대체하는 것이다
-
3x3 box filter 예시
-
코드 구현부
def getBoxFilter(self, filter_length=3): # 필터의 모양은 정사각형이라고 간주하며 Normalization을 위해 필터 사이즈의 제곱만큼 스케일링 해줌 return np.ones((filter_length, filter_length)) / (filter_length**2)
Gaussian Filter
- Linear filter
-
Gaussian 분포
- 평균 $\mu$와 표준편차 $\sigma^2$에 의해 그 분포가 확정된다. 평균이 0, 표준편차가 1인 정규분포일 경우 표준정규분포라고 한다
- Gaussian Filter 수식
- 위 그림의 가우시안 필터에서 볼 수 있듯 가까운 픽셀은 큰 가중치를, 멀리있는 픽셀은 작은 가중치를 사용하여 컨볼루션 연산을 진행함
- 하지만 위 결과에서 볼 수 있듯 이상치(Noise)가 흐려지기는 하지만 효과적으로 제거하지는 못하며 엣지(Edge)가 blur 처리 되어 상당한 정보손실이 발생한다.
-
코드 구현부
def getGaussianFilter(self, filter_length=3, sigma=1): # meshgrid를 사용을 위해 지정된 범위의 연속된 배열 선언 ax = np.linspace(-(filter_length - 1) / 2., (filter_length - 1) / 2., filter_length) # meshgrid를 통한 2차원 평면상의 x, y 좌표 선언 xx, yy = np.meshgrid(ax, ax) # 위 언급한 Gaussian filter 수식 구현 filter = 1 / (2 * np.pi * np.square(sigma)) * np.exp(-1 * (np.square(xx) + np.square(yy)) / 2 * np.square(sigma)) return filter / np.sum(filter)
Median Filter
-
Nonlinear filter
-
Gaussian filter에 비해 Noise 제거가 좀더 효과적으로 이루어진 것을 확인할 수 있음
-
그러나 Noise가 salt and pepper 수준이 아니라 위 그림처럼 명확하게 노이즈가 보이지 않을경우 그 효과가 떨어짐
-
-
코드 구현부
def getMedianFilterResult(self, img, palette, index_y, index_x, filter_length=3): temp = [] # 필터와 겹치는 이미지의 픽셀값 크기 비교를 위한 일시적 리스트 # 필터 사이즈 만큼 temp리스트에 이미지 픽셀값 추가 for filter_y in range(filter_length): for filter_x in range(filter_length): temp.append(img[index_y + filter_y][index_x + filter_x]) # 리스트의 내부함수인 sort 호출하여 크기순으로 정렬 temp.sort() # 정렬된 리스트 값들 중 중간에 위치하는 값으로 palette[index_y][index_x] 대체 palette[index_y][index_x] = temp[filter_length*filter_length // 2]
Bilateral Filter
- Nonlinear filter
- bilateral filter 수식
- \[g[i, j] = {1 \over W_s} \sum_m \sum_n f[m, n]n_{\sigma_s}[i-m, j-n]n_{\sigma_b}(f[m,n] - f[i,j])\]
- 가운데 그림의 Spatial Gaussian filter를 오른쪽 그림 Input에 대해 적용하였을 때 왼쪽 그림과 같은 Gaussian Smootehd Output이 나옴
- Spatial gaussian filter는 Input이 어떤것이냐에 상관없이 필터 크기에 의해서만 값이 달라지는 필터를 내놓게 됨. 즉 3x3, 5x5, 7x7 필터를 예로 들면 Input이 어떤 것이냐에 상관없이
- 이번엔 Brightness Gaussian filter를 오른쪽 그림 Input에 대해 적용하였을 때 가운데 그림과 같은 Output이 나옴
- 위 Brightness Gaussian은 비슷한 Intensity를 지닌 pixel이 있을 경우 높은 weight를 줘서 신호를 살리고 Intensity 차이가 클 경우 낮은 weight를 줘서 신호를 죽임. 예를 들어 Brightness Gaussian 필터(실제로 이런 필터가 있지는 않는것 같지만 잠깐 이렇게 명칭을 쓰자)
왼쪽 그림에서 볼 수 있듯 비슷한 Intensity를 가질 경우 높은 weight를 줘서 신호를 살리고 Intensity차이가 클 경우 낮은 weight를 줘서 신호를 죽임. 이를 통해 기존의 Spatial gaussian filter에서 불가능했던 Edge Preserving이 가능함
- 이 두 필터를 곱해서 비슷한 Intensity를 지닌 부분들의 값 분포를 Gaussian 형태로 만들고 엣지와 같이 Intensity 차이가 큰 부분들에는 낮은 weight를 줘서 신호를 없애버리는 Combined 필터를 형성하고 이를 통해 왼쪽 그림의 Bilateral Filtered output을 만들 수 있게 된다
- 코드 구현부
def getBilateralFilterResult(self, img, palette, index_y, index_x, filter_length=3, sigma_i=20.0, sigma_s=20.0):
'''
Convolution 연산을 진행하지 않기 때문에 바로 픽셀값 대체까지 진행함
'''
def gaussian(x, sigma):
return (1.0/(2*np.pi*(sigma**2)))*np.exp(-(x**2)/(2*(sigma**2)))
def distance(x1,y1,x2,y2):
return np.sqrt(np.abs((x1-x2)**2-(y1-y2)**2))
wp_total = 0
filtered_image = 0
for k in range(filter_length):
for l in range(filter_length):
n_x = index_y - (filter_length/2 - k)
n_y = index_x - (filter_length/2 - l)
if n_x >= len(palette): # index error 방지. 이미지 범위 초과시에도 반대쪽 픽셀을 가리킬 수 있도록 함
n_x -= len(palette)
if n_y >= len(palette[0]):
n_y -= len(palette[0])
# Brightness Gaussian 구하는 부분
gi = gaussian(img[int(n_x)][int(n_y)] - img[index_y][index_x], sigma_i)
# Spatial Gaussian 구하는 부분
gs = gaussian(distance(n_x, n_y, index_y, index_x), sigma_s)
wp = gi * gs
filtered_image = (filtered_image) + (img[int(n_x)][int(n_y)] * wp)
wp_total = wp_total + wp
filtered_image = filtered_image // wp_total
palette[index_y][index_x] = int(np.round(filtered_image))
# 위 코드는 아래 깃허브 소스코드를 참고함
# https://github.com/anlcnydn/bilateral
이미지 적용 결과
3x3 크기 필터 적용
-
gray.png
-
gray_n1.png
-
gray_n2.png
5x5 크기 필터 적용
-
gray.png
-
gray_n1.png
-
gray_n2.png
출처
PREVIOUS경제