OpenCV란?
기본 자료구조 및 픽셀
기본 명령어 (입출력 및 그리기)
컬러 변환 및 채널 분리
블러링(Blurring) 필터
이진화(Binarization) & Threshold
형태학적(Morphology) 필터
중요 에지(Edge) 검출
중요 직선 검출 <- Hough Line 변환
기본 알고리즘
핵심 코드 분석
(main, img_process, split_left_right, find_line 함수)
영상 처리 오픈소스 라이브러리
컴퓨터 비전, 머신러닝에 활용 가능
header와 data body로 구성되어 있다.header는 데이터 형식(bit), 행과 열(size), 채널 개수(color)등의 이미지를 구성하는 핵심 데이터를 보유한다.data body는 실제로 이미지나 비디오 프레임의 픽셀 값을 저장한다.body가 복제되지 않는다.body를 실제로 복제하려면, clone()등의 함수를 사용해야 한다.pixel_type을 아래와 같이 표현한다.
Ex)
CV_8UC3 : 3개의 channel, 8-bit/pixel
CV_8UC1 : 1개의 channel, 8-bit/pixel
CV_16UC3 : 3개의 channel, 16-bit/pixel
Pixel Type
BGR: blue - green - red (0 ~ 255)
GRAYSCALE: 0 (balck) ~ 255 (white)
HSV: Hue-saturation-value
H=색상(색조), S=채도(강도), V=명도(밝기)
YUV: Luma-chroma
Y=휘도, U,V=색차
BGR은 주로 이미지 처리에 사용되고,
YUV는 주로 비디오 처리나 압축에 사용된다.
HSV는 색상을 쉽게 조작할 수 있는 색상 공간이다
(실제 차선인식에서, 검은 도로와 흰 차선을 명도(V)차이로 쉽게 구분할 수 있다)
Creating Mat
Mat M (rows, colums, pixel_type, initial_value); Mar M (Size(width, height), pixel_type, initial_value);
row = height, colum = width와 동일하다.
Size()를 사용할 때 순서가 뒤바뀌는 것에 주의해야 한다.
pixel_type에는 위의 CV_8UC3 등의 표현이 쓰인다.
initial_value의 값은 (R,G,B)가 아니라 (B,G,R) 순서로 작성된다. (0~255 사이의 값)
Ex) Mat M (Size(20,15), CV_U8C3, Scalar(0,0,255));
Mat 객체의 데이터 타입, 채널 수 반환
Mat.type() 출력 >> CV_8UC3 # 8-bit unsigned integer # 3 channels (color image)
Mat 객체의 데이터의 비트 깊이 반환
Mat.depth() 출력 >> CV_8U # 8-bit unsigned integer (uchar)
Mat 객체의 특정 위치 Pixel값 반환
Mat.at<datatype>(row,col)[channel] Ex) i행 j열 Matrix BGR 타입인 경우, Mat.at<Vec3b>(i,j)[0] => blue(0~255) Mat.at<Vec3b>(i,j)[1] => green(0~255) Mat.at<Vec3b>(i,j)[2] => red(0~255)(i,j) 위치의 픽셀값 중 차례로 1, 2, 3 채널의 값을 벡터 자료형으로 반환하는 것
int imagesize = M.cols * M.rows * M.channels() * M.elemSize1();이미지 크기 = 가로 x 세로 x 채널수 x 요소개수
M.elemSize1()
M.elemSize() = M.elemSize1() * M.channels
Mat img = imread(name, IMREAD_COLOR); if (img.empty()) return; resize(img,img,Size(300,300.0*img.rows/img.cols)); imshow("org",img); Mat M1(img);
M2 = img;
M3 = Mat.clon(); Mat.copyTo(M4);
Read the color Image named "foo.png" - (이미지 읽어들이기)
Mat img1 = imread("foo.png",IMREAD_COLOR); Mat img2 = imread("foo.png",IMREAD_GRAYSCALE);
Write data contained in img1 to "bar.png" - (이미지 파일로 출력(저장)하기)
imwrite("bar.png",img1);
Creating a Window
namedWindow ("display",WINDOW-AUTOSIZE);
Display data stored in img1 - (window에 이미지 표시하기)
imshow("display",img1);
Closing and Destroying Window
destroyWindow("display")
Drawing on a Frame
drawing line
cv::line(img, start, end, color, thickness) cv::arrowedLine()drawing box
cv::rectangle(img, start, end, color, thickness)drawing circle
cv::circle(img, center, radius, color, thickness)drawing text
cv::putText(img, text, org, font, fontScale, color)
cvtColor(img, dst, COLOR_BGR2GRAYSCALE);
cvtColor(img, dst, COLOR_BGR2YUV);
cvtColor(img, dst, COLOR_BGR2HSV);
cvtColor(img, dst, COLOR_GRAY2BGR);
- 1채널 -> 3채널 변환
- 실제로는 1채널과 같은 색으로 보이지만, 3개의 채널이 모두 동일한 값
cvtColor(img, dst, COLOR_BGR2RGB);
split(Mat, vector<Mat>);
- R, G, B 흑백 이미지에 대한 3개의 Mat 행렬이 반환된다.
merge(vector<Mat>, Mat);
- 3개의 흑백 이미지(단일 채널 이미지)를 합쳐 1개의 컬러 이미지 Mat 행렬을 반환한다.
Mat M(img, Rect(30, 60, 200, 400)); #좌측 상단 모서리 좌표 = (30, 60) #폭 = 200, 높이 = 400 Mat M(img, Rect(Point(230, 60), Point(430, 460) #좌측 상단 모서리 좌표 = (230, 60) #우측 하단 모서리 좌표 = (430, 460)
- 기존 img 위에 ROI를 지정해 M이라는 사각형을 덮어 씌운 것
- Rect로 사각형 영역을 지정하는 방법 비교!
- 필터의 크기를 보통 홀수로 지정한다.
- 필터 내의 각 픽셀의 밝기에 대한 평균값을 구한다.
- 해당 픽셀의 밝기 값을 평균값으로 대체한다.
- 위 과정을 필터 내 모든 픽셀에 반복한다.
blur(img, blurred, Size(5,5));
- img: 노이즈가 있는 이미지
- blurred: 필터링후 반환된 이미지
- 5x5 크기의 필터 -> 25개의 픽셀
- 필터의 크기가 클 수록 노이즈 제거엔 유리하지만, 경계선이 뭉개져 blur 현상이 심해질 수 있다.
- 필터의 크기와 가중치를 정한다.
- 필터 내의 각 픽셀에 대해 가중치 값을 적용
- 해당 픽셀과 주변 픽셀의 값을 가중치를 곱하여 합산
- 위 과정을 필터 내 모든 픽셀에 반복한다.
GaussianBlur(img, blurred, Size(5, 5), 0, 0);
- img: 노이즈가 있는 이미지
- blurred: 필터링후 반환된 이미지
- 5x5 크기의 필터 -> 25개의 픽셀
- sigmaX = 0 (가로방향 가중치 0)
- sigmaY = 0 (세로방향 가중치 0)
- 가중치라 함은 픽셀 간 변화가 빠르게 일어나는 곳에 필터를 더 세게 가한다는 의미이다.
- 평균값 필터보다 blur 현상이 덜 심하게 일어난다.
- 입력 이미지의 각 픽셀을 순회
- 현재 픽셀을 중심으로 필터(window) 크기 내의 모든 픽셀을 추출
- 추출된 픽셀들을 크기 순으로 정렬 (sorting)
- 정렬된 픽셀들 중 중간값(median)을 계산
- 현재 픽셀을 해당 중간값으로 대체
- 위 과정을 필터 내 모든 필셀에 반복한다.
medianBlur() 함수
medianBlur(img, blurred, 5);
- img: 노이즈가 있는 이미지
- blurred: 필터링후 반환된 이미지
- 5x5 크기의 필터 -> 25개의 픽셀
특징
- 중간값 필터는 매우 튀는 값을 삭제해 버리기 때문에 에지(edge)정보를 효과적으로 유지하면서 노이즈 제거를 할 수 있는 장점이 있다.
- slat & pepper Noise를 제거하는데 매우 효과적이다.
- 연산 비용이 매우 높기 때문에, 필터의 크기가 작을 때는 효과적이지만, 필터의 크기가 커질수록 처리 시간이 길어질 수 있다.
- 입력 이미지의 각 픽셀을 순회
- 현재 픽셀을 중심으로 필터 윈도우(window)를 설정
- 해당 윈도우 내의 각 픽셀과 현재 픽셀 간의 거리와 값의 차이를 계산
- 거리와 값의 차이에 따라 가중치(weight)를 계산
- 계산된 가중치를 이용하여 윈도우 내의 모든 픽셀에 대한 평균값을 계산
- 계산된 평균값을 현재 픽셀의 새로운 값으로 설정
- 위 과정을 필터 내 모든 필셀에 반복한다.
bilateralFilter() 함수
bilateralFilter(img, blurred, 5, 75, 75);
- img: 노이즈가 있는 이미지
- blurred: 필터링후 반환된 이미지
- 5x5 크기의 필터 -> 25개의 픽셀
- 색 공간 필터 시그마: sigma_color
- 거리 공간 필터 시그마: sigma_space
- sigma_color이 클 수록 더 넓은 범위의 색상이 평균화되어 이미지가 더 많이 흐려진다.
- sigma_space가 클 수록 더 멀리 있는 픽셀까지 고려하여 이미지가 더 많이 흐려진다.
특징
- 일반적으로 이미지 필터링은 픽셀 간 거리(distance)와 차이(difference)를 기반으로 수행된다.
- 거리 기반 필터는 필터(window) 내의 픽셀 간 거리에 따라 가중치를 부여하여 필터링을 수행 하지만(Gaussian), 이러한 필터링 방식은 엣지 정보를 제대로 보존하지 못하는 단점이 있다.
- 이를 해결하기 위해 양방향 필터는 거리와 차이 두 가지 요소를 모두 고려한다. ( 주변 픽셀과의 거리가 멀어지고, 값의 차이가 크면 가중치가 감소)
- 엣지 정보를 보존하면서 노이즈를 제거가 가능하다.
: 임계값 처리 필터이다.
영상의 픽셀값들을 기준값 이상과 미만의 두 가지로 나눠 픽셀값을 분류 결과에 따라 재설정 하는 처리 방법이다.
종류
- Global T (전역적 문턱치)
: 영상 전체에서 하나의 문턱치 값을 적용- Adaptive T (가변적 문턱치)
: 영상내 각 영역에 따라 문턱치 값을 다르게 적용
Threshold() 함수
threshold (img, dst, threshold_value, max_val, threshold_type)
- img: 입력 이미지
- dst: 출력 이미지
- threshold_value: T 값, 이진화를 시키는 기준 값
- max_val: T 이상인 픽셀이 취하는 값 (보통 255)
- threshold_type: 임계값을 정하는 7가지 방법(종류)
threshold_type 7가지 (Thresholding)
THRESHOLD_OTSU
threshold (img, dst, 0, 255, THRESH_OTSU)
adaptiveThreshold(img, dst, maxValue, adaptiveMethod, thresholdType, ksize, C)
- mxaValue: T 이상일 때 변환 할 값 (보통 255)
- adaptiveMethod 임계값 계산 방법: ADAPTIVE_THRESH_GAUSSIAN_C
- thresholdType 이진화 방법: THRESH_BINARY 또는 THRESH_BINARY_INV
- ksize: 임계값 계산에 사용할 블록 크기. 홀수 값만 가능
- C: 계산된 임계값에서 더할 상수. 이 값이 크면 더 많은 픽셀이 흰색으로 설정 됨
inRange() 함수
inRange(img_hsv, Scalar(50,100,50), Scalar(80,255,255), green)
- img_hsv: 원본 이미지
- 1채널: 50 ~ 80 인 경우만 녹색 반환
- 2채널: 100 ~ 255 인 경우만 녹색 반환
- 3채널: 50 ~ 255 인 경우만 녹색 반환
침식
커널 B에 의한 A에 대한 침식(A (-) B)
A pixel in the original image will be considered 1 only if all the pixels under the kernel is 1, otherwise it is eroded (made to 0).
(하나라도 1(흰색)인 pixel이 있는 kernel은 모두 0(검은색)으로)
erode() 함수 - 객체를 축소 또는 가늘게 만듦
erode(src, dst, kernel, anchor, iteration, borderType, boderValue);
- src: 입력이미지
- dst: 출력이미지
- kernel: 침식을 수행하는 구조적 요소
커널의 크기와 모양을 결정- anchor: 커널 내에서 참조 픽셀의 위치
기본값 = (-1, -1) 커널의 중심- iteration: 팽창 반복횟수
- borderType: 이미지 경계를 처리하는 방법
기본값 = cv::BORDER_CONSTANT- borderValue: 경계가 BORDER_CONSTANT로 설정된 경우, 경계를 채울 값
팽창
커널 B에 의한 A에 대한 팽창 (A (+) B)
just opposite of erosion. Here, a pixel element is '1' if at least one pixel under the kernel is '1'.
(하나라도 1(흰색)인 pixel이 있는 kernel은 모두 1(흰색)으로)
dilate() 함수 - 객체를 커지게 혹은 두꺼워지게 만듦
dilate(src, dst, kernel, anchor, iteration, borderType, boderValue);
- src: 입력이미지
- dst: 출력이미지
- kernel: 팽창을 수행하는 구조적 요소
커널의 크기와 모양을 결정- anchor: 커널 내에서 참조 픽셀의 위치
기본값 = (-1, -1) 커널의 중심- iteration: 팽창 반복횟수
- borderType: 이미지 경계를 처리하는 방법
기본값 = cv::BORDER_CONSTANT- borderValue: 경계가 BORDER_CONSTANT로 설정된 경우, 경계를 채울 값
Opening (A opening by B 인 경우,)
침식 후 팽창 (A' = A eroded by B --> A' dilated by B)
B에 의해 침식한 결과를 다시 팽창하는 것
객체의 윤곽을 부드럽게 만듦, 좁은 지협을 끊음, 돌출부 제거
white dots 노이즈를 제거하는데 효과적이다
Closing (A closing by B 인 경우,)
팽창 후 침식 (A' = A dilated by B --> A' eroded by B)
B에 의해 팽창한 결과를 다시 침식하는 것
마찬가지로 윤곽을 부드럽게 만들지만,
opening과 반대로 좁고 길고 가는 틈을 끊지 않고 붙인다.
간극을 채운다고 생각하면 될 것 같다.
black dots 노이즈를 제거하는데 효과적이다
morphologyEX(src, dst, op, kernel, anchor, iterations, borderType, borderValue)
- 위의 erode, dilate 함수와 동일하므로 추가된 op에 대해서만 설명한다.
- op: 연산 유형 (operation type)
- MORPH_ERODE: 침식 (erode)
- MORPH_DILATE: 팽창 (dilate)
- MORPH_OPEN: 열림 연산 (opening)
- MORPH_CLOSE: 닫힘 연산 (closing)
- MORPH_GRADIENT: 팽창과 침식의 차이를 계산하여 객체 경계를 강조
- MORPH_TOPHAT: 입력 이미지와 열림 연산 결과의 차이를 계산하여 입력 이미지에서 작은 객체를 제거
- MORPH_BLACKHAT: 입력 이미지와 닫힘 연산 결과의 차이를 계산하여 입력 이미지의 작은 구멍을 채움
- 이외에도 사각형, 타원, 십자가 등 구조 요소의 모양을 정의할 수도 있다.
정의
에지 타입
Step edge: 1화소 거리(인접한 픽셀)에서 이상적으로 일어나는 에지 (급격한 변화)
Ramp edge: 인접한 픽셀 간의 강도 값이 서서히 변하는 에지, 일반적으로 영상이 무뎌지고 노이즈가 낀 에지를 가짐
Roof edge: 스텝 엣지처럼 급격히 변하지는 않지만 강도 값이 변하는 엣지로 스텝 엣지와 램프 엣지의 조합, 영역을 지나델 선에 대한 모델
Ridge edges: 서로 가깝게 위치하고 평행한 두 엣지가 있을 때 발생, 릿지처럼 생긴 선으로 나타남
Junctions: 두 개 이상의 엣지가 만나는 이미지 상의 지점, 객체 인식에 중요하며 이미지 상의 객체의 모양을 감지하는 데 사용할 수 있다.
미분에 의한 에지 검출
대표적인 에지 검출 필터이다.
멀리 있는 것 보다 가까이 있는 pixel값이 더 중요하다는 논리
Sobel() 함수
Sobel(input, output, ddepth, dx, dy, ksize, scale, delta, border);
- input: 입력 이미지
- ouput: 출력 이미지
- ddepth: 출력 이미지의 깊이
(일반적으로 입력 이미지와 동일)- dx: x축 방향으로 미분할 때 사용되는 미분 차수
(일반적으로 0 or 1)- dy: y축 방향으로 미분할 때 사용되는 미분 차수
(일반적으로 0 or 1)- ksize: Sobel 필터 커널의 크기
(일반적으로 3 or 5) (default = 3)- scale: 연산 결과에 적용되는 배율 계수
(default = 1.0)- delta: 연산 결과에 적용되는 이동 계수
(default = 0)- border: 이미지 경계 처리 방법
(일반적으로 BORDER_DEFAULT)
대표적인 에지 검출 필터이다.
Non-Maximum Suppression (local maximum을 찾는 방법)
(1) 각 필셀은 gradient와 magnitude 값을 가진다. (vector생각)
(2) 각 픽셀의 gradient 정보를 얻는다.
(3) 원하는 방향과 같은 gradient를 가진 인접한 픽셀들을 추린다.
(4) 추려진 픽셀들 중 magnitude가 가장 큰 픽셀만 남기고 모두 제거한다.
(5) magnitude가 작아 blur로 표현되던 픽셀들이 제거된다.
Double Threshold (조금 더 신중한 에지 검출)
(1) 각 픽셀의 gradient 값을 계산하여 각 픽셀의 엣지 강도를 추정한다.
(2) 두 개의 임계값(threshold)을 설정한다.
(3) low threshold보다 작은 엣지 픽셀은 엣지가 아니라고 간주한다.
(4) high threshold보다 큰 엣지 픽셀은 엣지로 간주한다주
(5) 그 사이값을 "weak edge pixel" 로 정의한다.
(6) "weak edge pixel" 중에서 강한 엣지와 직접 연결되어 있는 픽셀들을 강한 엣지로 재분류하고, 그렇지 않은 경우에는 엣지에서 제외한다.
(7) 노이즈나 작은 세부 정보를 제거하고 보다 정확한 엣지 검출을 수행할 수 있다.
Canny()
Canny(input, output, threshold1, threshold2, ksize, [L2flag]);
- input: 입력 이미지
- ouput: 출력 이미지
- threshold1: low threshold
- threshold2: high threshold
- ksize: Canny 검출 커널의 크기
- L2flag: 유클리드 거리(L2 norm)를 사용할 지 여부
- True: L2 norm(유클리드 거리) 사용하여 더 정확한 gradient magnitude를 계산한다.
- False: L1 norm(맨해튼 거리) 사용하여 빠르지만 정확도 손실이 있을 수 있다.
(유클리드/맨해튼은 기본 상식이다. 까먹었거나 헷갈리면 바로 공부해라!!)
low / high threshold 동적으로 바꾸기 예제
static Mat input_img, edge_img; static char *win_name = "Canny"; static int lowTh, highTh; void CannyThr(int, void*){ Canny(input_img, edge_img, lowThr, highThr, 3); imshow(win_name, edge_img); } void Canny_test_thresholds(char *name){ input_img = imread(name, 0); edge_img.create(input_img.size(), input)img,type()); namedWindow(win_name, CV_WINDOW_AUTOSIZE); createTrackbar("Threshold1", win_name, &lowThr, 200, CannyThr); createTrackbar("Threshold2", win_name, &highThr, 255, CannyThr); waitKey(0); }
- Threshold1 (low), Threshold2(high) 값을 바꿔가며 결과를 확인할 수 있다.
필요성: sobel이나 canny등으로 에지를 추출한 후, 에지 정보를 연결하여 관심 정보(직선)을 추출하기 위해 필요한 과정
개념: 이미지에서 선과 원 같은 단순한 형태를 찾는 방식
이진화 된 이미지에서 직선을 찾을 때 많이 사용된다. (직선 검출)
Image space => Parameter space (그림이 있으면 좋은데 귀찮으니 말로만..)
Image space(x-y평면)에서 직선의 기울기를 a, y절편을 b라 하자 (y = ax + b)
Image space 에서의 한 점(xi, yi)은 Parameter space(a-b 평면)의 한 직선(b=-ax+y)이 될 수 있다.
이 때, Img space 에서 한 직선위의 (xi, yi) 점들을 Param space 에서 직선으로 표현하면, 직선들이 만나는 한 점이 생기게 된다.
그러니까 각각의 공간에서 점이 선이되고 선이 점이되는 것이다.
Img space에서 추출된 에지 점들(xi, yi)이 Param space에서 직선으로 표현되고, 그 직선들의 교점들이 나타내는 기울기 및 절편 성분(a, b)들이 다시 Img space에서 직선으로 표현될 수 있는 것이다!! (이해 가능?)
그런데, 여기에 문제가 있다. Img space 에서 기울기가 무한대인 경우(vertical), Param space에서 표현이 불가능하다.
이를 해결하기 위해 직선의 원점으로부터의 수직거리(rho)와 수직각도(theta)로 표현되는 Hough space (ρ-θ 평면)을 사용한다.
Img space에서의 점이 Hough space에서는 곡선으로 표현된다.
Param space에서의 직선 개념을 Hough space의 곡선에 적용시켜 위와 동일하게 이해하면 된다.
(1) 선 Grayscale 영상 변환
(2) 에지 영상 획득
(3) ρ-θ 평면에서 구획 지정 (간격)
(4) 에지르ㄹ 이루는 점들에 대해 각각 ρ, θ를 계산하여 각 구획에 vote
문턱값 이상의 vote를 받은 구간에서의 ρ,θ 값을 직선으로 간주
HoughLinesP(input, output, rho, theta, threshold, minLineLength = 0, manLingGap = 0);
- input: 입력 이미지
- ouput: 출력 이미지
- rho: rho 값의 간격
(일반적으로 1)- theta: theta 값의 간격
(일반적으로 CV_PI/180로 라디안 값 사용)- threshold: 허프 변환 결과로 직선으로 판단할 임계값
(값이 작을수록 더 많은 직선이 검출)- minLineLength: 검출된 직선 중에서 최소한으로 인정할 선분의 길이
(해당 길이 이하의 직선은 삭제)- maxLineGap: 끊어진 선분의 허용할 수 있는 간격
(선분으로 판단되는 두 직선 사이의 거리가 이 값 이하일 경우, 같은 선분으로 간주)
이미지에서 객체를 찾아내기 위해 객체의 외곽을 감지하는 기술
입력 이미지에서 적절한 임계값(threshold)을 사용하여 이진화(binary)된 이미지를 만들고 윤곽선(contour)을 찾아낸다.
알고리즘 작동
(1) 입력 이미지를 그레이스케일(grayscale)로 변환한다.
(2) 그레이스케일 이미지를 이진화(binarization)한다.
(3) 윤곽선을 검출한다.
(대표적인 알고리즘으로 Canny Edge Detection, Sobel Edge Detection, Laplacian Edge Detection 등이 있다.)
(4) 검출된 윤곽선으로 객체의 경계를 추출한다.
findContours(image, contours, hierarchy, mode, method, offset);
- image: 입력 이미지
(8-비트 단일 채널 이미지)- contours: 검출된 윤곽선 좌표들을 저장할 리스트
(vector<vector<Point>>)- hierarchy: 윤곽선들의 계층 정보를 저장할 리스트
(hierarchy[i] list에서,
[i][0]=next, [i][1]=prev, [i][2]=first child, [i][3]=parent)- mode: 윤곽선 검출 모드
- CV_RETR_EXTERNAL: 외곽 경계선만 찾는다.
즉, 계층 구조에서 가장 바깥쪽 윤곽선만 검출한다.- CV_RETR_LIST: 모든 경계선을 찾는다.
계층 구조를 구성하지 않고 각각의 윤곽선을 검출한다.- CV_RETR_CCOMP: 객체의 계층 구조를 검출한다.
계층 구조에서 상위 윤곽선과 하위 윤곽선을 모두 검출하여 객체의 구성 요소를 구분한다.- CV_RETR_TREE: 전체 계층 구조를 검출한다.
검출된 윤곽선들이 전체적인 계층 구조를 이루며, 계층 구조에서 모든 윤곽선을 검출한다.- method: 윤곽선 근사화 방법
- CV_CHAIN_APPROX_SIMPLE: 윤곽선의 모든 점을 저장한다. (대부분 이걸 사용한다.)
- CV_CHAIN_APPROX_NONE: 윤곽선의 일부 점들만 저장한다.
- offset: 윤곽선 좌표에 더할 오프셋 값
(기본값은 (0, 0))
drawContours(image, contours, contourIdx, color, thickness, herarchy, maxLevel, offset);
- contours: 윤곽선을 저장하는 리스트
(findContours()함수에서 구해진 윤곽선)- contourIdx: 그리고자 하는 윤곽선의 인덱스
- color: 윤곽선의 색상
- thickness: 윤곽선의 두께
(기본값은 1) (-1인 경우 윤곽선 내부를 채움)- hierarchy: 윤곽선의 계층 구조 정보를 담은 배열
(일반적으로 None)- maxLevel: 그리고자 하는 윤곽선의 최대 레벨
(기본값은 INT_MAX)
Mat src = imread("bird.png"); //edge extraction Mat canny; Canny (src, canny, 30, 60); dilate(canny, canny, Mat(), Point(-1,-1), 1); //find contours vector<vector<Point>> contour; vector<Vec4i> hierarchy; findContours(canny, controus, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE); Mat result = src.clone(); for (size_t i=0; i<contours.size(); i++){ Scalar color(rand()%256, rand()%256, rand()%256); drawContours(result, contours, i, color, -1); } imshow("src", src); imshow("Canny", canny); imshow("result", result); waitKey(0);
approxPolyDP(curve, approxCurve, epsilon, closed)
- curve: 근사화 할 윤곽선
(findContours의 vector 를 입력으로 사용)- approxCurve: 근사화된 윤곽선이 저장될 변수
- epsilon: 근사화할 정확도를 결정한다.
(값이 작을수록 정확도가 높다.)- closed: 컨투어가 폐곡선인지 아닌지를 결정한다.
(True: 폐곡선으로 근사화)