πŸ‘©‍πŸ’» IoT (Embedded)/Image Processing

[v0.19]μ˜μƒμ²˜λ¦¬_ 필터와 μ»¨λ³Όλ£¨μ…˜ μ—°μ‚°, λΈ”λŸ¬λ§

μ§•μ§•μ•ŒνŒŒμΉ΄ 2022. 1. 8. 01:45
728x90
λ°˜μ‘ν˜•

220108 μž‘μ„±

<λ³Έ λΈ”λ‘œκ·ΈλŠ” 귀퉁이 μ„œμž¬λ‹˜μ˜ λΈ”λ‘œκ·Έλ₯Ό μ°Έκ³ ν•΄μ„œ κ³΅λΆ€ν•˜λ©° μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€>

https://bkshin.tistory.com/entry/OpenCV-17-%ED%95%84%ED%84%B0Filter%EC%99%80-%EC%BB%A8%EB%B3%BC%EB%A3%A8%EC%85%98Convolution-%EC%97%B0%EC%82%B0-%ED%8F%89%EA%B7%A0-%EB%B8%94%EB%9F%AC%EB%A7%81-%EA%B0%80%EC%9A%B0%EC%8B%9C%EC%95%88-%EB%B8%94%EB%9F%AC%EB%A7%81-%EB%AF%B8%EB%94%94%EC%96%B8-%EB%B8%94%EB%9F%AC%EB%A7%81-%EB%B0%94%EC%9D%B4%EB%A0%88%ED%84%B0%EB%9F%B4-%ED%95%84%ED%84%B0?category=1148027 

 

OpenCV - 17. ν•„ν„°(Filter)와 μ»¨λ³Όλ£¨μ…˜(Convolution) μ—°μ‚°, 평균 λΈ”λŸ¬λ§, κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§, λ―Έλ””μ–Έ λΈ”λŸ¬λ§,

이번 ν¬μŠ€νŒ…λΆ€ν„°λŠ” μ˜μƒ 필터에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. 이번 ν¬μŠ€νŒ… μ—­μ‹œ '파이썬으둜 λ§Œλ“œλŠ” OpenCV ν”„λ‘œμ νŠΈ(μ΄μ„Έμš° μ €)'λ₯Ό μ •λ¦¬ν•œ κ²ƒμž„μ„ λ°νž™λ‹ˆλ‹€. μ½”λ“œ: github.com/BaekKyunShin/OpenCV_Project_Python/tree

bkshin.tistory.com

 

 

 

 

 

1. ν•„ν„° (Filter)와 μ»¨λ³Όλ£¨μ…˜ (Convolution)

곡간 μ˜μ—­ 필터링(spacial domain filtering)

: μƒˆλ‘œμš΄ ν”½μ…€ 값을 얻을 λ•Œ ν•˜λ‚˜μ˜ ν”½μ…€ 값이 μ•„λ‹Œ κ·Έ μ£Όλ³€ ν”½μ…€λ“€μ˜ 값을 ν™œμš©

: μ—°μ‚° λŒ€μƒ ν”½μ…€κ³Ό κ·Έ μ£Όλ³€ 픽셀듀을 ν™œμš©ν•˜μ—¬ μƒˆλ‘œμš΄ ν”½μ…€ 값을 μ–»λŠ” 방법

 

λΈ”λŸ¬λ§(Blurring)

: 기쑴의 μ˜μƒμ„ νλ¦Ών•˜κ²Œ λ§Œλ“œλŠ” μž‘μ—…

 

컀널(kernel)

: μ£Όλ³€ 픽셀을 μ–΄λŠ λ²”μœ„κΉŒμ§€ ν™œμš©ν• μ§€ 그리고 연산은 μ–΄λ–»κ²Œ 할지λ₯Ό κ²°μ •

: μœˆλ„(window), ν•„ν„°(filter), 마슀크(mask)라고도 λΆ€λ₯Έλ‹€

3 x 3 μ»€λ„λ‘œ μ»¨λ³Όλ£¨μ…˜ μ—°μ‚°

μ»¨λ³Όλ£¨μ…˜ μ—°μ‚°

: μΌλŒ€μΌλ‘œ λŒ€μ‘ν•˜λŠ” μœ„μΉ˜μ— μžˆλŠ” μ»€λ„μ˜ μš”μ†Œμ™€ λŒ€μ‘ν•˜λŠ” μž…λ ₯ ν”½μ…€ 값을 κ³±ν•΄μ„œ λͺ¨λ‘ ν•©ν•œ 것을

κ²°κ³Ό ν”½μ…€ κ°’μœΌλ‘œ κ²°μ •

: 연산을 λ§ˆμ§€λ§‰ ν”½μ…€κΉŒμ§€ 반볡

좜처:&amp;amp;nbsp;https://www.slipp.net/wiki/pages/viewpage.action?pageId=26641520

dst = cv2.filter2D(src, ddepth, kernel, dst, anchor, delta, borderType)

: μ»¨λ³Όλ£¨μ…˜ μ—°μ‚°ν•˜κΈ°
- src : μž…λ ₯ μ˜μƒ, Numpy λ°°μ—΄
- ddepth: 좜λ ₯ μ˜μƒμ˜ dtype (-1: μž…λ ₯ μ˜μƒκ³Ό 동일)
- kernel: μ»¨λ³Όλ£¨μ…˜ 컀널, float32의 n x n 크기 λ°°μ—΄
- dst(optional): κ²°κ³Ό μ˜μƒ
- anchor(optional): μ»€λ„μ˜ 기쀀점, default: 쀑심점 (-1, -1)
- delta(optional): ν•„ν„°κ°€ 적용된 결과에 μΆ”κ°€ν•  κ°’borderType(optional): μ™Έκ³½ ν”½μ…€ 보정 방법 지정

 

 

 

 

 

2. 평균 λΈ”λŸ¬λ§ (Average Blurring)

λΈ”λŸ¬λ§μ€ 초점이 λ§žμ§€ μ•Šλ“―μ΄ μ˜μƒμ„ νλ¦Ών•˜κ²Œ ν•˜λŠ” μž‘μ—…

: 평균 λΈ”λŸ¬λ§μ€ μ£Όλ³€ ν”½μ…€ κ°’λ“€μ˜ 평균을 적용

: μ£Όλ³€ ν”½μ…€λ“€μ˜ 평균값을 μ μš©ν•˜λ©΄ ν”½μ…€ κ°„ 차이가 적어져 μ„ λͺ…도가 λ–¨μ–΄μ Έ μ „μ²΄μ μœΌλ‘œ νλ¦Ών•˜λ‹€

# 평균 ν•„ν„°λ₯Ό μƒμƒν•˜μ—¬ λΈ”λŸ¬ 적용
import cv2
import numpy as np

img = cv2.imread('img/night.jpg')
'''
#5x5 평균 ν•„ν„° 컀널 생성    ---β‘ 
kernel = np.array([[0.04, 0.04, 0.04, 0.04, 0.04],
                   [0.04, 0.04, 0.04, 0.04, 0.04],
                   [0.04, 0.04, 0.04, 0.04, 0.04],
                   [0.04, 0.04, 0.04, 0.04, 0.04],
                   [0.04, 0.04, 0.04, 0.04, 0.04]])
'''
# 5x5 평균 ν•„ν„° 컀널 생성  ---β‘‘
kernel = np.ones((5,5))/5**2
# ν•„ν„° 적용             ---β‘’
blured = cv2.filter2D(img, -1, kernel)

# κ²°κ³Ό 좜λ ₯
cv2.imshow('origin', img)
cv2.imshow('avrg blur', blured) 
cv2.waitKey()
cv2.destroyAllWindows()

&amp;amp;nbsp;5 x 5 평균 λΈ”λŸ¬λ§ ν•„ν„°λ₯Ό ν™œμš©ν•˜μ—¬ μ»¨λ³Όλ£¨μ…˜ μ—°μ‚°

# 5x5 평균 λΈ”λŸ¬λ§ ν•„ν„° 컀널 생성

kernel = np.ones((5,5))/5**2

- 5 x 5 ν•„ν„°μ˜ μš”μ†Œ 개수인 5**2(=25)둜 λ‚˜λˆ„κΈ°

 

좜처:&amp;amp;nbsp;https://hsg2510.tistory.com/112

- ν•„ν„°μ˜ 크기가 클수둝 평균 λΈ”λŸ¬λ§μ„ μ μš©ν–ˆμ„ λ•Œ μ„ λͺ…도가 더 떨어진닀

 

 

 

 

+) opencv 의 평균 λΈ”λŸ¬λ§ ν•¨μˆ˜

dst = cv2.blur(src, ksize, dst, anchor, borderType)

: μ»€λ„μ˜ 크기만 μ •ν•΄μ£Όλ©΄ μ•Œμ•„μ„œ 평균 컀널을 μƒμ„±ν•΄μ„œ 평균 λΈ”λŸ¬λ§μ„ μ μš©ν•œ μ˜μƒμ„ 좜λ ₯

: 컀널 ν¬κΈ°λŠ” 일반적으둜 ν™€μˆ˜


- src : μž…λ ₯ μ˜μƒ, numpy λ°°μ—΄
- ksize : μ»€λ„μ˜ 크기
- λ‚˜λ¨Έμ§€ νŒŒλΌλ―Έν„°λŠ” cv2.filter2D()와 동일

 

dst = cv2.boxFilter(src, ddepth, ksize, dst, anchor, normalize, borderType)

: normalize에 Trueλ₯Ό μ „λ‹¬ν•˜λ©΄ cv2.blur() ν•¨μˆ˜μ™€ λ™μΌν•œ κΈ°λŠ₯

 

- ddepth : μΆœλ ₯ μ˜μƒμ˜ dtype (-1: μž…λ ₯ μ˜μƒκ³Ό 동일)

- normalize(optional) : 컀널 크기둜 μ •κ·œν™”(1/ksize²) 지정 μ—¬λΆ€ (Boolean), default=True

- λ‚˜λ¨Έμ§€ νŒŒλΌλ―Έν„°λŠ” cv2.filter2D()와 동일

# λΈ”λŸ¬ μ „μš© ν•¨μˆ˜λ‘œ λΈ”λŸ¬λ§ 적용
import cv2
import numpy as np

file_name = 'img/taekwonv1.jpg'
img = cv2.imread(file_name)

# blur() ν•¨μˆ˜λ‘œ λΈ”λŸ¬λ§  ---β‘ 
blur1 = cv2.blur(img, (10,10))
# boxFilter() ν•¨μˆ˜λ‘œ λΈ”λŸ¬λ§ 적용 ---β‘‘
blur2 = cv2.boxFilter(img, -1, (10,10))

# κ²°κ³Ό 좜λ ₯
merged = np.hstack( (img, blur1, blur2))
cv2.imshow('blur', merged)
cv2.waitKey(0)
cv2.destroyAllWindows()

&amp;amp;nbsp;cv2.blur() ν•¨μˆ˜μ™€ cv2.boxFilter() ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ 평균 λΈ”λŸ¬λ§

- 두 ν•¨μˆ˜ λ™μΌν•œ 이미지 좜λ ₯ !

 

 

 

 

 

3. κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§(Gaussian Blurring)

: κ°€μš°μ‹œμ•ˆ 뢄포λ₯Ό κ°–λŠ” μ»€λ„λ‘œ λΈ”λŸ¬λ§ ν•˜λŠ” 것

: κ°€μš°μ‹œμ•ˆ 뢄포(gaussian distribution)λž€ μ •κ·œ 뢄포(normal distribution)이라고도 함

: 평균 κ·Όμ²˜μ— λͺ°λ € μžˆλŠ” κ°’λ“€μ˜ κ°œμˆ˜κ°€ 많고 ν‰κ· μ—μ„œ λ©€μ–΄μ§ˆμˆ˜λ‘ κ·Έ κ°œμˆ˜κ°€ μ μ–΄μ§€λŠ” 뢄포

: κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§ 컀널은 쀑앙값이 κ°€μž₯ 크고 μ€‘μ•™μ—μ„œ λ©€μ–΄μ§ˆμˆ˜λ‘ κ·Έ 값이 μž‘μ•„μ§„λ‹€

좜처:&amp;amp;nbsp;https://hsg2510.tistory.com/112

 

- κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§ 컀널을 μ μš©ν•˜λ©΄ λŒ€μƒ 픽셀에 κ°€κΉŒμšΈμˆ˜λ‘ λ§Žμ€ 영ν–₯을 μ£Όκ³ ,

λ©€μ–΄μ§ˆμˆ˜λ‘ 적은 영ν–₯을 μ£ΌκΈ° λ•Œλ¬Έμ—

μ›λž˜μ˜ μ˜μƒκ³Ό λΉ„μŠ·ν•˜λ©΄μ„œλ„ λ…Έμ΄μ¦ˆλ₯Ό μ œκ±°ν•˜λŠ” 효과

 

cv2.GaussianBlur(src, ksize, sigmaX, sigmaY, borderType)

: 컀널 크기와 ν‘œμ€€ 편차λ₯Ό μ „λ‹¬ν•˜λ©΄ κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§μ„ 적용


- src : μž…λ ₯ μ˜μƒ
- ksize : 컀널 크기 (주둜 ν™€μˆ˜)
- sigmaX : X λ°©ν–₯ ν‘œμ€€νŽΈμ°¨ (0: auto)      -> μžλ™μœΌλ‘œ ν‘œμ€€νŽΈμ°¨λ₯Ό μ„ νƒν•΄μ„œ μ‚¬μš©
- sigmaY(optional) : Y λ°©ν–₯ ν‘œμ€€νŽΈμ°¨ (default: sigmaX)     -> μƒλž΅ν•˜λ©΄ sigmaX κ°’κ³Ό 동일
- borderType(optional) : μ™Έκ³½ ν…Œλ‘λ¦¬ 보정 방식

 

ret = cv2.getGaussianKernel(ksize, sigma, ktype)

: 컀널 크기와 ν‘œμ€€ 편차λ₯Ό μ „λ‹¬ν•˜λ©΄ κ°€μš°μ‹œμ•ˆ ν•„ν„°λ₯Ό λ°˜ν™˜

: λ°˜ν™˜λœ ν•„ν„°λŠ” 1μ°¨μ›μ΄λ―€λ‘œ cv2.filter2D() ν•¨μˆ˜μ— μ‚¬μš©ν•˜λ €λ©΄ ret * ret.T와 같은 ν˜•μ‹μœΌλ‘œ 전달

 

- ret : κ°€μš°μ‹œμ•ˆ 컀널 (1μ°¨μ›μ΄λ―€λ‘œ ret * ret.T ν˜•νƒœλ‘œ μ‚¬μš©ν•΄μ•Ό 함)

# κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§
import cv2
import numpy as np

img = cv2.imread('img/gaussian_noise.jpg')

# κ°€μš°μ‹œμ•ˆ 컀널을 직접 μƒμ„±ν•΄μ„œ λΈ”λŸ¬λ§  ---β‘ 
k1 = np.array([[1, 2, 1],
                   [2, 4, 2],
                   [1, 2, 1]]) *(1/16)
blur1 = cv2.filter2D(img, -1, k1)

# κ°€μš°μ‹œμ•ˆ 컀널을 API둜 μ–»μ–΄μ„œ λΈ”λŸ¬λ§ ---β‘‘
k2 = cv2.getGaussianKernel(3, 0)
blur2 = cv2.filter2D(img, -1, k2*k2.T)

# κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬ API둜 λΈ”λŸ¬λ§ ---β‘’
blur3 = cv2.GaussianBlur(img, (3, 3), 0)

# κ²°κ³Ό 좜λ ₯
print('k1:', k1)
print('k2:', k2*k2.T)
merged = np.hstack((img, blur1, blur2, blur3))
cv2.imshow('gaussian blur', merged)
cv2.waitKey(0)
cv2.destroyAllWindows()

μ„Έ 가지 λ°©λ²•μ˜ κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§

- 첫 λ²ˆμ§ΈλŠ” κ°€μš°μ‹œμ•ˆ ν•„ν„°λ₯Ό 직접 μƒμ„±ν•΄μ„œ cv2.filter2D() ν•¨μˆ˜μ— μ „λ‹¬ν•˜μ—¬ λΈ”λŸ¬λ§

- 두 λ²ˆμ§ΈλŠ” cv2.getGaussianKernel() ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄ κ°€μš°μ‹œμ•ˆ 컀널 μ–»κ³  cv2.filter2D() ν•¨μˆ˜μ— μ „λ‹¬ν•˜μ—¬ λΈ”λŸ¬λ§

- μ„Έ λ²ˆμ§ΈλŠ” cv2.GaussianBlur() ν•¨μˆ˜λ₯Ό ν™œμš©ν•˜μ—¬ ν•„ν„°λ₯Ό λ³„λ„λ‘œ κ΅¬ν•˜μ§€ μ•Šκ³  직접 κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§μ„ 적용

- κ°€μš°μ‹œμ•ˆ λΈ”λŸ¬λ§μ€ λ…Έμ΄μ¦ˆλ₯Ό μ œκ±°ν•˜λŠ” 효과

 

 

 

 

 

4. λ―Έλ””μ–Έ λΈ”λŸ¬λ§(Median Blurring)

: μ»€λ„μ˜ ν”½μ…€ κ°’ 쀑 쀑앙값을 μ„ νƒν•˜λŠ” 것

: λ―Έλ””μ–Έ λΈ”λŸ¬λ§μ€ μ†ŒκΈˆ-ν›„μΆ” μž‘μŒμ„ μ œκ±°ν•˜λŠ” 효과

: μ†ŒκΈˆ-ν›„μΆ” μž‘μŒμ΄λž€ 이미지에 μ†ŒκΈˆκ³Ό ν›„μΆ”λ₯Ό 뿌린 것과 같이 생긴 작음

 

dst = cv2.medianBlur(src, ksize)
src: μž…λ ₯ μ˜μƒ
ksize: 컀널 크기

# λ―Έλ””μ–Έ λΈ”λŸ¬λ§
import cv2
import numpy as np

img = cv2.imread("img/salt_pepper_noise.jpg")

# λ―Έλ””μ–Έ λΈ”λŸ¬ 적용 --- β‘ 
blur = cv2.medianBlur(img, 5)

# κ²°κ³Ό 좜λ ₯ 
merged = np.hstack((img,blur))
cv2.imshow('media', merged)
cv2.waitKey(0)
cv2.destroyAllWindows()

&amp;amp;nbsp;λ―Έλ””μ–Έ λΈ”λŸ¬λ§μ„ μ μš©ν•˜λ‹ˆ μ†ŒκΈˆ-ν›„μΆ” 작음이 제거

 

 

 

 

 

5. λ°”μ΄λ ˆν„°λŸ΄ ν•„ν„°(Bilateral Filter)

: 경계λ₯Ό νλ¦Ών•˜κ²Œ ν•˜λŠ” 문제λ₯Ό 보완

: κ°€μš°μ‹œμ•ˆ 필터와 경계 ν•„ν„°λ₯Ό κ²°ν•©

: 경계도 λšœλ ·ν•˜κ³  λ…Έμ΄μ¦ˆλ„ μ œκ±°λ˜λŠ” νš¨κ³Όκ°€ μžˆμ§€λ§Œ 속도가 λŠλ¦¬λ‹€

 

dst = cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace, dst, borderType)
- src : μž…λ ₯ μ˜μƒ
- d : ν•„ν„°μ˜ 직경(diameter), 5보닀 크면 맀우 느림
- sigmaColor : μƒ‰κ³΅κ°„μ˜ μ‹œκ·Έλ§ˆ κ°’
- sigmaSpace : μ’Œν‘œ κ³΅κ°„μ˜ μ‹œκ·Έλ§ˆ κ°’

# λ°”μ΄λ ˆν„°λŸ΄ 필터와 κ°€μš°μ‹œμ•ˆ ν•„ν„° 비ꡐ
import cv2
import numpy as np

img = cv2.imread("img/gaussian_noise.jpg")

# κ°€μš°μ‹œμ•ˆ ν•„ν„° 적용 ---β‘ 
blur1 = cv2.GaussianBlur(img, (5,5), 0)

# λ°”μ΄λ ˆν„°λŸ΄ ν•„ν„° 적용 ---β‘‘
blur2 = cv2.bilateralFilter(img, 5, 75, 75)

# κ²°κ³Ό 좜λ ₯
merged = np.hstack((img, blur1, blur2))
cv2.imshow('bilateral', merged)
cv2.waitKey(0)
cv2.destroyAllWindows()

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;κ°€μš°μ‹œμ•ˆ ν•„ν„°&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; λ°”μ΄λ ˆν„°λŸ΄ ν•„ν„°

 

- κ°€μš°μ‹œμ•ˆμ€ 경계값 흐릿, 더 흐리멍텅..

- λ°”μ΄λ ˆν„°λŸ΄μ€ λ…Έμ΄μ¦ˆ μ€„λ©΄μ„œ 경계값 μœ μ§€, κ°€μš°μ‹œμ•ˆλ³΄λ‹¨ μ„ λͺ… 뚜렷

 

 

 

 

728x90
λ°˜μ‘ν˜•