#OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝 - 17
12.2 외곽선 검출
이진 영상에서 객체의 위치 및 크기 정보를 추출하는 유용한 방법
객체의 외과선 픽셀 좌표를 모두 추출하여 계층 정보와 함께 반환함
12.2.1 외곽선 검출
객체의 외곽선 : 객체 영역 픽셀 중에서 배경 영역과 인접한 일련의 픽셀을 의미
객체 바깥쪽 외곽선과 안쪽 홀 외곽선으로 구분할 수 있음
객체 하나의 외곽선은 여러 개의 점으로 구성됨 : vector<Point> 타입으로 저장
하나의 영상에는 여러 개의 객체가 존재할 수 있음 : vector<vector<Point>> 타입으로 표현함
// 외곽선 검출 및 그리기
void contours_basic()
{
Mat src = imread("contours.bmp", IMREAD_GRAYSCALE);
if (src.empty())
{
return;
}
vector<vector<Point>> contours;
findContours(src, contours, RETR_LIST, CHAIN_APPROX_NONE);
Mat dst;
cvtColor(src, dst, COLOR_GRAY2BGR);
for (int i = 0; i < contours.size(); i++)
{
Scalar c(rand() & 255, rand() % 255, rand() % 255);
drawContours(dst, contours, i, c, 2);
}
imshow("src", src);
imshow("dst", dst);
waitKey();
destroyAllWindows();
}
// 계층 구조를 사용하는 외곽선 검출 및 그리기
void contours_hier()
{
Mat src = imread("contours.bmp", IMREAD_GRAYSCALE);
if (src.empty())
{
return;
}
vector<vector<Point>> contours;
vector<Vec4i> heirarchy;
findContours(src, contours, heirarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
Mat dst;
cvtColor(src, dst, COLOR_GRAY2BGR);
for (int idx = 0; idx >= 0; idx = heirarchy[idx][0])
{
Scalar c(rand() & 255, rand() % 255, rand() % 255);
drawContours(dst, contours, idx, c, -1, LINE_8, heirarchy);
}
imshow("src", src);
imshow("dst", dst);
waitKey();
destroyAllWindows();
}
12.2.2 외곽선 처리 함수
boundingRect() : 주어진 외곽선 점들을 같싸는 가장 작은 크기의 사각형
minAreaRect() : 외곽선 또는 점들을 감싸는 최소 크기의 회전된 사각형
minEnclosingCircle() : 외곽선 또는 점들을 감싸는 최소 크기의 원
arcLength() : 임의의 곡선을 형성하는 점들의 집합을 가지고 있을 때, 해당 곡선의 길이
contourArea() : 임의의 외곽선 정보를 가지고 있을 때, 외곽선이 감싸는 면적
approxPolyDP() : 더글라스 포이거 알고리즘을 사용하여 외곽선 또는 곡선을 근사화함
더글라스 포이커 알고리즘 : 입력 외곽선에서 가장 멀리 떨어져 있는 두 점을 찾아 직선으로 연결하고 해당 직선에서 가장 멀리 떨어져 있는 외곽선상의 점을 찾아 근사화 점으로 추가하는 작업으로 반복하다가 새로 추가할 외곽선상의 점과 근사화에 의한 직선과의 수직 거리가 epsilon 인자(입력 외곽선 또는 곡선 길이의 일정 비율)보다 작으면 근사화를 멈춤
// 다각형 검출 및 인식
void setLabel(Mat& img, const vector<Point>& pts, const String& label)
{
Rect rc = boundingRect(pts);
rectangle(img, rc, Scalar(0, 0, 255), 1);
putText(img, label, rc.tl(), FONT_HERSHEY_PLAIN, 1, Scalar(0, 0, 255));
}
int main(int argc, char* argv[])
{
Mat img = imread("polygon.bmp", IMREAD_COLOR);
if (img.empty())
{
return -1;
}
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
Mat bin;
threshold(gray, bin, 200, 255, THRESH_BINARY_INV | THRESH_OTSU);
vector<vector<Point>> contours;
findContours(bin, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
for (vector<Point>& pts : contours)
{
if (contourArea(pts) < 400)
continue;
vector<Point> approx;
approxPolyDP(pts, approx, arcLength(pts, true) * 0.02, true);
int vtc = (int)approx.size();
if (vtc == 3)
{
setLabel(img, pts, "TRI");
}
else if (vtc == 4)
{
setLabel(img, pts, "RECT");
}
else if (vtc > 4)
{
double len = arcLength(pts, true);
double area = contourArea(pts);
double ratio = 4. * CV_PI * area / (len * len);
if (ratio > 0.8)
{
setLabel(img, pts, "CIR");
}
}
}
imshow("img", img);
waitKey();
return 0;
}