OpenCV - 이진화와 모폴로지

4 분 소요

영상 이진화

이진화(binarization)는 영상의 각 필셀값을 0 또는 255로 만드는 연산이다.

이진화를 할 때는 픽셀 값을 특정 값과 비교하여 특정 값보다 크면 255, 작으면 0으로 설정하는데, 비교대상이 되는 이 특정값을 임계값(threshold)라고 한다.

이진화는 threshold() 함수를 통해 할 수 있다.

cv2.threshold(src, thresh, maxval, type) → ret(임계값), dst(결과영상) 반환

  • thresh: 임계값
  • maxval: THRESH_BINARY 또는 THRESH_BINARY_INV 를 사용할 때 결과의 최대값 (255 사용)
  • type: 이진화 종류
    • THRESH_BINARY: 임계값 기준으로 0, maxval로 표현
    • THRESH_BINARY_INV: THRESH_BINARY의 반전
    • THRESH_TRUNC: 임계값 이상은 maxval, 나머지는 그대로
    • THRESH_TOZERO: 임계값 이상은 그대로, 나머지는 0
    • THRESH_TOZERO_INV: THRESH_TOZERO의 반전
    • THRESH_OTSU: 오츠(Otsu) 알고리즘을 이용한 자동 임계값 지정
    • THRESH_TRIANGLE: 삼각(triangle) 알고리즘을 이용한 자동 임계값 결정
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('img/lenna.png',0)

ret, thresh1 = cv2.threshold(img,128,255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img,128,255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img,128,255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img,128,255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img,128,255, cv2.THRESH_TOZERO_INV)

titles =['Original','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')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])

plt.show()

img

자동 이진화

위 thresh() 함수에 사용되는 이진화 type 중 THRESH_OTSU와 THRESH_TRIANGLE은 픽셀값 히스토그램을 분석하여 임계값을 자동으로 결정하고 이진화를 진행한다.

보통 자동 이진화는 논리합 연산자 | 를 사용해서 다른 이진화 상수와 함께 사용된다. 이 때 thresh 파라미터는 사용되지 않으며, 자동 임계값 결정은 CV_8UC1 타입의 영상에만 적용된다.

ret, dst = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)

지역 이진화

앞의 threshold() 함수는 임계값을 영상 전체에 동일하게 적용하여 이진화 영상을 생성했다. 이런 방식을 전역 이진화(global binarization)라 한다.

하지만 다음 사진과 같이 밝기의 차이로 인해 전역 이진화를 적용하기 어려운 경우가 있다.

img

이런 경우 이미지를 일정 크기의 사각형으로 분할하여 각 영역 내부의 픽셀 값 분포에 맞게 임계값을 결정해서 이진화를 할 수 있다. 이 방법을 지역 이진화(local binarization)라 한다.

src = cv2.imread('img/sudoku.jpg', cv2.IMREAD_GRAYSCALE)

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

# 지역 이진화 by Otsu's method
dst2 = np.zeros(src.shape, np.uint8)  # 입력 영상과 같은 사이즈의 배경 생성

# 영상을 4x4, 총 16칸으로 나눔
bw = src.shape[1] // 4  # src.shape = (h, w, c), bw = w*(1/4)
bh = src.shape[0] // 4  # bh = h*(1/4)

# 영상의 각 칸 별로 자동 이진화 실행
for y in range(4):
    for x 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]
        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()

img

적응형 이진화

모든 픽셀에서 정해진 블록 크기에 따라 블록 영역 내부의 픽셀값 분포도를 확인하고 임계값을 계산하여 자동으로 지역 이진화를 하도록 하는 것이 적응형 이진화(adaptive binarization)다.

cv2.adaptiveThreshold(src, maxVal, adaptiveMethod, thresholdType, blockSize, C, dst)

  • maxVal: 임계값 함수 결과의 최대값 (255)
  • adaptiveMethod: 블록 평균 계산 방법
    • ADAPTIVE_THRESH_MEAN_C (산술평균)
    • ADAPTIVE_THRESH_GAUSSIAN_C (가우시안 가중치 평균)
  • thresholdType: 이진화 방법
  • blockSize: 블록크기. 3 이상의 홀수 입력
  • C: 평균에서 뺄 값
src = cv2.imread('img/sudoku.jpg', cv2.IMREAD_GRAYSCALE)

dst = cv2.adaptiveThreshold(src, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 9, 5)

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

img

모폴로지 연산 (Morphology)

영상을 형태학적 측면에서 다루는 기법. 객체의 형태 및 구조에 대해 분석하고 처리하며 수학적 모폴로지(mathematical morphology)라고도 한다. 영상의 전처리 또는 후처리 과정에서 객체의 모양을 단순화 시키거나 잡음을 제거하는 용도로 사용된다.

필터링처럼 작은 크기의 행렬로 이루어진 구조 요소(structuring element)를 사용해서 연산을 진행하며 구조 요소의 크기나 모양을 다양하게 정의할 수 있으나 일반적으로 3x3 정방형 구조 요소를 사용한다.

침식과 팽창

img

침식 - 객체 영역의 외곽을 골고루 깎아 내는 연산으로 전체적으로 객체 영역은 축소되고 배경은 확대. 커널(구조 요소)이 객체 영역 안에 모두 포함될 때 고정점(anchor point)을 255로 설정. 작은 크기의 잡음을 제거할 때 좋다.

cv2.erode(src, kernel, anchor, iterations, borderType, borderValue)

  • kernel: 구조 요소. None이면 3x3
  • anchor: 고정점 위치. 기본값(-1,-1)은 정중앙점
  • iterations: 반복횟수. 기본값 1.
  • borderType: 테두리 처리 종류
  • borderValue: 확장 테두리 채울 값

팽창 - 객체 외곽을 확대하는 연산. 객체 영역을 확대되고, 배경 영역은 줄어든다. 커널이 객체 영역 안에 하나라도 포함되면 고정점을 255로 설정. 영상의 구멍을 채우는 효과가 있다.

cv2.dilate(src, kernel, anchor, iterations, borderType, borderValue)

  • kernel: None이면 3x3
  • anchor: 고정점 위치. 기본값(-1,-1)은 정중앙점
  • iterations: 반복횟수. 기본값 1.
  • borderType: 테두리 처리 종류
  • borderValue: 확장 테두리 채울 값

커널 생성 - erode() 와 dilate() 함수를 사용하기 위해 커널이 필요한데, getStructuringElement() 함수를 사용하면 손쉽게 0과 1로 이루어진 커널 혹은 구조 요소 행렬을 생성할 수 있다.

cv2.getStructuringElement(shape, ksize, anchor)

  • shape: 커널모양.
    • cv2.MORTH_RECT: 사각형 모양
    • cv2.MORTH_CROSS: 십자가 모양
    • cv2.MORPH_ELLIPSE: 사각형에 내접하는 타원
  • ksize: 커널크기. (가로, 세로)튜플
  • anchor: 앵커 위치. (-1, -1)은 중앙점
src = cv2.imread('img/circuit.bmp', cv2.IMREAD_GRAYSCALE)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 3)) # 5x3사각형 커널

dst1 = cv2.erode(src, kernel)
dst2 = cv2.dilate(src, None)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

img

열기와 닫기

열기(opening)
입력 영상에 대해 침식 연산을 수행한 후 다시 팽창연산을 수행. 작은 개체나 돌기 제거 효과가 있다. 얇은 선은 끊어짐.

닫기(closing)
입력 영상에 대해 팽창 연산을 수행한 후 다시 침식연산을 수행. 작은 홈이나 구멍이 사라지고 얇은 연결선이 두꺼워짐. 윤곽 파악에 적합하다.

img

img

범용 모폴로지 연산 함수

cv2.morphologyEx(src, mode, kernel, anchor, iterations, borderType, borderValue)

  • mode: 연산 종류
    • cv2.MORPH_ERODE: 침식
    • cv2.MORPH_DILATE: 팽창
    • cv2.MORPH_OPEN: opening
    • cv2.MORPH_CLOSE: closing
    • cv2.MORPH_GRADIENT: dilate 결과 – erode결과(d와 e의 차)
  • 나머지 파라미터는 erode(), dilate()와 동일
src = cv2.imread('img/rice.png', cv2.IMREAD_GRAYSCALE)
_, temp = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)

dst1 = cv2.morphologyEx(temp, cv2.MORPH_OPEN, None)
dst2 = cv2.morphologyEx(temp, cv2.MORPH_CLOSE, None)

cv2.imshow('src', src)
cv2.imshow('temp', temp)
cv2.imshow('opening', dst1)
cv2.imshow('closing', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

img

댓글남기기