유용한 OpenCV 함수

행렬의 합 구하기

Scalar sum(InputArray src);
  • src : 입력 행렬. 1~4 채널 행렬만 지원
  • 반환값 : 행렬 원소들의 합
uchar data[] = {1, 2, 3, 4, 5, 6};
Mat mat1(2, 3, CV_8UC1, data); // 

int sum1 = (int)sum(mat1)[0]; //  sum이 double 형태로 반환하니 int형태로 변환해야 함

행렬의 평균 구하기

Scalar mean(InputArray src, InputArray mask = noArray());
  • src : 입력 행렬 1~4 채널
  • mask : 마스크 영상 / mask 행렬 값이 0이 아닌 부분에서만 src 행렬의 평균값을 계산함
  • 반환값 : 행렬의 평균 값
Mat img = imread("lenna.bmp", IMREAD_GRAYSCALE); // 그레이스케일이라 1채널 
double mean1 = mean(img)[0]; // val[0] = 124.0, val[1] = 0, va1[2] = 0, val[3] = 0 
Mat img = imread("lenna.bmp", IMREAD_COLOR); // 그레이스케일이라 1채널 
Scalar m = mean(img); // m 변수에 b g r 성분의 평균값이 다 저장되고 m[0]에는 blue 평균값, m[1]에는 green 평균값, m[2]에는 red 평균값, m[3]에는 기본값인 0이 저장됨

행렬의 최댓값/최솟값 구하기

void minMaxLoc(InputArray src, double* minVal, double* maxVal = 0, Point* minLoc = 0, Point* maxLoc =0, InputArray mask = noArray());
  • src : 입력 영상. 단일 채널
  • minVal, maxVal : 최솟값/최댓값 변수 포인터 (필요 없으면 NULL 지정)
  • minLoc, maxLoc : 최솟값/최댓값 위치 변수 포인터 (필요 없으면 NULL 지정)
  • mask : 마스크 영상. mask 행렬 값이 0이 아닌 부분에서만 연산을 수행
Mat img = imread("lenna.bmp", IMREAD_GRAYSCALE);

double minv, maxv;
Point minLoc, maxLoc;
minMaxLoc(img, &minv, &maxv, &minLoc, &maxLoc); // minv : 25, maxv : 245, minLoc : [508, 71], maxLoc : [116, 273]

영상의 속성 변환

행렬의 자료형 변환
void Mat::convertTo(OutputArray m, int rtype, double alpha=1, double beta=0) const;
  • m : 출력 영상(행렬)
  • rtype : 원하는 출력 행렬 타입
  • alpha : 추가적으로 곱할 값
  • beta : 추가적으로 더할 값

image

Mat img = imread("lenna.bmp", IMREAD_GRAYSCALE); // CV_8UC1

Mat fimg;
img.converTo(fimg, CV_32FC1); // Float 형식의 실수 값을 저장할 수 있는 형태로 저장
// 주어진 영상의 픽셀 타입을 Unsigned char을 그대로 사용하는 것이 아닌 실수 값 형태로 변환을 해서 어떤 연산을 수행을 했을 때 소수점 아래가 자동으로 잘려나가지 않게 하여 더 정밀한 연산을 하기 위해 사용함\

Mat dst;
fimg.converTo(dst, CV_8UC1);
// 연산을 다했으면 ~~ 다시 그레이스케일로 지정
행렬의 정규화(원소 값 범위 정규화)
void normalizse(InputArray src, InputOutputArray dst, double alpha = 1, double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray());
  • src : 입력 영상(행렬)
  • dst : 출력 행렬로 src와 같은 크기
  • alpha : 목표 노름(norm) 값, (NORM_MINMAX인 경우) 최솟값
  • beta : (NORM_MINMAX인 경우 최댓값
  • norm_type : 정규화 타입으로 NORM_INF, NORM_L1, NORM_L2, NORM_MINMAX 중 하나를 지정
    • NORM_MINMAX를 지정할 경우, 출력 행렬 dst의 최솟값은 alpha, 최댓값은 beta가 되도록 설정함
  • dtype : 출력 행렬의 타입
  • mask : 마스크 영상
Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

Mat dst;
normalize(src, dst, 0, 255, NORM_MINMAX); // src인 레나 그레이스케일의 최솟값은 25 최댓값은 245 인데 25 값이 0 에 가까워지는 형태가 되고 245 값이 255에 가까워지는 형태로 전체 픽셀 값을 변화 시켜 dst 값을 만듬
imageimage
색 공간 변환 함수
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0);
  • src : 입력 영상
  • dst : 출력 영상
  • code : 색 변환 코드
    • COLOR_BGR2GRAY / COLOR_GRAY2BGR : BGR < - > GRAY
    • COLOR_BGR2HSV / COLOR_HSV2BGR : BGR < - > HSV
    • COLOR_BGR2YCrCb / COLOR_YCrCb2BGR : BGR < - > YCrCb
  • dstCn : 결과 영상의 채널 수로 0이면 자동 결정됨
Mat src = imread("lenna.bmp");
Mat dst;

cvtColor(src, dst, COLOR_BGR2GRAY);

채널 분리와 병합

채널 분리
void split(const Mat& src, Mat* mvbegin);
void split(InputArray src, OutputArrayOfArrays mv);
  • src : (입력) 다채널 행렬
  • mvbegin : (출력) Mat 배열의 주소
  • mv : (출력) 행렬의 벡터 vetor
Mat src = imread("lenna.bmp"); // B, G, R 순서로 3개의 색상이 있는 3개의 채널
vector<Mat> planes; // 비어있는 행렬의 벡터 만듬

split(src, planes); // planes[0]에 B 성분, planes[1]에 G 성분, planes[2]에 R 성분이 들어감

image

채널 결합
void merge(const Mat* mv, size_t count, OutputArray dst);
void merge(InputArrayofArrays mv, OutputArray dst);
  • mv : (입력) 1채널 Mat 배열 또는 행렬의 벡터
  • count : (mv가 Mat 타입의 배열인 경우) Mat 배열의 크기
  • dst : (출력) 다채널 행렬
Mat src = imread("lenna.bmp"); // B, G, R 순서로 3개의 색상이 있는 3개의 채널
vector<Mat> planes; // 비어있는 행렬의 벡터 만듬

split(src, planes); // planes[0]에 B 성분, planes[1]에 G 성분, planes[2]에 R 성분이 들어감

swap(planes[0], planes[2]); // B 성분과 R 성분을 바꿈

Mat dst;
merge(planes, dst); // 3개의 planes를 하나로 합쳐서 dst로 저장

image

유용한 OpenCV 기능

연산 시간 측정 방법

  • 대부분의 영상 처리 시스템은 대용량 영상 데이터를 다루고 복잡한 알고리즘 연산을 수행함
  • 영상 처리 시스템 각 단계에서 소요되는 연산 시간을 측정하고 시간이 오래 걸리는 부분을 찾아 개선하는 시스템 최적화 작업이 필수적임

  • 기본적인 연산 시간 측정방법
int64 t1 = getTickCount();

my_func();

int64 t2 = getTickCount();
double ms = (t2 - t1) * 1000 / getTickFrequnecy();
  • TickMeter 클래스
    • 연산 시간 측정을 위한 직관적인 인터페이스를 제공함
    • 클래스 내부에서 getTickCount()와 getTickFrequency() 함수를 조합해서 시간을 측정함
class TickMeter
{
public : 
	TickMeter(); // 기본 생성자
	
	void start(); // 시간 측정을 시작할때 사용
	void stop(); // 시간 측정을 멈출때 사용
	void reset(); // 시간 측정을 초기화할 때 사용
	
	double getTimeMicro() const; // 연산시간을 마이크로 초 단위로 반환
	double getTimeMilli() const; // 연산시간을 밀리 초 단위로 반환
	double getTimeSec() const;   // 연산시간을 초 단위로 반환
};
TickMeter tm;

tm.start();

func1();

tm stop();
cout << "func1(): " << tm.getTimeMilli() << "ms." << endl;

tm.reset(); // reset을 안하면 func1 과 func2의 연산 시간이 합쳐서 아래에서 반환됨

tm.start();

func2();

tm.stop();
cout << "func1(): " << tm.getTimeMilli() << "ms." << endl;

관심 영역과 마스크 연산

  • 관심영역 : Region of Interest
    • 영상에서 특정 연산을 수행하고자 하는 임의의 부분 영역
  • 마스크 연산

    • OpenCV는 일부 함수에 대해 ROI 연산을 지원하며, 이때 마스크 영상(mask image)을 인자로 함께 전달해야 함
      • 마스크 영상은 CV_8UC1 타입인 그레이스케일 영상
      • 마스크 영상의 픽셀 값이 0이 아닌 위치에서만 연산이 수행 됨
      • 보통 마스크 영상으로는 0 또는 255로 구성된 이진 영상(binary image)를 사용함

    e.g. copyTo(), calcHist(), bitwise_or(), matchTemplate(), mean(), minMaxLoc(),

void MaskOp1()
{
	Mat src = imread("airplane.bmp", IMREAD_COLOR);
	Mat mask = imread("mask_plane.bmp", IMREAD_GRAYSCALE);
	Mat dst = imread("field.bmp", IMREAD_COLOR);

	if (src.empty() || mask.empty() || dst.empty()) { // 예외 처리 코드
		cerr << "Image load failed!" << endl;
		return;
	}

	//copyTo(src, dst, mask); 
	src.copyTo(dst, mask); // mask 영상이 흰색인 부분만 복사해서 출력 윗 줄의 코드와 동일한 내용의 코드

	imshow("src", src);
	imshow("dst", dst);
	imshow("mask", mask);
	waitKey();
	destroyAllWindows();
}

image

투명한 PNG 파일 합성

void MaskOp2()
{
	Mat src = imread("cat.bmp", IMREAD_COLOR);
	Mat logo = imread("opencv-logo-white.png", IMREAD_UNCHANGED); // IMREAD_UNCHANGED 알파 채널을 가진 4개의 채널로 형성이 됨

	if (src.empty() || logo.empty()) {
		cerr << "Image load failed!" << endl;
		return;
	}

	vector<Mat> planes;
	split(logo, planes); // Mat 클래스 객체 4개가 들어감

	Mat mask = planes[3]; // 4번째의 planes(알파 채널) 를 mask 영상으로 사용
	merge(vector<Mat>(planes.begin(), planes.begin() + 3), logo);
	
	Mat crop = src(Rect(10, 10, logo.cols, logo.rows)); // src에서 좌측상단 (10,10)부터 logo의 가로, 세로 크기만큼의 부분영상을 추출하여 crop으로 만듬

	logo.copyTo(crop, mask); //logo에서 mask의 흰색으로 된 부분만 crop으로 복사가 됨

	imshow("src", src); // crop 영상에 logo가 합성이되면서 기존 src도 logo가 합성이 됨
	waitKey();
	destroyAllWindows();
}

image