반응형

이진화는 가장 간단한 세그멘테이션(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



반응형

포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
개선 사항을 댓글로 남겨주면 가능한 빨리 반영하도록 하겠습니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 ~~

유튜브 구독하기


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

  1. qpq93 2017.03.25 14:57

    라즈베리파이에서 이런 사진은 어디다가 저장하놔야 opencv동작하나요 ..??
    c++은 프로젝트폴더안에 넣으면 되긴하던데 ...

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.03.25 15:12 신고

      통합개발환경(IDE)를 쓴다면 사용하는 것에 따라 이미지 저장위치가 달라집니다.

      그냥 터미널에서 작업하는 거라면 보통 소스코드 있는 곳에 실행파일이 생성되니 사용하는 이미지도 같이 넣어두면 됩니다..

  2. studentt 2017.07.08 18:15

    이미지 파일로 이진화 하셨는데
    동영상으로도 이진화 가능한가요?
    방식은 어떻게 되는지 궁금합니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.07.08 20:50 신고

      웹캠에서 한프레밈 읽어오고 그레이 영상으로 바꾼 후 포스팅에 나온대로 해주면 됩니다..


      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);

  3. 학생 2017.09.26 22:22

    안녕하세요 글 올리신거 잘보고 있습니다.

    이진화를 하면서 궁금한 게 생겼습니다.

    이진화 된 사진에서 흰색 부분과 검은색 부분의 비율을 알기위해서

    검은 픽셀 부분의 수치(개수)를 알 수 있을까요?

    • 학생 2017.09.26 22:30

      예를들어 맨 위에 첫번째 사진에서
      빨간색 공의 크기를 검은색 픽셀의 길이를 통해 재는 방식이 궁금합니다

  4. jai3kkkoi 2020.05.24 16:21

    이미지에서 이진화는 성공 하였습니다...
    그런데 동영상에서 이진화를 시키지 못하고 있습니다ㅜㅜㅜ
    밑에 댓글 참고하여서 한 프레임당 이진화 시켜서 해보고 있는데
    자꾸 동영상이 재생이 안되네요ㅜㅜㅜㅜ
    혹시 가능하시다면 코드 한번만 적어 주실 수 있나요?
    (혹시 헷갈리실까봐 적을게요.
    "동영상->이진화된 동영상"을 원하는 것입니다.
    "동영상->이진화된 사진"이 아닙니다.)

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.24 16:26 신고

      OpenCV에서 동영상 저장시 컬러로 변환해서 해야합니다. cvtColor 함수에 변환파라미터로 COLOR_GRAY2B♬♩♫♪ 사용하여 이진화된 영상을 컬로로 바꾸어 저장해보세요

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.24 16:26 신고

      GRAY2B G R 입니다. 댓글이 이상하게 보이는군요

    • jai3kkkoi 2020.05.24 20:18

      예전에 올려주신 동영상 재생하는 게시물 참고해서 동영상 재생 코드에 위에 있는 adaptivethreshold 함수를 끼워 넣어 보았습니다. 말씀해주신 cvtcolor 함수를 어디에 넣어야 할까요..
      #include "opencv2/opencv.hpp"
      #include <iostream>

      using namespace cv;
      using namespace std;



      int main(int, char**)
      {

      VideoCapture cap1("D:\\test1.mp4");
      if (!cap1.isOpened())
      {
      printf("동영상 파일을 열수 없습니다. \n");
      }


      cap1.set(CAP_PROP_FRAME_WIDTH, 320);
      cap1.set(CAP_PROP_FRAME_HEIGHT, 240);


      Mat frame1;
      namedWindow("video", 1);



      for (;;)
      {


      cap1 >> frame1;

      Mat input_gray_image = imread("frame1", IMREAD_GRAYSCALE);

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

      Mat result_binary_image;


      adaptiveThreshold(input_gray_image, result_binary_image,

      255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 5, 10);



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

      imshow("video", frame1);



      if (waitKey(33) == 30) break;
      }


      return 0;
      }

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.24 20:28 신고

      코드에 잘못된 부분이 보이네요.. 아래처럼 코드를 수정해서 해보세요.

      수정전
      Mat input_gray_image = imread("frame1", IMREAD_GRAYSCALE);

      수정후
      Mat input_gray_image;
      cvtColor(frame1, input_gray_image, COLOR_BGR2GRAY);

    • jai3kkkoi 2020.05.24 20:36

      감사합니다ㅜㅜㅜ 5시간동안 헤메다가 드디어 해결합니다..

+ Recent posts