Computer Vision/OpenCV

[OpenCV 4] 4. OpenCV 주요 기능

SooHyun2i 2020. 11. 12. 17:17

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();
}

 

원본 영상 , 픽셀값의 차이를 이용해 만든 Mask 영상

오른쪽 그림 : 기본 filed image

 

왼쪽 그림 : 기존의 filed image에 마스크 이미지와 원본을 이용해서 copyTo 함수를 활용한 결과 사진

 

마스크 연산 관련 함수를 이용해 두 그림을 합쳐보았다.

 

참고자료 : OpenCV 4로 배우는 컴퓨터 비전과 머신러닝