12
2016-Mar
[OpenCV] 영상 이진화 & 레이블링(Blob Labeling)
작성자: Blonix
IP ADRESS: *.148.87.98 조회 수: 1535
아래 문자인식 강좌와 동일한 블로그의 글이다.
출처 :: http://martinblog.net/824
예제파일
http://fogeaters.cafe24.com/xe/board11/13777
이는 입력으로 들어가는 영상의 각 채널을 이진화 한다.
이런 이유로 컬러영상(3개 채널)을 이진화 하는 경우, R, G, B 각각을 이진화 하기 때문에
아래와 같이 의도하지 않은 영상을 얻게 될 수도 있다.
그런 이유 때문에 이진화 전에는 cvCvtColor()를 이용하여 Gray로 변환 후 이진화 한다.
cvCvtColor()의 세 번 째 인수는 어떤 컬러맵으로 변환할 것인지 선택할 수 있으며,
OpenCV에서 제공하는 방법들은 CV_BGR2GRAY 뿐만 아니라
CV_BGR2YCrCb, CV_BGR2HSV, CV_BGR2Lab 등 여러가지이며 이는 cv.h 파일에서 확인할 수 있다.
cvThreshold() 함수는 실제로 이진화를 수행하는 함수로,
세 번 째 인수는 문턱치를 설정하고,
네 번 째 인수는 문턱치를 넘어선 픽셀들을 어떤 값으로 설정할 것인지를,
다섯 번 째 인수는 이진화 알고리즘을 선택할 수 있다.
cvThreshold(gray, output, threshold, 255, CV_THRESH_BINARY);
위 함수는 입력된 gray 이미지에서 threshold 값을 초과한 값은 255 로 변환하고 이하는 0 으로 변환하여 output 이미지를 만들어 냅니다. 이와는 반대로 적용되게 한다거나 일정 값 이하는 0 으로 변환하고 초과되는 값만 보여주는 등의 옵션이 있습니다. 옵션을 변화 시켜가며 변화되는 모습을 익혀 보도록 합시다.
CV_THRESH_BINARY : threshold 값 초과는 255, 이하는 0
CV_THRESH_BINARY_INV : threshold 값 초과는 0, 이하는 255
CV_THRESH_TRUNC : threshold 값 초과는 threshold, 이하는 그대로
CV_THRESH_TOZERO : threshold 값 초과는 그대로, 이하는 0
CV_THRESH_TOZERO_INV : threshold 값 초과는 0, 이하는 그대로
이번 프로그램에는 Trackbar 가 추가되었는데 이는 나중에도 유용하게 쓰이므로 잘 익혀 두시기 바랍니다. 아래 함수는 "T9-camera" 이름의 윈도우에 0 부터 255 범위를 같는 "T" 이름의 Trackbar 를 붙입니다. Trackbar 의 초기 위치는 threshold 값이 결정하며 bar 를 조절하여도 그 값은 threshold 변수에 들어가게 됩니다. 그리고 현재는 NULL 로 되어 있지만 이부분에 함수명을 적으면 bar 가 변경될 때마다 해당하는 함수를 호출하게 됩니다. 이 함수는 반드시 int 형 변수 하나를 가지고 있어야 하고 이 변수에 Trackbar 의 값이 넘어 오므로 함수에서 값을 가지고 영상처리를 해주면 됩니다.
cvCreateTrackbar("T", "T9-camera", &threshold, 255, NULL);
cvCvtColor() 함수는 Color 변환을 해주는 함수입니다. 아래 함수는 RGB 채널을 가지고 있는 image 를 Gray 채널로 바꾸어서 gray 에 넣어주는 역할을 합니다. 마지막 인자는 Color 변환 모드인데 여기에도 다양한 옵션이 존재하므로 필요한 모델을 사용하시면 됩니다.
cvCvtColor(image, gray, CV_RGB2GRAY);
CV_RGB2GRAY : 흑백으로 변환
CV_RGB2YCrCb : 주로 Skin Color 모델을 할때 변환( 손 제스쳐 인식, 얼굴 인식 )
CV_RGB2HLS
CV_RGB2HSV
CV_RGB2Lab
CV_RGB2Luv
5. 결과




앞서 이진화된 영상을 반전시킨다.
미리 작성해둔 레이블링 클래스를 이용 레이블링을 수행한 뒤,
각 레이블의 정보를 담고 있는 m_recBlobs 변수로 부터 각 레이블의 정보를 가져와
화면출력을 위해 만들어둔 이미지에 각각의 영역을 빨간색으로 표시한다.

cvAdaptiveThreshold(src_gray, addap_thr, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 21, 10);
//세번째 255는 최대값. 이렇게 하면 0과 255로 이루어진 영상으로 됨
//마지막에서 두번째값은 threshold 계산 때 주변 픽셀 사용하는 크기. 3,5,7 식으로 홀수로 넣어줌
//마지막은 Constant subtracted from mean or weighted mean. It may be negative
출처 :: http://webnautes.tistory.com/823
openCV 3.0부터 라벨링 알고리즘이 추가되었습니다.. 이미지를 라벨링하고 원하는 라벨을 색으로 표현한다든가.. 각각의 영역들을 박스치는 것등이 쉽게되네요.. 해당 영역의 크기도 각각 계산되서 나옵니다.. 자세한건 아래 소스코드를 읽어 보세요...
- #include <iostream>
- #include <opencv2/core/mat.hpp>
- #include <opencv2/imgcodecs.hpp>
- #include <opencv2/imgproc.hpp>
- #include <opencv2/highgui.hpp>
- using namespace cv;
- using namespace std;
- int main() {
- Mat img_gray, img_color, img_binary;
- img_gray = imread("art8.jpg", IMREAD_GRAYSCALE );
- threshold(img_gray, img_binary, 127, 255, THRESH_BINARY);
- cvtColor( img_gray, img_color, COLOR_GRAY2BGR );
- Mat img_labels,stats, centroids;
- int numOfLables = connectedComponentsWithStats(img_binary, img_labels,
- stats, centroids, 8,CV_32S);
- //라벨링된 이미지중 특정 라벨을 컬러로 표현해주기
- for ( int y=0; y<img_labels.rows; ++y ) {
- int *label = img_labels.ptr<int>(y);
- Vec3b* pixel = img_color.ptr<Vec3b>(y);
- for (int x = 0; x < img_labels.cols; ++x) {
- if (label[x] == 3 ) {
- pixel[x][2] = 0;
- pixel[x][1] = 255;
- pixel[x][0] = 0;
- }
- }
- }
- //라벨링 된 이미지에 각각 직사각형으로 둘러싸기
- for (int j = 1; j < numOfLables; j++) {
- int area = stats.at<int>(j, CC_STAT_AREA);
- int left = stats.at<int>(j, CC_STAT_LEFT);
- int top = stats.at<int>(j, CC_STAT_TOP);
- int width = stats.at<int>(j, CC_STAT_WIDTH);
- int height = stats.at<int>(j, CC_STAT_HEIGHT);
- rectangle( img_color, Point(left,top), Point(left+width,top+height),
- Scalar(0,0,255),1 );
- putText(img_color, to_string(j), Point(left+20,top+20),
- FONT_HERSHEY_SIMPLEX, 1, Scalar(255,0,0), 2);
- }
- imshow( "result", img_color );
- waitKey(0);
- }
centroids를 이용하여 라벨링 된 영역의 중심을 출력해보았습니다..
- #include <iostream>
- #include <opencv2/core/mat.hpp>
- #include <opencv2/imgcodecs.hpp>
- #include <opencv2/imgproc.hpp>
- #include <opencv2/highgui.hpp>
- using namespace cv;
- using namespace std;
- int main() {
- Mat img_gray, img_color, img_binary;
- img_gray = imread("art8.jpg", IMREAD_GRAYSCALE );
- threshold(img_gray, img_binary, 127, 255, THRESH_BINARY);
- cvtColor( img_gray, img_color, COLOR_GRAY2BGR );
- Mat img_labels,stats, centroids;
- int numOfLables = connectedComponentsWithStats(img_binary, img_labels,
- stats, centroids, 8,CV_32S);
- //라벨링된 이미지중 특정 라벨을 컬러로 표현해주기
- for ( int y=0; y<img_labels.rows; ++y ) {
- int *label = img_labels.ptr<int>(y);
- Vec3b* pixel = img_color.ptr<Vec3b>(y);
- for (int x = 0; x < img_labels.cols; ++x) {
- if (label[x] == 3 ) {
- pixel[x][2] = 0;
- pixel[x][1] = 255;
- pixel[x][0] = 0;
- }
- }
- }
- //라벨링 된 이미지에 각각 직사각형으로 둘러싸기
- for (int j = 1; j < numOfLables; j++) {
- int area = stats.at<int>(j, CC_STAT_AREA);
- int left = stats.at<int>(j, CC_STAT_LEFT);
- int top = stats.at<int>(j, CC_STAT_TOP);
- int width = stats.at<int>(j, CC_STAT_WIDTH);
- int height = stats.at<int>(j, CC_STAT_HEIGHT);
- double *centroid = ¢roids.at<double>(j, 0); //중심좌표
- int x = centroid[0];
- int y = centroid[1];
- circle( img_color, Point(x,y), 5, Scalar(255,0,0), 1);
- rectangle( img_color, Point(left,top), Point(left+width,top+height),
- Scalar(0,0,255),1 );
- putText(img_color, to_string(j), Point(left+20,top+20), FONT_HERSHEY_SIMPLEX,
- 1, Scalar(255,0,0), 2);
- }
- imshow( "result", img_color );
- waitKey(0);
- }
영상 이진화 고급편
http://darkpgmr.tistory.com/115