[OpenCV] 3장 OpenCV 주요 클래스
"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> 같은 타입의 변수를 전달하는 형태로 코드를 작성해야 한다.