[opencv] Mat클래스의 기본연산

글쓴이 Engineer Myoa 날짜

앞으로 나올 수학적 개념과 연산… 들이 많이 걱정되긴 하지만

결국은 배워야 늘고 해야 느는것이기 때문에 마인드컨트롤하며 무너지지않고 잘 진행할 수 있도록 노력해야겠다.

 

이번에는 수학적 개념에 앞서 Mat클래스내에서 연산(API의 상위 레벨)하는것을 목표로 한다.

  • flip()
#include <opencv2\opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define WIN_NAME "winSrcImage"
#define FILE_LENA_JPG "lena.jpg"
int main(void) {

	IplImage* image = cvLoadImage("lena.jpg", CV_LOAD_IMAGE_GRAYSCALE); // Intel Processing Library
	Mat matrixA1 = cvarrToMat(image);		  // 데이터 공유
	Mat matrixA2 = cvarrToMat(image).clone(); // 데이터까지 복사

	Mat matrixB, matrixC;
	flip(matrixA1, matrixB, 0); // 상하
	imshow("matB", matrixB);
	imshow("origin1", matrixA1);

	flip(matrixA1, matrixC, 1); // 좌우
	imshow("matC", matrixC);
	imshow("origin2", matrixA1);

	// flip을 해도 원본데이터에는 당연히 영향이 가지않음 ->  clone:데이터복사

	waitKey(0);
	destroyAllWindows();

	return 0;
}

flip함수는 이름과 같이 직관적이다.

기억해야할 것은 마지막 flipCode인자에 0을 넣으면 상하(x축을 기준으로), 1을 넣으면 좌우(y축을 기준으로)에 대하여 대칭이 된다.

위에 주석에도 적었듯이 flip을 적용한다고 원본 매트릭스에 영향이 가지는 않는다.

flip을 한 후에 다시 원본데이터를 imshow했을 때 기존 원본가 같음을 확인할 수 있다.

 


 

  • repeat
  • roi
#include <opencv2\opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

#define WIN_NAME "winSrcImage"
#define FILE_LENA_JPG "lena.jpg"
int main(void) {

	Mat srcImage = imread("lena.jpg", IMREAD_GRAYSCALE);
	if (srcImage.empty()) {
		return -1;
	}

	Rect rectROI(0, 0, 256, 256);
	Mat extractROI = srcImage(rectROI);
	for (int i = 0; i < 64; i++) {
		for (int j = 0; j < 64; j++) {
			extractROI.at<uchar>(i, j) = 0;
		}
	}
	imshow("ROI", extractROI);
	imshow("src", srcImage);

	waitKey(0);

	Mat repeatedMat = repeat(extractROI, 2, 2);
	imshow("repeatedMat", repeatedMat);
	waitKey(0);

	return 0;
}

 

ROI는 관심영역일 뿐 데이터를 클로닝하여 저장하는 것이 아니다.

ROI를 지정한 Mat객체의 값을 인위적으로 손실시킨 다음 원본 Mat객체를 확인해보자.

위처럼 원본 srcImage 객체의 데이터에 반영된 것을 볼 수 있다.

 

repeat함수는 주어진 Mat객체의 데이터를 바탕으로 n * m형태의 바둑판식 패턴처럼 이어붙여준다.

물론 주어진 Mat객체의 데이터는 우리가 64*64만큼을 손실시켜놨으므로 위와같은 결과가 나오게 된다.

 


 

  • roi
  • 모자이크화

모자이크 함수를 쓰는것은 아니고, ROI를 이용해 모자이크 효과를 내는것이 목표이다.

 

 

 

 

TYPE값이 왜 0인가… 설마 CV_8UC1의 상수값이 0이던가~~했는데

역시나. 이왕이면 좀 알아두는게 좋을 것 같아 아래 링크를 참고하였다.

LIST OF MAT TYPE IN OPENCV

http://ninghang.blogspot.kr/2012/11/list-of-mat-type-in-opencv.html
A Mapping of Type to Numbers in OpenCV
C1 C2 C3 C4
CV_8U 0 8 16 24
CV_8S 1 9 17 25
CV_16U 2 10 18 26
CV_16S 3 11 19 27
CV_32S 4 12 20 28
CV_32F 5 13 21 29
CV_64F 6 14 22 30


Unsigned 8bits uchar 0~255
IplImage: IPL_DEPTH_8U
Mat: CV_8UC1, CV_8UC2, CV_8UC3, CV_8UC4

Signed 8bits char -128~127
IplImage: IPL_DEPTH_8S
Mat: CV_8SC1,CV_8SC2,CV_8SC3,CV_8SC4

Unsigned 16bits ushort 0~65535
IplImage: IPL_DEPTH_16U
Mat: CV_16UC1,CV_16UC2,CV_16UC3,CV_16UC4

Signed 16bits short -32768~32767
IplImage: IPL_DEPTH_16S
Mat: CV_16SC1,CV_16SC2,CV_16SC3,CV_16SC4

Signed 32bits int -2147483648~2147483647
IplImage: IPL_DEPTH_32S
Mat: CV_32SC1,CV_32SC2,CV_32SC3,CV_32SC4

Float 32bits float -1.18*10-38~3.40*10-38 
IplImage: IPL_DEPTH_32F
Mat: CV_32FC1,CV_32FC2,CV_32FC3,CV_32FC4

Double 64bits double 
Mat: CV_64FC1,CV_64FC2,CV_64FC3,CV_64FC4Unsigned 1bit bool
IplImage: IPL_DEPTH_1U

왜 64와 128에서 zeros 영역들이 남아있나 했더니.. 생각해보니 원본 입력데이터가 480*480인데
480%32, 480%16 … 모두 결과가 0이지만 480%64는 32이다. 따라서 가로세로 32만큼이 zeros로 남아있게 되는 것.
따라서 이를 해결하는 방법이 여러가지가 존재할 것..
나의 경우는 nWidth와 nHeight를 float값으로 변경하여 계산하였다. 물론 이렇게 되면 정말 바둑판처럼…

이렇게 될 것!

왜 이렇게 될까는 단순하다. float * int = int에서 소실되는 floating value들 때문에 ROI를 지정할 때 취약구간이 발생.

ex) Rect(0,0, 150.5, 150.5) / Rect(151, 151, 150.5, 150.5)

이 두개만봐도 결과적으로는

이렇게 될 것이다. 반복된다면 위의 사진처럼 바둑판식 무늬가 나타날테다.

따라서 나는 이 문제를 해결하기위해  width,height에 대해 roundup, 즉 ceiling하도록 했다.

	Mat destImage = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());
	int mosaicConst = 64 ;
	float fWidth = float(srcImage.cols) / mosaicConst;
	float fHeight = float(srcImage.rows) / mosaicConst;

	int x, y;
	Rect rectROI;
	Mat extractROI;

	for (int i = 0; i < mosaicConst; i++) {
		for (int j = 0; j < mosaicConst; j++) {
			y = int(i * fHeight);
			x = int(j * fWidth);
			//rectROI = Rect(x, y, fWidth, fHeight);
			rectROI = Rect(x, y, ceil(fWidth), ceil(fHeight)) ;
			extractROI = srcImage(rectROI);
			destImage(rectROI) = mean(extractROI); // mean은 평균

		}
	}

	imshow("result", destImage);
	waitKey(0);

int nWidth -> float fWidth

rectrectROI = Rect(x, y, ceil(fWidth), ceil(fHeight)) ;

 

128의 모자이크 상수를 주더라도 위처럼 깔끔하게 처리가 된다.

흐뭇..

 

 


  • merge
  • split
#pragma warning(disable: 4819)
#include "opencv.hpp"
#include <iomanip>

using namespace cv;
using namespace std;
int main()
{
	Mat srcImage = imread("lena.jpg");

	if (srcImage.empty()) {
		return -1;
	}

	vector<Mat> planes;
	split(srcImage, planes);

	// BGR 순서
	imshow("plane 0", planes[0]);
	imshow("plane 1", planes[1]);
	imshow("plane 2", planes[2]);

	waitKey(0);

	Mat destImage;
	merge(planes, destImage);
	imshow("destImage", destImage);
	waitKey(0);

	return 0;

}

먼저 lena.jpg를 BGR로 읽어들인다.

 

혹여나 다른 용도로 RGB로 사용하기 원한다면 아래 코드를 이용하면 된다…

cvtColor(srcImage, srcImage, CV_BGR2RGB);

만 뒤에 사용할 merge에서도 BGR컬러셋을 사용하기 때문에 예제를 따라하고 싶다면 변환하지 않는것이 좋다.

 

위에서 불러들였던 lena.jpg를 split으로 채널별로 쪼개고, imread로 확인하면

각각 Blue, Green, Red채널의 단편적 화상을 나타냄을 알 수 있다.

 

merge는 InputArrayOfArrays를 인자로 받기때문에 Matrix Array의 vector Array를 전달. 즉, 채널별 mat data를 보관하고있는 planes을 넘겨주면 된다.

결과적으로는 각 채널이 합쳐서 BGR컬러의 이미지가 만들어지게 된다.

 


 

  • channel

channel에 관련된 함수는 3개가 소개되어있다.

mixChannels, insertImageCOI, extractImageCOI

  1. mixChannels : 채널을 분리 및 합성할 수 있게 해준다.
#pragma warning(disable: 4819)
#include "opencv.hpp"
#include <iomanip>

using namespace cv;
using namespace std;
int main()
{
	Mat srcImage = imread("lena.jpg");

	if (srcImage.empty()) {
		return -1;
	}

	Mat imB(srcImage.rows, srcImage.cols, CV_8UC1);
	Mat imG(srcImage.rows, srcImage.cols, CV_8UC1);
	Mat imR(srcImage.rows, srcImage.cols, CV_8UC1);

	Mat outImage[] = { imB, imG, imR };

	int fromTo[] = { 0,0,1,1,2,2 }; // src의 0번채널은 out의 0번채널에 ...
	// src, src내 갯수, out, out의 갯수, fromTo는 복사할 채널 대응 배열, 대응 채널이 몇 쌍인지
	mixChannels(&srcImage, 1, outImage, 3, fromTo, 3); // 1개의 이미지를 3개의 이미지(채널)로 쪼갬

	Mat destImage(srcImage.rows, srcImage.cols, srcImage.type());
	mixChannels(outImage, 3, &destImage, 1, fromTo, 3);
	// 3개의 이미지를 1개의 이미지로 합침
	// 대응되는 채널 순서쌍은 3개.

	imshow("destImage", destImage);
	waitKey(0);

	return 0;

}

 

2. insertImageCOI :

3. extractImageCOI :

카테고리: 개발노트

5개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다