황선규 박사님의 'OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝' 을 공부하면서 정리해 보았습니다.
예제 코드 출처 : 황선규 박사님 github홈페이지
이전 포스팅에서는 자동으로 임계값을 설정해주는 Otsu 이진화 방법에 대해 공부해보았습니다.
이번에는 균일하지 않은 조명 환경에서 찰영된 영상에서 사용할 수 있는 지역 이진화에 대해 알아보겠습니다.
지역 이진화
균일하지 않은 조명 환경에서 찰영된 영상에서 사용할 수 있습니다.
이처럼 불균일한 조명이 있는 영상에서는 이진화 결과가 지저분하게 나옵니다.
이를 해결하기 위해서는 지역 이진화 방법을 이용해야 합니다.
두 가지 지역 이진화 방법에 대해 알아보겠습니다.
1. 구역을 나눠서 Otsu 이진화 사용하기
전체 구역을 N등분 하고 각각의 구역에 이진화를 한 뒤에 이어 붙이는 방법입니다.
여러 개의 임계값을 이용하게 됩니다.
예제 코드
src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)
# 입력 영상 불러오기
if src is None:
print('Image load failed!')
sys.exit()
# 전역 이진화
_, dst1 = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 지역 이진화
dst2 = np.zeros(src.shape, np.unit8) # 검정색 영상
# 넓이 / 4, 높이 / 4
bw = src.shape[1] // 4
bh = src.shape[0] // 4
# 가로 세로 4등분 하기
for y in range(4):
for x in range(4):
src_ = src[y*bh:(y+1)*bh, x*bw:(x+1)bw] # threshold 입력값으로 주기 위해 입력 영상도 등분
dst_ = dst2[y*bh:(y+1)*bh, x*bw:(x+1)*bw] # dst_를 변경하면 dst2도 변경됍니다.
# dst_를 입력인자로 주었습니다.
# dst_를 입력인자로 입력하면 dst_를 입력이자 출력으로 받을 수 있습니다.
# _, dst_ = cv2.~ 로 값을 받게 되면 등분한 dst_ 정보는 사라집니다.
cv2.threshold(src_, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU, dst_)
# 결과 출력
cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()
전역 이진화와 지역 이진화 영상을 비교했을 때 아랫 부분에서 확연한 차이를 볼 수 있습니다.
2. OpenCV 적응형 이진화 - cv2.adaptiveThreshold
OpenCV에서 제공하는 적응형 이진화 함수입니다.
가우시안 블러를 적용하여 노이즈를 제거한 뒤에 Otsu 이진화를 적용합니다.
방법 1 보다 더 느립니다.
함수 설명
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None) -> dst
• src: 입력 영상. 그레이스케일 영상
• maxValue: 임계값 함수 최댓값. 보통 255.
• adaptiveMethod: 블록 평균 계산 방법 지정. cv2.ADAPTIVE_THRESH_MEAN_C는 산술 평균, cv2.ADAPTIVE_THRESH_GAUSSIAN_C는 가우시안 가중치 평균
• thresholdType: cv2.THRESH_BINARY 또는 cv2.THRESH_BINARY_INV 지정
• blockSize: 블록 크기. 3 이상의 홀수
• C: 블록 내 평균값 또는 블록 내 가중 평균값에서 뺄 값. (x, y) 픽셀의 임계값으로 𝑇(𝑥, 𝑦) = 𝜇(𝑥, 𝑦 )− 𝐶 를 사용
blosize는 크게 줘야 합니다. 작게 주면 결과가 좋지 않습니다.
또한 3 이상의 홀수를 입력해야 하며 경우에 따라 51 X 51을 이용할 때도 있습니다.
C는 임계값을 결정하는 파라미터입니다.
픽셀의 임계값으로 T(x,y) = u(x,y) - C를 이용합니다.
예제 코드
src = cv2.imread('sudoku.jpg', cv2.IMREAD_GRAYSCALE)
if src is None:
print('Image load failed!')
sys.exit()
# 블럭 사이즈
def on_trackbar(pos):
bsize = pos
# 짝수면 -1
if bsize % 2 == 0:
bsize = bsize - 1
# 3보다 작으면 무조건 3
if bsize < 3:
bsize = 3
dst = cv2.adaptiveThreshold(src, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, bsize, 5) # C값은 5를 줌
cv2.imshow('dst', dst)
cv2.imshow('src', src)
cv2.namedWindow('dst')
cv2.createTrackbar('Block Size', 'dst', 0, 200, on_trackbar)
cv2.setTrackbarPos('Block Size', 'dst', 11)
cv2.waitKey()
cv2.destroyAllWindows()
블럭 사이즈를 너무 작게 주면 블럭 안에 배경이나 객체만 존재하는 상황이 생깁니다.
이 경우에 픽셀값의 차이가 적어 지저분하게 결과가 출력될 수 있습니다.
감사합니다.
'Python > 파이썬 OpenCV 공부' 카테고리의 다른 글
[파이썬 OpenCV] 영상의 모폴로지 - 열기와 닫기 - cv2.morphologyEx (0) | 2020.10.10 |
---|---|
[파이썬 OpenCV] 영상의 모폴로지 - 침식과 팽창 - cv2.erode, cv2.dilate, cv2.getStructuringElement (0) | 2020.10.10 |
[파이썬 OpenCV] 이진 영상 처리 - 자동 이진화 - Otsu 방법 (0) | 2020.10.10 |
[파이썬 OpenCV] 영상의 이진화 - cv2.threshold (0) | 2020.10.10 |
[파이썬 OpenCV] 동전 카운터 - cv2.HoughCircles (0) | 2020.10.09 |