영상에서 원하는 정보만 통과시키고, 나머지 정보는 걸러내는 작업을 필터링(filtering) 이라고 하고, 주로 '마스크(Mask) 연산'을 사용합니다. (마스크 = 커널(kernel)
모든 데이터에는 잡음(noise) 이 있어서 데이터 전처리로 영상 자체의 노이즈를 제거해야하는 데요. 이를 위해 노이즈 제거 필터링를 사용합니다. 또한, 블러링(bluring) 은 거친 느낌의 입력 영상을 부드럽게 만들거나, 영상에 존재하는 잡음(noise) 제거 목적으로 사용하고, 샤프닝(sharpening) 은 반대로 초점이 잘 맞은 사진처럼 윤곽이 뚜렷한 느낌이 나도록 합니다.
blur
는 평균값 필터(Mean Filter) 사용합니다. 주변 픽셀 값의 산술 평균으로 설정하면 픽셀 간 그레이스케일값 변화가 줄어들어 날카로운 엣지가 무뎌지고, 노이즈 제거되는 효과를 가져옵니다.
cv2.blur(src, ksize, dst=None, anchor=None, borderType=None)
- src : 입력 영상
- ksize : 평균값 필터 크기. (width, height) 튜플 형식
- dst : 결과 영상, 입력 영상과 같은 크키와 타입
- boarderType : 가장자리 픽셀 확장 방식
평균값 필터의 단점은 가까이 있는 픽셀과 멀리 있는 픽셀이 같은 가중치 사용하여 평균 계산하다보니, 멀리 있는 픽셀에 영향 많이 받게되는데요. GaussianBlur
는 표준 정규 분포 곡선을 활용하여 중심 픽셀에 가까울 수록 가중치를 더 주는 방식으로 이 부분을 보완합니다.
평균값 필터가 Basik Kernel을 사용한다면, 가우시안 필터(Gaussian Filter)는 Gaussian Kernel을 활용하여 픽셀 별 가중치를 조정하여 계산합니다.
Gaussian Noise는 정규 분포를 갖는 Noise로 일반적인 Noise로 생각하면 됩니다. 지지직 거리는 듯한 느낌을 주며 보통 영상 신호의 변동, 이미지의 압축, 전송 등의 과정에서 발생하는데, 이러한 노이즈를 제거하는데 GaussianBlur
가 사용됩니다.
cv2.GaussianBlur(src, ksize, sigmaX, sigmaY, boarderType)
- src : 입력 영상
- ksize : 가우시안 커널 크기, ksize.width와 ksize.heigth는 0보다 큰 홀수 .
- sigmaX : x 방향으로의 가우시안 커널 표준 편차
- sigmaY : y 방향으로의 가우시안 커널 표준 편차. sigmaY = 0 이면, sigmaX와 같은 값 사용.
- boarderType : 가장자리 픽셀 확장 방식
가우시안 필터의 단점은 픽셀 값이 급격하게 변하는 에지 부근에 적용되면, 잡음 뿐 아니라 에지 성분까지 함께 무뎌져서 객체의 윤곽이 흐릿하게 변하게 되는데요. bilateralFilter
(양방향필터)는 에지가 아닌 영역을 블러링 함으로써 에지 성분은 유지하면서 Gaussian Noise를 효과적으로 제거합니다. 다만, GaussianBlur
에 비해 훨씬 많은 연산량 필요로 하구요.
cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None)
- src: 입력 영상. 8비트 또는 실수형, 1채널 또는 3채널.
- d: 필터링에 사용될 이웃 픽셀의 거리(지름)
음수(-1)를 입력하면 sigmaSpace 값에 의해 자동 결정(권장)
- sigmaColor: 색 공간에서 필터의 표준 편차
- sigmaSpace: 좌표 공간에서 필터의 표준 편차
- dst: 출력 영상. src와 같은 크기, 같은 타입.
- borderType: 가장자리 픽셀 처리 방식
medianBlur
는 주변 픽셀 값을 정렬하여 중앙값(median)으로 픽셀을 대체하는 방식으로 이미지에 무작위로 나타는 희고 검은 점들, 즉 점 잡음(Salt and pepper noise) 제거하는데 효과적 입니다.
cv2.medianBlur(src, ksize)
- src : 입력 영상
- ksize : 커널 크기, 1보다 큰 홀수 지정
OpenCV에서 샤프닝 함수를 제공하지는 않습니다. 그러나 날카롭지 않은(unsharp) 이미지를 활용하여 반대로 날카로운 영상을 생성할 수 있으며, 이를 위해 블러링된 이미지를 이용합니다.
원본 영상에서 블러링 이미지를 뺀 후 이를 원본 영상에 다시 더해주면 에지 영역이 강조된 샤프닝 이미지를 얻을 수 있습니다.
매번 plt.subplot
으로 이미지 출력 위치 지정하고 title 설정하기 번거로우니, 편의를 위해 함수를 만들어서 사용합니다.
def plt_imshow(title='image', img=None, figsize=(16 ,10)):
plt.figure(figsize=figsize)
if type(img) == list:
if type(title) == list:
titles = title
else:
titles = []
for i in range(len(img)):
titles.append(title)
for i in range(len(img)):
if len(img[i].shape) <= 2:
rgbImg = cv2.cvtColor(img[i], cv2.COLOR_GRAY2RGB)
else:
rgbImg = cv2.cvtColor(img[i], cv2.COLOR_BGR2RGB)
plt.subplot(1, len(img), i + 1), plt.imshow(rgbImg)
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
else:
if len(img.shape) < 3:
rgbImg = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)
else:
rgbImg = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(rgbImg)
plt.title(title)
plt.xticks([]), plt.yticks([])
plt.show()
import cv2
import matplotlib.pyplot as plt
import numpy as np
image_list_title = []
image_list = []
image = cv2.imread('./images/stoner.jpg', cv2.IMREAD_COLOR)
blur = cv2.blur(image, (5,5))
gaussian = cv2.GaussianBlur(image, (5,5), sigmaX=0)
bilateral = cv2.bilateralFilter(image, -1, 10, 5)
medianblur = cv2.medianBlur(image, ksize=3)
alpha = 1.0 # 0~1 사이 설정
sharp = np.clip((1.0+alpha/10)*image - alpha/10*blur_image, 0, 255).astype(np.uint8)
image_list_title = ['original','blur','gaussian','bilateral','medianblur','sharp']
image_list = [image, blur, gaussian, bilateral, medianblur, sharp]
plt_imshow(image_list_title, image_list)
[출력]
* 황선규 박사님의 패스트 캠퍼스 OpenCV 강의와
<OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝> 책을 토대로 정리한 내용 입니다.