ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OpenCV Mat class 기초 사용법
    OpenCV/OpenCV_C++ 2023. 12. 12. 17:33

    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;
    }

    결과

     

    'OpenCV > OpenCV_C++' 카테고리의 다른 글

    OpenCV 유용한 함수  (2) 2023.12.21
    OpenCV Draw & Event  (0) 2023.12.14
    OpenCV VideoCapture class  (0) 2023.12.13
    OpenCV 주요 클래스  (1) 2023.11.27
    OpenCV 기초 및 편하게 사용하기  (2) 2023.11.26
Designed by Tistory.