이진화는 가장 간단한 세그멘테이션(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
'OpenCV > OpenCV 강좌' 카테고리의 다른 글
Hough Line Transform 구현 (15) | 2016.09.21 |
---|---|
ArUco Marker Detection 구현 및 Pose Estimation (29) | 2016.09.09 |
opencv 윈도우 상에서 마우스 클릭한 위치 출력하기 (0) | 2016.07.06 |
opencv와 wxwidgets을 연동하여 웹캠에서 캡처한 영상을 화면에 출력하기 (2) | 2016.06.07 |
RANSAC을 이용한 Line fitting (0) | 2016.05.27 |
시간날때마다 틈틈이 이것저것 해보며 블로그에 글을 남깁니다.
블로그의 문서는 종종 최신 버전으로 업데이트됩니다.
여유 시간이 날때 진행하는 거라 언제 진행될지는 알 수 없습니다.
영화,책, 생각등을 올리는 블로그도 운영하고 있습니다.
https://freewriting2024.tistory.com
제가 쓴 책도 한번 검토해보세요 ^^
그렇게 천천히 걸으면서도 그렇게 빨리 앞으로 나갈 수 있다는 건.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!