😎 κ³΅λΆ€ν•˜λŠ” μ§•μ§•μ•ŒνŒŒμΉ΄λŠ” μ²˜μŒμ΄μ§€?

[v0.10]μ˜μƒμ²˜λ¦¬_OpenCV_λ°”μ΄λ„ˆλ¦¬ 이미지λ₯Ό λ§Œλ“œλŠ” μŠ€λ ˆμ‹œν™€λ”© λ³Έλ¬Έ

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

[v0.10]μ˜μƒμ²˜λ¦¬_OpenCV_λ°”μ΄λ„ˆλ¦¬ 이미지λ₯Ό λ§Œλ“œλŠ” μŠ€λ ˆμ‹œν™€λ”©

μ§•μ§•μ•ŒνŒŒμΉ΄ 2021. 12. 30. 17:09
728x90
λ°˜μ‘ν˜•

211230 μž‘μ„±

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

https://bkshin.tistory.com/entry/OpenCV-8-%EC%8A%A4%EB%A0%88%EC%8B%9C%ED%99%80%EB%94%A9Thresholding?category=1148027 

 

OpenCV - 8. μŠ€λ ˆμ‹œν™€λ”©(Thresholding), 였츠의 μ•Œκ³ λ¦¬μ¦˜(Otsu's Method)

이번 ν¬μŠ€νŒ…μ—μ„œλŠ” λ°”μ΄λ„ˆλ¦¬ 이미지λ₯Ό λ§Œλ“œλŠ” λŒ€ν‘œμ μΈ 방법인 μŠ€λ ˆμ‹œν™€λ”©μ— λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. 이번 ν¬μŠ€νŒ… μ—­μ‹œ '파이썬으둜 λ§Œλ“œλŠ” OpenCV ν”„λ‘œμ νŠΈ(μ΄μ„Έμš° μ €)'λ₯Ό μ •λ¦¬ν•œ κ²ƒμž„μ„ λ°νž™λ‹ˆλ‹€

bkshin.tistory.com

 

 

 

 

1. μŠ€λ ˆμ‹œν™€λ”© (Thresholding)

: μ—¬λŸ¬ 값을 μ–΄λ–€ μž„κ³„μ μ„ κΈ°μ€€μœΌλ‘œ 두 가지 λΆ€λ₯˜λ‘œ λ‚˜λˆ„λŠ” 방법

: λ°”μ΄λ„ˆλ¦¬ 이미지( 검은색과 ν°μƒ‰λ§ŒμœΌλ‘œ ν‘œν˜„ν•œ 이미지) λ₯Ό λ§Œλ“œλŠ” κ°€μž₯ λŒ€ν‘œμ μΈ 방법

 

 

1) μ „μ—­ μŠ€λ ˆμ‹œν™€λ”©

: μ—¬λŸ¬ μž„κ³„κ°’μ„ μ •ν•œ λ’€ ν”½μ…€ 값이 μž„κ³„κ°’μ„ λ„˜μœΌλ©΄ 255, μž„κ³„κ°’μ„ λ„˜μ§€ μ•ŠμœΌλ©΄ 0 으둜 μ§€μ •ν•˜λŠ” 방식

- numpy

numpy 연산을 톡해 ν”½μ…€ 값이 127보닀 크면 255, ν”½μ…€ 값이 127보닀 μž‘κ±°λ‚˜ κ°™μœΌλ©΄ 0으둜 λ°”κΎΈκΈ°

- ret, out = cv2.threshold(img, threshold, value, type_flag)

ret : μŠ€λ ˆμ‹œν™€λ”©μ— μ‚¬μš©ν•œ μž„κ³„κ°’

out : μŠ€λ ˆμ‹œν™€λ”©μ΄ 적용된 λ°”μ΄λ„ˆλ¦¬ 이미지

 

img : λ³€ν™˜ν•  이미지
threshold : μŠ€λ ˆμ‹œν™€λ”© μž„κ³„κ°’
value : μž„κ³„κ°’ 기쀀에 λ§Œμ‘±ν•˜λŠ” 픽셀에 μ μš©ν•  κ°’
type_flag : μŠ€λ ˆμ‹œν™€λ”© 적용 방법

- cv2.THRESH_BINARY: ν”½μ…€ 값이 μž„κ³„κ°’μ„ λ„˜μœΌλ©΄ value둜 μ§€μ •ν•˜κ³ , λ„˜μ§€ λͺ»ν•˜λ©΄ 0으둜 지정

- cv2.THRESH_BINARY_INV : cv.THRESH_BINARY의 λ°˜λŒ€

- cv2.THRESH_TRUNC : ν”½μ…€ 값이 μž„κ³„κ°’μ„ λ„˜μœΌλ©΄ value둜 μ§€μ •ν•˜κ³ , λ„˜μ§€ λͺ»ν•˜λ©΄ μ›λž˜ κ°’ μœ μ§€

- cv2.THRESH_TOZERO : ν”½μ…€ 값이 μž„κ³„κ°’μ„ λ„˜μœΌλ©΄ μ›λž˜ κ°’ μœ μ§€, λ„˜μ§€ λͺ»ν•˜λ©΄ 0으둜 지정

- cv2.THRESH_TOZERO_INV : cv2.THRESH_TOZERO의 λ°˜λŒ€

# μ „μ—­ μŠ€λ ˆμ‹œν™€λ”©

import cv2
import numpy as np
import matplotlib.pylab as plt

#이미지λ₯Ό 그레이 μŠ€μΌ€μΌλ‘œ 읽기
img = cv2.imread('img/gray_gradient.png', cv2.IMREAD_GRAYSCALE)

# --- β‘  NumPy API둜 λ°”μ΄λ„ˆλ¦¬ 이미지 λ§Œλ“€κΈ°
thresh_np = np.zeros_like(img)   # 원본과 λ™μΌν•œ 크기의 0으둜 μ±„μ›Œμ§„ 이미지
thresh_np[ img > 127] = 255      # 127 보닀 큰 κ°’λ§Œ 255둜 λ³€κ²½

# ---β‘‘ OpenCV API둜 λ°”μ΄λ„ˆλ¦¬ 이미지 λ§Œλ“€κΈ°
ret, thresh_cv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY) 
print(ret)  # 127.0, λ°”μ΄λ„ˆλ¦¬ 이미지에 μ‚¬μš©λœ λ¬Έν„± κ°’ λ°˜ν™˜

# ---β‘’ 원본과 결과물을 matplotlib으둜 좜λ ₯
imgs = {'Original': img, 'NumPy API':thresh_np, 'cv2.threshold': thresh_cv}
for i , (key, value) in enumerate(imgs.items()):
    plt.subplot(1, 3, i+1)
    plt.title(key)
    plt.imshow(value, cmap='gray')
    plt.xticks([]); plt.yticks([])

plt.show()

numpy 와 cv2.threshold 둜 μž„κ³„κ°’ κ΅¬ν˜„

 

 

+) type_flag ν™œμš©

# μŠ€λ ˆμ‹œν™€λ”© ν”Œλž˜κ·Έ
import cv2
import numpy as np
import matplotlib.pylab as plt

img = cv2.imread('img/gray_gradient.png', cv2.IMREAD_GRAYSCALE)

_, t_bin = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
_, t_bininv = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
_, t_truc = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
_, t_2zr = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
_, t_2zrinv = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)

imgs = {'origin':img, 'BINARY':t_bin, 'BINARY_INV':t_bininv, \
        'TRUNC':t_truc, 'TOZERO':t_2zr, 'TOZERO_INV':t_2zrinv}
for i, (key, value) in enumerate(imgs.items()):
    plt.subplot(2,3, i+1)
    plt.title(key)
    plt.imshow(value, cmap='gray')
    plt.xticks([]);    plt.yticks([])
    
plt.show()

type_flag νŒŒλΌλ―Έν„°μ— 따라 κ²°κ³Ό λ„μΆœ

binary : 127 λ„˜μœΌλ©΄ 1, λ„˜μ§€ λͺ»ν•˜λ©΄ 0

binary_inv : μœ„μ™€ λ°˜λŒ€

trunc : 127 λ„˜μœΌλ©΄ 1, λ„˜μ§€ λͺ»ν•˜λ©΄ 원본

tozero : 127 λ„˜μœΌλ©΄ μ›λž˜κ°’, λ„˜μ§€ λͺ»ν•˜λ©΄ 0

tozero_inv : μœ„μ™€ λ°˜λŒ€

 

 

 

 

+) 였츠의 이진화 μ•Œκ³ λ¦¬μ¦˜

: 였츠의 μ•Œκ³ λ¦¬μ¦˜μ€ μž„κ³„κ°’μ„ μž„μ˜λ‘œ μ •ν•΄ 픽셀을 두 λΆ€λ₯˜λ‘œ λ‚˜λˆ„κ³  두 λΆ€λ₯˜μ˜ λͺ…μ•” 뢄포λ₯Ό κ΅¬ν•˜λŠ” μž‘μ—…μ„ 반볡

: λͺ¨λ“  경우의 수 μ€‘μ—μ„œ 두 λΆ€λ₯˜μ˜ λͺ…μ•” 뢄포가 κ°€μž₯ 균일할 λ•Œμ˜ μž„κ³„κ°’μ„ 선택

: 반볡적인 μ‹œλ„ 없이 ν•œ λ²ˆμ— μž„κ³„κ°’μ„ 찾을 수 μžˆλŠ” 방법

 

cv2.threshold() ν•¨μˆ˜μ˜ λ§ˆμ§€λ§‰ νŒŒλΌλ―Έν„° cv2.THRESH_OTSU

# 였츠의 μ•Œκ³ λ¦¬μ¦˜μ„ μ μš©ν•œ μŠ€λ ˆμ‹œν™€λ”©
import cv2
import numpy as np
import matplotlib.pylab as plt

# 이미지λ₯Ό 그레이 μŠ€μΌ€μΌλ‘œ 읽기
img = cv2.imread('img/newspaper.jpg', cv2.IMREAD_GRAYSCALE) 
# 경계 값을 130으둜 지정  ---β‘ 
_, t_130 = cv2.threshold(img, 130, 255, cv2.THRESH_BINARY)        
# 경계 값을 μ§€μ •ν•˜μ§€ μ•Šκ³  OTSU μ•Œκ³ λ¦¬μ¦˜ 선택 ---β‘‘
t, t_otsu = cv2.threshold(img, -1, 255,  cv2.THRESH_BINARY | cv2.THRESH_OTSU) 
print('otsu threshold:', t)                 # Otsu μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ μ„ νƒλœ 경계 κ°’ 좜λ ₯

imgs = {'Original': img, 't:130':t_130, 'otsu:%d'%t: t_otsu}
for i , (key, value) in enumerate(imgs.items()):
    plt.subplot(1, 3, i+1)
    plt.title(key)
    plt.imshow(value, cmap='gray')
    plt.xticks([]); plt.yticks([])

plt.show()

μž„κ³„κ°’ μ„€μ • λ°”μ΄λ„ˆλ¦¬μ΄λ―Έμ§€, 였츠의 μ•Œκ³ λ¦¬μ¦˜ λ°”μ΄λ„ˆλ¦¬ 이미지

=> λ°”μ΄λ„ˆλ¦¬ 이미지 λ³€ν™˜ ν›„ 글씨 μ„ λͺ…

=> λ°”μ΄λ„ˆλ¦¬ 졜적의 μž„κ³„κ°’μ€ 130 μ„€μ •

=> 였츠의 μ•Œκ³ λ¦¬μ¦˜ 졜적의 μž„κ³„κ°’μ€ 114

=> 였츠의 μ•Œκ³ λ¦¬μ¦˜μ΄ 졜적의 μž„κ³„κ°’μ„ μžλ™μœΌλ‘œ μ°Ύμ•„μ€€λ‹€

=> λͺ¨λ“  경우의 μˆ˜μ— λŒ€ν•΄ 쑰사해야 ν•˜λ―€λ‘œ 속도가 λΉ λ₯΄μ§€ μ•Šλ‹€

 

 

 

 

2) μ μ‘ν˜• μŠ€λ ˆμ‹œν™€λ”© (Adaptive Thresholding)

: 이미지λ₯Ό μ—¬λŸ¬ μ˜μ—­μœΌλ‘œ λ‚˜λˆˆ λ’€, κ·Έ μ£Όλ³€ ν”½μ…€ κ°’λ§Œ ν™œμš©ν•˜μ—¬ μž„κ³„κ°’μ„ ꡬ함

 

cv2.adaptiveThreshold(img, value, method, type_flag, block_size, C)
img : μž…λ ₯μ˜μƒ
value : μž„κ³„κ°’μ„ λ§Œμ‘±ν•˜λŠ” 픽셀에 μ μš©ν•  κ°’
method : μž„κ³„κ°’ κ²°μ • 방법

- cv2.ADAPTIVE_THRESH_MEAN_C : 이웃 ν”½μ…€μ˜ ν‰κ· μœΌλ‘œ κ²°μ •
- cv2.ADAPTIVE_THRESH_GAUSSIAN_C : κ°€μš°μ‹œμ•ˆ 뢄포에 λ”°λ₯Έ κ°€μ€‘μΉ˜μ˜ ν•©μœΌλ‘œ κ²°μ •
type_flag : μŠ€λ ˆμ‹œν™€λ”© 적용 방법 (cv2.threshod()와 동일)
block_size : μ˜μ—­μœΌλ‘œ λ‚˜λˆŒ μ΄μ›ƒμ˜ 크기(n x n), ν™€μˆ˜
C : κ³„μ‚°λœ μž„κ³„κ°’ κ²°κ³Όμ—μ„œ 가감할 μƒμˆ˜(음수 κ°€λŠ₯)

# μ μ‘ν˜• μŠ€λ ˆμ‹œν™€λ”© 적용
import cv2
import numpy as np 
import matplotlib.pyplot as plt 

blk_size = 9        # λΈ”λŸ­ μ‚¬μ΄μ¦ˆ
C = 5               # 차감 μƒμˆ˜ 
img = cv2.imread('img/sudoku.jpg', cv2.IMREAD_GRAYSCALE) # 그레이 μŠ€μΌ€μΌλ‘œ  읽기

# ---β‘  였츠의 μ•Œκ³ λ¦¬μ¦˜μœΌλ‘œ 단일 경계 값을 전체 이미지에 적용
ret, th1 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# ---β‘‘ μ–΄λŽν‹°λ“œ μ“°λ ˆμ‹œν™€λ“œλ₯Ό 평균 적용
th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,\
                                      cv2.THRESH_BINARY, blk_size, C)
# κ°€μš°μ‹œμ•ˆ 뢄포 적용
th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \
                                     cv2.THRESH_BINARY, blk_size, C)

# ---β‘’ κ²°κ³Όλ₯Ό Matplot으둜 좜λ ₯
imgs = {'Original': img, 'Global-Otsu:%d'%ret:th1, \
        'Adapted-Mean':th2, 'Adapted-Gaussian': th3}
for i, (k, v) in enumerate(imgs.items()):
    plt.subplot(2,2,i+1)
    plt.title(k)
    plt.imshow(v,'gray')
    plt.xticks([]),plt.yticks([])

plt.show()

μ „μ—­ μŠ€λ ˆμ‹œν™€λ”©μ„ μ μš©ν•œ λ°”μ΄λ„ˆλ¦¬ 이미지

=> μ „μ—­μ˜ μ˜€μΈ λŠ” 더 κ·ΈλŠ˜μ§€κ³  μ–΄λ‘μ›Œμ§€λŠ” ν˜„μƒ 발견

=> μ μ‘μ˜ 평균은 κ°€μš°μ‹œμ•ˆλ³΄λ‹€ 더 μ„ λͺ…, μž‘ν‹° 더 발견

=> μ μ‘μ˜ κ°€μš°μ‹œμ•ˆμ€ μ„ λͺ…도 떨어짐, μž‘ν‹° 덜 발견

 

==> μ μ‘ν˜• μŠ€λ ˆμ‹œν™€λ”© μ•Œκ³ λ¦¬μ¦˜

- 전체 이미지에 총 9개의 블둝을 μ„€μ • (이미지 9λ“±λΆ„)

- 각 λΈ”λ‘λ³„λ‘œ μž„κ³„κ°’μ„ μ •ν•©λ‹ˆλ‹€.

- cv2.ADAPTIVE_THRESH_MEAN_Cλ₯Ό νŒŒλΌλ―Έν„°λ‘œ μ „λ‹¬ν•˜λ©΄ 각 λΈ”λ‘μ˜ 이웃 ν”½μ…€μ˜ ν‰κ· μœΌλ‘œ μž„κ³„κ°’

- cv2.ADAPTIVE_THRESH_GAUSSIAN_Cλ₯Ό νŒŒλΌλ―Έν„°λ‘œ μ „λ‹¬ν•˜λ©΄ κ°€μš°μ‹œμ•ˆ 뢄포에 λ”°λ₯Έ κ°€μ€‘μΉ˜μ˜ ν•©μœΌλ‘œ μž„κ³„κ°’

- 정해진 μž„κ³„κ°’μ„ λ°”νƒ•μœΌλ‘œ 각 λΈ”λ‘λ³„λ‘œ μŠ€λ ˆμ‹œν™€λ”©

- μ „μ—­ μŠ€λ ˆμ‹œν™€λ”©μ„ μ μš©ν•œ 것보닀 더 μ„ λͺ…ν•˜κ³  λΆ€λ“œλŸ¬μš΄ κ²°κ³Ό

 

==> λŒ€λΆ€λΆ„μ˜ μ΄λ―Έμ§€λŠ” κ·Έλ¦Όμžκ°€ μžˆκ±°λ‚˜ μ‘°λͺ… 차이가 μžˆκΈ°μ—

μ „μ—­ μŠ€λ ˆμ‹œν™€λ”©λ³΄λ‹€ μ μ‘ν˜• μŠ€λ ˆμ‹œν™€λ”©μ„ 더 많이 μ‚¬μš©

 

 

 

 

ν›„... 끝

728x90
λ°˜μ‘ν˜•
Comments