본문 바로가기

프로그래밍 강좌/C++ - OpenCV

[OpenCV][C++] Thresholding 이진화 영상 만들기 총정리(2) - adaptiveThreshold() image binarize trackbar gaussian 지역 local

지난 번에는 일반적인 이진화 방법(binarization)에 대해 알아 보았습니다.

이번에는 영상 내에서 밝기가 불균일 할 때 사용할 수 있는 적응형 이진화(adaptive threshold) 기법에 대해 알아보겠습니다.

이번에는 opencv에서 제공하는 trackbar를 이용하여 실시간으로 값을 조정하면서 영상 변화를 확인해보도록 하겠습니다.

적응 이진화(adaptive threshold)

 

적응형 이진화 지역 이진화(local binarization)이라고도 하는데,

이는 영상 전체에서 일정 크기의 블럭 내부의 픽셀값 분포로부터 임계값을 자동으로 설정하여 이진화하는 기법입니다.

기본형태는 다음과 같습니다.

src
입력 영상
dst
출력 영상
maxval
이진화 결과 최대값 밝기
adaptiveMethod
블록 평균 방법 (MEAN_C, GAUSSIAN_C 가 있음)
thresholdType
THRESH_BINARY, THRESH_BINARY_INV, BINARY_TRUNC 등 binary 옵션 중 하나 지정
blockSize
local block 크기(3 이상의 홀수 사용)
C
임계값 조정을 위한 상수(블록 평균에서 C를 뺀 값을 threshold로 사용)

 

[cv::ADAPTIVE_THRESH_MEAN_C]

설정된 block size 크기의 window 내의 평균값을 계산하여 threshold로 처리함

 

[cv::ADAPTIVE_THRESH_GAUSSIAN_C]

설정된 block size 크기의 windows 내의 평균값을 구하되 gaussian 가중치를 곱해서 계산하여 threshold로 처리함

 

MEAN_C의 예를 들어 보겠습니다.

아래와 같은 영상이 있다고 할 때 영상 전체를 window 로 순회하면서 지역의 평균값을 계산한 후에 threshold를 정하고 binarization 옵션에 따라 그 픽셀의 값을 대체합니다.

아래의 경우 THRESH_BINARY 옵션을 주었기 때문에 계산된 thresh 보다 픽셀 값이 크기 때문에 255로 변환되게 됩니다.

adaptive threshold를 할 경우 영상의 경계 부분이 뚜렷히 나타나는 현상이 있습니다.

 

sudoku 영상으로 직접 실습을 해 보겠습니다.

#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
    cv::Mat src = cv::imread("sudoku.jpg", cv::IMREAD_GRAYSCALE);

    cv::Mat dst;
    cv::adaptiveThreshold(src, dst, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 37, 5);
    return 0;
}
 

일반적인 이진화 방법과 adaptive 이진화 방법의 결과가 차이가 나는 것을 볼 수 있습니다.

Trackbar를 이용하여 최적화 하기

cv::adaptiveThreshold()에는 변수가 2개가 들어갑니다.

block size threshold를 조정할 수 있는 constant 변수, C 가 그것이죠.

일반적으로 opencv에서 제공하는 trackbar는 변수를 하나만 가질 수 있지만

다른 강의에서 2개의 변수를 가지는 trackbar에 대해 공부를 했었죠?

https://blog.naver.com/dorergiverny/223060058845

 

[OpenCV][C++] 트랙바(trackbar) 사용하기 총정리 - createTrackbar convertTo slider control 슬라이더 waitkey 컨트

지난번에는 마우스 이벤트를 처리하는 방법에 대해 알아봤습니다. https://m.blog.naver.com/dorergiverny/...

blog.naver.com

 

그걸 응용하면, 아래와 같이 소스를 만들 수 있습니다. 간단하죠?

#include <iostream>
#include <opencv2/opencv.hpp>

cv::Mat src;

void trackbar1(int bsize, void* pImage)
{
    int c = *(static_cast<int*>(pImage));
        
    if (bsize % 2 == 0) bsize++;
    if (bsize < 3) bsize = 3;

    cv::Mat dst;
    cv::adaptiveThreshold(src, dst, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, bsize, 0);
    imshow("dst", dst);
}

void trackbar2(int c, void* pImage)
{
    int bsize = *(static_cast<int*>(pImage));

    cv::Mat dst;
    cv::adaptiveThreshold(src, dst, 255, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, bsize, c);
    imshow("dst", dst);
}

int main()
{
    src = cv::imread("sudoku.jpg", cv::IMREAD_GRAYSCALE);
    cv::namedWindow("dst", 1);

    int bsize = 9;
    int c = 0;

    cv::createTrackbar("BlockSize", "dst", &bsize, 200, trackbar1, &c);    
    cv::setTrackbarPos("BlockSize", "dst", 9);
    cv::createTrackbar("C", "dst", &c, 100, trackbar2, &bsize);
    cv::setTrackbarPos("C", "dst", 0);
    cv::waitKey(0);
    return 0;
}
 

그럼 아래와 같이 두개의 변수를 동시에 control 하면서 영상의 변화를 관찰하실 수 있습니다.

위에서 볼 수 있듯이 block size가 작으면 영상 noise에 의한 영향을 많이 받을 수 있고 너무 커지면 전역 이진화(global binarization)와 유사해지겠죠?

적당한 크기를 잘 설정하셔서 application에 맞도록 사용하시기 바랍니다.