이번 글에서는 외곽선 검출 응용인 외곽선 근사화에 대해 알아보겠습니다.
더글라스 포이커(Douglas-Peucker) 알고리즘
우선 외곽선 또는 곡선을 근사화하는 더글라스-포이커 알고리즘에 대해 알아보겠습니다.
더글라스-포이커 알고리즘은 아래 순서를 따릅니다.
- 입력된 외곽선에서 가장 멀리 떨어져있는 두 점을 직선으로 연결합니다.
- 이 직선에서 가장 멀리 떨어져있는 점을 근사화 점으로 추가합니다.
- 1, 2 작업을 반복하다가 새로 추가할 근사화 점이 epsilon 값보다 작으면 근사화를 멈춥니다.
위 이미지는 더글라스-포이커 알고리즘을 설명하는 샘플 움짤입니다.
approxPolyDP
OpenCV에서는 외곽선 또는 곡선을 근사화하는 approxPolyDP 함수를 제공합니다.
/**
@param curve 입력 외곽선 정보
@param approxCurve 출력 근사화 점
@param epsilon 근사화 정밀도 파라미터
@param closed 폐곡선 여부. true: 폐곡선, false: 폐곡선 아님
*/
void approxPolyDP( InputArray curve, OutputArray approxCurve, double epsilon, bool closed );
샘플 코드는 아래와 같습니다.
Mat img = imread("polygons.png", IMREAD_COLOR);
if (img.empty())
{
cerr << "image load fail" << endl;
system("pause");
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) < 100)
continue;
// 외곽선 or 곡선 근사화
vector<Point> approx;
double dbArcLen = arcLength(pts, true);
approxPolyDP(pts, approx, arcLength(pts, true)*0.02, true);
Scalar cr = Scalar(255,0,255);
int approxSize = approx.size();
if (approxSize == 3)
{
Rect rc = boundingRect(pts);
rectangle(img, rc, cr, 2);
Point pntTl = rc.tl(); // rc.tl(): top, left 반환
putText(img, "TRI", pntTl, FONT_HERSHEY_SIMPLEX, 1, cr);
}
else if (approxSize == 4)
{
Rect rc = boundingRect(pts);
rectangle(img, rc, cr, 2);
Point pntTl = rc.tl(); // rc.tl(): top, left 반환
putText(img, "RECT", pntTl, FONT_HERSHEY_SIMPLEX, 1, cr);
}
else if (4 < approxSize)
{
double len = arcLength(pts, true);
double area = contourArea(pts);
double ratio = 4. * CV_PI * area / (len *len);
if (ratio > 0.8)
{
Rect rc = boundingRect(pts);
rectangle(img, rc, cr, 2);
Point pntTl = rc.tl(); // rc.tl(): top, left 반환
putText(img, "CIR", pntTl, FONT_HERSHEY_SIMPLEX, 1, cr);
}
}
}
imshow("img", img);
waitKey();
destroyAllWindows();
실행 결과는 아래와 같습니다.
이상으로 외곽선 근사화에 대해 알아보았습니다.