영상의 이진화는 영상의 각 픽셀을 두 개의 부류로 나누는 작업이다. 예를 들어 입력 영상을 주요 객체 영역과 배경 영역으로 나누거나 또는 영상에서 중요도가 높은 관심영역(ROI) 과 그렇지 않은 비관심 영역으로 구분하는 용도로 이진화가 사용될 수 있다.
기본적으로 영상의 각 픽셀 값을 이용한다. 그레이 스케일 영상에 대해 이진화를 수행하려면 영상의 픽셀값이 특정 값보다 크면 255로 설정하고, 작으면 0으로 설정한다. 이때 각 픽셀과의 크기 비교 대상이 되는 값을 임계값이라고 한다.
OpenCV 에서 이진화는 threshold()함수를 이용하여 수행할 수 있다.
double threshold(InputArray src, OutputArray dst,
double thresh, double maxval, int type);
src : 입력영상
dst : 출력영상
thresh : 임계값
maxval : THRESH_BINARY 또는 THRESH_BINARY_INV 방법을 사용할때 결과 영상의 최댓값
type : 임계값 연산 방법
반환값 : 사용된 임계값
void on_threshold(int pos, void* userdata);
int main(int argc, char* argv[]) {
Mat src;
if (argc < 2)
src = imread("neutrophils.png", IMREAD_GRAYSCALE);
else
src = imread(argv[1], IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
imshow("src", src);
namedWindow("dst");
createTrackbar("Threshold", "dst", 0, 255, on_threshold, (void*)&src);
setTrackbarPos("Threshold", "dst", 128);
waitKey();
return 0;
}
void on_threshold(int pos, void* userdata) {
Mat src = *(Mat*)userdata;
Mat dst;
threshold(src, dst, pos, 255, THRESH_BINARY);
imshow("dst", dst);
}
위와 같이 영상의 모든 픽셀에 같은 임계값을 적용하여 이진화를 수해하는 방식을 전역 이진화하고 한다. 그런데 영상의 특성에 따라서 전역 이진화를 적용하기 어려운 경우가 있다. 예를 들어 균일하지 않은 조명 환경에서 촬영된 영상에 대해 전역 이진화를 수행하면 객체와 배경이 적절하게 분리되지 않는 경우가 발생한다.
이럴때는 각 픽셀마다 다른 임계값을 사용하는 적응형 이진화 기법을 사용하는 것이 효과적이다. 영상의 모든 픽셀에서 정해진 크기의 사각형 블록 영역을 설정하고, 블록 영역 내부의 픽셀 값 분포로부터 고유의 임계값을 결정하여 이진화하는 방식이다. 이때 (x,y) 좌표에서의 임계값 T(x,y)는 다음 수식을 이용하여 결정한다.
주변 블록 영역의 픽셀값 평균에서 임계값의 크기를 조정하는 상수를 빼서 임계값을 정한다.
void adaptiveThreshold(InputArray src, OutputArray dst,
double maxValue, int adaptiveMethod,
int thresholdType, int blockSize, double C);
image : 입력영상
dst : 출력영상
maxValue : 이진화 결과 영상의 최댓값
adaptiveMethod : 적응형 이진화에서 블록 평균 계산 방법 지정
thresholdType : THRESH_BINARY 또는 THRESH_BINARY_INV 둘 중 하나 지정
blockSize : 임계값 계산시 사용하는 블록 크기
C : 임계값 조정을 위한 상수
void on_trackbar(int pos, void* userdata);
int main() {
Mat src = imread("sudoku.jpg", IMREAD_GRAYSCALE);
if (src.empty()) {
cerr << "Image load failed!" << endl;
return -1;
}
imshow("src", src);
namedWindow("dst");
createTrackbar("Block Size", "dst", 0, 200, on_trackbar, (void*)&src);
setTrackbarPos("Block Size", "dst", 11);
waitKey();
return 0;
}
void on_trackbar(int pos, void* userdata) {
Mat src = *(Mat*)userdata;
int bsize = pos;
if (bsize % 2 == 0)
bsize--;
if (bsize < 3)
bsize = 3;
Mat dst;
adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, bsize, 5);
imshow("dst", dst);
}