본문 바로가기
Python/Computer Vision

[파이썬, Python] OpenCV - 영상의 이진화

by coding-choonsik 2023. 8. 27.
728x90
반응형
SMALL

1. 영상의 이진화(Binarization)

  • 픽셀을 검은색 또는 흰색과 같이 두 개의 값으로 나누는 작업
  • 영상에서 의미있는 관심영역(ROI)과 비관심 영역으로 구분할 때 사용
  • 배경과 객체를 나눌 때
  • 영상의 이진화 연산을 할 때 나누는 특정값을 임계값이라고 함
  • cv2.threshold(영상, 임계값, 최대값, 플래그값)
  • 플래그값
    • cv2.THRESH_BINARY: 픽셀값이 임계값을 넘으면 최대값으로 지정하고, 작거나 같으면 0으로 지정
    • 예) 105로 임계값을 설정했을 때 105보다 큰 것은 최대값 으로 지정, 105보다 작으면 0
    • cv2.THRESH_BINARY_INV: cv2.THRESH_BINARY의 반대
    • cv2.THRESH_TRUNC: 픽셀 값이 임계값을 넘으면 value로 지정하고, 넘지 못하면 원래값을 유지
    • cv2.THRESH_TOZERO: 픽셀 값이 임계값을 넘으면 원래 값을 유지, 넘지 못하면 0으로 지정
    • cv2.THRESH_TOZERO_INV: cv2.THRESH_TOZERO와 반대

 

import cv2
import matplotlib.pyplot as plt

src = cv2.imread('cells.png', cv2.IMREAD_GRAYSCALE)
hist = cv2.calcHist([src], [0], None, [256], [0, 255])

a, dst1 = cv2.threshold(src, 100, 255, cv2.THRESH_BINARY)
b, dst2 = cv2.threshold(src, 210, 255, cv2.THRESH_BINARY)  # 210(거의흰색에 가까운)보다 큰 픽셀들은 255, 그 외엔 0
print(a)  # threshold값
print(b)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
plt.plot(hist)
plt.show()
cv2.waitKey()

▲ src의 히스토그램

 

▲ 순서대로 src, dst1, dst2


2. 오츠의 이진화 알고리즘

  • 자동 이진화
  • 자동으로 임계값을 구하는 알고리즘, 임계값을 구분하는 가장 좋은 방법으로 많이 사용
  • cv2.threshold(영상, 임계값, 최대값, 플래그값 | cv2.THRESH_OTSU)
    • 임계값을 임의로 정해 픽셀을 두 부류로 나누고 두 부류의 명암 분포를 구하는 작업을 반복하여
      모든 경우의 수 중에서 두 부류의 명암 분포가 가장 균일할 때의 임계값을 선택
import cv2


src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)
th, dst = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
print('otsu', th)  # 임계값: 131

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()

▲ src와 dst

 


3. 지역 이진화

  • 균일하지 않은 조명 환경에서 사용하는 이진화 방법
  • 전체 구역을 N등분 하고 각각의 구역에 이진화를 한 뒤에 이어 붙이는 방법
  • 여러개의 임계값을 이용할 수 있음
# rice.png를 가로, 세로 4등분하여 자동 이진화
import cv2
import numpy as np

src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)

# 전역이진화
_, dst1 = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)


# 지역이진화
dst2 = np.zeros(src.shape, np.uint8)
bw = src.shape[1] // 4  # 4로 나눈 나머지
bh = src.shape[0] // 4

for x in range(4):
    for y in range(4):
        src_ = src[y*bh: (y+1)*bh, x*bw:(x+1)*bw]
        dst_ = dst2[y*bh: (y+1)*bh, x*bw:(x+1)*bw]
        # 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()

▲src와 dst1, dst2

 


4. 적응형 이진화

  • 노이즈를 제거한 뒤에 Otsu 이진화를 적용
  • 영상을 여러 영역으로 나눈 뒤, 그 주변 픽셀 값만 활용하여 임계값을 구함
  • cv2.adaptiveThreshold(영상, 임계값을 만족하는 픽셀에 적용할 값, 임계값 결정 방법,Threshold 적용방법, 블록사이즈, 가감할 상수)
  • 임계값 결정 방법
    • cv2.ADAPTIVE_THRESH_MEAN_C: 이웃 픽셀의 평균으로 결정
      • 선명하지만 잡티(노이즈)가 많아짐
    • cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 가우시안 분포에 따른 가중치 합
      • 선명도가 조금 떨어지지만 잡티가 적음
  • 블록사이즈: 3이상의 호출(3의 배수), 블록 사이즈가 클수록 연산 시간이 오래 걸림

 

import cv2
import matplotlib.pyplot as plt

block_size = 9
img = cv2.imread('sudoku.jpg', cv2.IMREAD_GRAYSCALE)

# 자동 이진화
th, dst1 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# 평균을 이용한 자동 적응 이진화
# 5: 가감상수(양수일경우 밝아짐)
dst2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, block_size, 5)

# 가우시안을 이용한 자동 적응 이진화
dst3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, block_size, 5)

dic = {'img':img,'dst1': dst1, 'dst2':dst2, 'dst3':dst3}
for i, (k, v) in enumerate(dic.items()):
    plt.subplot(2, 2, i+1)
    plt.title(k)
    plt.imshow(v,'gray')

plt.show()

▲ 가우시안을 적용한 것이 잡티가 좀 더 적음

728x90
반응형
LIST