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

nugurii0·2022년 9월 24일
0

다중차로인식

목록 보기
10/11

VideoCapture 클래스

동영상은 정지 영상을 압축하여 저장한 파일로, 프레임(frame)이라고 부르는 일련의 정지 영상이다.

따라서 동영상을 처리하기 위해서는 각 프레임을 추출하여 영상 처리를 진행하는 방식으로 이루어진다.

컴퓨터에 카메라를 연결하여 촬영하는 것은 일정 시간 간격으로 정지 영상 프레임을 받아오는 것이다.

카메라와 동영상 모두 연속적인 프레임을 받아와 처리한다는 공통점이 있다.

이러한 정지 영상 프레임을 받아올 때 사용하는 클래스가 VideoCapture 클래스이다.

VideoCapture::open()

동영상 파일을 불러올 때 사용하며 다음은 생성자와 함수의 원형이다.

VideoCapture::VideoCapture(const String& filename,
                           int apiPreference = CAP_ANY);
bool VideoCapture::open(const String& filename, 
                        int apiPreference = CAP_ANY);
  • filename
    • 동영상 파일의 이름(상대 경로, 절대경로) 또는 비디오 스트림 URL을 지정할 수 있다. 또한 일련의 영상 파일을 format string으로 표기하여 일련의 영상 파일을 차례대로 불러올 수 있다. ex) “img%04d.jpg”
  • apiPreference
    • 파일을 불러오는 방법이다. 기본 값은 자동 선택을 의미하고 시스템이 알아서 선택하여 사용하게 된다.

카메라로 촬영한 영상을 사용할 때도 VideoCapture::open() 함수를 사용한다. 다음은 생성자와 함수의 원형이다.

VideoCapture::VideoCapture(int index, int apiPreference = CAP_ANY);
bool VideoCapture::open(int index, int apiPreference = CAP_ANY);
  • index
    • camera_id + domain_offset_id로 구성된다.
    • camera_id ≥ 0으로, 카메라가 한 대 연결되어 있다면 0이다.
    • domain_offset_id는 카메라 장치를 사용하는 방식으로 VideoCaptureAPIs 열거형 상수 중 하나를 사용한다. 대부분 자동 선택(CAP_ANY, 0)을 선택하기 때문에 보통 camera_id == index이다.

VideoCapture::isOpened()

영상이 성공적으로 열렸는지 확인하는 용도

bool VideoCapture::isOpened() const;
  • 사용 가능하면 true, 그렇지 않으면 false를 반환한다.

VideoCapture::release()

사용하고 있던 자원을 해제하는 용도

소멸자에도 동일한 코드가 들어가 있기 때문에 객체가 소멸할 때 열려 있던 장치 또는 파일도 함께 닫힌다.

VideoCapture::operator >>() , VideoCapture::read()

카메라, 동영상으로부터 한 프레임씩 영상을 받아오는 용도

VideoCapture& VideoCapture::operator >> (Mat& image);
bool VideoCapture::read(OutputArray image);
  • image : 다음 비디오 프레임, 더 가져올 프레임이 없다면 빈 행렬
  • 만약 프레임을 받아올 수 없다면 false를 반환한다.

사용 예시는 다음과 같다.

VideoCapture cap(0); // 카메라 장치 연결

Mat frame1, frame2;
cap >> frame1;       // 첫 번째 프레임
cap.read(frame2);    // 두 번째 프레임

VideoCapture::grab() , VideoCapture::retrieve()

VideoCapture의 멤버 함수로 VideoCapture::grab()VideoCapture::retrieve() 함수가 존재한다고 한다. 각 함수는 카메라에 다음 프레임을 획득하라는 명령과 획득한 프레임을 받아 오는 함수이다. 즉 >> 연산자 재정의와 read()grab()retrieve() 를 합쳐 놓은 것이라고 볼 수 있다.

보통의 경우 >> 연산자 재정의나 read() 함수를 사용하지만, retrieve()grab() 에 비해 느리기 때문에 여러 카메라로부터 동일 시점의 영상을 획득하고 싶다면 grab()retrieve() 를 따로 호출하는 것이 좋다고 한다.

VideoCapture::get(int propId)

열려 있는 카메라 장치나 동영상에서 여러 정보를 받아오는 용도

double VideoCapture::get(int propId) const;
  • propId : 속성 ID, VideoCaptureProperties 열거형 중 하나
  • 속성 값을 반환, 속성을 얻을 수 없다면 0을 반환

다음은 시스템 기본 카메라를 열고 기본 프레임 크기를 확인하는 예제 코드이다.

VideoCapture cap(0);

int w = cvRound(cap.get(CAP_PROP_FRAME_WIDTH));
int h = cvRound(cap.get(CAP_PROP_FRAME_HEIGHT));

get()은 double 자료형으로 반환하기 때문에 cvRound() 함수로 반올림하여 저장하는 것이 좋다.

VideoCapture::set(int propId, double value)

get() 이 속성 값을 가져온다면 set() 은 속성 값을 설정하는 용도이다.

bool VideoCapture::set(int propId, double value);
  • propId : 속성 ID
  • value : 지정할 속성 값
  • 속성 지정이 가능하면 true, 아니면 false

다음은 video.mp4 동영상 파일의 100번째 프레임으로 이동하는 예제 코드이다.

VideoCapture cap("video.mp4");
cap.set(CAP_PROP_POS_FRAMES, 100);

카메라 입력 처리

기존에 배운 함수들을 모두 사용하면 된다.

예시는 다음과 같다.

void canera_in()
{
	VideoCapture cap(0); // 카메라 장치가 1개일 때

	if(!cap_isOpened()){ // 카메라 장치에 연결이 안 되면
		cerr << "Open failed" << endl;
		return;
	}

	Mat frame, inversed; // 행렬(영상) 저장할 변수들
	while(true){
		cap >> frame;     // 다음 프레임 받아오기
		if(frame.empty()) // 프레임을 받아오지 못한다면(영상의 끝 or Error) 종료
			break;

		inversed = ~frame; // 원소 반전 not 연산

		imshow("frame", frame); // 원본 영상 출력
		imshow("inversed", inversed); // 반전 영상 출력
		
		if(waitKey(10) == 27) // ESC key 입력 시 종료
			break;
	}
	destroyAllWindows(); // 영상 출력하던 모든 창(frame, inversed) 닫기
}

while(true)와 wiatKey()를 통해 10ms를 기다리며 새로 프레임을 받아온다.

cap.release() 함수를 통해 변수와 장치 연결을 해제해야 하지만 해당 함수가 종료됨과 동시에 변수가 소멸되며 자동으로 호출되기에 명시하여 사용하지 않은 예제이다.

동영상 파일 처리

대부분의 동영상은 고유의 코덱(codec)으로 압축되어 저장된다.

OpenCV는 여러 종류의 코덱을 해석하는 기능도 포함되어 있다.

따라서 카메라 입력 처리와 동일한 방법으로 구현하면 된다.

동영상의 경우 FPS 값이 정해져 있다. 따라서 VideoCapture::get() 멤버 함수를 통해 CAP_PROP_FPS 속성 값을 가져와 delay를 계산하여 사용한다.

VideoCapture cap("test.mp4");
double fps = cap.get(CAP_PROP_FPS);
int delay = cvRound(1000 / fps);

delay 마다 영상 프레임을 받아와서 영상처리를 하면 된다.

위의 카메라 장치를 통한 영상 처리 예제에서

if(waitKey(delay) == 27) // ESC key 입력 시 종료
	break;

해당 부분만 변경하면 된다. 이러면 delay만큼 기다리고 새로운 프레임을 받아오게 된다.

동영상 파일 저장

VideoWriter 클래스를 사용한다.

영상을 입력받아 반전하여 output.avi로 저장하는 사용 예시이다.

fourcc는 코덱을 지정하는 멤버함수이다.

<< 연산자 재정의를 통해 쉽게 프레임단위로 저장할 수 있다.

void camera_in_video_out()
{
	VideoCapture cap(0);

	if(!cap.isOpened()){ ... }
	
	int fourcc = VideoWriter::fourcc('D', 'I', 'V', 'X');
	double fps = cap.get(CAP_PROP_FPS);
	int delay = cvRound(1000/fps);
	VideoWriter outputVideo("output.avi", fourcc, fps, Size(w, h));
	
	if(!outputVideo.isOpened()){ ... }
	
	Mat frame, inversed;
	while(true){
		cap >> frame;
		if(frame.empty()) break;

		inversed = ~frame;
		outputVideo << inversed;

		imshow("frame", frame);
		imshow("inversed", inversed);

		if(waitKey(delay)==27) break;
	}
}
profile
개발과 보안을 공부하는 학생

0개의 댓글