영상의 필터링(image filtering)
- 영상에서 필요한 정보만 통과시키고 원치 않는 정보는 걸러내는 작업
1. 주파수 공간에서의 필터링(수학적 이론이 필요함)
*저주파 = 부드러운 성분
*고주파 = 픽셀 값이 급격하게 바뀌는 성분
2. 공간의 필터링(Correlation, Deep Learning ==> Conv)
*영상의 픽셀 값을 직접 이용하는 필터링 방법
*대상 좌표의 픽셀 값과 주변 픽셀 값을 동시에 사용
= 주로 마스크(mask) 연산을 이용함 (마스크 = 커널(kernel) = 윈도우(window) = 템플릿(template))
다양한 모양과 크기의 마스크
마스크의 형태와 값에 따라 필터의 역할이 결정됨
• 영상 부드럽게 만들기(Mean filter, GaussianBlur)
• 영상 날카롭게 만들기(Unsharp mask)
• 에지(edge) 검출(Gradient, Sobel, Canny)
• 잡음 제거(GaussianBlur, Median filter, Bilateral filter(양방향))
Filtering 연산 방법
- Mask 연산, 옆으로 이동하면서 각각 연산(Scanning)
최외곽 픽셀 처리
- OpenCV는 가상의 픽셀이 있다고 생각하고 연산(= padding)
= 테두리 외삽법(borderType)
영상 부드럽게 만들기
Mean filter
- 픽셀들 간의 그레이스케일 값 변화가 줄어들어 날카로운 에지가 무뎌지고, 영상에 있는 잡음(Noise)의
영향이 사라지는 효과
- 마스크 크기가 커질수록 평균 값 필터 결과가 더욱 부드러워짐
= 더 많은 연산량이 필요!
cv2.filter2D(src, ddepth, kernel, dst=None, anchor=None, delta=None,
borderType=None) -> dst
'''
ddepth: -1을 지정하면 src와 같은 타입의 dst 영상을 생성
출력 영상 데이터 타입. (e.g) cv2.CV_8U, cv2.CV_32F, cv2.CV_64F
== 1byte, 4byte, 8byte
anchor: 고정점 위치. (-1, -1)이면 필터 중앙을 고정점으로 사용
delta: 추가적으로 더할 값
'''
import cv2
import sys
import numpy as np
src = cv2.imread('ch04\\images\\rose.bmp', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
sys.exit()
'''
kernel = np.array([[1/9,1/9,1/9],
[1/9,1/9,1/9],
[1/9,1/9,1/9]], dtype=np.float32) # 기본적으로 float64로 만들어짐
*mean*
kernel = np.ones((3, 3), dtype=np.float64) / 9.
'''
# dst = cv2.filter2D(src, -1, kernel, borderType)
# ddepth = -1: 입력영상과 동일한 datatype로 생성(grayscale)
# borderType = BORDER_REFLECT_101 or BORDER_DEFAULT
# cv2.blur()
dst = cv2.blur(src, (3,3))
for ksize in (3, 5, 7):
dst_ksize = cv2.blur(src, (ksize, ksize))
text = f'Mean:{ksize}x{ksize}'
cv2.putText(dst_ksize, text, (10,30), cv2.FONT_HERSHEY_SIMPLEX,\
fontScale= 1.0, color= 255, thickness= 1, lineType=cv2.LINE_AA)
cv2.imshow('dst_ksize', dst_ksize)
cv2.waitKey()
# cv2.imshow('image', src)
# cv2.imshow('dst', dst)
cv2.waitKey()
GaussianBlur
평균값 필터에 의한 블러링의 단점
• 필터링 대상 위치에서 가까이 있는 픽셀과 멀리 있는 픽셀이 모두 같은 가중치를 사용하여 평균을 계산
• 멀리 있는 픽셀의 영향을 많이 받을 수 있음
사용자가 지정한 파라미터로 Bell Shape의 filter를 만들어서 모든 pixel 영역에
대해서 동일한 형태로 Correlation진행
Filter Mask size = (8sigma + 1) or (6sigma + 1)
= +-4sigma(float) or +-3sigma(uint8)
= sigma 값이 클수록 더 많은 data를 포함(= 연산속도 느려짐)
ksize를 지정한다는건
ex) (5,5)를 설정하면 총 (8sigma + 1) or (6sigma + 1) ==> +-41 or +-31
좌우 끝 특정 영역은 사용하지 않겠다는 말(= 평균에 가까운 것만 사용하겠다)
cv2.GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None,
borderType=None) -> dst
'''
ksize: (0, 0)을 지정하면 sigma 값에 의해 자동 결정됨(왠만하면 (0,0) 쓸것)
ksize = (8시그마 +1) or (6시그마 + 1)
sigmaX: x방향 sigma.
sigmaY: y방향 sigma. 0이면 sigmaX와 같게 설정
'''
# cv2.GaussianBlur(src, ksize, sigmaX)
# sigmax= 1, ksize가 자동으로 설정됨= 값이 커지면 더 blurring 됨
dst = cv2.GaussianBlur(src, (0,0), sigmaX= 3)
# blur( mean filter)의 경우 line(edge?)이 부드럽게 blur처리가 되지 않아
dst2 = cv2.blur(src, (7,7))
for sigma in range(1,6):
dst_sigma= cv2.GaussianBlur(src, (0,0), sigma)
text = f'sigma = {sigma}'
cv2.putText(dst_sigma, text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1.0, 255, 1, cv2.LINE_AA)
cv2.imshow('dst_sigma', dst_sigma)
cv2.waitKey()
영상 날카롭게 만들기
언샤프 마스크(Unsharp mask)
- 날카롭지 않은(unsharp) 영상, 즉, 부드러워진 영상을 이용하여 날카로운 영상을 생성
f'(X) = Blur(부드러운 영상)
g(X) = f(X) - f'(X) ==> 날카로운 성분만 남음
h(X) = 2f(X) - f'(X) ==>원래 이미지에서 날카로운 부분이 강조됨
높이가 달라짐 == Contrast가 증가
즉, Edge 금방에서 Contrast를 높여주면 사람 눈에는 Sharp하게 보인다
# sharpening edge 강조 = Contrast(Histogram)
# g(x) = 2f(x) - f1(x)
src = cv2.imread('ch04\\images\\rose.bmp', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
sys.exit()
blr = cv2.GaussianBlur(src, (0, 0), 2)
# sbutract = 그래프에서 양수 부분만 보임(흰색부분만)
# 그래프 음수 부분도 보이게 할려면 addweigthed
subtract = cv2.subtract(src, blr)
# gamma = 결과를 눈에 잘 보이게 하기 위함 값
# 가중치 부여
# g(x) = 2f(x) - f1(x)
addweighted = cv2.addWeighted(src, 2, blr, -1, 1)
# *np.clip의 내부 연산은 float로 해줘야함(= 2.0)*
# np.clip = float64 / 변환필요
dst = np.clip(2.0*src - blr, 0, 255).astype(np.uint8)
###################################################################
# color 영상
src_ycrcb = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb)
# 밝기정보만 가우시안블러
src_f = src_ycrcb[:,:,0].astype(np.float32)
## *np.clip의 내부 연산은 float로 해줘야함(2.0)*
## np.clip = float32 / 변환필요
'''
float32 why?
중간 연산의 과정을 float로 해줘야하는 이유
GaussianBlur의 출력영상 type은 입력영상의 tpye과 같게 설정됨
if uint8의 경우 GaussianBlur의해서 나타나는 출력영상은 소수점 아래가 다짤림
== 정수형태로 변환되기 때문에 미세한 변화가 사라져
'''
blr = cv2.GaussianBlur(src_f, (0,0), 2.0) # 흰색??
# 최종적으로 화면에 보여지기 위한 영상을 만들때 다시 uint8로 변환
src_ycrcb[:,:,0] = np.clip(2.0*src_f - blr, 0, 255).astype(np.uint8)
dst = cv2.cvtColor(src_ycrcb, cv2.COLOR_YCrCb2BGR)
'OpenCV > OpenCV-Chapter' 카테고리의 다른 글
CH04 Opencv-Python Cartoon_filter (0) | 2021.12.18 |
---|---|
CH04 Opencv-Python 필터링(filtering) - Median, bilateral 잡읍제거 (0) | 2021.12.18 |
CH03 Opencv-Python Chroma_key (0) | 2021.12.18 |
CH03 Opencv-Python Histogram backprojection (0) | 2021.12.18 |
CH03 Opencv-Python Histogram, Contrast (0) | 2021.12.18 |
댓글