[OpenCV 4] 4. OpenCV 주요 기능
OpenCV에서는 카메라 또는 동영상 파일로부터 정지 영상 프레임을 받아 올 때 VideoCapture 클래스를 이용한다. VideoCapture 클래스의 정의와 다양한 멤버 함수 사용법에 대해 알아보고, 실제로 카메라와 동영상 파일을 재생하는 소스 코드 작성 방법에 대해 알아보자.
VideoCapture 클래스
동영상이란 일련의 정지 영상을 압축하여 파일로 저장한 형태이다. 이때 동영상에 저장되어 있는 일련의 정지 영상을 프레임(frame)이라고 한다. 그러므로 동영상을 처리하는 작업은 동영상에서 프레임을 추출한 후, 각각의 프레임에 영상 처리 기법을 적용하는 형태로 이루어진다.
void camera_in()
{
VideoCapture cap(0);
if (!cap.isOpened()) {
cerr << "Camera open failed!" << endl;
return;
}
cout << "Frame width: " << cvRound(cap.get(CAP_PROP_FRAME_WIDTH)) << endl;
cout << "Frame height: " << cvRound(cap.get(CAP_PROP_FRAME_HEIGHT)) << endl;
Mat frame, inversed;
while (true) {
cap >> frame;
if (frame.empty())
break;
inversed = ~frame;
imshow("frame", frame);
imshow("inversed", inversed);
if (waitKey(10) == 27) // ESC key
break;
}
destroyAllWindows();
}
카메라를 키는 기본적인 코드이다.
다양한 그리기 함수
직선 그리기
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0); | |
• img |
입출력 영상 |
• pt1 |
시작점 |
• pt2 |
끝점 |
• color |
선 색상(또는 밝기) |
• thickness |
선 두께 |
• lineType |
선 타입. LINE_4, LINE_8, LINE_AA 중 하나를 지정합니다. |
• shift |
그리기 좌표 값의 축소 비율(오른쪽 비트 시프트(>>) 연산) |
img 영상 위에 pt1 좌표부터 pt2 좌표까지 직선을 그린다.
화살표 형태의 직선을 그려야 하는 경우 arrowedLine() 함수
void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int line_type=8, int shift=0, double tipLength=0.1); | |
• img |
입출력 영상 |
• pt1 |
시작점 |
• pt2 |
끝점 |
• color |
선 색상 |
• thickness |
선 두께 |
• line_type |
선 타입. LINE_4, LINE_8, LINE_AA 중 하나를 지정합니다. |
• shift |
그리기 좌표 값의 축소 비율(오른쪽 비트 시프트(>>) 연산) |
• tipLength |
전체 직선 길이에 대한 화살표 길이의 비율 |
도형 그리기
사각형 void rectangle 함수
void rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0); | |
• img |
입출력 영상 |
• pt1 |
사각형 꼭지점 좌표. Point 객체 |
• pt2 |
pt1과 대각 방향에 있는 사각형 꼭지점 좌표. Point 객체 |
• rec |
사각형 위치 정보. Rect 객체 |
• color |
사각형 색상(또는 밝기) |
• thickness |
사각형 외곽선 두께. 이 값이 음수(-1 또는 FILLED)이면 내부를 채웁니다. |
• lineType |
선 타입 |
• shift |
그리기 좌표 값의 축소 비율(오른쪽 비트 시프트(>>) 연산) |
원 void circile 함수
void circle(InputOutputArray img, Point center, int radius, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0); | |
• img |
입출력 영상 |
• center |
원의 중심 |
• radius |
원의 반지름 |
• color |
원 색상 |
• thickness |
원 외곽선 두께. 이 값이 음수(-1 또는 FILLED)이면 내부를 채웁니다. |
• lineType |
선 타입 |
• shift |
그리기 좌표 값의 축소 비율(오른쪽 비트 시프트(>>) 연산) |
타원 void ellipse 함수
void ellipse(InputOutputArray img, Point center, Size axes, double angle, double startAngle, double endAngle, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0); | |
• img |
입출력 영상 |
• center |
타원의 중심 |
• axes |
타원의 반지름. Size(x축_반지름, y축_반지름) |
• angle |
타원 회전 각도(x축 기준, 시계 방향) |
• startAngle |
타원 호의 시작 각도(x축 기준, 시계 방향) |
• endAngle |
타원 호의 끝 각도(x축 기준, 시계 방향) |
• color |
타원 색상 |
• thickness |
타원 외곽선 두께. 이 값이 음수(-1 또는 FILLED)이면 내부를 채웁니다. |
• lineType |
선 타입 |
• shift |
그리기 좌표 값의 축소 비율(오른쪽 비트 시프트(>>) 연산) |
마스크 연산
OpenCV에서는 임의의 모양을 갖는 ROI 설정을 위하여 일부 행렬 연산 함수에 대하여 마스크(mask) 연산을 지원한다. 마스크 연산을 지원하는 OpenCV 함수는 보통 입력 영상과 크기가 같고 깊이가 CV_8U인 마스크 영상을 함께 인자로 전달받는다. 마스크 영상이 주어질 경우, 마스크 영상의 픽셀 값이 0이 아닌 좌표에 대해서만 연산이 수행된다. 일반적으로 마스크 영상은 픽셀 값이 0 또는 255로 구성된 흑백 영상이 사용된다.
Mat& Mat::setTo(InputArray value, InputArray mask = noArray()); |
|
• value |
행렬 원소에 설정할 값 |
• mask |
마스크 행렬. 마스크 행렬의 원소가 0이 아닌 위치에서만 value 값이 설정됩니다. 행렬 전체 원소 값을 설정하려면 noArray() 또는 Mat()을 지정합니다. |
• 반환값 |
Mat 객체의 참조 |
마스크 연산을 지원하는 setTo 함수이다.
void Mat::copyTo(OutputArray m, InputArray mask) const; |
|
• m |
복사본이 저장될 행렬. 만약 *this 행렬과 크기 및 타입이 다르면 메모리를 새로 할당한 후 픽셀 값을 복사합니다. |
• mask |
마스크 행렬. 마스크 행렬 원소 값이 0이 아닌 좌표에서만 행렬 원소를 복사합니다. mask 행렬은 *this와 같은 크기이고 깊이는 CV_8U이어야 합니다. |
행렬 복사 함수 인 copyTo 함수이다. 이 함수는 두 가지 형태로 정의되어 있다. 하나는 복사할 대상 행렬 하나만 인자로 받고, 다른 하나는 복사할 대상 행렬과 마스크 영상 두개를 인자로 받는다.
마스크 연산을 지원하는 Mat::copyTo() 함수는 mask 영상의 픽셀 값이 0이 아닌 위치에서만 *this 행렬 원소 값을 행렬 m으로 복사한다.
픽셀 값의 차이를 이용해 마스크 영상을 만들고 영상의 일부분만 복사해서 새로운 영상 합치기
copyTo 함수를 활용하기
void mask_copyTo()
{
Mat src = imread("image/Plane2.jpg", IMREAD_COLOR);
//Mat mask = imread("image/mask_plane.bmp", IMREAD_GRAYSCALE);
Mat dst = imread("image/field.bmp", IMREAD_COLOR);
Mat mask;
src.copyTo(mask);
if (src.empty() || mask.empty() || dst.empty()) {
cerr << "Image load failed!" << endl;
return;
}
for (int j = 0; j < mask.rows; j++)
{
for (int i = 0; i < mask.cols; i++)
{
Vec3b & pixel = mask.at<Vec3b>(j, i);
if (((pixel[0] >106) && (pixel[0]<116)) && ((pixel[1] >65 ) && (pixel[1] < 75)) && ((pixel[2] > 86) && (pixel[2] < 96)))
{
pixel[0] = 0;
pixel[1] = 0;
pixel[2] = 0;
}
else
{
pixel[0] = 255;
pixel[1] = 255;
pixel[2] = 255;
}
}
}
src.copyTo(dst, mask);
imshow("src", src);
imshow("dst", dst);
imshow("mask", mask);
waitKey();
destroyAllWindows();
}
오른쪽 그림 : 기본 filed image
왼쪽 그림 : 기존의 filed image에 마스크 이미지와 원본을 이용해서 copyTo 함수를 활용한 결과 사진
마스크 연산 관련 함수를 이용해 두 그림을 합쳐보았다.
참고자료 : OpenCV 4로 배우는 컴퓨터 비전과 머신러닝