3학년/OpenCV

[OpenCV] 3장 OpenCV 주요 클래스

천도복숭아에이드 2023. 5. 22. 04:30

 

 

"OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝"

 

3.1 기본 자료형 클래스

3.1.1 Point_ 클래스

 

// 3.1.1 Point class

template<typename _Tp> class Point_
{
public:
	Point_();	// 기본 생성자. x = 0, y = 0 으로 초기화한다.
	Point_(_TP _x, _TP _y);		// (_x, _y)좌표를 인자로 받는 생성자. x = _x, y = _y로 초기화된다.
	Point_(const Point_& pt);	// 복사 생성자. x = pt.x, y = pt.y로 초기화된다.

	Point_& operator = (const Point_& pt);		// 대입 연산자 재정의

	_Tp dot(const Point_& pt) const;	// Point::dot() 멤버 함수는 두 점 사이의 내적(dot product)를 계산하여 반환된다.
	double ddot(const Point_& pt) const;	// Point::ddot() 멤버 함수는 두 점 사이의 내적을 실수형으로 계산하여 double 자료형으로 반환
	double cross(const Point_& pt) const;	// Point::cross() 멤버 함수는 두 점 사이의 외적(cross product)을 반환
	bool inside(const Rect_<_Tp>& r) const;		// Point::inside() 멤버 함수는 점의 좌표가 사각형 r 영역 안에 있으면 true를 반환.

	_Tp x, y;	// 멤버 변수. x는 x축 좌표, y는 y축 좌표를 나타낸다.
};

// 다양한 자료형에 대한 Point_ 클래스 이름 재정의

typedef Point_<int> Point2i;
typedef Point_<int64> Point2l; 
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
typedef Point2i Point;

 

OpenCV 소스 코드에서 Point 클래스는 2차원 정수 좌표계에서 좌표를 표현하는 자료형이라고 생각하면 된다.

 

 

 

3.1.2. Size_ 클래스

 

// 3.1.2 Size class

template<typename _Tp> class Size_
{
public:
	Size_();							// 기본 생성자. width = 0, height = 0으로 초기화
	Size_(_Tp _width, _Tp _height);		// (_width, _height) 크기를 인자로 받는 생성자. width = _width, height = _height로 초기화된다.
	Size_(const Size_& sz);				// 복사 생성자. width = sz.width, height = sz.heigth로 초기화 된다.

	Size_& operator = (const Size_& sz);	// 대입 연산자 재정의

	_Tp area() const;	// Size::area() 멤버 함수는 사각형 크기에 해당하는 면적 (width x height)을 반환한다.
	bool empty() const;	// Size::empty() 멤버 함수는 유효하지 않은 크기이면 true를 반환한다.

	_Tp width, height;	// 멤버 변수. width는 사각형 영역의 가로 크기, height 는 사각형 영역의 세로 크기를 나타낸다.
};


// 다양한 자료형에 대한 Size_ 클래스 이름 재정의

typedef Size_<int>		Size2i;	
typedef Size_<int64>	Size2l;
typedef Size_<float>	Size2f;
typedef Size_<double>	Size2d;
typedef Size2i			Size;

 

Size 클래스는 정수형 멤버 변수 width, height를 가지고 있는 사각형 크기 표현 클래스이다.

 

 

 

3.1.3 Rect_ 클래스

 

// 3.1.3 Rect class

template <typename _Tp> class Rect_
{
public:
	Rect_();	// 기본 생성자. 모든 변수를 0으로 초기화
	Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height);			// (_x, _y, _width, _height) 사각형 정보를 인자로 ㅂ다는 생성자. x = _x, y = _y, width = _width, height = _height로 초기화된다.
	Rect_(const Rect_& r);									// 복사 생성자. x = r.x, y= r.y, width = r.width, height = r.height 로 초기화된다
	Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz);	// 좌측 상단 점의 좌표와 사각형의크기 정보를 인자로 받는 생성자
	Rect_(const Point_<_Tp>& pt1, const Point_<_Tp>& pt2);	// 사각형에서 서로 대각 위치에 있는 두 점의 좌표를 인자로 받는 생성자

	Rect_& operator = (const Rect_& r);		// 대입 연산자 재정의

	Point_<_Tp> tl() const;		// Rect::tl() 멤버 함수는 사각형의 좌측 상단 점의 좌표를 반환한다
	Point_<_Tp> br() const;		// Rect::br() 멤버 함수는 사각형의 우측 하단 점의 좌표를 반환한다
	Size_<_Tp> size() const;	// Rect::size() 멤버 함수는 사각형의 크기 정보를 반환한다
	_Tp area() const;			// Rect::area() 멤버 함수는 사각형의 면적 (width x height)을 반환한다
	bool empty() const;			// Rect::empty() 멤버 함수는 유효하지 않은 사각형이면 true를 반환한다
	bool contains(const Point_<_Tp>& pt) const;	// Rect::contains() 멤버 함수는 인자로 전달된 pt 점이 사각형 내부에 있으면 true를 반환한다.

	_Tp x, y, width, height;	// 멤버 변수 x, y는 사각형 좌측 상단 점의 좌표, width, height는 사각형의 가로와 세로 크기를 나타낸다

};

// 다양한 자료형에 대하여 Rect_ 클래스 이름 재정의

typedef Rect_<int>		Rect2i;
typedef Rect_<int64>	Rect2l;
typedef Rect_<float>	Rect2f;
typedef Rect_<double>	Rect2d;
typedef Rect2i			Rect;

 

 

Rect_ 클래스는 정수형 멤버 변수 x, y, width, height를 가지고 있는 사각형 표현 클래스이다.

 

 

3.1.4 RotatedRect 클래스

 

// 3.1.4 RotatedRect class

class RotatedRect
{
public: 
	RotatedRect();	// 기본 생성자. 모든 멤버 변수를 0으로 초기화한다
	RotatedRect(const Point2f& _center, const Size2f& _size, float _angle);				// (_center, _size, _angle)을 인자로 받는 생성자. center = _center, size = _size, angle = _angle로 초기화
	RotatedRect(const Point2f& point1, const Point2f& point2, const Point2f& point3);	// (point1, point2, point3)을 인자로 ㅂ다는 생성자. 인자로 전달되 세 점은 회전된 사각형의 세 꼭지점 좌표를 나타낸다.

	void points(Point2f pts[]) const;	// RotatedRect::points() 멤버 함수는 회전된 사각형은 네 꼭지점 좌표를 pts 인자에 저장한다.
	Rect boundingRect() const;			// RotatedRect::boundingRect() 멤버 함수는 회전되 사각형을 포함하는 최소 크기의 사각형 정보를 반환한다. (정수 단위)
	Rect_<float> boundingRect2f() const;// RotatedRect::boundingRect2f() 멤버 함수는 회전되 사각형을 포함하는 최소 크기의 사각형 정보를 반환한다. (실수 단위)


	// 멤버 변수. center는 사각형의 중심 좌표. size는 사각형의 크기, angle은 시계 방향 회전 각도를 나타낸다.
	Point2f center;
	Size2f size;
	float angle;
};

 

RotatedRect 클래스는 앞의 Point_, Size_, Rect_ 클래스와 달리 템플릿 클래스가 아니고, 모든 정보를 float 자료형으로 표현한다. 즉 중심점 좌표는 Point2f 클래스 사용, 크기정보는 Size2f클래스 사용, 회전 각도는 float 자료형을 사용한다. 

 

 

3.1.5 Range 클래스

 

class Range
{
public:
	Range();	// 기본 생성자. start = end = 0으로 초기화한다.
	Range(int _start, int _end);	// 두개의 정수를 인자로 받는 생성자. start = _start, end = _end로 초기화한다.

	int size() const;	// Range::size() 멤버 함수는 범위 크기 (end-start)를 반환한다.
	bool empty() const; // Range::empty() 멤버 함수는 start와 end의 크기가 같으면 true를 반환한다.
	static Range all(); // Range::all() 멤버 함수는 start = INT _MIN, end = INT _MAX로 설정한 Range 객체를 반환한다.

	int start, end;		// 멤버 변수, start는 범위의 시작, end는 범위의 끝을 나타낸다.
};

 

Range 클래스는 범위 또는 구간을 표현하는 클래스로, 범위의 시작을 나타내는 start와 끝을 나타내는 end 멤버 변수를 가지고 있다.

 

 

3.1.6 String 클래스

 

  • OpenCV에서는 cv::String 클래스를 사용하여 문자열을 저장하고 처리할 수 있다. 
  • fmt: 형식 문자열
  • ... : 가변 인자
  • 반환값: 지정한 형식으로 생성된 문자열

 

3.2 Mat 클래스

3.2.1 Mat 클래스 개요 

 

  • Mat 클래스는 OpenCV 라이브러리에서 가장 많이 사용하는 클래스로, 행렬을 표현한다.
  • 일반적인 2차원 행렬 뿐만 아니라 고차원 행렬 표현이 가능
  • <OPENCV-SRC>\modules\core\include\opencv2\core\mat.hpp 파일에 정의됨
  • <OPENCV-SRC>는 OpenCV 소스 코드가 있는 파일 위치이다.
class Mat
{
public:
	// Mat 클래스의 다양한 생성자와 소멸자
	Mat();
	Mat(int rows, int cols, int type);
	Mat(Size size, int type);
	Mat(int rows, int cols, int type, const Scalar& s);
	Mat(Size size, int type, const Scalar& s);
	Mat(const Mat& m);
	~Mat();
	
	// Mat 클래스의 멤버 함수. Mat 클래스의 멤버 함수에는 연산자 재정의 함수와 정적 멤버 함수도 포함된다.
	void create(int rows, int cols, int type);
	bool empty() const;

	Mat clone() const;
	void copyTo(OutputArray m)const;
	Mat& setTo(InputArray value, InputArray mask = noArray());

	static MatExpr zeros(int rows, int cols, int type);
	static MatExpr ones(int rows, int cols, int type);

	Mat& operator = (const Mat& m);
	Mat operator()(const Rect& roi) const;

	template<typename _Tp> _Tp* ptr(int i0 = 0);
	template<typename _Tp> _Tp& at(int row, int col);

	// Mat 클래스의 주요 멤버 변수
	int dims;	// Mat 행렬의 차원을 나타냄
	int row, int cols; // 2차원 행렬의 크기
	uchar* data;
	MatSize size;

};
  • Mat::dims : Mat 행렬의 차원을 나타낸다. ex) 2차원 행렬이면 dims = 2
  • Mat::rows : 행렬의 행 개수. 세로 픽셀 크기
  • Mat::cols : 행렬의 열 개수. 가로 픽셀 크기
  • Mat::rows 와 Mat::cols는 2차원 행렬일 경우에만 의미 있는 값 가짐. 3차원 이상 행렬에서는 -1이 저장됨
  • Mat::size : 3차원 이상 행렬의 크기 정보.
  • Mat::data : 행렬의 원소 데이터가 저장되어 있는 메모리 공간을 가리키는 포인터형 멤버 변수.
    만약 행렬에 아무것도 저장되어 있지 않은 상태라면 Mat::data = 0 (NULL)              

 

3.2.2 행렬의 생성과 초기화

- Mat 클래스를 이용하여 행렬 객체를 생성하는 여러 가지 방법

  • Mat img2(480, 640, CV_8UC1);
    - 가로크기가 640이고, 세로 크기가 480인 영상. 세로-가로 순인 것 기억하기!
    - CV_8UC1 :  unsigned char 자료형을 사용하고 한 개의 채널이 있는 영상. 
  • Mat img4(Size(480,640), CV_8UC3);
    - Size 로 가로, 세로 크기를 지정
  • Mat img5 (480, 640, CV_8UC1, Scalar(128));
    - 그레이스케일 영상 img의의 모든 픽셀 밝기를 128으로 설정. 
  • Mat img6 (480,640, CV_8UC3, Scalar(0,0,225));
    - 컬러영상으로, 초기값은 Scalar(0,0,225) 순수 레드 컬러임
  • Mat::zeros() : OpenCV에서 모든 원소가 0으로 초기화된 행렬을 만드는 함수

 

void MatOp1()
{
	Mat img1;	// empty matrix

	Mat img2(480, 640, CV_8UC1);	// unsigned char, 1-channel
	Mat img3(480, 640, CV_8UC3);	// unsigned char, 3-channels
	Mat img4(Size(480, 640), CV_8UC3); // Size(width, height)

	Mat img5(480, 640, CV_8UC1, Scalar(128));			//initial values, 128
	Mat img6(480, 640, CV_8UC3, Scalar(0, 0, 255));		//initial vaules, red

	Mat mat1 = Mat::zeros(3, 3, CV_32SC1);	// 0's matrix
	Mat mat2 = Mat::ones(3, 3, CV_32FC1);	// 1's matrix
	Mat mat3 = Mat::eye(3, 3, CV_32FC1);	// identify matrix
	
	float data[]{ 1, 2, 3, 4, 5, 6 };
	Mat mat4(2, 3, CV_32FC1, data);

	Mat mat5 = (Mat_<float>(2, 3) << 1, 2, 3, 4, 5, 6);
	Mat mat6 = Mat_<uchar>({ 2, 3 }, { 1, 2, 3, 4, 5, 6 });

	mat4.create(256, 256, CV_8UC3);	// uchar, 3-channels
	mat5.create(4, 4, CV_32FC1);

	mat4 = Scalar(355, 0, 0);
	mat5.setTo(1.f);

}

MatOp1() 함수에서 사용된 모든 변수는 지역변수로 선언되었으므로 함수가 종료되면 자동으로 소멸된다. 

 

 

3.2.3  행렬의 복사

 

- Mat 클래스 타입의 변수에 저장된 행렬 객체를 다른 행렬 객체에 대입하거나 복사하는 방법

더보기

Mat Mat::clone() const;

 

void Mat::copyTo(OutputArray m) const;

void Mat::copyTo(OutputArray m, InputArray mask) const;

: 복사본 영상을 새로 생성할 때, 픽셀 데이터를 공유하는 것이 아니라 메모리 공간을 새로 할당하여 픽셀 데이터 전체를 복사하고 싶다면 Mat::clone() 또는 Mat::copyTo() 함수를 사용해야 함.

Mat img2 = img1 는 얕은 복사. img1의 픽셀 데이터를 img2가 참조하도록 설정함.

 

void MatOp2()
{
	Mat img1 = imread("dog.bmp");

	Mat img2 = img1;
	Mat img3;
	img3 = img1;

	Mat img4 = img1.clone();
	Mat img5;
	img1.copyTo(img5);

	img1.setTo(Scalar(0, 255, 255));	// yellow

	imshow("img1", img1);
	imshow("img2", img2);
	imshow("img3", img3);
	imshow("img4", img4);
	imshow("img5", img5);

	waitKey();
	destroyAllWindows();
}

위와 같은 이미지가 실행된다.

 

 

3.2.4 부분 행렬 추출

- 영상에서 사각형 모양의 부분 영상을 추출하거나 참조하는 방법

 

아래 예제 코드는 cat.bmp 파일에 저장된 고양이 영상을 불러와서 고양이 얼굴 주변의 부분 영상을 추출하는 예제 코드. 

더보기

img1(Rect(220, 120, 340, 240)) // img1 영상의 (220,120) 좌표부터 340x240 크기 만큼의 사각형 부분 영상을 추출

컬러 영상의 경우 각각의 색상 성분에 대해 반전을 줄 수 있다. Mat 클래스에서는 앞에 ~을 붙이는 방식으로 사용 가능.

더보기

img2 = ~img2; // img2 영상을 반전하여 그 결과를 다시 img2에 저장

 

- 예제 코드

void MatOp3()
{
	Mat img1 = imread("cat.bmp");

	if (img1.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	Mat img2 = img1(Rect(220, 120, 340, 240));
	Mat img3 = img1(Rect(220, 120, 340, 240)).clone();

	img2 = ~img2;

	imshow("img1", img1);
	imshow("img2", img2);
	imshow("img3", img3);

	waitKey();
	destroyAllWindows();
}

위와 같은 결과가 출력된다.

 

 

3.2.5 행렬의 원소 값 참조

- 사용자가 직접 자신만의 알고리즘을 구현하여 적용해야하는 경우 필요한 영상의 픽셀 값을 참조하는 기능

 

행렬 원소 접근 방법

1. Mat::at()

2. Mat::prt()

3. Matlterator_ 반복자

 

아래는 위 세 가지 행렬 원소 참조 방법을 활용하여 행렬 원소 값을 1씩 증가시키는 예제 코

void MatOp4()
{
	Mat mat1 = Mat::zeros(3, 4, CV_8UC1);

	for (int j = 0; j < mat1.rows; j++) {
		for (int i = 0; i < mat1.cols; i++) {
			mat1.at<uchar>(j, i)++;
		}
	}

	for (int j = 0; j < mat1.rows; j++) {
		uchar* p = mat1.ptr<uchar>(j);
		for (int i = 0; i < mat1.cols; i++) {
			p[i]++;
		}
	}

	for (MatIterator_<uchar> it = mat1.begin<uchar>(); it != mat1.end<uchar>(); ++it) {
		(*it)++;
	}

	cout << "mat1:\n" << mat1 << endl;
}

 

 

3.2.6 행렬 정보 참조하기

 

아래 코드는 행렬의 정보 참조 예제 코드를 활용하여 lenna.bmp 파일에서 불로온 레나 영상의 크기, 채널 수, 타입 정보를 확인하여 화면에 출력하는 코드.

 

void MatOp5()
{
	Mat img1 = imread("lenna.bmp");

	cout << "Width: " << img1.cols << endl;
	cout << "Height: " << img1.rows << endl;
	cout << "Channels: " << img1.channels() << endl;

	if (img1.type() == CV_8UC1)
		cout << "img1 is a grayscale image." << endl;
	else if (img1.type() == CV_8UC3)
		cout << "img1 is a truecolor image." << endl;

	float data[] = { 2.f, 1.414f, 3.f, 1.732f };
	Mat mat1(2, 2, CV_32FC1, data);
	cout << "mat1:\n" << mat1 << endl;
}

 

 

3.2.7 행렬 연산

 

OpenCV에서는 일반적인 행렬 표현과 행렬 연산을 위한 기능도 제공 중. 행렬의 사칙 연산에 대하여 설명. 

// Mat 클래스를 이용한 간단한 행렬 연산
void MatOp6()
{
	float data[] = { 1, 1, 2, 3 };
	Mat mat1(2, 2, CV_32FC1, data);
	cout << "mat1:\n" << mat1 << endl;

	Mat mat2 = mat1.inv();
	cout << "mat2:\n" << mat2 << endl;

	cout << "mat1.t():\n" << mat1.t() << endl;
	cout << "mat1 + 3:\n" << mat1 + 3 << endl;
	cout << "mat1 + mat2:\n" << mat1 + mat2 << endl;
	cout << "mat1 * mat2:\n" << mat1 * mat2 << endl;
}

 

 

3.2.8 크기 및 타입 변환 함수

 

- Mat 클래스의 크기 또는 타입을 변화 시키는 멤버 함수에 대한 소개

void MatOp7()
{
	// lenna.bmp 파일을 그레이스케일 영상 img1으로 불러온 후, uchar 자료형 대신 float 자료형을 사용하는 행렬 img1f를 생성
	Mat img1 = imread("lenna.bmp", IMREAD_GRAYSCALE);

	Mat img1f;
	img1.convertTo(img1f, CV_32FC1);

	// 3x4 행렬을 1x12 크기의 행렬로 변환하는 예제 코드
	uchar data1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
	Mat mat1(3, 4, CV_8UC1, data1);
	Mat mat2 = mat1.reshape(0, 1);

	cout << "mat1:\n" << mat1 << endl;
	cout << "mat2:\n" << mat2 << endl;

	// 행렬의 행 개수를 sz개로 변경. 
	mat1.resize(5, 100);
	cout << "mat1:\n" << mat1 << endl;

	// 이미 존재하는 행렬에 원소 데이터를 추가하고 싶을 때 
	Mat mat3 = Mat::ones(1, 4, CV_8UC1) * 255;
	mat1.push_back(mat3);
	cout << "mat1:\n" << mat1 << endl;
}

 

 

 

3.3 Vec과 Scalar 클래스

 

3.3.1 Vec 클래스

 

벡터 = 같은 자료형을 가진 원소 몇 개로 구성된 데이터 형식 

 

 

3.3.2 Scalar 클래스

 

Scalar =  4채널 이하의 영상에서 픽셀 값을 표현하는 용도로 자주 사용.

void ScalarOp()
{
	Scalar gray = 128;	// Scalar 클래스 타입 변수 gray 에 128 정수 하나를 이용하여 초기화
	cout << "gray: " << gray << endl;

	Scalar yellow(0, 255, 255);	// yellow 객체에는 [0, 255, 255, 0] 이 저장됨
	cout << "yellow: " << yellow << endl;

	Mat img1(256, 256, CV_8UC3, yellow);	// yellow 변수를 Mat 클래스 생성자의 네 번째 인자로 전달하여, 노란색으로 초기화된 256x256 크기의 컬러 영상 img1을 재생

	for (int i = 0; i < 4; i++)	// yellow 객체에 저장된 값을 참조하기 위해 [] 연산자 재정의를 사용
		cout << yellow.val[i] << ", " << yellow[i] << endl;
}

 

 

 

3.4 InputArray와 OutputArray 클래스

 

3.4.1  InputArray 클래스

 

void InputArrayOp()
{
	uchar data1[] = { 1, 2, 3, 4, 5, 6 }; // data1 배열값을 원소로 갖는 2x3 행렬 mat1 생성
	Mat mat1(2, 3, CV_8UC1, data1);
	printMat(mat1); //printMat() 함수에 Mat 클래스 객체를 전달하여 원소 값을 출력

	vector<float> vec1 = { 1.2f, 3.4f, -2.1f };// 세 개의 실수로 이루어진 vec1 벡터를 생성
	printMat(vec1);
}

 

 

3.4.2 OutputArray 클래스

 

  • OutputArray 클래스의 경우 사용자가 직접 OutputArray 타입의 변수를 생성해서 사용하면 안된다. (InputArray도 동일)
  • OutputArray 타입으로 정의된 OpenCV 함수의 인자에는  Mat 또는 vector<T>  같은 타입의 변수를 전달하는 형태로 코드를 작성해야 한다.