[C++] E02. OpenCV cv::Mat
2021-07-14 # C++

OpenCV



cv::Mat


OpenCV의 Mat자료구조에 대해서 살펴보도록 하겠습니다.

먼저 간단한 예제를 통해 이미지를 읽어 Mat에 어떻게 저장되는지 확인해보겠습니다.

opencv를 이용하여 imread를 할 시 RGB로 순서가 아닌 BGR로 읽게됩니다. 따라서, 채널별로 조작을 하고 싶다면 cv::cvtColor()를 통해 BGR2RGB를 하거나 채널 순서를 헷갈리지 않고 작업을 해야합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "opencv2/opencv.hpp"
#include <iostream>

using namespace std;


int main(int, char**)
{
cv::Mat img_color = cv::imread("./test.jpg");

cv::namedWindow("name");

cv::imshow("name", img_color);

char ch = cv::waitKey(); // 무한 대기

if (ch == 27|| ch == 32){
cv::destroyWindow("name");
}

return 0;
}


위와 같은 사진이 저장된 img_color의 저장방식을 살펴보도록하겠습니다.


1
2
3
4
5
6
7
8
9
int main(int, char**)  
{
cv::Mat img_color = cv::imread("./test.jpg");
cv::Mat imgSample = img_color(cv::Rect(5,5,5,5));;

cout << "Region of (5,5,5,5) : " << imgSample << endl;

return 0;
}


1
2
3
4
5
Region of (5,5,5,5) : [167, 166, 168, 167, 166, 168, 167, 166, 168, 167, 166, 170, 168, 167, 171;
168, 167, 169, 168, 167, 169, 168, 167, 169, 168, 167, 171, 168, 167, 171;
168, 167, 169, 168, 167, 169, 168, 167, 169, 168, 167, 171, 168, 167, 171;
169, 168, 170, 169, 168, 170, 169, 168, 170, 169, 168, 172, 169, 168, 172;
169, 168, 170, 169, 168, 170, 169, 168, 170, 169, 168, 172, 169, 168, 172]

cv::Rect(5,5,5,5)는 point(5,5)로부터 width, height가 각각 5인 직사각형 영역을 의미합니다.

즉, 25개의 픽셀값이 들어가는데 출력시 75개의값이 반환되는 것을 볼 수 있습니다.

이는, RGB채널마다 픽셀값을 가지고 있기 때문입니다.

따라서, 5,5라는 점에서의 가로 5 세로 5의 사각형은 아래와 같습니다.


이해를 돕기위해 width, height = 100으로 증가시켰습니다.


Mat 생성 방법


C++: Mat::Mat()
C++ : Mat::Mat(int rows, int cols, int type)
C++ : Mat::Mat(Size size, int type)
C++ : Mat::Mat(int rows, int cols, int type, const Scalar& s)
C++ : Mat::Mat(Size size, int type, const Scalar& s)
C++ : Mat::Mat(const Mat& m)
C++ : Mat::Mat(int rows, int cols, int type, void* data, size_t step = AUTO_STEP)
C++ : Mat::Mat(Size size, int type, void* data, size_t step = AUTO_STEP)
C++ : Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange = Range::all())
C++ : Mat::Mat(const Mat& m, const Rect& roi)
C++ : Mat::Mat(const CvMat* m, bool copyData = false)
C++ : Mat::Mat(const IplImage* img, bool copyData = false)

출처: https://nextus.tistory.com/14 [ReStartAllKill]


위의 다양한 생성자에서 type이라는 변수가 있는데 아래와 같은 상수를 통해 값을 설정합니다.


CV_8UC1 : 8 - bit unsigned integer : uchar(0..255)
CV_8SC1 : 8 - bit signed integer : schar(-128..127)
CV_16UC1 : 16 - bit unsigned integer : ushort(0..65535)
CV_16SC1 : 16 - bit signed integer : short(-32768..32767)
CV_32SC1 : 32 - bit signed integer : int(-2147483648..2147483647)
CV_32FC1 : 32 - bit floating - point number : float(-FLT_MAX..FLT_MAX, INF, NAN)
CV_64FC1 : 64 - bit floating - point number : double(-DBL_MAX..DBL_MAX, INF, NAN)

출처: https://nextus.tistory.com/14 [ReStartAllKill]

CV_64FC1으로 살펴보면 다음과 같습니다.

  1. 64 : bit단위를 말하며 위의 예시는 64bit를 의미합니다. 즉, 0~2^64를 의미합니다.
  2. F : 부호 결정자로 F는 Floating, S는 Signed, U는 Unsigned를 의미합니다.
  3. C1: Channel의 약자로 C1의 경우 생략이 가능하며 그 외는 C1~C8범위가 가능합니다.

가장 처음 살펴본 강아지의 경우 RGB값이 있는 3 channel의 8bit image이므로 CV_8UC3의 데이터타입을 갖게 됩니다.

위와같은 데이터타입을 생성할 수도 있는데

1
2
3
4
5
6
//mat.hpp
#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))


//main.cpp
CV_MAKETYPE(CV_32F,5); //CV_32FC5

위와 같이 입력할 경우 CV_32FC5이라는 상수명으로 5 + 4 << 3 = 69라는 값이 저장되게 됩니다.




OpenCV 핸드북 - 다크프로그래머님 - https://darkpgmr.tistory.com/46

using namespace cv;


이미지 읽기 및 저장


  1. imread()를 통한 이미지 읽기

​ Mat img_color = imread(“sample.jpg”); // color load

​ Mat img_gray = imread(“sample.jpg”, 0); // gray load

​ Mat img_16bit = imread(“sample.jpg”, cv::IMREAD_ANYDEPTH); // 원본 depth로 읽기

  1. imwrite()를 통한 이미지 저장

    imwrite(“fname.jpg”, img);


이미지 생성, 복사, 형변환, 색상변환, roi 설정


​ int w = 320;// width

​ int h = 240;// height


  • 생성자를 통한 변수 생성

​ Mat img(h,w,CV_8UC1);//1채널 unsigned char

​ Mat img(h,w,CV_8UC3);//3채널 unsigned char

​ Mat img = Mat::zeros(h,w,CV_32FC1);//1채널 float

​ Mat img = Mat::ones(h,w,CV_64FC3);//3채널 double

​ unsigned char * img_buffer;// 이미지버퍼 포인터

​ Mat img(h, w, CV_8UC3, img_buffer);//메모리 공유


  • Scalar()를 통한 원소 초기화

​ Mat img(h,w,CV_8UC1);

​ img = Scalar(3); // img 모든 원소값 3으로 초기화


  • 이미지 참조 및 복사

​ Mat img2 = img; // 메모리 공유

​ Mat img2 = img.clone(); // 별도 메모리

​ Mat img2; img.copyTo(img2);//별도 메모리


  • 형변환을 통한 복사

​ Mat img1(h,w,CV_32FC1);

​ Mat img2;

​ img1.convertTo(img2, CV_8U);


  • gray-color 변환

​ cvtColor(color, gray, CV_BGR2GRAY);

​ cvtColor(gray, color, CV_GRAY2BGR);


  • ROI(Region Of Interst) 설정

​ Rect roi;

​ Mat sub_img = img(roi);//메모리공유

​ Mat sub_img = img(roi).clone();//별도메모리


행렬 원소접근 및 생성초기화


​ Mat A = (Mat_(3,1) << 1, 2, 3);

​ Mat B = (Mat_(3,2) << 1, 1, 2, 2, 3, 3); // 첫 번째 행부터 채워짐

​ A.at(2,0) = 3; // .at(y, x)

​ B.at(2,1) = -0.1;

​ Mat C(h, w, CV_8UC3);

​ C.at(y, x)[0]; // blue

​ C.at(y, x)[1]; // green

​ C.at(y, x)[2]; // red


영상 크기변경 및 상하좌우 반전


  • 크기 변경

​ Mat dst;

​ resize(img, dst, cv::Size(new_w,new_h));

​ resize(img, dst, cv::Size(), 0.5, 0.5);//scalex, scaley

  • 영상 반전(flip)

​ flip(img, dst, 0);// vertical flip

​ flip(img, dst, 1);// horizontal flip

​ flip(img, dst, -1);// vertial & horizontal flip


이미지에 그리기 (drawing)


​ Rect rc(x,y,w,h);

​ Scalar color(B,G,R);

​ int thickness=1; // line thickness

​ line(img, Point(x1,y1), Point(x2,y2), color, thickness);

​ rectangle(img, rc, color, thickness);

​ rectangle(img, rc.tl(), rc.br(), color, thickness); // tl = TopLeft, br = BottomRight

​ rectangle(img, rc, color, CV_FILLED); // filled rectangle

​ Point center(rc.x+rc.width/2, rc.y+rc.height/2);

​ Size radius(rc.width/2, rc.height/2);

​ double rot_deg = 0; // rotation of ellipse

​ double s_deg = 0; // start angle of arc

​ double e_deg = 360; // end angle of arc

​ ellipse(img,center,radius,rot_deg,s_deg,e_deg,color,thickness);

​ ellipse(img,center,radius,rot_deg,s_deg,e_deg,color,CV_FILLED);

​ int circle_radius = 10;

​ circle(img, center, circle_radius, color, thickness);

​ circle(img, center, circle_radius, color, CV_FILLED);

​ putText(img, “text”, Point(x,y), FONT_HERSHEY_SIMPLEX, 1., color, thickness);

​ putText(img, “text”, Point(x,y), FONT_HERSHEY_DUPLEX, 1., color, thickness);


이미지 디스플레이하기 (display)


​ namedWindow(“name”); // auto resized

​ namedWindow(“name”,CV_WINDOW_NORMAL); // manual resize

​ imshow(“name”, img);

​ char ch = waitKey(); // 무한 대기

​ char ch = waitKey(10); // 10 msec 대기

​ if(ch == 27) … // ESC key

​ if(ch == 32) … // SPACE key

​ destroyWindow(“name”);

​ destroyAllWindows();


웹캠 연결하기


​ VideoCapture vc(0);

​ if (!vc.isOpened()) return; // 연결실패

​ vc.set(CV_CAP_PROP_FRAME_WIDTH, 640);

​ vc.set(CV_CAP_PROP_FRAME_HEIGHT, 480);

​ Mat img;

​ while(1){

​ vc >> img;

​ if(img.empty()) break;

​ imshow(“cam”,img);

​ if(waitKey(10)==27) break; //ESC

​ }

​ destroyAllWindows();


avi 비디오 파일 읽어오기


​ VideoCapture vc(“sample.avi”);

​ if (!vc.isOpened()) return; // 불러오기 실패

​ Mat img;

​ while(1){

​ vc >> img;

​ if(img.empty()) break;

​ imshow(“video”,img);

​ if(waitKey(10)==27) break; //ESC

​ }

​ destroyAllWindows();


avi 비디오 녹화하기


​ double fps = 15;

​ int fourcc = CV_FOURCC(‘X’,’V’,’I’,’D’); // codec (opencv3.0이하)

​ int fourcc = VideoWriter::fourcc(‘X’,’V’,’I’,’D’); // opencv3.0이상

​ bool isColor = true;

​ VideoWriter *video = new VideoWriter;

​ if(!video->open(“result.avi”, fourcc, fps, Size(img_w, img_h), isColor)){

​ delete video;

​ return;

​ }

​ Mat img;

​ while(1){

​ // …

​ *video << img;

​ // …

​ }

​ delete video;

​ ※ 코덱 목록: http://www.fourcc.org/codecs.php


창에 Trackbar 붙이기


​ int threshold = 10;

​ cv::namedWindow(“display”);

​ cv::createTrackbar(“thr:”, “display”, &threshold, 1000);


REFERENCE

  1. https://nextus.tistory.com/15?category=762131
  2. https://darkpgmr.tistory.com/46