본문 바로가기

프로그래밍 강좌/C++ - OpenCV

[OpenCV][C++] cv::Mat 클래스 총정리(1) - 얕은 복사 깊은 복사 matrix 연산 CV_8UC1 filter2d

OpenCV를 사용하기 위해서 가장 기본이 되는 클래스는 바로 cv::Mat 클래스 입니다.

이번부터 몇번에 걸쳐 cv::Mat 클래스에 대해 알아보겠습니다.

cv::Mat 클래스란?

 

cv::Mat 클래스는 행렬(matrix)를 표현하기 위한 클래스에요.n차원 단일/멀티 채널 배열을 다 표현할 수 있습니다.

OpenCV 공식 문서(https://docs.opencv.org/4.x/d3/d63/classcv_1_1Mat.html) 를 기반으로 설명 드릴께요.

 

OpenCV: cv::Mat Class Reference

n-dimensional dense array class More... #include  Mat () CV_NOEXCEPT    Mat (int rows, int cols, int type)    Mat (Size size, int type)    Mat (int rows, int cols, int type, const Scalar &s)    Mat (Size size, int type, const Scalar &s)    Mat (

docs.opencv.org

cv::Mat은 실수/복소수, 행렬, 모노/컬러, 입체 벡터, 점의 군집, tensor, histogram 등을 표현할 수 있습니다.

2차원 영상 데이터를 저장하고 처리하는 용도로 가장 많이 사용됩니다.

영상처리 특성상 빠른 연산이 중요하기 때문에 interface.h을 보면 아래와 같이 기본 자료형을 매크로 상수로 정의하고 있습니다.

이름
의미
범위
CV_8U
unsigned char (uchar)
8-bit unsigned integer
0
0 ~ 255
CV_8S
signed char (schar)
8-bit signed integer
1
-128 ~ 127
CV_16U
unsigned short (ushort)
16-bit unsigned integer
2
0 ~ 65535
CV_16S
signed short (short)
16-bit signed integer
3
-32768 ~ 32767
CV_32S
signed integer (int)
32-bit signed integer
4
-2147483648 ~ 2147483647
CV_32F
float
32-bit floating-point number
5
CV_64F
double
64-bit floating-point number
6
CV_16F
float16
16-bit floating-point number
7
 

아래와 같은 규칙을 가지고 표현을 하기도 합니다.

channelsgray scale 이면 1, color 이면 3 을 사용합니다.

cv::Mat 생성 방법

cv::Mat 객체를 생성하고 초기화 하는 방법은 여러가지가 있습니다.

cv::Mat은 아래와 같이 정의됩니다.

//빈 행렬
cv::Mat src;

// 640(width) x 480(height) 사이즈 영상 (unsigned char, 1 channel)
cv::Mat src1(480, 640, CV_8UC1);   // rows, cols 순서
cv::Mat src2;
src2 = cv::Mat(480, 640, CV_8UC1);

// 640 x 480 사이즈 영상 (unsigned char, 1 channel)
cv::Mat src3(cv::Size(640, 480), CV_8UC1);  // cv::Size(cols, rows) 순서
cv::Mat src4;
src4 = cv::Mat(cv::Size(640, 480), CV_8UC1);

// 640 x 480, 125 값으로 초기화된 영상 (unsigned char, 1 channel)
cv::Mat src5(480, 640, CV_8UC1, cv::Scalar(128));
cv::Mat src6;
src6 = cv::Mat(480, 640, CV_8UC1, cv::Scalar(128));

// 640 x 480, 0 값으로 초기화된 영상 (unsigned char, 1 channel)
cv::Mat src7;
src7 = cv::Mat::zeros(cv::Size(640, 480), CV_8UC1);
 

저는 영상을 생성할 때 cv::Size()를 많이 애용합니다.

우리는 보통 width x heigh로 얘기를 하기 때문에 rows, clos 를 사용하는 것보다 Size()를 사용하는 것이 헷갈리지 않죠.

결과를 Image Watch로 보면

https://blog.naver.com/dorergiverny/223035739939

 

[OpenCV][C++] Image Watch 설치 사용 방법 - Visual Studio 영상 디버깅 이미지 디버그 모드 픽셀 보는 방법

이번에는 영상처리 개발자들이 가장 많이 하고 개발 효율성을 높일 수 있는 Visual Studio 에서 영상을 ...

blog.naver.com

사이즈 및 type을 지정하지 않은 src 의 경우 아무런 값이 없고, src1, src2, src3, src4 는 size와 type을 지정하니까

205 값으로 초기화가 되어 들어가네요. src5, src6는 정상적으로 128 값이 들어가 있고,

src7은 0 값이 들어가 있어요.

이 정도만 알면 cv::Mat 객체 생성은 하실 수 있을 꺼에요.

한가지만 더 알려드리면, 배열을 이용하여 cv::Mat 형태로 변환한다면, 아래와 같이 하실 수 있습니다.

일반적으로 필터 커널을 생성한 후 cv::filter2D()를 사용할 때 많이 사용하게 됩니다.

float afData[] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9};
cv::Mat src8(3, 3, CV_32FC1, afData);
 
cv::Mat 복사하기

 

복사하는 방법은 두가지가 있습니다.

얕은 복사(Shallow Copy)깊은 복사(Deep Copy) 입니다.

 

1. 얕은 복사(Shallow Copy)

얕은 복사는 같은 데이터를 공유합니다. 대입 연산자, 복사 생성자를 이용하여 얕은 복사를 수행할 수있습니다.

간단한 예제를 위해 uchar로 배열을 만들고 cv::Mat으로 변환하였습니다. 그리고 대입 연산자를 이용하여 src9에 src8을 대입하였습니다.

src9 = src8;

uchar aucData[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
cv::Mat src8(3, 3, CV_8UC1, aucData);
cv::Mat src9 = src8;
src8.at<uchar>(1, 1) = 0;
 

분명히 src8의 가운데 값((1,1)위치)을 0으로 바꿨는데, src9의 가운데 값도 0으로 바뀌었네요.

영상에서 ROI를 따 낸 다음에 ROI에서만 처리를 하고 다시 원본 영상에 붙일 경우에 사용합니다.

2. 깊은 복사(Deep Copy)

깊은 복사는 새로운 메모리에 값을 복사하는 것 입니다.

clone()과 copyTo()가 있습니다. 이는 크기가 같고 값이 같은 독립 객체를 하나 더 생성하는 것이에요.

uchar aucData[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
cv::Mat src8(3, 3, CV_8UC1, aucData);
cv::Mat src10 = src8.clone();
cv::Mat src11;
src8.copyTo(src11);
 

란 뜻 입니다.

아래 결과를 보시면, src8의 값을 바꿨는데, Deep Copy한 src10, src11의 가운데 값은 바뀌지 않았습니다.

Deep Copy 하면 서로 독립이 된다는 뜻이죠.