본문 바로가기
Computer Vision

OpenCV 주요 클래스 - 2. Mat 클래스(3)

by ksb0511 2020. 2. 12.

3. 행렬의 복사

// 메모리 공간을 새로 할당하여 픽셀 데이터 전체를 복사 ( 깊은복사 )
Mat Mat::clone() const;

void Mat::copyTo( OutputArray m ) const;
void Mat::copyTo( OutputArray m, InputArray mask ) const;

 

  • clone() 함수는 자기 자신과 동일한 Mat 객체를 완전히 새로 만들어서 반환
  • clone() 함수의 반환값은 *this 행렬의 복사본
  • copyTo() 함수는 인자로 전달된 m 행렬에 자기 자신을 복사함.
    • 함수를 호출한 행렬과 인자로 전달된 행렬 m이 서로 크기와 타입이 같으면 원소 값 복사만 수행
    • 함수를 호출한 행렬과 인자로 전달된 행렬 m이 서로 크기와 타입이 다르면 copyTo() 함수 내부에서 행렬 m을 새로 생성한 후 픽셀 값을 복사
  • m값은 복사본이 저장될 행렬, mask는 마스크 행렬을 의미.
  • 완전히 메모리 공간을 새로 할당하여 픽셀 값을 복사하는 형태의 복사를 깊은복사라고 함.

4. 부분 행렬 추출

// 저장된 영상에서 사각형 모양의 부분 영상 추출하는 방법
// Mat 클래스에 정의된 괄호 연산자 재정의
Mat Mat::operator()(const Rect& roi) const;
Mat Mat::operator()(Range rowRange, Range colRange) const;

 

  • roi : 사각형 관심 영역
  • rowRange, colRange : 관심 행, 열 범위
  • 반환값은 추출한 부분 행렬 또는 영상.
  • 영상 추출 예시 )
Mat img1 = imread("cat.bmp");
Mat img2 = img1(Rect(220, 120, 340, 240);

 

  • 부분 영상 추출 시 주의할 점으로는 픽셀 데이터를 공유하는 얕은 복사 형식이라는 점이다. 이 때문에 부분 영상 추출 후 부분 영상의 픽셀 값을 변경할 경우 추출한 부분 영상뿐 아니라 원본 영상의 픽셀 값도 같이 변경됨.
  • 얕은 복사 형식이라는 점때문에 입력 영상에 사각형 모양의 관심 영역(ROI)을 설정하는 용도로 사용 가능.
  • ROI : 영상의 전체 영역 중 특정 영역에 대해 영상 처리를 수행할 때 설정하는 영역
  • 만약 독립된 메모리 영역 확보하여 부분 영상 추출하고자 한다면, 괄호 연산자 뒤에 Mat::clone() 함수를 함께 사용.
// clone() 함수를 사용하여 독립적인 공간을 사용하게 됨. 
// 서로 다른 메모리 공간을 사용하기 때문에 img3 영상 픽셀 변경해도 img1의 변화는 x
Mat img3 = img1(Rect(220, 120, 340, 240)).clone();

// Mat 행렬에서 특정 범위의 행 또는 열을 부분 행렬로 추출할 때
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;

// 하나의 행 또는 열을 추출하여 1행 또는 1열짜리 행렬을 만들고자 할 때
Mat Mat::row(int y) const;
Mat Mat::col(int x) const;

 

  • 모두 부분 행렬을 얕은 복사 형태로 반환하기 때문에 깊은 복사를 하려면 clone() 함수와 함께 써야함.

5. 행렬의 원소 값 참조

OpenCV에서 제공하는 세 가지 픽셀 값 접근 방법

1) Mat::at() 함수

// Mat::at() 함수
template<typename_ Tp> _Tp& Mat::at(int y, int x)

 

  • 가장 직관적 행렬 원소 접근 방법
  • 행과 열 2개의 정수를 인자로 받음.
  • 해당 위치의 행렬 원소값을 참조 형식으로 반환.
  • 예시)
Mat mat1 = Mat::zeros(3,4,CV_8UC1);		// mat1은 0으로 초기화된 3x4 행렬, 각원소는 uchar 자료형

for (int j=0; j<mat1.rows; j++) {			// 행에 대한 반복
	for (int i=0; i<mat1.cols; i++) {			// 열에 대한 반복
   	 	// at() 함수가 행렬 원소를 참조로 반환하기 때문에 at() 함수의 반환값 변경 시, mat1 행렬 원소값도 함께 변경.
    	mat1.at<uchar>(j, i)++;				// 자료형 명시
     }
}


2) Mat::ptr() 함수

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

 

  • _Tp* 타입으로 형변환된 y번째 행의 시작 주소를 반환.
  • 지정한 자료형의 포인터를 반환. 이 포인터로 지정한 행의 원소에 접근 가능.
  • 행단위로 행렬원소에 접근
  • 예시)
Mat mat1 = Mat::zeros(3,4,CV_8UC1);		// mat1은 0으로 초기화된 3x4 행렬, 각원소는 uchar 자료형

for (int j=0; j<mat1.rows; j++) {			// 행에 대한 반복
	 // p를 1차원 배열처럼 사용
	uchar* p = mat1.ptr1<uchar>( j );		// j번째 행 원소의 시작 주소 반환. 포인터형 변수 p에 저장
    
	for (int i=0; i<mat1.cols; i++) {			// 열에 대한 반복
   	 	p[i]++;
     }
}

3) MatIterator_ 반복자

  • at(), ptr() 함수를 사용할 경우 함수 인자로 전달된 값이 행렬의 크기를 벗어나면 에러가 발생할 수 있음. 이 단점 보완을 위해 반복자라는 개념이 도입되었는데 이것이 MatIterator
  • Mat::begin() 함수를 이용하면 행렬의 첫 번째 원소 위치를 얻을 수 있음.
  • Mat::end() 함수를 이용하면 행렬의 마지막 원소 바로 다음 위치를 얻을 수 있음.
  • ptr()에 비해 속도가 느리고, at()처럼 임의의 위치에 자유롭게 접근이 불가능하므로 사용성이 낮음.

댓글