Computer Science/OpenCV

[OpenCV]기본 라이브러리와 이미지 출력

Master_Worm 2021. 3. 12. 14:07

1. 라이브러리들

OpenCV에는 다양한 라이브러리와 모듈이 존재한다. 어떤 모듈이 존재하는지는 OpenCV를 설치한 곳에 가면 볼 수 있으며, 공식 웹사이트에서도 레퍼런스를 얻을 수 있다. 어떤 모듈이 존재하는지만 머릿속에 넣어두면 이를 시기적절하게 써주면 되는 것이다. 마치 공구 상자함에 무엇이 들어있는지 알고 있다면 어떤 일을 하던지 수월하게 할 수 있을 것이다. 하지만 어떤 공구가 들어있는지 인지가 되지 않는다면 힘들게 일할 것이다. 

 

D:\opencv\sources\modules 

 

필자는 D:\에다가 opencv를 설치했으므로 이렇게 나오는 것이지만 설치한 파일에 따라서 다 다르다. 이곳에 기본적인 모듈을 확인할 수 있다.

 

 

이렇게 폴더의 형태로 존재함을 볼 수 있다. 각각의 기능을 하나하나 실행시키는 것도 좋은 일이지만, 어떻게 사용하는지 알아볼 수 있는 설명서가 있는데, 그것이 레퍼런스이다.

docs.opencv.org/4.5.1/

 

OpenCV: OpenCV modules

OpenCV  4.5.1 Open Source Computer Vision

docs.opencv.org

이곳에 가면 여러가지 모듈을 확인하고 설명서를 볼 수 있다. 자주자주 방문하는 습관을 들이자.

 

2. 헤더


opencv2/opencv.hpp에는 OpenCV의 모든 기능을 사용할 수 있도록 모든 헤더파일이 포함되어 있다. 그래서 하나만 넣어도 충분히 컴파일이 가능하다. 하지만 닭 잡는데 소 잡는 칼을 쓸 수는 없는 법이다. 물론 그래도 되지만 너무 힘들다. 헤더파일도 마찬가지이다. 너무 큰 헤더 파일은 컴파일 시간을 늘린다. 그러므로 가볍게 만들어주는 것도 중요하다. 

#include<opencv2/opencv.hpp>	// OpenCV의 기능을 사용할 수 있게 해주는 헤더파일

int main(int argc, char** argv) {
	cv::Mat img = cv::imread("cat.png");

	imshow("img", img);
	cv::waitKey(0);

	return 0;
}

이전 블로그 글에서 사용한 코드를 헤더파일만 바꾸었다. 

[Computer Science/OpenCV] - [OpenCV]OpenCV란 무엇인가? 그리고 설치

 

실제로 두개를 모두 컴파일 해보면 차이남을 알 수 있다. cv::를 사용하고 싶지 않다면 using namespace cv;를 활용하면 cv::를 붙이지 않아도 된다. 이는 C++에서 클래스 이름을 붙여주는 것이므로 컴파일러에게 사전에 알려주는 것이다. 

#include<opencv2/opencv.hpp>	// OpenCV의 기능을 사용할 수 있게 해주는 헤더파일

using namespace cv;				// namespace cv를 알려줌으로써 미리 선언해주는 역할을 한다.

int main(int argc, char** argv) {
	Mat img = imread("cat.png");

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

	return 0;
}

여기서 헤더 파일이 너무 많은 헤더 파일을 가지고 있으므로 필요한 기능을 담고 있는 헤더파일만 포함시키면 다음과 같다.

#include"opencv2/highgui/highgui.hpp"			// 헤더 파일을 가볍게 만들기 위해 교체!

using namespace cv;

int main(int argc, char** argv) {
	Mat img = imread("cat.png");

	if (img.empty()) return -1;

	namedWindow("img", cv::WINDOW_AUTOSIZE);
	imshow("img", img);
	waitKey(0);

	destroyWindow("img");

	return 0;
}

OpenCV에는 다양한 유형의 이미지 파일을 읽을 수 있는 유틸들을 제공한다. 기본 패키지 상에 포함되어 있는 HighGUI는 이를 도와준다. 위의 코드는 HighGUI를 활용해서 이미지 파일(여기서는 cat.png)를 열고 화면 상에 출력해주는 역할을 하고 있다.

 

여기서 int argc, char** argv는 argument라하는 인수들이 저장되는 공간이 된다. argc는 argument가 몇 개인지 알려주는 것이며, argv는 char형으로 저장되는 변수인데 배열로써 저장된다. 가장 처음에 있는 argv[0]는 실행 파일명이 저장되며, 그 뒤부터 argv[1], argv[2] ....  의 순서대로 사용자가 입력한 argument가 저장된다.

 

3. 코드 해설


다음 코드를 하나씩 해설해보면서 각각의 코드가 어떤 의미를 가지고 있는지 살펴보도록 하자.

 

#include"opencv2/highgui/highgui.hpp"	

using namespace cv;

int main(int argc, char** argv) {
	Mat img = imread("cat.png");

	if (img.empty()) return -1;

	namedWindow("img", cv::WINDOW_AUTOSIZE);
	imshow("img", img);
	waitKey(0);

	destroyWindow("img");

	return 0;
}

헤더 파일은 제외하고 main 함수 내부에 있는 것들을 살펴보도록 하자.

  1. Mat img = imread ("cat.png");

  2. if(img.empty()) return -1;

  3. namedWindow("img", cv::WINDOW_AUTOSIZE);

  4. imgshow("img", img);

  5. waitKey(0);

  6. destroyWindow("img");

이런 순서대로 살펴보도록 하자. 

1. Mat img = imread ("cat.png")

먼저 나오는 imread 이다.(namespace cv가 없으면 cv::imread) image read 라고 생각하면 되는데, 파일 이름을 기반으로 파일의 포맷을 결정해준다. 예를 들어, cat.png이므로 파일 포맷 PNG에 해당하는 데이터 구조에 필요한 메모리를 자동으로 할당해주는 것이다. 이미지 파일과 관련된 다양한 포맷을 지원한다. (BMP, JPEG, JPG, PNG, DIB, PPM, TIFF 등등 다양한 이미지 포맷을 지원한다.) imread는 최종적으로 구조체 Mat의 형태로 반환한다. 

 

Mat은 구조체로써 OpenCV의 기본을 이루는 구조체라 봐도 무방할 정도로 많이 쓰이는 구조체이다. 이미지 상에 담고 있는 정보를 처리한다. 이 정보는 단일 채널, 다중 채널, 정수 값, 부동 소수 값 등으로 컴퓨터가 이미지를 받아들이는데 사용되는 다양한 데이터를 의미한다. Mat 구조체의 기본 상속은 다음과 같다.

 

조금 더 자세한 내용은 docs.opencv.org/4.5.1/d3/d63/classcv_1_1Mat.html 이곳에서 보자.

 

2. if(img.empty()) reuturn -1;

Mat구조체의 이름을 img라 정했으며, 1번에서 imread를 통해 읽어들인 cat.png 파일을 Mat이라는 구조체 변수, 이름은 img에 넣었다. 이를 통해 img.empty()는 받은 파일이 비어있는지 아닌지를 판단해주는 것이다. 반환값은 true, false로 전달해 준다. (비어있으면 true로 반환해줌, 속에 무언가 들어있으먄 false를 반환) 

 

3. namedWindow("img", cv::WINDOW_AUTOSIZE);

namedWindow 함수는 High-level GUI, HighGUI 모듈에 속하는 함수이다. 화면에 이미지를 표시할 수 있는 윈도우를 열어준다. 인수가 두 가지가 있는데, 첫번째 인수는 윈도우 이름을 지정한다. 만약에 이름이 있으면(중복되면) 아무일도 하지 않는다. 두 번째 인수는 윈도우 사이즈를 결정해주는데,

  • 윈도우 사이즈는 자동으로 이미지 크기와 같을 것인지 - WINDOW_AUTOSIZE (이미지 사이즈에 맞춰 자동 조정되며 이후에 사용자가 조정 가능하다. 하지만 윈도우 창 크기를 마우스로 늘러거나 줄이는 것은 불가능하다. 함수에 의해서 조절해야 한다.)
  • 이미지 사이즈 관계없이 동일한 값으로 만들 것인지 - 0 (기본값이다.), 처음에 만들어진 값은 고정 값으로 만들어지지만, 마우스를 통해 늘리거나 줄이는 것이 가능하다.

두 번째 인수에 들어갈 flag값은 여기에서 확인이 가능하다. 

HighGUI는 4가지의 주요한 함수가 존재하는데, ButtonCallback, MouseCallBack, OpenGIDrawCallback, TrackbarCallback으로 차례대로 버튼, 마우스, 화면 출력, 트랙바에 해당하는 값을 담당한다. [opencv2/highgui.hpp 헤더에 들어있다.] 기본으로 제공된 함수 외 더 많은 기능을 사용하고자 한다면 Qt 를 활용해서 확장해나가면 된다. Qt는 C++을 통해 GUI를 제작하는 크로스플랫폼 프레임워크이다. (C++외에 다른 언어로도 만들 수 있다.) 자동차 계기판 GUI부터 시작해서 스마트 Tv에 들어가는 GUI까지 다양하게 만들어져있다. 

Qt를 더 알고 싶다면 -> www.qt.io/ko-kr/

 

Qt | 임베디드와 데스크탑을 위한 크로스플랫폼 소프트웨어 개발

Qt는 혁신적인 기기와 세련된 UI 및 어플리케이션을 만드는 빠르고, 스마트한 방법입니다. 우수한 크로스플랫폼 소프트웨어 개발을 만나보세요.

www.qt.io

 

4. imgshow("img", img);

Mat구조체 상에 이미지에 대한 정보가 들어있다면 imgshow를 통해 화면에 출력할 수 있다. (조금 더 정확히는 위에서 만든 윈도우에 출력할 수 있다. 만약에 만들어진 윈도우가 없다면 cv::imshow함수는 cv::namedWindow()를 활용해서 새롭게 생성한다. ) 실제로 보여주는 역할을 하게 해주는 함수이다.

 

5. waitKey(0);

waitKey(0); 의 기본 원형 int cv::waitKey ( int delay = 0 ) 의 원형을 가지고 있으며, 키 입력이 있을 때까지 기다리는 함수이다. 인수는 밀리세컨드 단위로 대기하는 것이다. 0 또는 음수로 설정이 될 경우에, 키 입력을 무한히 기다리는 형태이다. (항상 조심해야 하는 것은 시간의 단위가 밀리세컨드 단위라는 점이다! - millisecond는 1000 분의 1초이다. 0.5초라면 500 millisecond)

 

6. destroyWindow("img");

OpenCV는 메모리 사용에 대해서 많은 고려가 되었는데, destroyWindw 또한 메모리 해제와 관련된 함수이다. 일반적으로 표준 템플릿 라이브러리(STL)의 스코프 밖으로 벗어나게 되면 자동으로 메모리 할당이 해제 되지만, 규모가 조금이라도 커진다면 프로그래머가 임의로 해제해줘야 하는 상황이 발생할 수도 있다. destroyWindow는 윈도우를 닫고 관련된 모든 메모리 사용에 대해 해제하는 역할을 해준다. (메모리 누수를 방지하기 위해 중요한 역할을 한다.) 위의 코드 상에는 사실 필요가 없지만, 이러한 것이 있다라는 것을 보여주기 위해 넣어두었다.