해당 포스트는 "OpenCV로 배우는 영상 처리 및 응용", "C++ API OpenCV 프로그래밍" 책의 내용을 요약한 것이다.



※ Mat 클래스

Mat 클래스는 OpenCV C++ API에서 가장 중요한 클래스 중 하나로 1채널/다채널 소수, 복소수, 행렬, 영상 등의 수치 데이터를 표현하는 n차원 행렬 클래스이다.


1. Mat 행렬 생성

Mat 클래스는 다양한 생성자를 통하여 성한다. 다음은 Mat 행렬 생성에 관한 예제이다.


#include "opencv2\opencv.hpp"
using namespace cv;
using namespace std;
int main()
{
	Mat  A(2, 3, CV_8UC1);
	Mat  B(2, 3, CV_8UC1, Scalar(0));
	Mat  C(2, 3, CV_8UC3, Scalar(1, 2, 3));

	float data[] = { 1, 2, 3, 4, 5, 6 };
	Mat  D(2, 3, CV_32FC1, data);
	Mat  D1(Size(3, 2), CV_32FC1, data);

	cout << "A=" << A << endl;
	cout << "B=" << B << endl;
	cout << "C=" << C << endl;
	cout << "D=" << D << endl;
	cout << "D1=" << D1 << endl;

	Matx E(1, 2, 3, 4, 5, 6, 7, 8, 9);

	CvMat mat = cvMat(3, 3, CV_32FC1, E.val);
	Mat E1 = cvarrToMat(&mat);// copyData=false
	E.val[0] = 100;
	cout << "E=" << E << endl;
	cout << "E1=" << E1 << endl;

	Mat E2 = cvarrToMat(&mat, true);// copyData=true
	E.val[0] = 200;
	cout << "E=" << E << endl;
	cout << "E2=" << E2 << endl;
	return 0;
}


cvarrToMat 메서드는 OpenCV의 예전 버전에서 Mat 클래스를 담당했던 c언어로 된 CvMat, IplImage 등을 Mat 클래스로 변환하는 메서드이다. 해당 메서드 두 번재 매개변수인 copyData는 false일 시 메모리를 공유하고 true이면 메모리를 복사하여 공유하지 않는다. 따라서 위 결과값은 E1과 E2가 다른 결과값을 가진다.

추가로 Mat 행렬의 데이터를 복사하고 싶으면 Mat::clone() 메서드를 사용한다. Mat의 멤버 변수 중에 step 변수가 있는 행렬의 1행의 바이트 갯수를 나타내고 Mat::cols*Mat::elemSize()로 계산한다. Mat::elemSize()는 행렬의 한 원소에 대한 바이트 크기를 반환한다.



2. Mat::create() 메서드에 의한 행렬 생성

Mat::create() 메서드는 rows, cols, type, size, ndims 등에 의해 새로운 Mat 클래스 행렬을 생성한다. Mat 클래스 생성자 등에 의해 이전에 생성된 Mat 클래스 행렬과 크기(rows, cols, size)와 type이 같으면 행렬을 위한 메모리를 새로 할당하지 않고 바로 리턴한다. 그러나, 크기나 자료형이 다를 경우 Mat::release()를 호출하여 이전의 행렬 데이터를 위한 메모리를 해제하고 행렬을 위한 새로운 데이터를 생성한다. 다음은 Mat::create 메서드 선언부이다.


void Mat::create(int rows, int cols, int type);
void Mat::create(Size size, int type);
void Mat::create(int ndims(차원), const int * sizes, int type);



3. Mat 행렬 정보

- Mat::rows : 행의 개수를 가리킨다.

- Mat::cols : 열의 개수를 가리킨다.

- Mat::data : 행렬 데이터의 포인터를 가리킨다.

- Mat::dims : 행렬의 차원이며 항상 2보다 크거나 같다.

- Mat::step : 행렬의 한 행이 차지하는 바이트 수를 가리킨다.

- Mat::isContinuous() : 각 행의 마지막에 공백없이 연속으로 데이터가 저장되었는지를 확인한다.

- Mat:: total() : 행렬 요소의 전체 개수를 반환한다.

- Mat::elemSize() : 행렬 요소 하나의 바이트 크기를 반환한다. 행렬에서 한 행의 총 바이트 수는 Mat::cols*Mat::elemSize()이다.

- Mat::elemSize1() : 채널 크기를 무시하고 한 채널에서 하나의 행렬 요소 바이트 수를 반환한다.

- Mat::depth() : 행렬 요소의 자료형을 반환한다.

- Mat::step1() : Mat::step/Mat::elemSize1()을 반환한다.

- Mat::type() : 행렬의 데이터 타입(자료형+채널 수)를 반환한다.



#include "opencv2\opencv.hpp"
using namespace cv;
using namespace std;
int main()
{
	Mat  A(2, 3, CV_32FC3);

	cout << "A.elemSize() : " << A.elemSize() << endl;
	cout << "A.elemSize1() : " << A.elemSize1() << endl;
	cout << "A.type() : " << A.type() << endl;
	cout << "A.depth() : " << A.depth() << endl;

	A.at<Vec3f>(0, 0) = Vec3f(0.75, 1.0, 10.0);
	cout << "A.data[0] = " << *(float*)A.data << endl;
	cout << "A.data[4] = " << *(float*)(A.data + 4) <<endl;
	cout << "A.data[8] = " << *(float*)(A.data + 8) << endl;

	return 0;
}


elemSize()는 행렬 요소 하나의 바이트 크기이다. 위에서 A 행렬은 32bit인 float형이 3채널 이므로 하나의 행렬 요소는 12바이트가 된다. elemSize1()은 한 채널에서 행렬 요소의 바이트 크기를 가리킨다. A행렬은 한 채널이 32bit float형으로 되있으므로 4바이트가 된다. type()은 행렬의 데이터 타입을 가리키는데 21이 CV_32FC3이다. depth는 행렬의 자료형을 가리켜 CV_32F 즉, 5가 된다.



4. Mat 행렬의 = 연산자 함수

Mat 행렬의 = 연산자 함수는 다음과 같다.

- Mat& Mat::operator=(const Mat& m) : "=" 연산자 왼쪽 변수가 m과 같아진다. 단, 데이터가 복사되는 게 아니라 공유된다.

- Mat& Mat::operator=(const MatExpr& expr) : C = A+B 에서 A,B,C가 Mat행렬일 때 호출된다.

- Mat& Mat::operator=(const Scalar& s) : 행렬의 모든 요소를 s값으로 변경한다.


5. Mat::at()

Mat::at() 메서드는 Mat 행렬의 요소에 접근하여 행렬 요소의 레퍼런스를 반환한다.


6. Mat::ptr()

Mat::ptr() 메서드는 행렬의 지정된 행(row)의 시작 주소를 저장한 포인터를 반환한다.


7. Mat 행렬의 행/열 부분 행열 헤더 

Mat::row(y) 메서드는 y-행에 대한 행렬 헤더를 생성하고 Mat::col(x) 메서드는 x-열에 대한 행렬 헤더를 생성한다. Mat::rowRange(start,end) 메서드는 start에서 end-1까지의 행에 대한 행렬 해더를 생성한다. Mat::colRange(start, end) 또한 Mat::rowRange 메서드와 행과 열만 다를 뿐 나머지는 같다. 행렬 헤더를 생성할 때는 따로 메모리를 새로 생성하지 않고 기존 Mat 행렬의 데이터를 공유한다.


8. Mat 행렬의 복제, 복사, 변환, 값 설정 및 모양 변환

- Mat::clone() : 행렬을 완전히 복제하여 반환한다.

- Mat::copyTo(OutputArray m, InputArray mask) : 행렬을 다른 행렬 m으로 복사한다. mask 행렬은 필수 매개변수가 아니다. 만약 mask 행렬을 매개변수로 전달했다면 mask 행렬에서 0이 아닌 데이터를 가진 행렬 요소 위치만 복사한다.

- Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0) : 행렬을 m으로 복사하되 m은 rtype 자료형을 가지게 한다. alpha 값은 m의 행렬 요소에 곱해지고 beta 값은 더해진다.

- Mat::assignTo(Mat& m ,int type) : Mat::convertTo 메서드와 동일하나 alpha와 beta 가 없다.

- Mat::setTo(InputArray value, InputArray mask) : 행렬의 요소를 Scalar 등의 자료형을 가지는 value 값으로 설정한다. Mat::setTo 메서드는 Mat::operator=(const Scalar& s) 메서드와 동일하다. mask 행렬은 copyTo에 나오는 mask와 같은 역할을 한다. 

- Mat::reshape(int cn, int rows=0) : 데이터를 복사하지 않고 새로운 2차원 행렬의 모양(행, 열의 개수)과 채널 개수를 변경하여 반환한다. 단 rows*cols*channels()은 변경 전과 같아야 한다.


9. Mat 행렬의 크기 변경, 공간 확보

- Mat::resize(size_t sz, const Scalar& s) : 행렬에서 행의 개수를 sz로 바꾸고 추가된 행렬 요소는 s로 설정한다.

- Mat::reserve(size_t sz) : sz해의 개수만큼의 메모리 공간을 확보하지만, 메모리 공간이 충분하면 아무 일도 하지 않는다. reserve 메서드를 사용하면 미리 메모리 용량을 확보할 수 있어 메모리 재할당이 빈번히 일어나는 것을 방지해 성능 저하를 막을 수 있다.


10. Mat 행렬의 ROI

- Mat::locateROI(Size& wholeSize, Point& ofs) : row(), col(), rowRange(), colRange() 메서드에 의해 지정된 부분행렬에서 원본/부모 행렬의 전체 크기 wholeSize와 원본 해렬에서의 옵셋 위치 ofs를 알려준다.

- Mat::adjustROI(Int dtop, int dbottom, int dleft, int dright) : 부분 행렬이 dtop만큼 위로, dbottom만큼 아래로, dleft만큼 왼쪽으로 dright만큼 오른쪽으로 커진다.


11. Mat 행렬의 반복자(iterator)

c++ 일반 stl iterator와 똑같다. 다만 타입이 MatIterator_인 반복자는 읽기와 쓰기가 가능하고 MatConstIterator_는 읽기만 가능하다.


12. Mat 행렬의 push_back, pop_back

- Mat::push_back(const T& elem) : 행렬의 요소를 추가해 행을 늘린다. 추가되는 elem의 모든 자료형은 일치해야 하고 열이 1인 행렬만 추가 가능하다.

- Mat::push_back(const Mat& m) : 추가되는 행렬과 자료형과 열의 개수가 일치해야 한다.

- Mat::pop_back(size_t nelems=1) : 행렬의 뒤에서 부터 nelems 개수의 행을 제거한다. 단, 제거된 행을 반환하지 않는다.

+ Recent posts