이번 포스팅은 Image Thresholding에 대해 알아보고
OpenCV에서 사용하는 threshold에 대해 알아보겠습니다.
포스팅 관련 내용은 아래에서 출처를 얻어 사용하였습니다.
https://docs.opencv.org/4.5.2/d7/d4d/tutorial_py_thresholding.html
Thresholding(스레시홀딩)
- thresholding은 binary image(바이너리 이미지)를 만드는 대표적인 방법입니다. 바이너리 이미지는 검은색과 흰색만으로 나타내는 이미지이며 스레시홀딩이란 임계점을 기준으로 검은색(0)과 흰색(255), 두 가지로 나누는 것을 의미합니다.
바이너리 이미지를 만드는 이유는
1) 배경과 객체 구분
2) 관심 영역과 비관심 영역의 구분
mask도 바이너리 이미지의 한 형태라고 볼 수 있습니다.
Global Thresholding (전역 스레시홀딩)
이 방법은 간단합니다. 모든 픽셀에 대해 동일한 임계값이 적용됩니다. 픽셀 값이 임계 값을 넘기면 흰색(255) 넘지 않으면 검은색(0)을 반환하여 전역 스레시홀딩이라고 부릅니다.
이 작업은 Numpy에서도 연산이 가능하지만, OpenCV에서 cv2.threshold() 함수로 구현이 가능합니다.
- cv2.threshold(src, thresh, maxval, type, dst = None)
입력 매개변수
- src : 입력 이미지
- thresh : 지정 임계 값
- maxval : 임계값 기준에 만족하는 픽셀에 적용할 값, cv2.THRESH_BINARY 또는 cv2.THRESH_BINARY_INV 방법 사용 시 최댓값, 보통은 255
- type: : 스레시홀딩의 적용 방법, 함수 동작 지정 또는 임계 값 결정 방법 지정.
- cv2.THRESH_BINARY : 임계값을 넘으면 maxval로 지정하고, 넘지 못하면 0(검은색)으로 지정
- cv2.THRESH_BINARY_INV : cv.THRESH_BINARY의 반대
- cv2.THRESH_TRUNC : 임계값을 넘으면 maxval로 지정하고, 넘지 못하면 원래 값 유지
- cv2.THRESH_TOZERO : 임계값을 넘으면 값 유지, 넘지 못하면 0(검은색)으로 지정
- cv2.THRESH_TOZERO_INV : cv2.THRESH_TOZERO의 반대
출력 매개변수
- retval : 사용된 임계값
- dst : 출력 영상, src와 동일 크기, 타입, 채널 수
코드)
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('./images/gradient.png', 0)
_, thresh1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
_, thresh2 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
_, thresh3 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
_, thresh4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
_, thresh5 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i+1)
plt.imshow(images[i], 'gray', vmin = 0, vmax = 255)
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
결과)
type에 파라미터에 따라 결과가 도출된 것을 볼 수 있습니다.
Adaptive Thresholding (적응형 스레시홀딩)
- 위에서 설명한 global Thresholding이 좋은 성능을 계속 내는 것은 아닙니다. 원본 이미지의 배경색이 여러 개인 경우에 하나의 임계값으로 좋은 바이너리 이미지를 얻는데 힘들 수 있습니다. 이때 이미지를 여러 영역으로 나누고, 주변 픽셀 값만 이용하여 임계값을 구하는 방법을 Adaptive Thresholding(적응형 스레시홀딩)이라고 합니다
- cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)
- src : 입력 영상. grayScale 이미지
- maxValue : 임계값을 만족하는 픽셀에 적용할 값
- adaptiveMethod : 블록 평균 계산 방법 지정.
- cv2.ADAPTIVE_THRESH_MEAN_C : 산술평균, 이웃 픽셀의 평균으로 결정
- cv2.ADAPTIVE_THRESH_GAUSSIAN_C : 가우시안 가중치 평균, 가우시안 분포에 따른 가중치의 합으로 결정
- thresholdType : cv2.THRESH_BINARY 또는 cv2.THRESH_BINARY_INV 지정
- blockSize : 영역으로 나눌 이웃 크기 (nxn), 3 이상의 홀수
- C : 계산된 임계값 결과에서 가감할 상수(음수 가능)
코드)
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./images/sudoku.png', 0)
img = cv2.medianBlur(img, 5)
_, thresh1 = cv2.threshold(img, 127 , 255, cv2.THRESH_BINARY)
thresh2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
thresh3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)
titles = ['Original Image', 'Global Threshoding ( v = 127 )', 'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, thresh1, thresh2, thresh3]
for i in range(4):
plt.subplot(2, 2, i+1)
plt.imshow(images[i], 'gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
-적응형 스레시홀딩의 알고리즘
1) 전체 이미지에 11개의 블록을 설정 -> 이미지를 11등분 한다고 생각
2) 블록별로 임계값을 결정(adaptive Mean 또는 Gaussian 방법에 따른 임계값 결정)
3) 정해진 임계값으로 블록별로 스레시홀딩
결과)
- 결과를 보시면 위에서 했던 Global Threshoding, 즉 전역 스레시홀딩의 문제점을 보여줍니다. 입력 이미지가 조명이 일정하지 않아 왼쪽이 그늘지고 어두워 발생하는 문제입니다. 하지만 적응형 스레시홀딩을 적용한 두 개의 바이너리 이미지는 선명하게 나오는 것을 볼 수 있습니다. 대부분의 이미지는 그림자나 조명 차이가 발생하므로 적응형 스레시홀딩을 쓰는 것이 좋은 결과를 얻을 수 있습니다.
- 적응형 스레시홀딩 결과 두 개의 차이를 보면 산술 평균(Adaptive Mean)을 이용한 것이 가우시안 평균(Adaptive Gaussian)을 이용한 것보다 더 선명하지만 잡티가 있습니다.
이렇게 threshold에 대해 알아보았고 OpenCV에서 사용하는 방법에 대해 정리했습니다.
다음 포스팅으로 찾아오겠습니다
틀린 점이나 질문이 있으시면 댓글로 남겨주세요!
감사합니다 :)
'영상처리' 카테고리의 다른 글
Python - OpenCV (11) : Grayscale 함수 비교 (2) | 2021.07.16 |
---|---|
Python - OpenCV (10) : Otsu's Binarization (0) | 2021.07.15 |
Python - OpenCV (8) : k-means Clustering (0) | 2021.07.13 |
Python - OpenCV (7) : Background Subtraction (0) | 2021.07.11 |
Python - OpenCV (6) : Image Inpainting (0) | 2021.07.09 |