반응형

이진화는 가장 간단한 세그멘테이션(segmentation) 방법이다.  세그멘테이션이란 이미지를 분할하여 원하는 부분 혹은 물체를 검출하는데 많이 사용되는 기법이다. 


이진화는 원본 영상을 그레이 영상으로 변환한 후,  threshold값을 이용하여 배경과 물체를 분리해낸다.  

아래 화면은 책상 위에 빨간색 공이 놓여있는 컬러 이미지를 그레이스케일 이미지로 변환한 후, 이진화를 통해 빨간색 공 영역을 분리해낸 결과이다.


가장 단순한 이진화 방법은 이미지 전체에 고정된 전역 threahold값을 사용하는 것이다.

그레이 영상을 입력으로 받아서 영상 전체를 스캔하면서 픽셀값이 threshlod값보다 크면 결과 영상의 같은 위치의 픽셀값을 흰색(1 또는 255)으로 하고, 픽셀값이 threshold값보다 작으면 검은색(0)으로 한다.  결과적으로 이진화된 영상을 얻게 된다.


고정된 전역 threshold값을 이용한 이진화를 위해 opencv에서는 threshold 함수를 제공한다.  그레이영상을 입력으로 받아 이진화 영상을 결과로 내놓는다.


src    입력 이미지, 그레이 스케일 이미지여야 한다.

dst     결과 이미지, 원본 이미지와 같은 크기

thresh  threshold 값

maxval 입력 이미지의 픽셀값이 threshold보다 클 경우 결과 이미지의 픽셀값은 maxval이 된다.  

           threshold보다 작을 경우에는 0이 된다.

type    Thresholding type



이미지를 불러와서 이진화를 하는 간단한 예제 소스코드이다.

#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/imgcodecs.hpp"

#include "opencv2/highgui/highgui.hpp"

using namespace cv;

 

int main()

{

        //이미지 파일을 불러와 그레이 이미지로 변환한다.  

        Mat input_gray_image = imread( "input2.jpg", IMREAD_GRAYSCALE );

 

        namedWindow( "입력 이미지", WINDOW_AUTOSIZE);

        namedWindow( "이진화된 이미지", WINDOW_AUTOSIZE);

 

        Mat result_binary_image;

        //threshold값을 127로 해서 이진화 한다.

        //입력 이미지의 특정 필셀값이 threshold값보다 크면 결과 

        //이미지상의 같은 위치의 픽셀값을 255로 한다.

        //thshold값보다 작을 경우에는 0이 된다.

        threshold( input_gray_image, result_binary_image, 127, 255, THRESH_BINARY );

 

        imshow("이진화된 이미지", result_binary_image);

        imshow("입력 이미지", input_gray_image);

 

        //아무키나 누를 때 까지 대기한다.

        while (cvWaitKey(0) < 0);

}


그레이스케일 이미지를 고정된 threshold 값 127로 이진화한 결과이다.


이렇게 고정된 threshold값을 사용하는 것은 문제가 있다.  원본 이미지에 그림자가 있거나 빛에 의해 반사되는 부분이 있다면 좋지 않은 결과를 얻게 된다.


adaptive thresholding에서는 각 픽셀에 대한 threshold값을 이웃하는 픽셀값에 의해 결정한다.  (x,y) 위치에 있는 픽셀의 threshold값을 계산하기 위하여 다음 단계를 거친다.


  1. 선택된 픽셀 위치에 대해  b x b 크기의  블록을 설정한다.

  2. 블록에 대한 가중 평균(weighted average)을 구한다. 

    OpenCV에서는 가중평균을 구하기 위해  2가지 방법을 제공하고 있다. 블록 내의 모든 픽셀에 대한 평균을 구하는 방법과  블록 내의 픽셀들에 대한 가우시안 가중 평균(Gaussian weighted average)구하는 방법이 있다.

      가우시안 가중 평균은 블록 중심에 가까울수록 높은 가중치를 갖도록하여 평균을 구하는 방법이다.  여기서 구한 값을 WA(x, y) 라 하자.

  3. 앞에서 구한 평균값에서 상수 파라메터 값 param1 을 빼면 Threshold값이 구해진다. 

     T(x, y) = WA(x, y) - param1


Adaptive thresholding을 위해서 opencv에서는 adaptiveThreshold 함수를 제공한다.


src                      그레이 스케일 입력 이미지

dst                      결과 이미지

maxValue             threshold값보다 클 경우 결과 이미지의 해당 픽셀에 지정될 값

adaptiveMethod    Adaptive thresholding algorithm

                          ADAPTIVE_THRESH_MEAN_C

                          또는 ADAPTIVE_THRESH_GAUSSIAN_C

thresholdType       Thresholding type

                          THRESH_BINARY(thredhold보다 크면 maxvalue)

                          또는 THRESH_BINARY_INV(threshold보다 작으면 maxvalue)

blockSize              threshold값을 계산하기 위해 사용되는 블록 크기 : 3,5,7 등의 값

c                         계산된 평균으로부터 뺄 상수값


블록 내 모든 픽셀의 평균을 이용한 adaptive thresholding을 적용한 경우 결과이다.  반사된 부분과 그림자가 있는 부분에 대한 이진화 결과가 개선된 것을 볼 수 있다.


#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/imgcodecs.hpp"

#include "opencv2/highgui/highgui.hpp"

using namespace cv;

 

int main()

{

        //이미지 파일을 불러와 그레이 이미지로 변환한다.  

        Mat input_gray_image = imread( "input3.png", IMREAD_GRAYSCALE );

 

        namedWindow( "입력 이미지", WINDOW_AUTOSIZE);

        namedWindow( "이진화된 이미지", WINDOW_AUTOSIZE);

 

        Mat result_binary_image;

        //Adaptive Thresholding을 한다.

        adaptiveThreshold(input_gray_image, result_binary_image,

                255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 5, 10);

                

        imshow("이진화된 이미지", result_binary_image);

        imshow("입력 이미지", input_gray_image);

 

        //아무키나 누를 때 까지 대기한다.

        while (cvWaitKey(0) < 0);

}


블록 내의 픽셀들에 대해  가우시안 가중치를 적용하여 평균을 구하여  adaptive thresholding을 구한 경우의 결과이다. 평균값을 사용한 경우와 마찬가지로 이진화 결과가 개선되었다.


#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/imgcodecs.hpp"

#include "opencv2/highgui/highgui.hpp"

 

using namespace cv;

 

int main()

{

 

    //이미지 파일을 불러와 그레이 이미지로 변환한다.  

    Mat input_gray_image = imread( "input3.png", IMREAD_GRAYSCALE );

 

    namedWindow( "입력 이미지", WINDOW_AUTOSIZE);

    namedWindow( "이진화된 이미지", WINDOW_AUTOSIZE);

 

    Mat result_binary_image;

 

    //Adaptive Thresholding을 한다.

    adaptiveThreshold(input_gray_image, result_binary_image,

        255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 5, 10);

        

    imshow("이진화된 이미지", result_binary_image);

    imshow("입력 이미지", input_gray_image);

 

 

    //아무키나 누를 때 까지 대기한다.

    while (cvWaitKey(0) < 0);

}


위 코드를 마커 이미지에 대해 적용해봤더니 원치 않은 결과가 나왔다. 검은색 테두리 부분이 선으로만 검출되었다.


이미지에 따라서는 블록의 크기가 너무 작으면 에지 검출만된다해서 블록의 크기를 55로 바꾸었더니 검은색 테두리와 내부의 글자가 검출되었다. 

opencv에서 제공하는 Otsu 방법을 적용한 결과이다. 검은색 테두리와 내부의 글자가 제대로 검출되었다.


#include "opencv2/imgproc/imgproc.hpp"

#include "opencv2/imgcodecs.hpp"

#include "opencv2/highgui/highgui.hpp"

using namespace cv;

int main()

{

        //이미지 파일을 불러와 그레이 이미지로 변환한다.  

        Mat input_gray_image = imread( "input4.png", IMREAD_GRAYSCALE );

 

        namedWindow( "입력 이미지", WINDOW_AUTOSIZE);

        namedWindow( "이진화된 이미지", WINDOW_AUTOSIZE);

 

        Mat result_binary_image;

        //이진화를 한다.

        threshold(input_gray_image, result_binary_image, 0, 255, THRESH_BINARY | THRESH_OTSU);

                

        imshow("이진화된 이미지", result_binary_image);

        imshow("입력 이미지", input_gray_image);

 

        //아무키나 누를 때 까지 대기한다.

        while (cvWaitKey(0) < 0);

}


노이즈가 많은 경우에 대해서도 테스트 해보았다. 노이즈가 많은 경우 adaptive thresholding는 노이즈만 이진화되었는데 otsu방법을 사용하자 흰색 물체가 검출되었다.


adaptive thresholding


otsu 방법

 

참고 

http://hanzratech.in/2015/01/21/adaptive-thresholding.html

http://docs.opencv.org/3.1.0/d7/d4d/tutorial_py_thresholding.html



반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts