728x90
반응형

OpenCV 기본클래스 두번째 포스팅이다. 첫번째 포스팅 바로가기 클릭


2. Matx Vec, Scalar_, Range, Ptr 클래스

2.1 Matx 클래스


이 클래스는 고정된 작은 크기의 행렬을 위한 템플릿 클래스이다. 1x1에서 6x6 까지 작은 크기의 행렬과 다양한 행렬 연산함수를 제공한다. 예를 들어 Matx33f 자료형은 3x3 의 float 행렬이다. 더욱 더 일반적인 행렬의 형태는 알다시피 Mat 클래스로 변환하여 사용한다. 이 Matx 클래스에서는 '<<' 연산자 함수를 사용할 수 없다. 사용하려면 Mat 으로 변환하여 cout로 간단히 출력 할 수 있다. 


Matx33d A(1, -1, -2,

   2, -3, -5,

   -1, 3, 5);    // Matx<double, 3, 3> 템플릿 자료형이며, 3x3 배정도 실수 행렬, A를 초기화


여기까지 간단히 알아보도록하고, 더 중요한 클래스에 대해서 다뤄보도록하자.



2.2 Vec 클래스


이 클래스는 Matx 클래스에서 상속받은 클래스로, 짧은 수치 벡터를 위한 템플릿 클래스이다. 기본적인 벡터 연산이 가능하고, 3차원 외적을 계산할 수 있고, [] 연산자에 의해 접근할 수 있다. 


Vec<T,2> - Point_

Vec<T,3> - Point3_

Vec<T,4> - Scalar_, CvScalar 


사이에서 변환이 가능하다. Vec2b, Vec3b 등의 클래스가 있다. 쓰임은 다음과 같다. 


Vec<float, 3> X(1,0,0);    // 3차원 벡터 X와 Y를 선언하고 초기화한다.

Vec<float, 3> Y(0,1,0);




2.3 Scalar_ 클래스


이 클래스는 저번 포스팅에서 잠깐 다루었던 클래스이기도하다. 이는 Vec 클래스에서 상속받은 4개의 요소를 갖는 템플릿 클래스이다. 계속 하위개념을 상속하는 형식이다. 


Scalar X = Vec4f(1,2,3,4);

Scalar Y = Scalar(10,20,30); // = Scalar(10,20,30,0);

Scalar Z = Scalar(100,200,300); 


이러한 형식으로 사용할 수 있다. 




2.4 Range 클래스


이 클래스는 Mat 클래스에서 행 또는 열의 범위를 지정 할 수 있는 템플릿 클래스이다. start는 포함이고, end는 포함하지 않는다. 아래를 보면 이해 할 수 있다.


Range(int _start, int _end);

Range(0,4) // 범위 0,3을 나타낸다. 즉, 이 클래스는 슬라이싱과 같은 기능을 하고있다.


Matx33f A(1, 2, 3,

   4, 5, 6,

   7, 8, 9);

Mat B(A);


cout << "B[0:1, 0:3]" << B(Range(0,1), Range(0,3)) << endl;   // 예제 1

cout << "B[1:2, 0:3]" << B(Range(1,2), Range(0,3)) << endl;   // 예제 2


이러한 코드에서 예제 1과 예제2의 출력결과는 어떻게 될까.


예제 1은 Range(0,1)은 범위 0,0을 나타내고

          Range(0,3)은 범위 0,2를 나타내므로 0과 0,1,2가 겹치는 0, 즉 B의 0행인 1, 2, 3 을 출력


예제 2는 Range(1,2)는 범위 1,1을 나타내고 Range(0,3)은 예제 1과 같이 범위 0~2 까지 나타내므로 1과 0,1,2가 겹치는 1, 즉 B의 1행인 4, 5, 6을 출력하게 된다.




2.5 Ptr 클래스


OpenCV 3.0에서 이 클래스의 사용이 많이 확대되었다. 이는 포인터를 감싸서 메모리를 안전하게 사용하는 템플릿 클래스이다. 이 클래스의 사용으로 인해 OpenCV 2.~ 에서 사용하였던 메모리 해제(Release)를 번거럽게 하지 않고, 자동으로 메모리를 해제 해주는 클래스이다. 


Ptr<IplImage> Image(cvLoadImage("lena.jpg", IMREAD_GRAYSCALE));

Ptr<FILE> outFile(Fopen("matA.txt","w"));

Ptr<CvMat> mat(cvCreateMat(2, 3, CV_32FC1)); 


이러한 형식으로 사용되게 된다. 





3. Mat 클래스


Mat 클래스는 C++ API 에서 가장 중요한 클래스 중 하나로 1채널 또는 다채널의 실수, 복소수, 행렬, 영상 등의 수치 데이터를 표현하는 n 차원 행렬 클래스이다. Mat 클래스는 다양한 생성자와 메소드를 지원하게 된다. 구버전(3.0 미만)에서 사용되었던 CvMat, IplImage는 cvarrToMat 함수를 통해 호환하여 사용 할 수 있다. 



3.1 Mat 행렬 생성


Mat 클래스는 다양한 생성자를 통하여 행렬을 생성하게 된다. 


Mat A(2, 3, CV_8UC1);

Mat B(2, 3, CV_8UC1, Scalar(0));

Mat C(2, 3, CV_8UC3, Scalar(1, 2, 3));


이렇게 행렬 A, B, C를 생성하게 되었을 경우 각각 행렬의 내용은 어떻게 될지 생각해보자.

먼저 행렬 A는 2x3, 1채널 행렬이고, 초기화 된 결과가 없다. 두번째 행렬인 행렬 B는 2x3, 1채널 해열ㄹ에 0으로 초기화 되었다. 행렬C는 2x3, 3채널, 1,2,3으로 초기화 되었다.


각 행렬의 결과는 다음과 같다. 행렬B는 1채널이여서 배열 1칸에 1개의 값이 들어갔고, 행렬C는 3채널이여서 배열1칸에 3개의 값이 들어갔다.

 

행렬A

이상한 값 

이상한 값

이상한 값 

이상한 값 

이상한 값

이상한 값 


행렬B

0

0

0

0


행렬C

1, 2, 3

1, 2, 3

1, 2, 3

 1, 2, 3

1, 2, 3 

1, 2, 3


행렬A, B, C의 배정도 2x3을 표현하였던 2,3은 Size(3,2)로도 표현 할 수 있고, Scalar(값, 값, 값, 값) 대신에 배열값 즉, {1,2,3,4,5,6} 이러한 형식으로도 값을 넣을 수 있음을 알아두도록 하자.


Mat을 생성하는 또다른 방법은 다음과 같다. 


1) Vec을 이용하여 Mat 생성

// 3x1 행렬

Vec<float, 3> V(1,0,0);

Mat V1(V);

Mat V2(Vec<float, 3>(0,1,0));


2) Matx을 이용하여 Mat 생성

// 3x3 행렬

Matx<float, 3, 3> A(1,2,3,4,5,6,7,8,9);

Mat A1(A);


3) Range를 이용하여 Mat 생성

// 1x3 행렬

Mat A2(A1, Range(1,2), Range::all());


4) Rect을 이용하여 Mat 생성

// 3x3 행렬인 A1에 사각형만큼의 행렬을 생성, 2x2 행렬

Mat A3(A1, Rect(1,1,2,2)); 


5) cvMat을 변환하여 Mat 생성

// 3x3 행렬

CvMat mat = cvMat(3,3,CV_32FC1, A.val);

Mat A4 = cvarrToMat(&mat);

A.val[0]=100; // Matx을 이용하여 Mat을 생성했었던 코드 참조


Mat A5 = cvarrToMat(&mat, true);



행렬을 생성하는 방법이 너~무 많다. ㅠㅠ 앞으로가 더 많은데... 알아두면 좋겠지. 까먹지 않았으면 좋겠다. 다음은 3.차.원 행렬이다 ㅠㅠ


int sizes[] = {2,3,4};

Mat A(3, sizes, CV_32FC1);

Mat B(3, sizes, CV_32FC1, Scalar(0));


sizes = {2,3,4} 를 이용하여 행렬A,B는 2x3x4의 3차원 행렬으로 만들어졌다. 

A.dims, B.dims 으로 각각 출력을 해보면 차원이 나오게 되고, rows와 cols은 -1이 된다. 


행렬의 요소는 A.at<float>(i, j, k)로 접근하여 출력해볼 수 있다. 행렬 요소에 각각 접근하는 방법은 다음에 자세히 다뤄보도록 한다. 




  

3.2 Mat::create() 메소드에 의한 행렬 생성


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


Mat A(2, 3, CV_32FC1, Scalar(0)); // 생성자를 이용하여 행렬 A 초기화


A.create(2, 3, CV_32FC1); // 행렬 A와 동일하므로 바로 리턴

A.create(3, 3, CV_32FC1); // 배정도가 다르므로 메모리를 재할당하여 행렬을 생성

A.create(Size(3,3), CV_8UC1); // 자료형이 다르므로 재할당


Mat B;

int sizes[] = {3,3};

B.create(2, sizes, CV_8UC1); // dims=2인 2차원 행렬이며, 3x3 배정도를 가진 행렬



다음은 create 함수를 이용하여 영상을 하나 생성해보겠다.


create 함수를 이용하여 영상을 생성한뒤, 그 영상의 rows 값과 cols 값에 하나씩 접근하도록 for문을 생성하고, at<Vec3b>(i, j)를 이용하여 화소값에 직접 접근 한 뒤, 분홍색에 해당하는 값을 입력해주었다. 



 
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;

int main(){
	Mat srcImage;
	srcImage.create(512, 512, CV_8UC3);

	for (int i = 0; i < srcImage.rows; i++)
		for (int j = 0; j < srcImage.cols; j++)
			srcImage.at<Vec3b>(i, j) = Vec3b(125, 0, 255);

	imshow("srcImage", srcImage);
	waitKey();

	return 0;
}


결과는 다음과 같다. 포토샵을 하도 해대니까 분홍색을 생각하고 Vec 값을 주었더니 진짜 분홍색이 나왔다 ㅋㅋㅋㅋㅋ





Mat 행렬 생성은 여기까지 포스팅 하도록 하고, 다음에는 Mat 행렬에 대한 정보라던가, 연산자, 행렬의 복제, 복사 등 부수적인 것들을 다뤄보도록 할 것이다. 



728x90
반응형