이진 영상에서 객체의 외곽선을 추출하는 방법에 대해 소개하도록 하겠습니다.
이진 영상에서 외곽선 검출하기
객체의 외곽선을 검출하는 함수 이름은 findContours 입니다. Contour 글자에 s가 붙은 것을 확인할 수 있습니다. 영상 내부에는 여러 개의 객체가 존재할 수 있고, 각 객체마다 독립된 외곽선으로 표현할 수 있습니다. 즉, findContours 함수는 영상 내에 존재하는 여러 객체의 외곽선을 검출하여 반환합니다.
하나의 외곽선은 점의 집합으로 구성됩니다. 그러므로 객체를 검출한 결과는 최종적으로 std::vector<std::vector<cv::Point>>으로 반환됩니다.
외곽선 검출 함수
void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy,
int mode, int method, Point offset = Point())
- InputArray image: 입력영상 (8UC1)
- OutputArrayOfArrays: 검출된 객체의 외곽선들 (e.g. std::vector<std::vector<cv::Point>>)
- OutputArray hierarchy: 외곽선의 계층 정보를 받아옵니다. std::vector<cv::Vec4i>타입이며, 외곽선 개수만큼의 요소를 갖고 있습니다.
※ Vec4i 저장된 정보: hierarchy[i][0]: next, hierarchy[i][1]: prev, hierarchy[i][2]: first child, hierarchy[i][3]: parents
※ findContours 함수는 2개로 오버로딩 되어있는데, 차이는 hierarchy 인자의 유무임 - int mode: 외곽선 검출 모드 설정
- int method: 외곽선 근사화 알고리즘 설정
외곽선 검출모드
Enum | Description |
RETR_EXTERNAL | 객체 최외곽선만 검출 (계층구조 X) |
RETR_LIST | 계층적 관계를 설정하지 않고 모든 외곽선을 검출 |
RETR_CCOMP | 모든 외곽선을 검색하여 2단계 계층구조로 구성 |
RETR_TREE | 모든 외곽선을 검색하고 전체 계층 구조를 구성 |
외곽선 근사화 알고리즘 설정
Enum | Description |
CHAIN_APPROX_NONE | 모든 외곽선 점의 좌표를 저장 |
CHAIN_APPROX_SIMPLE | 외곽선에서 수평, 수직 및 대각선 성분은 끝점만 저장 (직사각형 윤곽은 4개의 점으로 인코딩됨) |
CHAIN_APPROX_TC89_L1 | Teh-Chin L1 근사화 적용 |
CHAIN_APPROX_TC89_KCOS | Teh-Chin k cos 근사화 적용 |
외곽선 검출 예제
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("contour.bmp", IMREAD_GRAYSCALE);
if (img.empty()) {
cerr << "failed to read image" << endl;
return 1;
}
vector<vector<Point>> contours;
//findContours(img, contours, RETR_LIST, CHAIN_APPROX_NONE);
findContours(img, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
Mat dst;
cvtColor(img, dst, COLOR_GRAY2BGR);
Scalar c(0, 0, 255);
for (int i = 0; i < contours.size(); i++) {
drawContours(dst, contours, i, c, 3);
}
imshow("img", img);
imshow("dst", dst);
waitKey(0);
destroyAllWindows();
return 0;
}
예제 결과
findContours(img, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE)
findContours(img, contours, hierarchy, RETR_LIST, CHAIN_APPROX_NONE)
위 예제에서는 RETR_LIST를 사용하든, RETR_TREE를 사용하든 보여지는 결과는 같습니다.
다만 RETR_LIST의 경우 hierarchy는 동등하기 때문에 vec4i는 0번(next)과 1번(prev) 요소만 사용하게 됩니다. RETR_TREE는 hierarchy가 설정되어 있기 때문에, 2번(first child)과 3번(parent)요소만 사용합니다.