[OpenCV] 2. 외곽선 응용 – approxPolyDP, 외곽선 또는 곡선 근사화

[OpenCV] 2. 외곽선 응용 – approxPolyDP, 외곽선 또는 곡선 근사화

이번 글에서는 외곽선 검출 응용인 외곽선 근사화에 대해 알아보겠습니다.

더글라스 포이커(Douglas-Peucker) 알고리즘

우선 외곽선 또는 곡선을 근사화하는 더글라스-포이커 알고리즘에 대해 알아보겠습니다.

더글라스-포이커 알고리즘은 아래 순서를 따릅니다.

  1. 입력된 외곽선에서 가장 멀리 떨어져있는 두 점을 직선으로 연결합니다.
  2. 이 직선에서 가장 멀리 떨어져있는 점을 근사화 점으로 추가합니다.
  3. 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();

실행 결과는 아래와 같습니다.

이상으로 외곽선 근사화에 대해 알아보았습니다.