Mat 클래스 정리

nugurii0·2022년 9월 12일
0

다중차로인식

목록 보기
4/11

행렬을 나타내는 클래스로 가장 많이 사용된다고 한다.

Mat 클래스 개요

Mat 클래스?

2차원, 고차원 행렬을 표현할 수 있다. 한 개 이상의 채널을 가질 수 있다.

정수, 실수, 복소수 등으로 구성된 행렬 또는 벡터를 저장할 수 있고, grayscale 또는 color 영상을 저장할 수도 있다. 그 외에도 벡터 필드, 포인트 클라우드, 텐서, 히스토그램 등 여러 정보를 저장할 수 있다.

주요 멤버 변수로는

  • int dims : 행렬의 차원을 의미 ( 2차원이면 dims == 2)
  • int rows, cols : 2차원 행렬의 크기 각각 행과 열의 개수. 만약 3차원이면 -1
  • MatSize size : 3차원 이상 행렬의 크기 정보
  • uchar* data : 행렬의 원소 데이터를 가리키는 포인터, 아무 것도 저장되지 않았다면 NULL.

이외에도 여러 멤버 변수가 존재하며 모두 public 지시자로 선언되어 있다.

Depth?

Mat 클래스에서 행렬이 사용하는 자료형을 깊이(depth)라고 부르며 다음과 같은 매크로 상수를 이용하여 표현한다.

CV_<bit-depth>{U|S|F}

<bit_depth> : 원소 값 하나의 비트 수 ex) 8, 16, 32, 64

{U|S|F} : Unsigned integer, Signed integer, Float 중 선택

ex) CV_8U : uchar, unsigned char, CV_64F : double

Channel?

Mat행렬 원소를 구성하는 값을 채널(channel)이라고 부른다.

한 원소를 몇 가지 값으로 표현할 것인지를 나타낸다.

그레이 스케일의 경우 밝기 정보만 있으므로 1채널, 트루컬러 영상의 경우 RGB 3가지 색상 정보가 필요하므로 3채널

Type?

depth 정보와 channel 정보를 합쳐서 Mat 객체의 type이라고 부른다.

매크로 상수는 다음과 같다.

CV_<bit-depth>{U|S|F}C(<number_of_channels>)

CV_8UC1 : 8비트 unsigned char 자료형을 사용하며 채널이 1인 행렬 또는 영상

Mat 클래스 생성과 초기화

  • 2차원 행렬 생성, 초기화 X

    Mat(int rows, int cols, int type)

    Mat(Size size, int type)

    ex) 640x480, unsigned char, 3-channels 행렬을 만드려면

    Mat img(480, 640, CV_8UC3)

    Mat img(Size(640, 480), CV_8UC3) //Size(width, height)

  • 2차원 행렬 생성, 특정 값으로 초기화

    Mat(int rows, int cols, int type, const Scalar& s)

    Mat(Size size, int type, const Scalar& s)

    ex) 680x480, unsigned char, 3channels 행렬을 만들고 모든 픽셀이 빨간색이라면

    Mat img(480, 640, CV_8UC3, Scalar(0,0,255))

    Mat img(Size(640, 480), CV_8UC3, Scalar(0,0,255))

    Scalar클래스를 이용하여 색상을 지정할 때엔 BGR 순서로 색상 성분을 저장한다.

  • 외부 메모리를 참조하여 행렬을 생성

    직접 메모리를 할당하지 않고 메모리를 참조하여 사용하는 방식으로 객체 생성이 빠르다는 장점이 있다.

    Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)

    Mat(Size size, int type, void* data, size_t step=AUTO_STEP)

    step 값은 패딩 바이트가 존재하다면 명시해줘야 하며 기본값은 패딩 바이트가 없다고 간주

    ex) 여섯 개의 원소를 가지는 float 배열 생성하고 이 배열을 사용하는 Mat 객체 생성

    float data[] = {1,2,3,4,5,6};

    Mat mat(2, 3, CV_32FC1, data);

    외부 배열과 행렬 원소의 개수와 자료형이 같아야 한다.

    말 그대로 참조하는 것이기에 외부 메모리가 변경되면 행렬도 변경된다.

  • Mat_클래스를 사용하여 생성

    Mat_<float> mat_(2, 3);
    mat_ << 1,2,3,4,5,6;
    Mat mat = mat_;

    결과적으로 mat 행렬은 2*3 크기를 갖고 타입은 CV32FC1이며 mat 행렬과 원소를 공유한다. 다음과 같이 한 줄로 표현할 수도 있다.

    Mat mat = (Mat_<float>2, 3)<<1,2,3,4,5,6);

    OpenCV4.0부터는 C++11의 초기화 리스트를 이용한 행렬 초기화도 사용할 수 있다.

    Mat mat = Mat_<float>({2,3},{1,2,3,4,5,6});

  • 이미 생성된 Mat 객체에 새로운 행렬 할당

    void Mat::create(int rows, int cols, int type)

    void Mat::create(Size size, int type)

    만약 create로 생성하려는 행렬의 크기와 타입이 기존 행렬과 같다면 함수를 종료한다.

    만약 기존 행렬과 다르다면 기존 메모리를 해제하고 새로운 메모리 공간을 할당한다.

  • 특정 값으로 초기화 시 간편한 함수들

    모든 원소를 0으로 초기화 하는 경우가 잦다. 이럴 땐 Scalar(0)을 사용하거나 zeros()를 사용한다.

    static MatExpr Mat::zeros(int rows, int cols, int type)

    static MatExpr Mat::zeros(Size size, int type)

    정적 멤버 함수이므로 Mat::을 붙여서 사용해야 한다.

    반환형인 MatExpr은 행렬의 대수 연산을 표현하는 클래스로 자동으로 Mat으로 형 변환된다.

    ex) Mat mat = Mat::zeros(3,3,CV_32SC1); : 3*3 정수형 행렬을 생성하여 Mat 객체에 할당

    zeros와 유사하게 모든 원소를 1로 초기화하려면 Mat::ones() 함수를 사용할 수 있다.

    단위 행렬을 생성하려면 Mat::eye() 을 사용할 수 있다.

    사용법은 zeros와 동일하다.

    그 외에도 = 연산자 재정의나 Mat::setTo() 함수를 사용하여 일괄적으로 원소 값을 설정할 수 있다.

    mat = Scalar(0, 255, 0)

    Mat& Mat::setTo(InputArray value, InputArray mas=noArray())

    setTo함수는 mask 행렬을 지정해주면 0이 아닌 위치에만 value를 설정한다. noArray나 Mat()으로 지정하면 전체 행렬의 값을 value로 설정한다.

    mat.setTo(1.f)

행렬의 복사

복사 생성자와 대입 연산자 모두 얕은 복사를 수행한다.

Mat img1 = imread("test.bmp");
Mat img2 = img1; // 복사 생성자
Mat img3;
img3 = img1;     // 대입 연산자

데이터를 공유하지 않고 메모리 공간을 새로 할당하려면(깊은복사)

Mat::clone() : 자기 자신과 동일한 객체를 만들어서 반환

Mat::copyTo() : 인자로 전달된 m 행렬에 자신을 복사

만약 copyTo() 함수를 호출한 행렬과 인자로 전달된 m 행렬의 크기 또는 타입이 다르면 내부에서 행렬 m을 새로 생성한 후 복사

Mat new_img = img.clone();

Mat new_img;
img.copyTo(new_img);

부분 행렬 추출

Mat 클래스에 저장된 영상에서 일부분(부분 행렬)을 추출하고 싶을 때 사용

괄호연산자 재정의를 사용한다.

Mat Mat::operator()(const Rect& roi) const

Mat Mat::operator()(Range rowRange, Range colRange) const

ex) new_img = img(Rect(220, 120, 340, 240))

괄호 연산자를 이용한 부분 행렬 추출은 얕은 복사, 즉 메모리를 공유한다.

이를 이용하여 특정 부분(new_img)에만 영상처리를 수행하면 원본 영상에서 부분 추출한 영역만 처리된 영상을 얻을 수 있다.

ex) new_img = img(Rect(220, 120, 340, 240)).clone()

특정 범위의 행 또는 열을 추출할 땐

Mat Mat::rowRange(int startrow, int endrow) const

Mat Mat::rowRange(const Range& r) const

Mat Mat::colRange(int startcol, int endcol) const

Mat Mat::colRange(const Range& r) const

Range클래스와 마찬가지로 값의 범위는 [start, end) 이다.

만약 1행 또는 1열짜리 행렬을 만들고 싶다면

Mat Mat::row(int y) const

Mat Mat::col(int x) const

해당 함수들은 모두 얕은 복사를 수행한다. 만약 깊은 복사를 원한다면 부분 추출한 행렬에 clone 함수를 사용하면 된다.

행렬의 원소 값 참조

template<typename _Tp> _Tp& Mat::at(int y, int x)

at() 함수를 사용할 때에는 자료형을 명시적으로 지정해야 한다.

ex) 그레이스케일 영상 img에서 (x, y) 좌표 픽셀 값을 참조하려면

img.at<uchar>(y, x)

template<typename _Tp> _Tp* Mat::ptr(int y)

y행의 시작 주소, 첫 원소의 주소를 반환한다.

ex) y행의 시작 주소를 가져오려면

mat.ptr<uchar>(y)

Mat::at()Mat::ptr() 비교

Mat mat = Mat::zeros(3, 4, CV_8UC1);

for(int j=0;j<mat.rows;j++){
	for(int i=0;i<mat.cols;i++){
		mat.at<uchar>(j, i)++;
	}
}

for(int j=0;j<mat.rows;j++){
	uchar* p = mat.ptr<uchar>(j);
	for(int i=0;i<mat.cols;i++){
		p[i]++;
	}
}

두 반복문 모두 각 원소에 접근하여 1을 증가시키는 코드이다.

ptr, at을 사용하여 접근할 때 행렬의 행과 열의 크기를 벗어나면 에러가 발생할 수도 있다. 하지만 MatIterator_ 반복자를 사용하면 행렬 크기에 상관 없이 행렬 전체 원소를 안전하게 차례대로 참조할 수 있다.

Mat 행렬 타입에 맞는 자료형을 명시하여 사용해야 한다.

Mat::begin() 함수는 행렬의 첫 번째 원소 위치를 반환하고, Mat::end() 함수는 마지막 원소 바로 다음 위치를 반환한다.

즉 다음과 같이 사용할 수 있다.

for(MatIterator_<uchar> it = mat1.begin<uchar>();it!=mat1.end<uchar>();++it){
	(*it)++;
}

위에서 작성하였던 반복문과 동일한 작업을 하는 코드이다.

반복자는 행렬의 크기와 관계없이 모든 원소를 안전하게(크기를 벗어나지 않고) 조회할 수 있지만, ptr과 at보다 느리다는 단점이 있다.

행렬 정보 참조

cols, rows로 행과 열의 개수를, data로 첫 원소의 주소를 멤버 변수로 접근하여 사용할 수 있다.

그 외에도 다양한 멤버 함수가 존재한다.

<< 연산자가 재정의 되어 있기 때문에 표준 출력 스트림으로 Mat 객체를 출력할 수 있다.

행렬 연산

연산자 재정의가 되어있기에 쉽게 행렬연산을 사용할 수 있다.

여기서 사용되는 *은 행렬의 수학적 곱셈 연산이다.

같은 위치의 원소끼리의 곱셈 연산을 하려면 Mat::mul() 함수를 사용해야 한다.

MatExpr Mat::mul(InputArray m, double scale=1) const

Mat::inv() 함수를 통해 역행렬(inverse matrix)을 구할 수 있다.

MatExpr Mat::inv(int method=DECOMP_LU) const

method 값으로 역행렬 계산 방법을 지정할 수 있다.

행과 열을 교환하는 전치 행렬(transpose matrix)은 Mat::t() 멤버 함수를 이용하여 구할 수 있다.

MatExpr Mat::t() const

크기 및 타입 변환 함수

void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0) const

행렬 원소의 타입을 변경하고 모든 원소에 일정한 값을 더하거나 곱한다. 출력 행렬은 다음 수식에 의해 결정된다.

연산의 정확도를 높이기 위해 픽셀 값을 부동 소수점으로 나타내거나 실수로 구성된 행렬을 255로 곱하여 그레이스케일로 만드는 등의 작업을 수행할 때 사용한다.

Mat Mat::reshape(int cn, int rows=0) const

행렬의 크기 또는 채널 수를 변경한다. 채널 수를 cn으로 변경하고, 행렬의 크기를 rows로 수정한다. 만약 cn이나 rows의 값이 0이면 변경하지 않는다.

void Mat::resize(size_t sz)

void Mat::resize(size_t sz, const Scalar& s)

reshape처럼 행렬의 모양을 변형하지 않고 단순히 크기를 변경할 때 사용한다.

기존 행 개수보다 작으면 아래쪽 행을 제거하고, 기존 행 개수보다 크면 행을 추가한다.

이때 초기 값으로 s를 지정할 수 있다.

template<typename _Tp> void Mat::push_back(const _Tp& elem)
template<typename _Tp> void Mat::push_back(const Mat_<_Tp>&elem)
template<typename _Tp> void Mat::push_back(const std::vector<_Tp>& elem)
void Mat::push_back(const Mat& m)

인자로 입력받은 행렬을 마지막 행에 추가하는 함수이다.

void Mat::pop_back(size_t nelems=1)

nelems 만큼 아래에 있는 행을 제거한다. nelems는 행 개수보다 크면 안 된다.

profile
개발과 보안을 공부하는 학생

0개의 댓글