메뉴 닫기

OpenCV Mat 클래스 사용하기 – 2

이전 글에 이어서, OpenCV Mat 클래스를 사용하는 방법을 정리합니다.

이전글


6) Mat 객체 정보 확인

영상을 불러와서 Mat 객체에 할당한 후, Mat 객체의 정보를 확인해야 할 때가 있습니다. 아름다운 레나님의 영상으로 예제를 보여드리도록 하겠습니다.

※ Lenna Grayscale bmp image 출처: www.ece.rice.edu/~wakin/images/

#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

using namespace cv;
using namespace std;

int main()
{
	Mat image = imread("lena512.bmp", IMREAD_GRAYSCALE);
	
	// Size
	cout << "Size: "<< image.size() << endl;
	// 행 & 열
	cout << "Row, Col:" << image.rows << ", " << image.cols << endl;
	// 채널 수
	cout << "Channel:" << image.channels() << endl;
	// Depth - 원소(Element)의 자료형
	cout << "Depth: " << image.depth() << endl;
	// Matrix 원소의 전체 크기 / Matrix 원소의 1채널의 크기 
	// ex) CV_32FC3 - 전자는 4*3=12이고 후자는 4
	cout << "ElementSize / ElementSize1: " << image.elemSize() << " / " << image.elemSize1() << endl;
	// 전체 원소의 개수 (행*열)
	cout << "Total: " << image.total() << endl;
	// Matrix type (CV_8UC1 = 0)
	cout << "Type: " << image.type() << endl;
	// 각 행의 원소가 연속적으로 저장되어 있는지?
	cout << "IsContinuous: " << image.isContinuous() << endl;
	// 부분행렬인지?
	cout << "IsSubmatrix: " << image.isSubmatrix() << endl;

	imshow("imageWnd", image);
	waitKey(0);

	return 0;
}


실행 결과


7) Matrix 연산

OpenCV는 행렬의 사칙 연산을 직관적으로 수행할 수 있도록 Operator가 재정의되어 있습니다. 또한 함수를 사용하여 전치행렬, 역행렬 등을 쉽게 구할 수 있습니다. 아래 예제 코드를 확인해 주시기 바랍니다.

행렬은 곱셈만 조금 유의하면, 사용하는 데 큰 어려움은 없습니다. 교환법칙 성립 안된다는 것을 유념하시면 됩니다. 또한 수학적 곱셈 연산은 * 연산자를 재정의한 것을 사용하고, 같은 위치에 있는 원소끼리 곱셈해서 결과를 내는 것은 mul()함수를 사용한다는 차이 또한 기억하시면 좋겠습니다.

#include <iostream>
#include <opencv2/core/core.hpp>

using namespace cv;
using namespace std;

int main()
{
	cout << "--------------------- 2 x 2  행렬 선언 ---------------------" << endl;
	Mat mat1 = Mat_<float>({ 2, 2 }, { 1, 2, 3, 4 });

	float data[] = { -1, 3, 2, -1 };
	Mat mat2(2, 2, CV_32FC1, data);
	
	cout << "mat1n" << mat1 << endl;
	cout << "mat2n" << mat2 << endl;

	cout << "--------------------- 행렬 & 숫자 연산 ---------------------" << endl;
	cout << "행렬 + 숫자: n" << mat1 + 100 << endl;
	cout << "행렬 - 숫자: n" << mat1 - 100 << endl;
	cout << "행렬 * 숫자: n" << mat1 * 100 << endl;
	cout << "행렬 / 숫자: n" << mat1 / 100 << endl;

	cout << "--------------------- 숫자 & 행렬 연산 ---------------------" << endl;
	cout << "숫자 + 행렬: n" << 100 + mat1 << endl;
	cout << "숫자 - 행렬: n" << 100 - mat1 << endl;
	cout << "숫자 * 행렬: n" << 100 * mat1 << endl;
	cout << "숫자 / 행렬: n" << 100 / mat1 << endl;

	cout << "--------------------- 행렬 & 행렬 연산 ---------------------" << endl;
	cout << "행렬 + 행렬: n" << mat1 + mat2 << endl;
	cout << "행렬 - 행렬: n" << mat1 - mat2 << endl;
	cout << "행렬 * 행렬(수학적 행렬 곱셈 공식을 따름): n" << mat1 * mat2 << endl;
	cout << "행렬 / 행렬(대응되는 원소끼리 나눗셈): n" << mat1 / mat2 << endl;
	cout << "대응되는 원소끼리 곱셈(mul함수): n" << mat1.mul(mat2) << endl;
	cout << "역행렬: n" << mat1.inv() << endl;
	cout << "전치행렬: n" << mat1.t() << endl;


	return 0;
}


실행 결과


8) Mat 객체 타입(Depth)/사이즈 변환

타입 변환

Mat 연산의 편의성 또는 정확성 향상을 위해 객체의 데이터 타입(Depth)를 변환할 필요가 가끔 있습니다. 아래는 Grayscale의 레나 이미지를 불러와서, UChar → Float으로 데이터 타입을 변환하는 예제입니다.

#include <iostream>
#include <opencv2/core/core.hpp>

using namespace cv;
using namespace std;

int main()
{
	Mat image = imread("lena512.bmp", IMREAD_GRAYSCALE);
	Mat image_float;

	image.convertTo(image_float, CV_32FC1);

	cout << "Depth: " << image.depth() << " vs " << image_float.depth() << endl;
	
	return 0;
}



사이즈 변환

Mat 객체의 사이즈를 변환하는 방법을 소개합니다. 아래 예제에서 Original한 mat 객체는 2채널, 5*5 사이즈의 float 형 데이터 타입을 사용합니다.

reshape 함수행렬의 크기를 변환하며, 채널 수도 함께 고려됩니다. 예제는 5*5 사이즈의 2채널 행렬을 의미하였는데, 이를 1채널의 2*25 사이즈 행렬로 변환합니다. reshape 함수의 첫번째 파라미터는 채널 수, 두번째 파라미터는 행의 개수를 의미합니다. 만약 파라미터가 0이면 기존 채널 수 또는 행의 개수는 변경되지 않습니다. 또한 기존 mat의 객체를 참조하기 때문에, 필요에 따라 clone함수를 사용하여 복사해서 사용하는 것이 좋습니다.

resize 함수행을 늘리고 싶을 때  이용합니다. resize 함수의 첫번째 파라미터는 변경하고 싶은 전체 행의 개수를 의미하며, 늘리고 줄이는 것 둘 다 가능합니다. 아래 예제는 행렬이 5행에서 7행으로 늘어난 것을 나타냅니다. 두 번째 파라미터는 행이 늘어났을 때, 새로 추가된 행의 원소의 초기값을 뜻합니다. 예제의 mat 객체가 2채널이기 때문에 Scalar를 사용하였습니다.

push_back 함수는 Mat 객체에 새로운 행을 추가하고 싶을 때 사용합니다. 먼저 새로운 행(예제코드의 mat3)을 준비한 다음, push_back 함수를 호출하면 됩니다.

#include <iostream>
#include <opencv2/core/core.hpp>

using namespace cv;
using namespace std;

int main()
{
	Mat mat(5, 5, CV_32FC2, Scalar(1, 3));
	
	cout << "Original: " << endl;
	cout << mat << endl;
	cout << "Mat size: " << mat.size() << endl;
	cout << "Channel: " << mat.channels() << endl;

	// Reshape
	cout << "nnReshape: " << endl;
	Mat mat_reshape = mat.reshape(1, 2);
	cout << mat_reshape << endl;
	cout << "Mat size: " << mat_reshape.size() << endl;
	cout << "Channel: " << mat_reshape.channels() << endl;

	// Resize (5행에서 7행으로 리사이즈)
	cout << "nnResize: " << endl;
	mat.resize(7, Scalar(7, 9));
	cout << mat << endl;
	cout << "Mat size: " << mat.size() << endl;

	// Push back
	cout << "nnPush Back: " << endl;
	Mat mat3(1, 5, CV_32FC2, Scalar(99, 100));
	mat.push_back(mat3);
	cout << mat << endl;
	cout << "Mat size: " << mat.size() << endl;

	return 0;
}


답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다