OpenCV 4로 배우는 컴퓨터 비전과 머신 러닝/ OpenCV 주요 기능

  • (각 함수의 상세한 파라미터에 대해서는 이전의 <OpenCV로 배우는 영상처리 및 응용>에서 정리하였으므로 생략)

카메라와 동영상 파일 다루기

VideoCapture 클래스

  • 동영상이란 일련의 정지 영상을 압축하여 파일로 저장한 형태
    • 이때 동영상에 저장되어 있는 정지 영상을 프레임(frame)이라고 함.
    • 동영상을 처리하는 작업은 동영상에서 프레임을 추출한 후, 각각의 프레임에 영상 처리 기법을 적용하는 형태로 이루어짐.
  • OpenCV에서는 VideoCapture라는 클래스를 이용하여 카메라 또는 동영상 파일로부터 정지 영상 프레임을 받아올 수 있음.
  • VideoCapture 클래스에서 동영상 파일을 불러오려면 처음 VideoCapture 객체를 생성할 때 동영상 파일 이름을 지정하거나 기본 생성자로 VideoCpature 객체를 생성한 후 VideoCapture::open() 멤버 함수를 호출해야 함.
  • 하나의 동영상 파일 대신 일련의 숫자로 구분되는 이름의 정지 영상 파일을 가지고 있고, 이 파일을 불러오고 싶을 때에도 VideoCapture 클래스를 이용할 수 있음.
    • 예컨대 img0001.jpg, img0002.jpg, img0003.jpg 라는 파일이 있을 때 filename 인자에 ‘img%04d.jpg”라고 입력하면 일련의 영상 파일을 차례로 불러올 수 있음.
    • 또한 filename 인자에 ‘protocol://host:port/script_name?script_params|auth’ 형태의 비디오 스트림 URL을 지정하여 인터넷 동영상을 사용할 수도 있음.
  • apiPreference 인자에는 아래 표와 같은 VideoCaptureAPIs 열거형 상수 중 하나를 사용하여 동영상 파일을 불러오는 방법을 지정할 수 있음.
    • 대부분의 경우 apiPreference 인자를 생략하거나 기본값인 CAP_ANY를 지정하는데, 이 경우 시스템이 알아서 적절한 방법을 선택하여 사용 함.
열거형 상수 설명
CAP_ANY 자동선택
CAP_V4L, CAP_V4L2 V4L/V4L2(리눅스)
CAP_FIREWIRE, CAP_IEEE1394 IEEE 1394 드라이버
CAP_DSHOW 다이렉트쇼(DirectShow)
CAP_PVAPI PvAPI, Prosilica GigE SDK
CAP_OPENNI OpenNI
CAP_MSMF 마이크로소프트 미디어 파운데이션
CAP_GSTREAMER GStreamer
CAP_FFMPEG FFMPEG 라이브러리
CAP_IMAGES OpenCV에서 지원하는 일련의 영상 파일
CAP_OPENCV_MJPEG OpenCV에 내장된 MotionJPEG 코덱

 

  • 컴퓨터에 연결된 카메라 장치를 열 때에도 VideoCapture 생성자 혹은 VideoCapture::open() 함수를 이용하는데, 이때 함수의 인자에 문자열이 아닌 정수 값을 전달 함.
    • 카메라 장치를 사용하려 할 때 VideoCapture 클래스의 생성자 혹은 VideoCapture::open() 함수에 전달하는 정수 값 index는 다음과 같은 형태로 구성됨
index = camera_id + domain_offset_id
  • 만일 컴퓨터에 한 대의 카메라만 연결되어 있다면 이 카메라의 camera_id는 0이 된다.
    • 만일 두 대 이상의 카메라가 연결되어 있다면 각각의 카메라는 0보다 같거나 큰 정수를 ID로 갖는다.
  • domain_offset_id는 카메라 장치를 사용하는 방식을 표현하는 정수 값이며 VideoCaptureAPIs 열거형 상수 중 하나를 지정한다.
    • 대부분의 경우 domain_offset_id는 자동 선택을 의미하는 0(CAP_ANY)을 사용하기 때문에 index 값은 결국 camera_id와 같은 값으로 설정한다.
  • 카메라 또는 동영상 파일 열기를 수행한 후에는 VideoCapture::isOpened() 함수를 이용하여 열기 작업이 성공적으로 수행되었는지를 확인하는 것이 좋다.
  • 카메라 장치 또는 동영상 파일의 사용이 끝나면 VideoCapture::release()를 호출하여 사용하던 자원을 해제해야 한다.
    • 참고로 VideoCapture 클래스의 소멸자에도 VideoCapture::release() 함수와 마찬가지로 사용하고 있던 자원을 모두 해제하는 코드가 들어가 있어서 VideoCapture 객체가 소멸할 때 자동으로 열려 있던 카메라 장치 또는 동영상 파일이 닫히게 된다.
  • VideoCapture 클래스를 이용하여 카메라 또는 동영상 파일을 정상적으로 열었다면 그 후에 공통의 멤버 함수를 사용하여 프레임을 받아올 수 있다.
    • VideoCapture 클래스에서 한 프레임을 받아 오기 위해서는 VideoCapture::operator >>() 연산자 재정의 함수 또는 VideoCapture::read() 함수를 사용한다.
  • >> 연산자 재정의와 VideoCapture::read() 함수는 모두 카메라 또는 동영상 파일로부터 다음 프레임을 받아 와서 Mat 클래스 형식의 변수 image에 저장한다.
    • 사실 >> 연산자 재정의는 함수 내부에 명시적으로 VideoCapture::read() 함수를 호출하는 형태로 구성되어 있기 때문에 그 둘은 완전히 같다.
VideoCapture cap(0);

Mat frame1, frame2;
cap >> frame1; // 1st frame
cap.read(frame2); // 2nd frame
  • 현재 열려 있는 카메라 장치 또는 동영상 파일로부터 여러 정보를 받아 오기 위해서는 VideoCapture::get() 함수를 사용한다.
    • VideoCapture::get() 함수는 인자로 지정한 속성 ID(propID)에 해당하는 속성 값을 반환한다.
    • VideoCapture::get() 함수의 인자로 지정할 수 있는 속성 ID는 VideoCaptureProperties 열거형 상수 중 하나를 지정할 수 있으며, 자주 사용되는 상수를 아래 표에 정리.
    • VideoCapture::get() 함수는 속성을 double 타입으로 반환한다.
VideoCaptureProperties 열거형 상수 설명
CAP_PROP_POS_MSEC 비디오 파일에서 현재 위치(밀리초 단위)
CAP_PROP_POS_FRAMES 현재 프레임 위치 (0-기반)
CAP_PROP_POS_AVI_RATIO [0, 1] 구간으로 표현한 동영상 프레임의 상대적 위치(0: 시작, 1: 끝)
CAP_PROP_FRAME_WIDTH 비디오 프레임의 가로 크기
CAP_PROP_FRAME_HEIGHT 비디오 프레임의 세로 크기
CAP_PROP_FPS 초당 프레임 수
CAP_PROP_FOURCC fourcc 코드(코덱을 표현하는 정수 값)
CAP_PROP_FRAME_COUNT 비디오 파일의 전체 프레임 수
CAP_PROP_BRIGHTNESS (카메라에서 지원하는 경우) 밝기 조절
CAP_PROP_CONTRAST (카메라에서 지원하는 경우)  명암비 조절
CAP_PROP_SATURATION (카메라에서 지원하는 경우) 채도 조절
CAP_PROP_HUE (카메라에서 지원하는 경우) 색상 조절
CAP_PROP_GAIN (카메라에서 지원하는 경우) 감도 조절
CAP_PROP_EXPOSURE (카메라에서 지원하는 경우) 노출 조절
CAP_PROP_ZOOM (카메라에서 지원하는 경우) 줌 조절
CAP_PROP_FOCUS (카메라에서 지원하는 경우) 초점 조절
VideoCapture cap(0);

int w = cvRound(cap.get(CAP_PROP_FRAME_WIDTH));
int h = cvRound(cap.get(CAP_PROP_FRAME_HEIGHT));
  • VideoCapture::get() 함수와 반대로 현재 열려 있는 카메라 또는 비디오 파일 재생과 관련된 속성 값을 설정할 때는 VideoCapture::set() 함수를 사용한다.
    • VideoCapture::set() 함수의 속성 ID도 위의 표에 정리한 VideoCaptureProperties 열거형 상수를 지정한다.
    • 만일 video.mp4 파일을 열어서 100번째 프레임으로 이동하려면 다음과 같은 코드를 작성하면 된다.
VideoCapture cap("video.mp4");
cap.set(CAP_PROP_POS_FRAMES, 100);

카메라 입력 처리하기

  • (앞서 나온 내용을 이용한 예제 코드와 설명이라 생략)

동영상 파일 처리하기

  • (앞서 나온 내용을 이용한 예제 코드와 설명이라 생략)

동영상 파일 저장하기

  • (앞서 나온 내용을 이용한 예제 코드와 설명이라 생략)

다양한 그리기 함수

직선 그리기

  • line()은 영상 위에 직선을 그리는 함수
    • 영상 위에 pt1 좌표부터 pt2 좌표까지 직선을 그린다.
    • 이때 선의 색상, 밝기는 color로 지정할 수 있고, thickness를 이용하여 두께를 지정할 수 있다.
    • lineType인자는 그리는 방식을 지정할 수 있는데, LineTypes 열거형 상수 중 하나를 지정할 수 있다.
LineTypes 설명
FILLED -1 내부를 채움(직선 그리기 함수에는 사용 불가)
LINE_4 4 4방향 연결
LINE_8 8 8방향 연결
LINE_AA 18 안티에일리어싱

 

  • 화살표 형태의 직선을 그려야 하는 경우에는 arrowedLine()을 이용하면 된다.
    • arrowedLine() 함수는 영상 위에 pt1 좌표부터 pt2 좌표까지 직선을 그리고 끝점인 pt2에 화살표 모양의 직선 두 개를 추가로 그린다.
    • 이때 화살표 모양의 직선 길이는 arrowedLine() 함수의 마지막 인자인 tipLength를 이용하여 조절할 수 있다.
  • drawMarker()는 직선 그리기 함수를 이용하여 다양한 모양의 마커를 그린다.
    • drawMarker() 함수는 img 영상의 position 좌표에 color 색상을 이용하여 마커를 그리는데, 마커의 종류는 markerType 인자로 지정할 수 있다. 기본값으로는 십자가 모양의 MARKER_CROSS 가 지정되어 있다.
MarkerTypes 설명
MARKER_CROSS 십자가 모양 (+)
MARKER_TILTED_CROSS 45도 회전된 십자가 모양 (x)
MARKER_STAR 별 모양 (*)
MARKER_DIAMOND 마름모 모양
MARKER_SQUARE 정사각형 모양
MARKER_TRIANGLE_UP 위로 뾰족한 삼각형
MARKER_TRIANGLE_DOWN 아래로 뾰족한 삼각형

도형 그리기

  • rectangle()은 사각형을 그리는 함수
    • rectangle(0 함수 인자 중 thickness는 도형 외곽선의 두께를 지정하는데, 만일 thickness에 -1 을 지정하거나 FILLED 열거형 상수를 지정하면 내부를 채운 사각형을 그린다.
  • circle()은 원을 그리는 함수
    • 원을 그리기 위해서는 원의 중심점 좌표와 반지름을 지정해야 한다.
  • ellipse()는 타원을 그리는 함수. 타원을 그리는 방식은 원을 그리는 방식보다 복잡하다.
    • ellipse() 함수는 다양한 형태의 타원 또는 타원의 일부인 호를 그릴 수 있다.
    • 타원의 크기는 axes 인자를 통해 지정하는데, axes 인자는 size 자료형을 사용하며, x축 방향 타원과 반지름과 y축 방향 반지름을 지정한다.
    • angle에 0이 아닌 값을 전달하면 회전된 타원을 그릴 수 있다.
    • startAngle과 endAngle 인자를 적절하게 이용하면 호를 그리는 용도로도 사용할 수 있다.
    • 예컨대 startAngle에 0을 지정하고 endAngle에 360을 지정하면 완전한 타원을 그리지만 startAngle에 0을 지정하고 endAngle에 180을 지정하면 타원에 반에 해당하는 호를 그린다.
    • thickness는 타원 외곽선 두께를 나타내는데, -1 또는 FILLED를 지정하면 내부를 채운 타원이나 호를 그린다.
  • polylines() 함수는 임의의 다각형을 그리는 함수
    • polylines()에는 다각형의 꼭지점 좌표를 전달해야 하며, 꼭지점 좌표는 vector<Point> 자료형에 저장하여 전달한다.

문자열 출력하기

  • 영상 위에 정해진 폰트로 문자열을 출력하려면 putText() 함수를 이용하면 된다.
    • putText() 함수는 img 영상의 org 위치에 text로 지정된 문자열을 출력한다.
    • 이때 사용할 폰트는 fontFace 인자로 지정할 수 있고, faceScale 인자를 이용하여 폰트 크기를 조절할 수 있다.
    • fontFace 인자에는 HersheyFonts 열거형 상수 값을 지정할 수 있다. HersheyFonts 열거형 중 FONT_ITALIC 상수는 논리합 연산자(|)를 이용하여 다른 상수와 함께 사용한다.
HersheyFonts 설명
FONT_HERSHEY_SIMPLEX 일반 크기의 산세리프 폰트
FONT_HERSHEY_PLAIN 작은 크기의 산세리프 폰트
FONT_HERSHEY_DUPLEX 일반 크기의 산세리프 폰트 (FONT_HERSHEY_SIMPLEX 보다 복잡)
FONT_HERSHEY_COMPLEX 일반 크기의 세리프 폰트
FONT_HERSHEY_TRIPLEX 일반 크기의 세리프 폰트 (FONT_HERSHEY_COMPLEX보다 복잡)
FONT_HERSHEY_COMPLEX_SMALL FONT_HERSHEY_COMPLEX 보다 작은 폰트
FONT_HERSHEY_SCRIPT_SIMPLEX 필기체 스타일의 폰트
FONT_HERSHEY_SCRIPT_COMPLEX 필기체 스타일의 폰트(FONT_HERSHEY_SCRIPT_SIMPLEX 보다 복잡한 형태)
FONT_ITALIC 이탤릭체를 위한 플래그

 

  • OpenCV는 문자열 출력을 위해 필요한 사각형 영역 크기를 가늠할 수 있는 getTextSize() 함수를 제공하는데, 이 함수를 잘 이용하면 문자열이 한쪼긍로 치우치지 않고 적당한 위치에 출력되도록 설정할 수 있다.
    • putText(0 함수를 이용하여 특정 위치 좌표에 문자열을 출력하면, 보통 문자열 길이와 크기에 따라 문자열이 차지하는 영역 크기가 달라지기 때문에 문자열이 한쪽에 치우쳐서 나타날 수 있다. 그러나 getTextSize() 함수가 반환하는 문자열 영역 크기 정보를 이용하면 문자열 출력 위치를 적절하게 조절할 수 있다.
Mat img(200, 640, CV_8UC3, Scalar(255, 255, 255));

const String text = "Hello, OpenCV";
int fontFace = FONT_HERSHEY_TRIPLEX;
double fontScale = 2.0;
int thickness = 1;

Size sizeText = getTextSize(text, fontFace, fontScale, thickness, 0);
Size sizeImg = img.size();

// 텍스트의 크기를 이용해서 적절한 위치를 계산
Point org((sizeImg.width - sizeText.width) / 2, (sizeImg.Height + sizeText.height) / 2);

putText(img, text, org, fontFace, fontScale, Scalar(255, 0, 0), thickness);

이벤트 처리

키보드 이벤트 처리

  • waitKey() 함수는 키보드 입력을 처리하는 OpenCV의 기본 함수
    • waitKey() 함수는 delay에 해당하는 밀리초 시간 동안 키 입력을 기다리다가 키 입력이 있으면 해당 키의 아스키 코드(ASCII code) 값을 반환한다. 만일 지정 시간 동안 키 입력이 없었으면 -1을 반환한다.
  • Window 운영체제에서 waitKey()는 일반적인 키보드 입력은 처리할 수 있지만, 함수키(F1, F2 등) 또는 화살표키 등 특수키 입력은 처리하지 못한다. 만일 키보드의 특수 키에 대한 처리를 하고 싶다면 waitKey() 대신 waitKeyEx()를 사용하면 된다.

마우스 이벤트 처리

  • OpenCV 에서 마우스 이벤트를 처리하려면 먼저 마우스 콜백 함수를 등록하고, 이후 마우스 콜백 함수에 마우스 이벤트를 처리하는 코드를 추가해야 한다.
    • OpenCv에서 특정 창에 마우스 콜백 함수를 등록할 때는 setMouseCallback() 함수를 사용한다.
    • setMouseCallback() 함수는 winname 창에서 마우스 이벤트가 발생하면 onMouse로 등록된 콜백 함수가 자동으로 호출되도록 설정한다.
    • userdata 인자에는 사용자가 마우스 콜백 함수에 전달하고 싶은 데이터를 void* 형식으로 전달할 수 있다.
    • 마우스 콜백 함수는 다음과 같이 정의되어 있다.
typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata);
  • 마우스 콜백함수의 event 인자에는 MouseEventTypes로 정의된 열거형 상수가 전달된다.
MouseEventTypes 설명
EVENT_MOUSEMOVE 0 마우스가 창 위에서 움직임
EVENT_LBUTTONDOWN 1 마우스 왼쪽 버튼 누름
EVENT_RBUTTONDOWN 2 마우스 오른쪽 버튼 누름
EVENT_MBUTTONDOWN 3 마우스 가운데 버튼 누름
EVENT_LBUTTONUP 4 마우스 왼쪽 버튼 뗌
EVENT_RBUTTONUP 5 마우스 오른쪽 버튼 뗌 
EVENT_MBUTTONUP 6 마우스 가운데 버튼 뗌
EVENT_LBUTTONDCLICK 7 마우스 왼쪽 버튼 더블 클릭
EVENT_RBUTTONDCLICK 8 마우스 오른쪽 버튼 더블 클릭 
EVENT_MBUTTONDCLICK 9 마우스 가운데 버튼 더블 클릭
EVENT_MOUSEWHEEL 10 마우스 휠을 앞으로 돌림
EVENT_MOUSEHWHEEL 11 마우스 휠을 좌우로 돌림

 

  • 마우스 콜백함수의 flags 인자에는 MouseEventFlags 열거형 상수의 논리합이 전달된다.
MouseEventFlags 설명
EVENT_FLAG_LBUTTON 1 마우스 왼쪽 버튼이 눌려 있음
EVENT_FLAG_RBUTTON 2 마우스 오른쪽 버튼이 눌려 있음
EVENT_FLAG_MBUTTON 4 마우스 가운데 버튼이 눌려 있음
EVENT_FLAG_CTRLKEY 8 ctrl 키가 눌려있음
EVENT_FLAG_SHIFTKEY 16 shift 키가 눌려있음
EVENT_FLAG_ALTKEY 32 alt 키가 눌려있음

트랙바 사용하기

  • OpenCV에는 Window, Linux, Mac OS에서 공통으로 사용할 수 있는 트랙바(trackbar)라는 인터페이스를 제공함.
    • 트랙바는 슬라이더 컨트롤(slider control)이라고도 부르며, 영상 출력 창에 부착되어 프로그램 동작 중에 사용자가 지정된 범위 안의 값을 선택할 수 있음.
    • 트랙바는 사용자가 지정한 영상 출력 창의 상단에 부착되며, 필요한 경우 창 하나에 여러 개의 트랙바를 생성할 수 있음. 각각의 트랙바에는 고유한 이름을 지정해야 하며, 이 이름은 트랙바 왼쪽에 나타남.
    • 트랙바 위치는 사용자가 마우스를 이용하여 이동시킬 수 있고, 트랙바의 현재 위치는 트랙바 이름 옆에 함께 표시 됨.
  • OpenCV에서 트랙바를 생성하려면 createTrackbar() 함수를 이용하면 된다.
    • createTrackbar(0 함수는 winname 이름의 창에 trackbarname 이름의 트랙바를 부착하고, 트랙바가 움직일 때마다 onChange에 해당하는 트랙바 콜백 함수가 호출되도록 설정함.
    • 사용자가 트랙바 콜백 함수에 전다랗고 싶은 데이터가 있다면 userdata 인자를 통해 void* 형식으로 전달할 수 있음.
    • onChange에 지정하는 트랙바 콜백 함수는 트랙바 위치가 변경될 때 자동으로 호출되는 함수로 다음과 같이 정의되어 있음.
typedef void (*TrackbarCallback)(int pos, void* userdata);
  • 트랙바 콜백 함수의 첫 번째 인자에는 현재 트랙바의 위치 정보가 전달되고, 두 번째 인자에는 createTrackbar() 함수에서 지정한 사용자 데이터 포인터 값이 전달 됨.
  • 트랙바를 생성한 후 트랙바의 현재 위치를 알고 싶다면 getTrackbarPos() 함수를 사용하면 된다.
  • 프로그램 동작 중에 트랙바 위치를 강제로 옮기고 싶다면 setTrackbarPos()를 사용하면 된다.

OpenCV 데이터 파일 입출력

  • OpenCV에서 제공하는 FileStorage 클래스는 Mat 클래스 객체 뿐만 아니라 일반적인 C/C++ 자료형 데이터를 XML, YAML, JSON 등 파일 형식으로 저장하는 기능을 제공한다.

FileStorage 클래스

  • OpenCV에서 데이터 파일 입출력은 FileStorage가 담당하는데, FileStorage 클래스는 데이터의 파일 입출력 기능을 캡슐화하여 지원하는 클래스이다.
  • FileStorage 클래스를 이용하여 OpenCV 데이터를 저장하거나 읽어오려면 먼저 FileStorage 클래스를 생성한 후 FileStorage::open() 함수를 이용하여 실제 사용할 파일을 열어야 한다.
    • FileStorage::open() 함수의 첫 번째 인자 filename에는 데이터 파일 이름을 지정하는데, FileStorage 클래스는 XML, YAML, JSON 형식의 파일 입출력을 지원하며, 사용할 파일 형식은 filename의 확장자에 의해 자동으로 결정된다.
    • 만약 파일 이름 뒤에 .gz를 추가하면 데이터 파일을 압축하여 저장한다. 예컨대 filename을 “mydata.xml.gz”로 설정하면 XML 파일 형식으로 데이터를 저장한 후 gzip 형식으로 압축한다.
    • 두 번째 인자 flags는 파일 열기 모드를 결정하는데, FileStorage::mode 열거형 상수를 지정할 수 있다.
FileStorage::mode 열거형 상수 설명
FileStorage::READ 읽기 모드
FileStorage::WRITE 쓰기 모드 (새로 생성)
FileStorage::APPEND 추가로 쓰기 모드
FileStorage::MEMORY 논리합 연산자(|)를 이용하여 FileStorage::READ 또는 FileStorage::WRITE 상수와 함께 사용될 경우, 실제 파일 입출력 대신 메모리 버퍼를 이용한 입출력을 수행함

 

  • FileStorage::open() 이후 파일이 정상적으로 열렸는지 확인하는 함수는 FileStorage::isOpened()이다.
  • 일반적으로 FileStorage 클래스를 이용하여 파일에 데이터를 저장할 때는 << 연산자 재정의 함수를 사용하고, 파일로부터 데이터를 읽어올 때는 >> 연산자 재정의 함수를 사용한다.
  • FileStorage 객체를 이용하여 파일 입출력 작업이 완료되면 FileStorage::release() 함수를 호출해서 객체를 해제해야 한다.

데이터 파일 저장하기

  • 예제 코드
String name = "Jane";
int age = 10;
Point pt1(100, 200);
vector<int> scores = { 80, 90, 50 };
Mat mat1 = (Mat_<float>(2, 2) << 1.0f, 1.5f, 2.0f, 3.2f);

FileStorage fs("mydata.json", FileStorage::WRITE);

if (!fs.isOpened())
{
cerr << "File open failed!" << endl;
return;
}

fs << "name" << name;
fs << "age" << age;
fs << "point" << pt1;
fs << "scores" << scores;
fs << "data" << mat1;

fs.release();

데이터 파일 불러오기

  • 예제 코드
String name;
int age;
Point pt1;
vector<int> scores;
Mat mat1;

FileStorage fs("mydata.json", FileStorage::READ);

if (!fs.isOpened())
{
cerr << "File open failed!" << endl;
return;
}

fs["name"] >> name;
fs["age"] >> age;
fs["point"] >> pt1;
fs["scores"] >> scores;
fs["data"] >> mat1;

fs.release();

유용한 OpenCV 기능

마스크 연산

  • OpenCV에서는 임의의 모양을 갖는 ROI를 설정하기 위하여 일부 행렬 연산 함수에 대하여 마스크 연산을 지원한다.
    • 마스크 연산을 지원하는 OpenCV 함수는 보통 입력 영상과 크기가 같고 깊이가 CV_8U인 마스크 영상을 함께 인자로 전달 받는다.
    • 마스크 영상이 주어질 경우, 마스크 영상의 픽셀값이 0이 아닌 좌표에 대해서만 연산이 수행된다.
    • 일반적으로 마스크 영상은 사람의 눈으로도 구분이 쉽도록 픽셀 값이 0 또는 255로 구성된 흑백 영상이 사용된다.
  • Mat::setTo() 함수는 마스크 연산을 지원하는 함수로 함수의 두 번째 인자 mask에 마스크 영상을 지정할 수 있다.
    • 기본값으로 설정되어 있는 noArray()를 mask 인자로 지정하면 입력 행렬의 모든 원소 값을 value 값으로 설정하고 적절한 마스크 영상을 mask 인자로 지정하면 특정 영역에 대해서만 픽셀 값을 설정할 수 있다. 이때 마스크 영상은 Mat::setTo()를 호출하는 대상 행렬과 크기가 같아야 한다.
Mat src = imread("lenna.bmp", IMREAD_COLOR);
Mat mask = imread("mask_smile.bmp", IMREAD_GRAYSCALE);

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

src.setTo(Scalar(0, 255, 255), mask);
  • 마스크 연산을 지원하는 또 다른 함수로 Mat::copyTo() 라는 함수가 있다.
    • 마스크 연산을 지원하는 Mat::copyTo() 함수는 mask 영상의 픽셀 값이 0이 아닌 위치에서만 *this 행렬 원소 값을 행렬 m으로 복사한다.
    • 만약 Mat::copyTo() 함수를 호출하는 *this 행렬과 인자로 전달된 m 행렬이 서로 크기 또는 타입이 같지 않을 경우, Mat::copyTo() 함수 내부에서 m.create() 함수를 호출하여 대상 영상 m을 새롭게 생성한 후 마스크 영상을 고려하여 픽셀 값을 복사한다.
    • 만약 *this 행렬과 m 행렬이 서로 크기와 타입이 같다면 m 행렬 원소 값을 그대로 유지한 상태에서 *this 행렬의 픽셀 값을 복사한다.
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())
{
cerr << "Image load failed!" << endl;
return;
}

src.copyTo(dst, mask);

연산 시간 측정

  • OpenCV 라이브러리는 정밀한 시간 측정 방법을 제공한다. 본래 특정 프로그램의 동작 시간을 측정하는 C/C++ 소스 코드 작성 방법은 운ㄷ영 체제마다 각기 다르지만 OpenCV 라이브러리를 이용하면 운영 체제에 상관 없이 통일된 인터페이스 함수를 사용하여 연산 시간을 측정할 수 있다.
  • OpenCV에서는 getTickCount() 함수와 getTickFrequency() 함수를 사용하여 특정 연산의 수행 시간을 측정한다.
    • getTickCount() 함수는 컴퓨터 시스템의 특정 시점부터 현재까지 발생한 틱(tick) 횟수를 반환한다. 여기서 틱 횟수는 컴퓨터 시스템에서 발생하는 클럭처럼 매우 빠르게 증가하는 성능 측정 계수를 의미하며, 컴퓨터의 성능에 따라 빠르게 증가할 수도 있고, 느리게 증가할 수도 있다.
    • 틱 횟수 차이 값은 사용하고 있는 컴퓨터의 시스템 성능에 따라 다르게 측정되므로 실제 연산 시간을 알아내기 위해서는 틱 횟수 차이를 시스템의 틱 주파수(tick frequency)로 나누는 작업이 동반되어야 한다.
    • 틱 주파수란 1초 동안 발생하는 틱 횟수를 의미하며 getTickFrequency()를 이용하여 시스템의 틱 주파수를 구할 수 있다.
int64 t1 = getTickCount();

my_func(); // do something

int64 t2 = getTickCount();
double ms = (t2 - t1) * 1000 / getTickFrequency();
  • getTickCount()와 getTickFrequency() 를 조합하는 것이 번거롭기 때문에 OpenCV 3.2.0 버전부터 연산 시간 측정을 위한 TickMeter 라는 이름의 클래스를 새롭게 제공한다.
    • 사용법은 아래와 같다.
TickMeter tm;
tm.start();

my_func(); // do something

tm.stop();
double ms = tm.getTimeMilli();

유용한 OpenCV 함수 사용법

sum() 함수와 mean() 함수

  • OpenCV에서 Mat 행렬의 원소 합을 구하고 싶을 때는 sum() 함수를 사용하고, 평균을 구하고 싶을 때는 mean() 함수를 사용한다.
    • 이 두 함수는 4채널 이하의 행렬에 대해서만 동작하며, 합과 평균을 Scalar 타입으로 반환한다.
  • mean() 함수는 마스크 연산을 지원하므로 필요한 경우 mask 영상을 지정하여 특정 영역의 원소 평균을 구할 수도 있다.
Mat img = imread("lenna.bmp", IMREAD_GRAYSCALE);

int sumVal = (int)sum(img)[0];
int meanVal = (int)mean(img)[0];

minMaxLoc() 함수

  • minMaxLoc() 함수는 행렬 또는 영상에서 최솟값, 최댓값, 그리고 최솟값과 최댓값의 위치를 찾을 때 사용한다.
    • minMaxLoc() 함수는 마스크 연산을 지원하므로 행렬 일부 영역에서의 최솟값, 최댓값과 그 위치를 구할 수 있다.
double minVal, maxVal;
Point minPos, maxPos;
minMaxLoc(img, &minVal, &maxVal, &minPos, &maxPos);

normalize() 함수

  • 행렬의 노름(norm) 값을 정규화하거나 원소 값 범위를 특정 범위로 정규화할 때 normalize() 함수를 사용한다.
  • normalize() 함수는 norm_type 인자에 따라 동작이 결정된다.
    • norm_type이 NORM_INF, NORM_L1, NORM_L2인 경우에는 \|dst\|_{L_{p}} = alpha(p = Inf, 1, 2) 수식을 만족하도록 입력 행렬 원소 값의 크기를 조정한다. 

\|dst\|_{L_{\infty}} = max_{i} |dst_{i}| = alpha

\|dst\|_{L_{1}} = \sum_{i} |dst_{i}| = alpha

\|dst\|_{L_{2}} = \sqrt{\sum_{i} dst_{i}^{2}} = alpha

  • 만약 norm_type 인자가 NORM_MINMAX인 경우에는 src 행렬의 최솟값이 alpha, 최댓값이 beta가 되도록 모든 원소 값 크기를 조절한다.
  • 많은 OpenCV 예제 코드에서 NORM_MINMAX 타입으로 normalize() 함수를 사용하고 있으며, 특히 실수로 구성된 행렬을 그레이스케일 영상 형태로 변환하고자 할 때 normalize() 함수를 사용하면 유용하다.
Mat src = Mat_<float>({1, 5}, {-1.f, -0.5f, 0.f, 0.5f, 1.f});

Mat dst;
normalize(src, dst, 0, 255, NORM_MINMAX, CV_8UC1);

cvRound() 함수

  • OpenCV에서 실수 갑의 반올림 연산을 위해 cvRound() 함수를 제공한다.
  • 이와 더불어 실수의 올림을 수행할 때는 cvCeil() 함수를 사용하고, 내림을 수행할 때는 cvFloor() 함수를 사용한다.
[ssba]

The author

지성을 추구하는 사람/ suyeongpark@abyne.com

댓글 남기기

This site uses Akismet to reduce spam. Learn how your comment data is processed.