1. OpenCV 주요 외곽선 함수

함수 이름 설명
arcLength() 외곽선 길이를 반환
contourArea() 외곽선이 감싸는 영역의 면적 반환
boundingRect() 주어진 점을 감싸는 최소 크기 사각형(바운딩 박스) 반환
minEnclosingCircle()  주어진 점을 감싸는 최소 크기 원 반환
minAreaRect() 주어진 점을 감싸는 최소 크기 회전된 사각형 반환
minEnclosingTriangle() 주어진 점을 감싸는 최소 크기 삼각형을 반환
approxPolyDP() 외곽선을 근사화(단순화)
FitEllipse() 주어진 점에 적합한 타원을 반환
fitLine() 주어진 점에 적합한 직선을 반환
isContourConvex() 컨벡스인지를 검사
convexHull() 주어진 점으로부터 컨벡스 헐을 반환
convexityDefects() 주어진 점과 컨벡스 헐로부터 컨벡스 디펙트를 반환

 

1) 외곽선 길이 구하기

double arcLength(InputArray curve, bool closed);

curve: 외곽선 좌표

closed: true이면 폐곡선으로 간주

반환값: 외곽선 길이

 

2) 면적구하기

double contourArea(InputArray contour, bool oriented = false);

contour: 외곽선좌표   // Vector<Point> 또는 Vector<Point2f> 사용

oriented: true 이면 외곽선 진행 방향에 따라 부호 있는  면적 반환

반환값: 외곽선으로 구성된 영역의 면적

 

3) 바운딩 박스 구하기

외곽선을 감싸는 가장 작은 크기의 사각형을 구하여 그 정보를 Rect 클래스 타입으로 반환

Rect boundingRect(InputArray points);

points: 외곽선 좌표

반환값: 외곽선을 외접하여 둘러싸는 가장 작은 직사각형

 

4) 바운딩 서클 구하기

가장 작은크기의 원의 정보를 반환

void minEnclosingCircle(InputArray points, Point2f& center, float& radius);

points: 외곽선 좌표

center: (출력) 바운딩 서클 중심 좌표

radius: (출력) 바운딩 서클 반지름

 

5) 외곽선 근사화

더글라스 포이커 알고리즘을 활용한 도형의 외곽선을 단순하게 근사화시키는 함수.

void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed);

curve: 입력곡선.  std::vector<Point2f> 또는 Mat

approxCurve: 더글라스 포이커 알고리즘(Douglas-Peucker algorithm)으로 근사화된 외곽선.

                     std::vector<Point2f> 또는 Mat.

epsilon: 근사화 정밀도 조절. 입력 곡선과 근사화 곡선 간의 최대 거리.

             eg) arcLength(curve) * 0.02

 

closed:  true를 전달하면 폐곡선

 

[더글라스 포이커 점 없애는 기준 다시 한번 보자[이해부족]]

 

https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm

https://thebook.io/006939/ch12/02/02-03/

 

bool isContourConvex(InputArray contour);

contour: 입력 곡선좌표

반환값: 컨벡스이면 true, 아니면 false

convex란 볼록다각형을 의미함.

오목한 부분이 하나라도 있는 다각형은 convex 다각형 아님

 

2. 다각형 검출 프로그램

**

1)다양한 다각형 객체 영상에서 삼각형, 사각형, 원을 찾아보자

  - 구현순서

   a. 이진화   // 만일 배경이 흰색바탕이라면 이진화 할 때 반전된 이진화를 해야, 배경을 흰색이 아닌 검정으로 변경시킴에 따라 객체 오검출(배경을 객체로)을 하지 않을 것이다.

   b. 외곽선찾기 (find contours)

   c. 외곽선 근사화 (각 도형의 꼭지점 좌표 검출. 세개의 점으로 근사화 시 삼각형, 4개의 점 일 시 사각형으로 , 원이나 타원 처럼 꼭지점 없는 경우 입실론 값 설정에 따라 6,7,8각형으로 근사화가 될 것이다.

해당 프로그램에선 e. 에서  처럼 삼/사 각형만 검출하고 그 외는 원을 판별하는 검사 수행.

   d. 너무 작은 객체와 컨벡스가 아닌 객체는 제외 

   e. 꼭지점 개수 확인

         i) 삼각형, 사각형 검출

        ii) 원 판별

 

 

2) 원 판별하기.

 정해진 (findcountours로 외곽선 길이 구함)외곽선 길이에 대한 넓이 비율이 가장 큰 형태가 원

  -> 도형의 넓이(A) 와 외곽선 길이(P)의 비율을 검사.

A/P = pi*r^2/(2pi*r)  ->  A/P^2 = pi*r^2/(4pi^2*r^2) = 1/4pi  ->  4pi * (A/P^2) = 1

 

4pi(A/P^2) 값이 1에 가까울 수록 원으로 판단.

 

...
double area = contourArea(contours[i]);  //외곽선 면적
double len = arcLength(contours[i], true);  // 외곽선 길이
double ratio = 4. * CV_PI * AREA / (LEN * LEN); 

if (ratio > 0.85){  // ratio가 1에 가까울 수록 원으로 판단
    /*원*/
}

 

 

 다각형 판별 프로그램 예제

connectedComponents 나 findContours에서 이진화 전 객체를 흰색으로 놓으면 안된다. 흰색이라면 이진화 후 검정으로 바뀜에 따라 객체를 못찾기 때문이다.

 

polygon.bmp
0.88MB

#include <iostream>
#include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

void setLabel(Mat& img, const vector<Point>& pts, const String& label)
{
	Rect rc = boundingRect(pts);  //주어진 영상에 특정 좌표들을 둘러쌓인 bounding box 계산해
	rectangle(img, rc, Scalar(0, 0, 255), 1); //해당 위치에 해당 박스들 그림
	putText(img, label, rc.tl(), FONT_HERSHEY_PLAIN, 1, Scalar(0, 0, 255)); // rc.tl():top left 약자로 특정 사각형의 좌측 상단 좌표 반환. 그 위치에 label 문자열을 출력하는  putText함수
}

int main()
{
	Mat img = imread("polygon.bmp", IMREAD_COLOR);

	if (img.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	Mat gray;
	cvtColor(img, gray, COLOR_BGR2GRAY);

	Mat bin;
	threshold(gray, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);

	vector<vector<Point>> contours;
	findContours(bin, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE); // RETR_EXTERNAL : 객체 안에 홀이없기에 외곽선만 도출
	//hirarchy 정보 포함x

	for (vector<Point>& pts : contours) { //  외곽선 점들이 내재된 contours의 외곽선들을 하나씩 스캐닝하여 .
		if (contourArea(pts) < 400) // 각 외곽선 면적 400 이상만 검출
			continue;  // 작은 크기의 외곽선 객체인 흰색객체가 나타날수 있기에 이런 오검을 방지하기 위해 조건문 세움.

		vector<Point> approx;
		approxPolyDP(pts, approx, arcLength(pts, true)*0.02, true); // 각각의 외곽선을 근사화시킴.  3번쨰 인자 입실론 : 주어진 외곽선 전체 길이 * 0.02 형태로 입실론 값 지정함.
		// 각 도형의 꼭지점 좌표만으로 구성된 approx 벡터 포인터 타입 변수에 값들이 채워짐
		if (!isContourConvex(approx)) 
			continue;

		int vtc = (int)approx.size();  // 각 도형의 좌표들을 카운팅해 vtc 에 저장

		if (vtc == 3) { // vtc 갯수가 3이면 삼각형
			setLabel(img, pts, "TRI");
		} else if (vtc == 4) { // 4개이면 사각형
			setLabel(img, pts, "RECT");
		} else { // 나머지는 원을 검출하는 코드 
			double len = arcLength(pts, true);
			double area = contourArea(pts);
			double ratio = 4. * CV_PI * area / (len * len);

			if (ratio > 0.70) {  // ratio가 1에 가까울 수록 원으로 지정.
				setLabel(img, pts, "CIR");
			}
		}
	}

	imshow("img", img);
	waitKey();
}

ratio 조절 통해 타원을 원으로 검출할지 여부 결정 가능

영상 해상도에 따라 ratio 조절 해주는 상황도 있다.

'프로그래머스 > OPENCV' 카테고리의 다른 글

2주차 과제)subpixel 값으로 차선 엣지 좌표 검출하기  (0) 2022.12.11
참고  (0) 2022.12.09
레이블링과 외곽선 검출  (0) 2022.12.08
모폴로지 (잡음 제거)  (0) 2022.12.08
지역이진화  (0) 2022.12.08

+ Recent posts