ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • OpenCV 유용한 함수
    OpenCV/OpenCV_C++ 2023. 12. 21. 21:29

    1. 행렬 합, 평균, 최댓값, 최솟값

    - 합(sum)

    Scalar sum(InputArray src);
    • src: 입력 행렬, 1 ~ 4 channel
    • return: 행렬 원소들의 합

    예제

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	uchar data[]{ 1, 2, 3, 4, 5, 6 };
    	cv::Mat mat{ 2, 3, CV_8UC1, data };
    	int sum = static_cast<int>(cv::sum(mat)[0]);
    	std::cout << sum << std::endl;
    	return 0;
    }

    결과

     

    - 평균(mean)

    Scalar mean(InputArray src, InputArray mask = noArray());
    • src: 입력 행렬, 1 ~ 4 channel
    • mask: 마스크 영상
    • return: 행렬의 평균 값

    예제

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	uchar data[]{ 1, 2, 3, 4, 5, 6 };
    	cv::Mat mat{ 2, 3, CV_8UC1, data };
    	double mean = static_cast<double>(cv::mean(mat)[0]);
    	std::cout << mean << std::endl;
    	return 0;
    }

    결과

    color 이미지의 경우 BGR의 평균값과 0이 저장된다.

    예제

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	uchar data[]{ 1, 2, 3, 4, 5, 6 };
    	cv::Mat mat{ 2, 3, CV_8UC1, data };
    	cv::Mat img = cv::imread("lenna.bmp");
    	double mean0 = static_cast<double>(cv::mean(img)[0]);
    	double mean1 = static_cast<double>(cv::mean(img)[1]);
    	double mean2 = static_cast<double>(cv::mean(img)[2]);
    	double mean3 = static_cast<double>(cv::mean(img)[3]);
    
    	std::cout << mean0 << std::endl;
    	std::cout << mean1 << std::endl;
    	std::cout << mean2 << std::endl;
    	std::cout << mean3 << std::endl;
    	return 0;
    }

    결과

    - 최댓값 / 최솟값(minMaxLoc)

    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이 아닌 부분에서만 연산을 수행.

    예제

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	cv::Mat img = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
    	double minv, maxv;
    	cv::Point minLoc, maxLoc;
    	minMaxLoc(img, &minv, &maxv, &minLoc, &maxLoc);
    
    	std::cout << minv << std::endl;
    	std::cout << maxv << std::endl;
    	std::cout << minLoc << std::endl;
    	std::cout << maxLoc << std::endl;
    	return 0;
    }

    결과

     

    - 행렬의 자료형 변환

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

     

    예시

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	cv::Mat img = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE); // CV_8UC1 img 생성
    	cv::Mat dst;
    	img.convertTo(dst, CV_32FC1);// fimg에 CV_32FC1 형태의 이미지 저장
    
    	return 0;
    }

     

    - 행렬의 정규화 (원소값 범위 정규화)

    void normalize(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: 마스크 영상.

    현 이미지에서 가진 최소, 최대 값을 각각 alpha, beta의 최소, 최대의 값의 이미지로 변경해줌.

    이 과정은 이미지의 선명도를 높여줄 수 있다.

     

    예제

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	cv::Mat img = cv::imread("lenna.bmp", cv::IMREAD_GRAYSCALE);
    	cv::Mat norm_img;
    	cv::normalize(img, norm_img, 0, 255, cv::NORM_MINMAX);
    	
    	cv::imshow("img", img);
    	cv::imshow("normalize", norm_img);
    	cv::waitKey(0);
    
    	return 0;
    }

    결과

    L: 기존 이미지, R: 정규화된 이미지

    정규화된 이미지가 조금 더 선명해진 것을 볼 수 있다.

     

    - 색 공간 변환 함수

    void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0);
    • src: 입력 영상.
    • dst: 출력 영상.
    • code: 색 변환 코드 (다양한 방법이 있으므로 OpenCV문서를 참고하자.)
    COLOR_BGR2GRAY / COLOR_GRAY2BGR BGR ↔ GRAY
    COLOR_BGR2HSV / COLOR_HSV2BGR BGR ↔ HSV
    COLOR_BGR2YCrCb / COLOR_YCrCb2BGR BGR ↔ YCrCb

     

    • dstCn: 결과 영상의 채널 수. 0이면 자동 결정됨.

    예제

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	cv::Mat img = cv::imread("lenna.bmp"); // BGR img 생성
    	cv::Mat dst;
    	cv::cvtColor(img, dst, cv::COLOR_BGR2GRAY);
    	
    	cv::imshow("img", img);
    	cv::imshow("dst", dst);
    	cv::waitKey(0);
    
    	return 0;
    }

    결과

     

    - 채널 분리

    void split(const Ma& src, Mat* mvbegin);
    void split(InputArray src, OutputArrayOfArrays mv);
    • src: 입력 영상, 다채널 행렬
    • mvbegin: (출력) Mat 배열의 주소
    • mv: (출력) 행렬의 벡터. std::vector<cv::Mat>

    예제

    #include <iostream>
    #include <vector>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	cv::Mat img = cv::imread("lenna.bmp"); // BGR img 생성
    	std::vector<cv::Mat> planes;
    	cv::split(img, planes);
    	
    	cv::imshow("B", planes[0]);
    	cv::imshow("G", planes[1]);
    	cv::imshow("R", planes[2]);
    	
    	cv::waitKey(0);
    
    	return 0;
    }

    결과

    왼쪽부터 B, G, R

     

    - 채널 결합

    void merge(const Mat* mv, size_t count, OutputArray dst);
    void merge(InputArrayOfArrays mv, OutputArray dst);
    • mv: (입력) 1채널 Mat 배열 또는 행렬의 벡터.
    • count: (mv가 Mat타입의 배열인 경우) Mat 배열의 크기.
    • dst: 출력 영상. 다채널 행렬.

    예제

    #include <iostream>
    #include <vector>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	cv::Mat img = cv::imread("lenna.bmp"); // BGR img 생성
    	cv::Mat dst1;
    	cv::Mat dst2;
    	std::vector<cv::Mat> planes;
    	
    	cv::split(img, planes);
    	std::swap(planes[0], planes[2]); // B와 R의 위치 변경 -> RGB
    	cv::merge(planes, dst1);
    
    	std::swap(planes[0], planes[1]); // R과 G의 위치 변경 -> GRB
    	cv::merge(planes, dst2);
    	
    	cv::imshow("img", img);
    	cv::imshow("dst1", dst1);
    	cv::imshow("dst2", dst2);
    	
    	cv::waitKey(0);
    
    	return 0;
    }

    결과

     

    - 연산 시간 측정

    • 대부분 영상 처리 시스템은 대용량 영상 데이터를 다루고 복잡한 연산을 수행.
    • 영상 처리 시스템 각 단계에서 소요되는 연산 시간을 측정하고 시간이 오래 걸리는 부분을 찾아 개선하는 시스템 최적화 작업이 필수!!
    • OpenCV에서는 연산 시간 측정을 위한 함수 지원.
    • 연산 시간 측정은 Release모드에서 수행해야 한다!! (g++의 경우 -O2 옵션을 사용한다.)

    - TickMeter class

    • 연산 시간 측정을 위한 직관적인 인터페이스 제공.
    • 클래스 내부에서 getTickCount(), getTickFrequency() 함수를 조합해서 시간을 측정.
    getTimeMicro() 연산 시간을 마이크로 초 단위로 변환
    getTimeMilli() 연산 시간을 밀리초 단위로 변환
    getTimeSec() 연산 시간을 초 단위로 변환

    예제

    #include <iostream>
    #include <vector>
    #include "opencv2/opencv.hpp"
    
    int main()
    {
    	cv::Mat img = cv::imread("lenna.bmp"); // BGR img 생성
    	cv::Mat dst1;
    	cv::Mat dst2;
    	std::vector<cv::Mat> planes;
    
    	cv::TickMeter tm;
    	
    	tm.start(); // 측정1
    	
    	cv::split(img, planes);
    	std::swap(planes[0], planes[2]); // B와 R의 위치 변경 -> RGB
    	cv::merge(planes, dst1);
    
    	tm.stop(); // 측정1 중지
    	std::cout << "split & merge: " << tm.getTimeMilli() << "ms." << std::endl;
    
    	tm.reset(); // 시간 리셋
    
    	tm.start(); // 측정2
    
    	std::swap(planes[0], planes[1]); // R과 G의 위치 변경 -> GRB
    	cv::merge(planes, dst2);
    	
    	cv::imshow("img", img);
    	cv::imshow("dst1", dst1);
    	cv::imshow("dst2", dst2);
    	tm.stop(); // 측정2 중지
    	std::cout << "show img: " << tm.getTimeMilli() << "ms." << std::endl;
    
    	cv::waitKey(0);
    
    	return 0;
    }

    결과

     

    - 마스크 연산과 관심 영역 ROI(Region Of Interest)

    ROI(Region of Interest)는 영상에서 특정 연산을 수행하고자 하는 임의의 부분 영역을 이야기한다.

    OpenCV는 일부 함수에 대해 ROI연산을 지원한다. 이때 마스크 영상(mask image)을 인자로 함께 전달해야 한다.

    e.g.) copyTo(), calcHist(), bitwise_or(), matchTemplate(), etc.

    • 마스크 영상 타입: CV_8UC1 (Gray scale)
    • 마스크 영상의 픽셀 값이 0이 아닌 위치에서만 연산이 수행된다. (보통 마스크 영상은 0 or 255로 구성된 이진 영상(binary image)을 사용한다.)

     

    - 마스크 연산을 지원하는 픽셀 값 복사 함수

    void Mat::copyTo(InputArray m, InputArray mask) const;
    • m: 출력 영상. 만약 *this와 크기 및 타입이 같은 m을 입력으로 지정하면, m을 새로 생성하지 않고 연산을 수행한다. 그렇지 않으면 m을 새로 생성하여 연산을 수행후 반환한다.
    • mask: 마스크 영상. CV_8U. 0이 아닌 픽셀에 대해서만 복사 연산 수행.

     

    - 마스크 연산을 지원하는 픽셀 값 복사 함수 (전역 함수)

    void Mat::copyTo(InputArray src, OutputArray dat, InputArray mask);
    • src: 입력 영상.
    • mask: 마스크 영상. CV_8U. 0이 아닌 픽셀에 대해서만 복사 연산 수행.
    • dst: 출력 영상.

    예제1

    목적: 비행기 이미지에서 Mask이미지를 추출하고 Field이미지에 합쳐서 Result 이미지를 얻어보자.

    코드

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    void MaskOp1();
    
    int main()
    {
    	MaskOp1();
    }
    
    void MaskOp1()
    {
    	cv::Mat src = cv::imread("airplane.bmp", cv::IMREAD_COLOR);
    	cv::Mat mask = cv::imread("mask_plane.bmp", cv::IMREAD_GRAYSCALE);
    	cv::Mat dst = cv::imread("field.bmp", cv::IMREAD_COLOR);
    
    	if (src.empty() || mask.empty() || dst.empty()) {
    		std::cerr << "Image load failed!" << std::endl;
    		return;
    	}
    
    	copyTo(src, dst, mask); // 두개는 같은 역할을 한다.
    	//src.copyTo(dst, mask);
    
    	cv::imshow("src", src);
    	cv::imshow("dst", dst);
    	cv::imshow("mask", mask);
    	cv::waitKey();
    	cv::destroyAllWindows();
    }

    결과

     

    예제2

    목적: Alpha channel이 있는 PNG 파일을 다른 영상에 합성하기.

    고양이 이미지에 OpenCV 로고를 붙여보자.

    코드

    #include <iostream>
    #include "opencv2/opencv.hpp"
    
    void MaskOp2();
    
    int main()
    {
    	MaskOp2();
    }
    
    void MaskOp2()
    {
    	cv::Mat src = cv::imread("cat.bmp", cv::IMREAD_COLOR);
    	cv::Mat logo = cv::imread("opencv-logo-white.png", cv::IMREAD_UNCHANGED);
    
    	if (src.empty() || logo.empty()) {
    		std::cerr << "Image load failed!" << std::endl;
    		return;
    	}
    
    	std::vector<cv::Mat> planes;
    	cv::split(logo, planes);
    
    	cv::Mat mask = planes[3]; // split한 Logo의 Alpha channel 이미지를 mask 변수에 저장.
    	cv::merge(std::vector<cv::Mat>(planes.begin(), planes.begin() + 3), logo); // logo변수에 BGR이미지를 저장.
    	cv::Mat crop = src(cv::Rect(10, 10, logo.cols, logo.rows)); // logo의 크기만큼 src에서 잘라내어 crop변수에 저장.
    
    	logo.copyTo(crop, mask); // logo를 crop에 mask영역부분 만큼만 복사.
    
    	imshow("src", src);
    	imshow("mask", mask);
    	imshow("logo", logo);
    	imshow("crop", crop);
    
    	cv::waitKey();
    	cv::destroyAllWindows();
    }

    결과

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

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