n-차원의 밀집형 배열 클래스로 다양한 데이터를 저장할 수 있다.
- 단일 채널 혹은 다중 채널의 값
- 실수나 복소수로 구성된 벡터
- 명암도(gray-scale) 영상이나 컬러 영상 데이터
- 점의 집합
- 히스토그램 데이터
생성자를 통해 생성할 수 있고 매개변수로 (rows, cols, type, 초기값)을 생성할 수 있다.
Mat 객체를 생성할 때에는 행렬의 자료형과 데이터 원소의 자료형이 맞지 않으면 여러 가지 문제들이 발생할 수 있는데 예를들어 uchar행렬은 0~255까지의 범위를 갖기때문에 255가 초과하는 값을 넣어도 255의 값이 저장된다.
Mat클래스는 행렬의 값을 초기화할 때, 자료형이 일치하지 않으면, 에러가 발생하기 때문에 자료형에 주의해야 한다.
행렬 자료형의 종류
- CV_8U - uchar(unsigned char)
- CV_8S - signed char
- CV_16U - unsigned short int
- CV_16S - signed short int
- CV_32S - int
- CV_32F - float
- CV_64F - double
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main() {
float data[] = {
1.1f, 2.2f, 3.3f,
4.1f, 5.2f, 6.6f
};
Mat m1(2, 3, CV_8U, Scalar(10));
Mat m2(2, 3, CV_32F, data);
Mat m3(3, 3, CV_16S, 10);
cout << "m1" << endl << m1 << endl << endl;
cout << "m2" << endl << m2 << endl << endl;
cout << "m3" << endl << m3 << endl << endl;
return 0;
}
매개변수로 초기값을 설정하지 않으면 쓰레기값이 들어갔다.
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Size sz1(2, 3);
Mat m1(2, 3, CV_8U, Scalar(10));
Mat m2(sz1, CV_8U, Scalar(10));
cout << "m1" << endl << m1 << endl << endl;
cout << "m2" << endl << m2 << endl << endl;
return 0;
}
Mat클래스의 생성자로 Size_클래스의 객체를 매개변수로 넣을 때 주의해야 한다.
Size_ 클래스는 가로X세로이고 Mat 클래스는 행X열이기 때문이다.
Mat클래스의 초기화 메소드로 ones(), eye(), zeros()가 있다.
- ones()는 행렬의 모든 원소값을 1로 반환한다.
- eye()는 top left에서 bottom right까지 대각선 방향의 원소값만 1로하고 나머지 원소들은 0으로 반환한다.
- zeros()는 행렬의 모든 원소값을 0으로 반환한다.
Mat클래스는 다양한 멤버변수와 멤버메소드가 존재한다.
멤버변수
- dims - 차원 수
- rows - 행의 개수
- cols - 열의 개수
- data - 행렬 원소 데이터에 대한 포인터
- step - 행렬의 한 행이 차지하는 바이트 수
멤버메소드
- channels() - 행렬의 채널 수 반환
- depth() - 행렬의 깊이값 반환
- elemSize() - 행렬의 한 원소에 대한 바이트 크기 반환
- elemSize1() - 행렬의 한 원소의 한 채널에 대한 바이트 크기 반환
- empty() - 행렬 원소가 비어있는지 여부 반환
- isSubmatrix() - 참조 행렬인지 여부 반환
- size() - 행렬의 크기를 Size형으로 반환
- step1() - step을 elemSize1()로 나누어서 정규화된 step 반환
- total() - 행렬 원소의 전체 개수 반환
- type() - 행렬의 데이터 타입(자료형 + 채널 수)반환
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat m1(2, 3, CV_16S, 10);
Mat m2(3, 3, CV_32F, 5);
Mat m3(4, 2, CV_64F, 10);
cout << "m1.elemSize = " << m1.elemSize() << endl;
cout << "m2.elemSize = " << m2.elemSize() << endl;
cout << "m3.elemSize = " << m3.elemSize() << endl;
return 0;
}
제일 햇갈리는 elemSize()메소드를 집중적으로 연구해봤다.
m1의 자료형은 CV_16S이다. 숫자16은 bit수이고 S는 데이터타입이 short라는 것이다.
elemSize()메소드는 바이트를 반환하기 때문에 16bit는 2byte가 된다.
m2의 자료형은 CV_32F이다. 32bit를 바이트로 변환하면 4byte가 된다.
그러므로 m3은 8byte가 되는데 여기까지는 데이터타입이 바이트의 값에 영향을 미치는것 같지가 않다.
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat m1(2, 3, CV_16SC2, 10);
Mat m2(3, 3, CV_32FC3, 5);
Mat m3(4, 2, CV_64FC2, 10);
cout << "m1.elemSize = " << m1.elemSize() << endl;
cout << "m2.elemSize = " << m2.elemSize() << endl;
cout << "m3.elemSize = " << m3.elemSize() << endl;
return 0;
}
다음은 채널을 추가한뒤 elemSize()메소드의 반환값을 연구해봤다.
앞의 bit값을 byte로 변환한뒤 채널의 수를 곱해주면 값이 나온다.
데이터타입은 값에 영향을 미치지 않는다.
Mat클래스의 할당(=) 연산자
할당 연산자를 통해 복사나 공유작업을 사용할 수 있다.
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat m1(2, 3, CV_8U, 10);
Mat m2(2, 3, CV_8U, 5);
Mat m3 = m1;
cout << "m3" << endl << m3 << endl << endl;
m1 = m1 + m2;
cout << "m1" << endl << m1 << endl << endl;
cout << "m3" << endl << m3 << endl << endl;
return 0;
}
연산의 결과값을 저장하는 것은 행렬이 복사되지만 m3=m1과 같이 연산을 하지않고 할당 연산자만 사용하는 경우에는 복사가 아니라 m1의 값을 공유하게 된다. 따라서 m1의 값이 바뀌게 되면 자연스레 m3의 값도 바뀌게 된다. 포인터변수와 비슷한 개념인 것 같다. m3은 m1의 주소값을 저장하고 m1의 실질적인 값을 저장하는 것은 아니다.
reshape() 메소드
Mat클래스의 햇갈리는 메소드 중 3top안에 드는 것 같다.
이 메소드는 일반적으로 전체 원소의 개수를 변경하지 않은 채, 채널의 개수나 행의 개수를 바꾸고자 할 때 사용된다.
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
void print_matInfo(string m_name, Mat m) {
cout << "[" << m_name << " 행렬]" << endl;
cout << " 채널 수 = " << m.channels() << endl;
cout << " 행 x 열 = " << m.rows << " x " << m.cols << endl << endl;
}
int main() {
Mat m1(2, 6, CV_8U, Scalar(0));
Mat m2 = m1.reshape(2);
Mat m3 = m1.reshape(3, 2);
print_matInfo("m1(2, 6)", m1);
print_matInfo("m2 = m1_reshape(2)", m2);
print_matInfo("m3 = m1_reshape(3, 2)", m3);
cout << "m1 = " << m1 << endl;
cout << "m2 = " << m2 << endl;
cout << "m3 = " << m3 << endl;
m1.create(2, 2, CV_8U);
cout << "m1 = " << m1 << endl;
return 0;
}
채널수가 2개이면 원소2개를 묶어 1개의 원소로 따지므로 2X3 행렬이 된다.
채널수가 3개이면 원소 3개를 묶어 1개의 원소로 따지므로 2X2 행렬이 된다.
출력문으로 보아 원래의 구조는 변경되지 않는 듯 하다.
create()메소드를 통해 완전히 새로운 행렬로 생성이 가능하다.
resize() 메소드
행의 개수를 기준으로 기존 행렬의 크기를 변경하고 기존 행렬의 행의 개수보다 행의 개수가 자긍면 하단 행을 제거하고, 크면 기존 행렬 하단에 추가한다.
추가시 초기값을 주지 않으면 쓰레기값이 들어간다.
Mat객체의 복사 및 자료형 반환
어떠한 작업을 할 때 원본의 복사본을 만들어서 작업할 때 유용하게 사용된다.
- clone() - 행렬 데이터와 같은 값을 복사해서 새로운 행렬을 반환한다.
- copyTo() - 행렬 데이터를 인자로 입력된 mat 행렬에 복사한다.
- converTo() - 행렬 원소의 데이터 타입을 변경하여 인수로 입력된 mat 행렬에 반환한다.
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main() {
Mat m1(2, 3, CV_8U, Scalar(10));
Mat m2 = m1.clone();
Mat m3, m4;
m1.copyTo(m3);
m1.convertTo(m4, CV_16S);
cout << "m1\n" << m1 << endl;
cout << "m2\n" << m2 << endl;
cout << "m3\n" << m3 << endl;
cout << "m4\n" << m4 << endl;
return 0;
}
위와같이 사용할 수 있다.
'C++ > OpenCV' 카테고리의 다른 글
[OpenCV]vector 클래스 (0) | 2019.04.11 |
---|---|
[OpenCV]Mat_ 클래스와 Matx 클래스 (0) | 2019.04.10 |
[OpenCV]RotatedRect 클래스 (0) | 2019.04.10 |
OpenCV의 기본 자료 구조 (0) | 2019.04.10 |
두개의 회전사각형을 포함하는 bounding 사각형 (0) | 2019.03.15 |