Tech이야기~!
welcom 자세히보기

Computer/OpenCV

[Opencv] 이미지 필터_가우시안필터

Enhold 2019. 12. 16. 19:27

donaricano-btn donaricano-btn donaricano-btn donaricano-btn

픽셀 값에 임의의 함수를 적용해 이미지를 향상시키는 것을 필터링이라고 한다. 

픽셀 근처의 값을 이용해 픽셀 값으 ㄹ수정하는데 사용, 이것은 이미지 행렬에 커널로 합성곱으로써 이루어진다. 

필터가 다르면 다른 종류의 커널을 만들 수 있다. 

 

  • 가우시안 블러 Gaussian blur
  • 중간값 필터 median filter
  • 팽창 dilation과 침식 erosion
  • 커스텀 필터 custom filter
  • 이미지 임계 처리 image thresholding

가우시안 스무딩 필터링 이란?
       - 가우시안 분포를 영상처리에 적용한 것
       - 정규분포, 확률분포에 의해 생성된 잡음을 제거하기 위한 필터
          (랜덤하게 분포된 영상의 잡음을 분석해보면 가우시안 분포를 보인다.)

가우시안 블러 Gaussian blur

가우시안 분포를 따르는 커널을 만들고 합성곱을 수행할 때 중심 픽셀이 가증 큰 가중치를 가지고 인접 픽셀의 가중치는 줄어든다는 것을 의미한다. 

 

* 가우시안 분포 공식은 연속 함수, 이미지는 이산적, 따라서 커널 함수를 만들기 전에 가우시안 분포에서 값을 이산화 한다. 

from PIL import Image
from PIL import ImageFilter
img = Image.open("Chapter02\image.jpg")
blur_img = img.filter(ImageFilter.GaussianBlur(5))
blur_img.save("GaussianBlur.jpg")
blur_img.show()

 

 

skimage사용

라이브러리의 필터 모듈은 이미지와 가우시안 커널의 표준 편자(sigma)를 취하는 가우시안 필터를 제공한다

sigma = 5 ,10일 때 결과(값이 높을 수록 더 흐려진다)

from skimage import io
from skimage import filters
img = io.imread("Chapter02\image.jpg")
out = filters.gaussian(img, sigma=5)
io.imsave("gaussian_filter_scikit.jpg", out)
io.imshow(out)
io.show()

 

중간값 필터

특정 픽셀과 이웃 픽셀 값으로부터 중간 값을 반환하는 매우 간단한 필터

Pillow와 나skimage모두 이 필터 함수 제공

from PIL import Image
from PIL import ImageFilter
img = Image.open("image.png")
blur_img = img.filter(ImageFilter.MedianFilter(7))
blur_img.show()
from skimage import io
from skimage.morphology import disk
from skimage import color
from skimage import filters
img = io.imread("image.png")
img = color.rgb2gray(img)
out = filters.median(img, disk(7))
io.imshow(out)
io.show()

 

팽창과 침식

이미지에 대한 모폴로지 연산 Morphological operations은 이미지가 가지고 있는 고유한 구조 또는 특징을 사용

전반적인 구조를 유지하면서 이미지를 처리하는 것이다. 

일반적인 예로 침식과 팽창이다.

 

침식

이미지의 일부를 제고하는 것을 의미

이미지의 전체 구조와 모양을 유지하면서 이미지 속의 물체가 축소된다. 

 

이미지에 두개의 물체가 있고 그 물체가 정말로 가까이 있는경우

침식 연산을 통해 두 물체를 축소시켜서 두 물체 간의 차이가 명확해지게하낟. 

 

이미지에서 노이즈를 제거

노이즈를 제거하는 최선의 방법은 아니지만, 침식으로 처리할 수 있는 전형적인 노이즈의 예

 

skimage 에서는 binary_erosion() or erosion() 함수 제공

from skimage import morphology
from skimage import io 

img = io.imread('image.png')
#eroded_img = morphology.binary_erosion(img)
eroded_img = morphology.erosion(img)

io.imshow(eroded_img)
io.show()

 

 

팽창

이미지의 작은 부분을 확대

이미지의 원하지 않는 틈/구멍을 메우고 싶을 때 유용

skimage에서는 binary_dilation() or dilation() 함수 지원

from skimage import morphology
from skimage import io
img = io.imread('Chapter02/image.jpg')
#dilated_img = morphology.binary_dilation(img)
dilated_img = morphology.dilation(img)

io.imshow(dilated_img)
io.show()

커스텀 필터

자신만의 필터를 디자인 하고 싶을 때

처음 부터 다시 모든 합성곱 연산을 구현할 필요가 없다. 

skimage & Pillow모두 커스텀 필터를 옵션을 지원한다. 

 

from PIL import Image
from PIL import ImageFilter
img = Image.open("Chapter02/image.jpg")
img = img.convert("L")
new_img = img.filter(ImageFilter.Kernel((3,3),[1,0,-1,5,0,-5,1,0,1]))
new_img.save("create_filter_image.jpg")
new_img.show()

커널 함수는 크기, 커널 가중치 값, 비율, 오프셋을 파라미터 입력으로 받는다. 

  • 크기는 행렬의 크기
  • 비율은 해당 픽셀이 나뉘는 값
  • 오프셋은 비율 조정 후 에 더해지는 값
  • 기본비율값은 거널 가중치의 합

 

 

 

 

 

 

 

 

 

이미지 임계 처리

임계처리란 임계값에 따라 픽셀의 색상 값으 ㄹ흰색 또는 검은색으로 변경하는 것

픽셀 값이 임계값 보다 큰 경우 픽셀을 흰색, 반대면 검은색 설정

임계처리에는 여러 종류가 있다. 

 

1. 전역 고정 이진화(Global fixed thresholding)

 

영상처리에서 이진화는 어떤 주어진 임계값(threshold)보다 밝은 픽셀들은 모두 흰색으로, 그렇지 않은 픽셀들은 모두 검은색으로 바꾸는 것을 지칭한다.

 

아래 예는 이진화 문제 중 가장 단순한 형태로서 균일한 밝기를 갖는 배경과 물체에 약간의 노이즈를 섞어서 생성한 영상이다.

 

2. 지역 가변 이진화 I (Locally adaptive thresholding)

진화 과정은 얼핏 global thresholding처럼 보이지만 사실은 원래 입력 영상 관점에서 봤을 때 local adaptive thresholding이 적용된 것이다. 위 과정을 다시 살펴보면 배경의 밝기 변화가 심한 영상에서 먼저 밝기 변화를 제거한 후 밝기 보정된 영상에 대해 global thresholding을 적용한 것이다. 그러나, 이를 원래 영상 관점에서 보면 배경의 밝기를 근사한 평면에 상수값을 더한 것을 threshold 평면으로 볼 수 있고 이 threshold 평면에 대해 직접 이진화를 수행한 것으로 해석할 수 있다.

 

3. 지역 가변 이진화 II (Locally adaptive thresholding)

이 방법은 영상 픽셀마다 서로 다른 threshold를 사용하는데, 그 threshold 값은 그 픽셀을 중심으로 한 n x n 주변 영역의 밝기 평균에 일정한 상수를 빼서 결정한다.

 

 

주변 영역의 크기를 어떻게 잡느냐와 상수 C를 어떻게 주느냐(+, - 모두 가능)에 따라서 결과가 달라질 수 있으며 문제에 따라서 적절히 값을 조정하면 좋은 이진화 결과를 얻을 수 있다.

 

4. Hysteresis(히스테레시스) thresholding

이진화 문제는 결국 어떤 값이 있을 때 이 값이 A 클래스인지 B 클래스인지 둘 중 하나로 분류하는 것인데 그 경계가 모호한 경우가 많다. 예를 들어 영상에서 어떤 픽셀값이 p1 = 79, p2 = 81이고 임계값(threshold)이 T = 80이라고 하자. 이 경우 p1과 p2는 사실 큰 값의 차이가 없음에도 불구하고 이진화를 하게 되면 전혀 다른 클래스로 분류되게 된다. 이와 같이 경계에 걸친 모호한 값들에 대해서는 하나의 엄격한 기준을 적용하는 것 보다는 이진화 오류를 최소화하기 위해 자신의 값 뿐만 아니라 주변의(공간적 또는 시간적으로) 값을 같이 참조하는 것이 효과적이다.

 

Hysteresis thresholding은 주변의 분류 결과에 따라서 자신의 분류 결과가 달라질 수 있는 thresholding 기법으로서 Canny edge detector에 사용된 이진화 기법이 가장 대표적인 예이다. Hysteresis(히스테레시스)라는 용어는 위키피디아에는 "Hysteresis is the dependence of a system not only on its current environment but also on its past environment"로 설명되어 있다. 즉, hysteresis는 어떤 시스템의 상태가 자신의 현재 값 뿐만 아니라 과거 또는 주변의 값에 의존하는 현상을 지칭한다.

 

5. 시간축이 고려된 이진화 문제

우리가 어떤 대상에 대해 판단을 할 때 어느 한 순간의 한 값만을 보고 판단을 하는 것은 사실 오류 가능성이 높은 위험한 방법이다. 하지만 방법이 없다보니 어떤 임계값을 미리 정해놓고 관측된 값이 임계값보다 낮으면 A, 높으면 B 이런식으로 판단을 하게 된다. 그런데, 이 대상을 계속 관찰하다 보면 관측된 값이 어떤 때는 임계값보다 높고 또 어떤 때는 임계값보다 낮을 수도 있을 것이다. 이럴 때마다 그 분류가 계속 바뀐다면 결과적으로 매우 불안정한 시스템이 될 것이다.

 

 이런 경우에 생각할 수 있는 한 방법은 high와 low의 두 개의 임계값을 정해놓고 어떤 대상의 분류가 바뀌는 데에는 보다 엄격한 기준을 적용하는 것이다. 만일 어떤 대상의 관측값이 임계값 이하여서 A 클래스로 분류되었다고 하자. 이후 대상의 관측값이 high threshold 이하인 경우에는 계속 A 클래스로 분류하되, high threshold를 넘어서면 B 클래스로 분류를 바꾼다. 그리고 일단 B가 된 이후에는 관측값이 low threshold보다 낮게 나타난 경우에만 A로 분류를 변경하는 방식이다. 즉, 분류의 경계를 넘나들 때에는 그 방향에 따라서 서로 다른 문턱값을 적용하는 방식이다.

 

scikit-image예제

import matplotlib.pyplot as plt

from skimage import data
from skimage.filters import threshold_otsu, threshold_local
from skimage.io import imread
from skimage.color import rgb2gray

image = imread('chapter02/image.jpg')
image = rgb2gray(image)

global_thresh = threshold_otsu(image)
binary_global = image > global_thresh

block_size = 35
binary_adaptive = threshold_local(image, block_size, offset=10)

fig, axes = plt.subplots(nrows=3, figsize=(7, 8))
ax0, ax1, ax2 = axes
plt.gray()

ax0.imshow(image)
ax0.set_title('Image')

ax1.imshow(binary_global)
ax1.set_title('Global thresholding')

ax2.imshow(binary_adaptive)
ax2.set_title('Adaptive thresholding')

for ax in axes:
    ax.axis('off')

plt.show()