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에서 이진화 전 객체를 흰색으로 놓으면 안된다. 흰색이라면 이진화 후 검정으로 바뀜에 따라 객체를 못찾기 때문이다.
#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 |