지난 글에서는 hconcat()
, vconcat()
을 사용해서 이미지를 붙여서 출력, 저장하는 방법에 대해 썼었다. 그러나 라이브러리 함수를 그대로 사용하다 보니 한 두개 프레임을 이어 붙일 때는 편하긴 하지만 여러 사진을 이어 붙이려면 오히려 불편한 부분이 있었다.
예를 들어, 2x2 영상을 만들기 위해서는 가로로 이어 붙인 두개의 프레임을 다시 세로로 붙여야 한다.
영상이 많아질 수록 더 복잡해지고, 중간 결과를 계속 저장해야 하므로... 그냥 Mat 클래스를 잘 이용해보기로 했다.
시도하려는 방식은 특별한게 아니고, 전체 영상을 저장할 Mat 객체를 하나 선언하고, 작은 영상들을 해당 영역에 집어넣는 방식이다.
Mat 클래스에서 부분 행렬을 추출하기 위해서는 괄호 연산자 재정의를 사용하면 된다. 연산자 재정의란 기존에 있는 연산자 (+, -, == 등)을 오버로딩하여 다른 의미를 부여하는 것이다. Mat 클래스에서 + 기호 하나만으로 각 원소들끼리 더해진 결과가 나오는 것 또한 +
연산자가 재정의 되어 있기에 가능한 일이다.
참고 - 다시 정의할 수 있는 연산자
Mat 클래스에서 괄호 연산자는 다양한 자료형에 대해 정의되어 있는데, 이 중 Rect 클래스를 이용한 재정의를 사용할 수 있다.
Mat matrix1(100, 200); // 100x200 크기의 행렬
Mat matrix2 = matrix1.(Rect(0, 0, 50, 50)); // (0, 0)을 좌측 모서리로 하는 50x50크기의 부분행렬
여기서 주목해야 할 점은 반환 값이다. 반환된 Mat 클래스는 주소 값으로, 원본(matrix1)과 데이터를 공유하므로 관심영역(ROI)를 설정할 때 유용하다.
matrix2 = ~matrix2; // 영상 색 반전
위와 같이 matrix2의 값을 변경하면 matrix1의 값도 같이 변경이 된다.
만약 공유하고 싶지 않으면, .clone()
등과 같은 방법으로 복사본을 저장해주어야 한다.
별건 아닌데 이 부분에서 잠깐 헤맸다. 아무 생각 없이 대입을 해버렸기 때문이다.
위의 색 반전시키는 예제를 봐서 그런지, 프레임을 채워 넣을 때도 똑같이 했다.
matrix2 = frame1;
위와 같이 하면, matrix2이 그냥 frame1을 가리키게 된다. 즉, 아까 연결해두었던 matrix1의 부분 행렬을 변경할 수 없게 되는 것이다. 따라서 Mat::copyTo()
를 사용한다.
frame1.copyTo(matrix2); // frame1을 matrix2로 복사
주의할 점은 원본 행렬과 결과 행렬의 크기와 데이터타입이 일치하지 않는 경우 주소를 재할당하게 된다. 즉, matrix2에는 frame1 값이 제대로 복사되었지만, 재할당이 일어났기 때문에 matrix1과의 연동은 끊기게 된다.
그저 이해를 돕기 위해 만든 아주 무식한 예시 코드이다.
int width, height; // 작은 영상의 너비, 높이 (다 동일하다고 가정)
Mat frame1, frame2, frame3, frame4;
Mat result;
Mat roi1 = result(Rect(0, 0, width, height));
Mat roi2 = result(Rect(width, 0, width, height));
Mat roi3 = result(Rect(0, height, width, height));
Mat roi4 = result(Rect(width, height, width, height));
frame1.copyTo(roi1); // frame1을 roi1로 복사
frame2.copyTo(roi2); // frame2을 roi2로 복사
frame3.copyTo(roi3); // frame3을 roi3로 복사
frame4.copyTo(roi4); // frame4을 roi4로 복사
imshow("result", result);
waitKey(0);