지난번에는 함수 실행 시간을 측정하는 방법에 대해 알아봤습니다.
https://blog.naver.com/dorergiverny/223079726064
이번에는 그레이 영상을 이진화 하는 방법에 대해 알아보겠습니다.
이진화는 특정 밝기 범위 또는 밝은 영역과 어두운 영역 등으로 이분화해서
전경과 배경으로 나누는 방법입니다
Thresholding (이진화)
이진화는 영상의 픽셀들을 두 개의 부류로 나누는 작업입니다.
아래와 같은 사진에서 흰색 탁구공을 구분하려고 합니다.
그 때 아래와 같이 cv::threshold() 함수를 사용하면 됩니다.
픽셀 밝기가 180 이상인 값을 255로, 180보다 작은 값은 0으로 처리하는 함수는 아래와 같습니다.
cv::threshold(src, binImage, 180, 255, cv::THRESH_BINARY);
결과 영상은 아래처럼 나오네요.
자 그럼 threshold 함수에 대해 좀 더 알아봐요.
src
|
입력 영상
|
dst
|
출력 영상, 이진화 영상
|
thresh
|
임계값(threshold)
|
maxval
|
임계값 이상일 때 이진화 영상의 최대값(보통 255 사용)
|
type
|
thresholding 연산 방법 (하기 설명 참조)
|
반환 (double)
|
자동으로 결정되는 경우의 threshold 값(THRESH_OTSU, THRESH_TRIANGLE)
|
cv::threshold() 함수의 동작은 연산 방법인 type에 따라 결정됩니다.
OpenCV 4.7.0 에는 아래와 같이 8가지가 지원됩니다.
type에 대한 설명을 자세히 살펴보면(docs.opencv.org를 재구성함),
원본 영상이 아래와 같이 생겼다고 가정하면
각 방법은 아래와 같이 동작합니다.
[THRESH_OTSU 방법]
영상의 히스토그램을 분석하여 이 분포를 둘로 가장 잘 나눌 수 있는 threshold를 찾아서
그 threshold를 적용하는 방법입니다.
이는 밝기 분포를 분석하여 클래스 내의 분산과 클래스 간의 분산을 계산하여
분산의 비율이 최대가 되는 위치를 threshold로 결정하는 방법 입니다.
(수식은 생략할께요.)
그럼 아래와 같은 그림을 얻을 수 있습니다.
[THRESH_TRIANGLE]
이 알고리즘은 그래프 상에서 peak를 이용하는 방법으로
그래프의 peak 점과 양 끝 중에서 peak에서 더 먼쪽의 값 2개를 선으로 그어 본 후
그 선과 그래프간 가장 먼 지점의 밝기를 threshold로 결정하는 방법입니다.
이처럼 THRESH_OTSU와 THRESH_TRIANGLE을 사용할 경우 threshold를 자동으로
설정하기 때문에 전체 밝기 값이 약간씩 바뀔 수 있는 경우(분포는 안바뀌고)에
유용하게 사용될 수 있는 방법입니다.
저는 THRESH_BINARY와 THRESH_OTSU를 가장 많이 사용하는 것 같습니다.
저는 THRESH_OTSU에서 얻은 thresh 값을 THRESH_BINARY에 한번 넣어 봤습니다.
아래 영상에서는 th 값이 97 값이 나오고 th 값을 그대로 넣으시면 똑같은 결과가 나오기 때문에 그것보다 좀 아래 값을 입력으로 넣어서 binarization을 해 봤습니다.
#include <iostream>
#include <opencv2/opencv.hpp>
int main()
{
cv::Mat src = cv::imread("sudoku.jpg", cv::IMREAD_GRAYSCALE);
cv::Mat binImage1, binImage2;
double th = cv::threshold(src, binImage1, 150, 255, cv::THRESH_OTSU);
cv::threshold(src, binImage2, (th - 10), 255, cv::THRESH_BINARY);
return 0;
}
결과 영상은 아래와 같습니다.
OTSU의 강점은 바로 아래와 같은 영상에서 발휘됩니다.
소스를 아래와 같이 작성후 결과를 내어 봤습니다.
cv::threshold(src, binImage1, 0, 255, cv::THRESH_OTSU);
cv::threshold(src, binImage2, 120, 255, cv::THRESH_BINARY);
cv::threshold(src2, binImage3, 0, 255, cv::THRESH_OTSU);
cv::threshold(src2, binImage4, 120, 255, cv::THRESH_BINARY);
sudoku 영상 같이 한 영상 내에서 밝기 분포가 균일하지 않을 경우에는 어떻게 할까요?
이런 경우를 위해 adaptive threshold라는 기법이 있습니다.
이것은 다음에 다뤄보기로 해요.