영상의 이진화(Binarization)란?
• 영상의 픽셀 값을 0 또는 255(1)로 만드는 연산
▪ 배경(background) vs. 객체(object)
▪ 관심 영역 vs. 비관심 영역
* GrayScale 영상의 이진화
# 검은 객체 찾기
'''
OpenCV에선 '흰색이 객체다' 라고 정의
'''
src = cv2.imread('ch07\\images\\cells.png', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
sys.exit()
def onChange(pos):
# cv2.THRESH_BINARY_INV = 검은 객체 흰색으로
ret, dst = cv2.threshold(src, pos, 255, cv2.THRESH_BINARY)
print(ret) # Threshold 값
cv2.imshow('thresh', dst)
cv2.imshow('src', src)
cv2.namedWindow('thresh')
cv2.createTrackbar('threshold', 'thresh', 0, 255, onChange)
cv2.setTrackbarPos('threshold', 'thresh', 50)
BINARY | BINARY_INV |
T보다 크면 255 | T보다 크면 0 |
T보다 작으면 0 | T보다 작으면 255 |
일반적으로 영상을 이진화할 때 이진화 값(Threshold)은 어떻게 정할까?
- 영상이 조명이나 환경에 따라서 픽셀값이 아주 미세하게 다르기 때문에
영상에서 사용할 수 있는 임계값(Threshold)이 조금씩 다르다.
자동 이진화 - Otsu 방법
▪ 임계값 자동 결정 방법
• 영상의 히스토그램이 bimodal이고, 전경&배경 픽셀 분포가 비슷하다면?
• 히스토그램이 bimodal이지만, 전경&배경 픽셀 분포가 크게 다르다면?
Otsu 이진화 방법
• 입력 영상이 배경(background)과 객체(object) 두 개로 구성되어 있다고 가정 → Bimodal histogram
• 임의의 임계값 T 에 의해 나눠지는 두 픽셀 분포 그룹의 분산이 최소가 되는 T 를 선택
• 일종의 최적화 알고리즘(optimization algorithm)
1. 평균 구하고
2. 분산(시그마²)
평균을 두번 구해야해서 느리다(= within-class variance 최소화)
그래서 Between-class variance 최대화 사용== 점화식으로 구현되있어서 빠르다.
# Otsu = Threshold 값을 자동으로 잡아줌
src = cv2.imread('ch07\\images\\rice.png', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
sys.exit()
# cv2.THRESH_BINARY는 생략가능 (default)
th, dst = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print("otsu's threshold:", th) # 131(실수형태로 나옴)
잘 안나오는 부분이 있음 = 지역이진화로 처리 가능
원래 이미지에서 약간 어두운 부분은 잘 안나옴(= 이미지 불균일)
Threshold를 낮추면 잘 보이는데 원래 보이는 것들이 잘안보임
지역 이진화
- 어둡다가 밝아지는 이미지? ==> 지역 이진화
- 전체 영상을 n등분하여 각각의 구역마다 따로 이진화하고 이어 붙임(= Window)
* 균일하지 않은 조명의 영향을 해결하려면?
• 픽셀 주변에 작은 윈도우를 설정하여 지역 이진화 수행
▪ 윈도우의 크기는? - block
▪ 윈도우 형태는? Uniform? Gaussian?
▪ 윈도우를 겹칠 것인가? Overlap? Non-overlap?
▪ 윈도우 안에 배경 또는 객체만 존재한다면?
* 지역 이진화 직접구현
# cv2.THRESH_BINARY는 생략가능 (default)
th, dst1 = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print("otsu's threshold:", th) # 131(실수형태로 나옴)
# Local 이진화
dst2 = np.zeros(src.shape, np.uint8)
# block 설정(128, 128)
bw = src.shape[1] // 4
bh = src.shape[0] // 4
print(bw, bh)
# Local 이진화 직접구현
for y in range(4):
for x in range(4):
# crop
src_ = src[y*bh:(y+1)*bh, x*bw:(x+1)*bw]
dst_ = dst2[y*bh:(y+1)*bh, x*bw:(x+1)*bw]
'''
cv2.threshold(src, thresh, maxval, type, dst=None) -> retval, dst
출력영상을 입력영상으로 받는다.
why? _, dst_ = cv2.threshold 로 진행하면 이전 dst_의 정보를 잃어버려
dst: 출력 영상. src와 동일 크기, 동일 타입, 같은 채널 수
dst_ = src_의 크기가 같아야함
'''
cv2.threshold(src_, 0,255,cv2.THRESH_OTSU, dst_)
* 적응형 이진화(= AdaptiveThreshold)
# 적응형 이진화(= 지역이진화)
'''
조명의 변화나 반사가 심한 경우 이미지 내의 밝기 분포가 달라 국소적으로
임곗값을 적용해야 하는 경우
'''
src = cv2.imread('ch07\\images\\sudoku.jpg', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
sys.exit()
# cv2.adaptiveThreshold(src, maxValue, adaptiveMethod,
# thresholdType, blockSize, C, dst=None) -> dst
'''
C: 블록 내 평균값 또는 블록 내 가중 평균값에서 뺄 값.
(x, y) 픽셀의 임계값으로
'''
def onChange(pos):
bsize = pos
if bsize % 2 == 0:
bsize = bsize -1
if bsize < 3:
bsize = 3
dst = cv2.adaptiveThreshold(src, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \
cv2.THRESH_BINARY, bsize, 5)
cv2.imshow('dst', dst)
Local 이진화를 직접 구현한 것 보다 느림
blockSize: 블록 크기. 3 이상의 홀수(클 수록 좋게 보임)
작을 경우 window에 배경 or 객체만 있을 수 있기 때문에 약간의 noise가 발생
'OpenCV > OpenCV-Chapter' 카테고리의 다른 글
CH08 OpenCV-Python 영상 분할 그랩컷(GrabCut) (0) | 2021.12.24 |
---|---|
CH07 OpenCV-Python 객체 단위 분석(Labeling) (0) | 2021.12.24 |
CH06 Opencv-Python Hough Transform(직선, 원) (0) | 2021.12.19 |
CH06 Opencv-Python 영상의 특징추출(Gradient) (0) | 2021.12.19 |
CH05 Opencv-Python 기하학적(Geometry) 변환 (0) | 2021.12.18 |
댓글