OpenCV Mat class 기초 사용법
OpenCV에서 Mat은 자주 사용되며 행렬과 연관이 깊은 클래스이다.
https://mokchanic.tistory.com/91
OpenCV 주요 클래스
1. Point, Size, Rect Class - Point Class 2차원 점의 좌표 표현을 위한 템플릿 클래스 멤버 변수: x, y 멤버 함수: dot(), ddot(), cross(), inside() 등 다양한 사칙 연산에 대해 연산자 오버로딩과 std::cout 출력을 위
mokchanic.tistory.com
이번에는 예제를 직접 실행하며 Mat class에 대해 알아보자.
Example 1. 객체 생성 및 초기화
void MatOp1()
{
cv::Mat img1; // empty cv::Matrix
cv::Mat img2(480, 640, CV_8UC1); // unsigned char, 1-channel
cv::Mat img3(480, 640, CV_8UC3); // unsigned char, 3-channels
cv::Mat img4(cv::Size(640, 480), CV_8UC3); // cv::Size(width, height)
cv::Mat img5(480, 640, CV_8UC1, cv::Scalar(128)); // initial values, 128
cv::Mat img6(480, 640, CV_8UC3, cv::Scalar(0, 0, 255)); // initial values, red
cv::Mat Mat1 = cv::Mat::zeros(3, 3, CV_32SC1); // 0's cv::Matrix
cv::Mat Mat2 = cv::Mat::ones(3, 3, CV_32FC1); // 1's cv::Matrix
cv::Mat Mat3 = cv::Mat::eye(3, 3, CV_32FC1); // identity cv::Matrix
float data[] = { 1, 2, 3, 4, 5, 6 };
cv::Mat Mat4(2, 3, CV_32FC1, data); // 2rows, 3cols Matrix, float. data변수와 동기화됨.
cv::Mat Mat5 = (cv::Mat_<float>(2, 3) << 1, 2, 3, 4, 5, 6);
cv::Mat Mat6 = cv::Mat_<uchar>({ 2, 3 }, { 1, 2, 3, 4, 5, 6 });
Mat4.create(256, 256, CV_8UC3); // uchar, 3-channels
Mat5.create(4, 4, CV_32FC1); // float, 1-channel
Mat4 = cv::Scalar(255, 0, 0);
Mat5.setTo(1.f);
}
OpenCV에서는 RGB가 아닌 BGR을 기본 순서로 사용한다.
zeros는 0행렬, ones는 1행렬, eye는 단위 행렬(identity matrix)을 만들어준다.
create는 기존의 것을 지우고 새로 만들어준다. 대신 초기 값 지정할 수 있는 4번째 값은 지원하지 않는다.
Example 2. 객체의 참조와 복사
void MatOp2()
{
cv::Mat img1 = cv::imread("dog.bmp");
// 얕은 복사
cv::Mat img2 = img1;
cv::Mat img3;
img3 = img1; // img3(img1)과 같음.
//깊은 복사
cv::Mat img4 = img1.clone();
cv::Mat img5;
img1.copyTo(img5);
img1.setTo(cv::Scalar(0, 255, 255)); // yellow
imshow("img1", img1);
imshow("img2", img2);
imshow("img3", img3);
imshow("img4", img4);
imshow("img5", img5);
cv::waitKey();
cv::destroyAllWindows();
}
Mat 객체에 대해 = 연산자는 참조(얕은 복사)를 수행한다.
Mat::clone() 또는 Mat::copyTo()함수를 이용하여 깊은 복사를 수행한다.
프로그램을 실행하면 img1이 노란색으로 바뀌는 것과 동시에 img2, img3도 노란색으로 바뀌지만, img4, img5는 그렇지 않음을 볼 수 있다.
Example 3. 부분 행렬 추출
void MatOp3()
{
cv::Mat img1 = cv::imread("cat.bmp");
if (img1.empty()) {
std::cerr << "Image load failed!" << std::endl;
return;
}
cv::Mat img2 = img1(cv::Rect(220, 120, 340, 240));
cv::Mat img3 = img1(cv::Rect(220, 120, 340, 240)).clone();
img2 = ~img2; // 이미지 반전
imshow("img1", img1);
imshow("img2", img2);
imshow("img3", img3);
cv::waitKey();
cv::destroyAllWindows();
}
Rect로 img를 부분적으로 추출할 수 있다.
~는 Not연산으로 이미지 색상 반전을 해준다.
img2에서 반전이 되며, img1 부분도 해당 부분이 반전된 것을 볼 수 있다.
결과
Example 4. 영상의 픽셀 값 참조
영상의 픽셀 값에 접근할 수 있으며 OpenCV에서 제공하는 기능이 아닌, 자신만의 새로운 기능을 추가해야 하는 경우에 유용하다.
영상의 픽셀은 다음과 같이 접근할 수 있다.
Mat::data 사용 방법 | 메모리 연산이 잘못될 경우 프로그램이 비정상적으로 종료할 수 있다.![]() |
Mat::at() 함수 사용 방법 | 좌표 지정이 직관적이며 임의 좌표에 접근할 수 있다. |
Mat::ptr() 함수 사용 방법 | Mat::at()보다 빠르게 동작하며 행 단위 연산을 수행할 때 유리하다. |
Matiterator_ 반복자 사용 방법 | 좌표를 지정하지 않아서 안전하다. 성능은 느리다. |
기본적으로 Mat::data 멤버 변수가 픽셀 데이터 메모리 공간을 가리키지만, Mat 클래스 멤버 함수를 사용하는 방법을 권장한다.
void MatOp4()
{
cv::Mat Mat1 = cv::Mat::zeros(3, 4, CV_8UC1);
for (int y = 0; y < Mat1.rows; y++) {
for (int x = 0; x < Mat1.cols; x++) {
Mat1.at<uchar>(y, x)++; // at 접근
}
}
for (int y = 0; y < Mat1.rows; y++) {
uchar* p = Mat1.ptr<uchar>(y); // ptr 접근
for (int x = 0; x < Mat1.cols; x++) {
p[x]++;
}
}
for (cv::MatIterator_<uchar> it = Mat1.begin<uchar>(); it != Mat1.end<uchar>(); ++it) {
(*it)++; // 반복자 접근
}
std::cout << "cv::Mat1:\n" << Mat1 << std::endl;
}
Example 5. 행렬 연산
행렬의 연산은 다음과 같이 할 수 있으며 inverse, transpose등의 연산도 가능하다.
void MatOp5()
{
float data[] = { 1, 1, 2, 3 };
cv::Mat Mat1(2, 2, CV_32FC1, data);
std::cout << "cv::Mat1:\n" << Mat1 << std::endl;
cv::Mat Mat2 = Mat1.inv(); // inverse 연산
std::cout << "cv::Mat2:\n" << Mat2 << std::endl;
std::cout << "cv::Mat1.t():\n" << Mat1.t() << std::endl; // transpose 연산
std::cout << "cv::Mat1 + 3:\n" << Mat1 + 3 << std::endl;
std::cout << "cv::Mat1 + cv::Mat2:\n" << Mat1 + Mat2 << std::endl;
std::cout << "cv::Mat1 * cv::Mat2:\n" << Mat1 * Mat2 << std::endl;
}
결과