Computer Vision and OpenCV [Day 3]

 Published On

4. 사용자 인터페이스 및 I/O 처리 및 실습

4.1. 이미지 파일 처리

Mat imread(const string& filename, int flags=1)
    - filename: 로드되는 영상 파일 이름
    - flags: 로드된 영상이 행렬로 반환될 때 컬러 타입을 결정하는 상수
    
        1) IMREAD_UNCHANGED(-1): 파일에 지정된 컬러 영상을 반환
        2) IMREAD_GRAYSCALE(0): 명암도 영상으로 변환하여 반환
        3) IMREAD_COLOR(1): 컬러 영상으로 변환하여 반환
        4) IMREAD_ANYDEPTH(2): 입력파일에 정의된 깊이에 따라 16비트/32비트 영상으로 변환,
                               설정되지 않으면 8비트 영상으로 변화나
        5) IMREAD_ANYCOLOR(4): 파일에 정의된 타입으로 변환
    
bool imwrite(const string& filename, InputArray img, const vector<int>& params)
    - filename: 저장되는 영상 파일 이름, 확장자명에 따라 영상파일 형식 결정
    - inputArray img: 저장하고자 하는 행렬
    - vector<int>& params: 압축 양식에 사용되는 인수쌍들의 벡터
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void  print_matInfo(string name, Mat img)
{
  string str;
  int depth = img.depth();

  if (depth == CV_8U)	 str = "CV_8U";
  else if (depth == CV_8S)	 str = "CV_8S";
  else if (depth == CV_16U) str = "CV_16U";
  else if (depth == CV_16S)	 str = "CV_16S";
  else if (depth == CV_32S) str = "CV_32S";
  else if (depth == CV_32F) str = "CV_32F";
  else if (depth == CV_64F) str = "CV_64F";

  cout << name;
  cout << format(": depth(%d) channels(%d) -> 자료형: ", depth, img.channels());
  cout << str << "C" << img.channels() << endl;
}

int main()
{
  string filename1 = "../image/read_gray.jpg";
  Mat gray2gray = imread(filename1, IMREAD_GRAYSCALE); // 채널이 1개
  Mat gray2color = imread(filename1, IMREAD_COLOR); // RGB가 들어가서, 채널이 3개
  CV_Assert(gray2gray.data && gray2color.data); // 데이터를 읽어서 파일이 있는지 없는지 Assert 에러 확인(디버깅)

  Rect roi(100, 100, 5, 5); // 5 x 5
  cout << "행렬 좌표 (100,100) 화소값 " << endl;
  cout << "gray2gray " << gray2gray(roi) << endl;
  cout << "gray2color " << gray2color(roi) << endl << endl;

  print_matInfo("gray2gray", gray2gray);
  print_matInfo("gray2color", gray2color);
  imshow("gray2gray", gray2gray);
  imshow("gray2color", gray2color);
  waitKey(0);
  return 0;
}
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void  print_matInfo(string name, Mat img)
{
  string str;
  int depth = img.depth();

  if (depth == CV_8U)	 str = "CV_8U";
  else if (depth == CV_8S)	 str = "CV_8S";
  else if (depth == CV_16U) str = "CV_16U";
  else if (depth == CV_16S)	 str = "CV_16S";
  else if (depth == CV_32S) str = "CV_32S";
  else if (depth == CV_32F) str = "CV_32F";
  else if (depth == CV_64F) str = "CV_64F";

  cout << name;
  cout << format(": depth(%d) channels(%d) -> 자료형: ", depth, img.channels());
  cout << str << "C" << img.channels() << endl;
}

int main()
{
  string filename = "../image/read_color.jpg";
  Mat color2gray = imread(filename, IMREAD_GRAYSCALE);
  Mat color2color = imread(filename, IMREAD_COLOR);
  CV_Assert(color2gray.data && color2color.data);

  Rect roi(100, 100, 5, 5); // (100, 100)위치에서 5x5의 사각형을 추출(추출된 화소)
  cout << "행렬 좌표 (100,100) 화소값 " << endl;
  cout << "color2gray " << color2gray(roi) << endl; // 한 화소값 표시(부분 행렬 원소 출력)
  cout << "color2color " << color2color(roi) << endl;

  print_matInfo("color2gray", color2gray);
  print_matInfo("color2color", color2color);
  imshow("color2gray", color2gray);
  imshow("color2color", color2color);
  waitKey(0);
  return 0;
}

4.2. 행렬을 영상 파일로 저장 - I

cv::imwrite(): 확장자명으로 영상파일을 쉽게 저장
    - IMWRITE_JPEG_QUALITY: JPG 파일 화질, 높은 값일 수록 화질이 좋음
    - IMWRITE_PNG_COMPRESSION: PNG 파일 압축레벨, 높은 값일 수록 적은 용량, 긴 압축시간
    - IMWRITE_PXM_BINARY: PPM, PGM 파일의 이진 포맷 설정
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	Mat img8 = imread("../image/read_color.jpg", IMREAD_COLOR);
	CV_Assert(img8.data);

	vector<int> params_jpg, params_png;
	params_jpg.push_back(IMWRITE_JPEG_QUALITY); // 영상 파일 저장, JPEG 압축 옵션
	params_jpg.push_back(50);
	params_png.push_back(IMWRITE_PNG_COMPRESSION);
	params_png.push_back(9);

	imwrite("../image/write_test1.jpg", img8);
	imwrite("../image/write_test2.jpg", img8, params_jpg);
	imwrite("../image/write_test.png", img8, params_png);
	imwrite("../image/write_test.bmp", img8);
	return 0;
}

4.2. 행렬을 영상 파일로 저장 - II

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	Mat img8 = imread("../image/read_color.jpg", IMREAD_COLOR);
	Mat img16, img32;
	CV_Assert(img8.data);

	// converTo를 할 때, scaling 필수
	img8.convertTo(img16, CV_16U, 65535 / 255.0);
	img8.convertTo(img32, CV_32F, 1 / 255.0f);

	Rect  roi(10, 10, 3, 3);
	cout << "img8 행렬의 일부 " << endl << img8(roi) << endl << endl;
	cout << "img16 행렬의 일부 " << endl << img16(roi) << endl << endl;
	cout << "img32 행렬의 일부 " << endl << img32(roi) << endl;

	imwrite("../image/write_test_16.tif", img16);
	imwrite("../image/write_test_32.tif", img32);

	imshow("img16", img16);
	imshow("img32", img32);
	waitKey();
	return 0;
}

4.3. 비디오 처리

비디오 파일 - 본질적으로 대용량이기에 압축이 필요

bool VideoCapture()
    - string& filename: 개방할 동영상 파일의 이름 혹은 이미지 시퀀스
    - int device: 개방할 동영상 캡처 장치의 id(카메라 한 대만 연결되면 0을 지정)

bool open()
    - string& filename: 개방할 동영상 파일의 이름 혹은 이미지 시퀀스
    - int device: 개방할 동영상 캡처 장치의 id
    
bool isOpened(): 캡처 장치의 연결 여부를 반환한다.

bool release(): 동영상 파일이나 캡처 장치를 해제한다(클래스 소멸자에 의해서 자동으로 호출되어 명시적으로 수행하지 않아도 됨)

double get(): 비디오 캡처의 속성 식별자로 지정된 속성의 값을 반환한다. 캡처 장치가 제공하지 않는 속성은 0을 반환한다.
    - int propId: 속성 식별자
    
        1) CAR_PROP_POS_MSEC: 동영상 파일의 현재 위치(ms)
        2) CAR_PROP_POS_FRAMES: 캡처되는 프레임의 번호
        3) CAR_PROP_POS_AVI_RATIO: 동영상 파일의 상대적 위치(0 시작, 1 끝)
        4) CAR_PROP_FRAME_WIDTH: 프레임의 너비
        5) CAR_PROP_FRAME_HEIGHT: 프레임의 높이
        6) CAR_PROP_FPS: 초당 프레임의 수
        7) CAR_PROP_FOURCC: 코덱의 4문자
        8) CAR_PROP_FRAME_COUNT: 동영상 파일의 총 프레임 수
        9) CAR_PROP_MODE: retrieve()에 의해 반환되는 Mat 영상 포맷
        10) CAR_PROP_BRIGHTNESS: 카메라에서 영상의 밝기
        11) CAR_PROP_CONTRAST: 카메라에서 영상의 대비
        12) CAR_PROP_SATURATION: 카메라에서 영상의 포화도
        13) CAR_PROP_HUE: 카메라에서 영상의 색조
        14) CAR_PROP_GAIN: 카메라에서 영상의 Gain
        15) CAR_PROP_EXPOSURE: 카메라에서 노출
        16) CAR_PROP_AUTOFOCUS: 자동 초점 조절

bool set(): 지정된 속성 식별자로 비디오캡처의 속성을 설정한다.
    - int propId: 속성 식별자
    - double value: 속성 값

bool grab(): 캡처 장치나 동영상 파일로부터 다음 프레임을 잡는다.

bool retrive(): grab()으로 잡은 프레임을 디코드해서 image 행렬로 전달한다.
    - Mat& image: 잡은 프레임이 저장되는 행렬
    - int channel: 프레임의 채널 수

bool read(), >>: 다음 동영상 프레임을 잡아서 디코드하고 image 행렬로 전달한다.
                 즉, grab()과 retrieve()를 동시에 수행한다.

VideoWriter()
    - string& filename: 출력 동영상 파일의 이름
    - int fourcc: 프레임 압축에 사용되는 코덱의 4문자
    - double fps: 생성된 동영상 프레임들의 프레임 레이트
    - Size frameSize: 동영상 프레임의 크기(가로 x 세로)
    - bool isColor: true이면 컬러 프레임으로 인코딩, false이면 명암도 프레임으로 인코딩
    
bool open(): 영상을 동영상 파일의 프레임으로 저장하기 위해 동영상 파일을 개방한다. 인수는 생성자의 인수와 동일하다.

bool isOpend(): 동영상 파일 저장을 위해 VideoWriter 객체의 개방 여부를 확인한다.

void write(), <<: image 행렬(프레임)을 동영상 파일로 저장한다.
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

// 문자열 출력 함수 - 그림자 효과 
void put_string(Mat& frame, string text, Point pt, int value)
{
	// 화면에 보이는 EXPOSE 문자열의 그림자 효과
	text += to_string(value);
	Point shade = pt + Point(2, 2);
	int font = FONT_HERSHEY_SIMPLEX;
	putText(frame, text, shade, font, 0.7, Scalar(0, 0, 0), 2);		// 그림자 효과
	putText(frame, text, pt, font, 0.7, Scalar(120, 200, 90), 2);	// 작성 문자
}

int main()
{
    // 0번 카메라 연결
	VideoCapture capture(0);
	if (!capture.isOpened())
	{
		cout << "카메라가 연결되지 않았습니다." << endl;
		exit(1);
	}
	cout << "너비 " << capture.get(CAP_PROP_FRAME_WIDTH) << endl;
	cout << "높이 " << capture.get(CAP_PROP_FRAME_HEIGHT) << endl;
	cout << "노출 " << capture.get(CAP_PROP_EXPOSURE) << endl;
	cout << "밝기 " << capture.get(CAP_PROP_BRIGHTNESS) << endl;

	for (;;) {
		Mat frame;
		capture.read(frame);

		// (10, 40) 위치에 노출
		put_string(frame, "EXPOS: ", Point(10, 40), capture.get(CAP_PROP_EXPOSURE));

		imshow("카메라 영상보기", frame);
		if (waitKey(30) >= 0) break;
	}
	return 0;
}

4.4. 카메라 속성 설정하기

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
// 문자열 출력 함수 - 그림자 효과 
void put_string(Mat& frame, string text, Point pt, int value)
{
	text += to_string(value);
	Point shade = pt + Point(2, 2);
	int font = FONT_HERSHEY_SIMPLEX;
	putText(frame, text, shade, font, 0.7, Scalar(0, 0, 0), 2);
	putText(frame, text, pt, font, 0.7, Scalar(120, 200, 90), 2);
}

VideoCapture capture;					// 전역 변수 선언

void zoom_bar(int value, void*) {		// 트랙바 콜백 함수들
	capture.set(CAP_PROP_ZOOM, value);	// 카메라 속성 지정
}
void focus_bar(int value, void*) {
	capture.set(CAP_PROP_FOCUS, value); // 카메라 속성 지정
}

int main()
{
	capture.open(0);												// 0번 카메라 연결
	CV_Assert(capture.isOpened());								// 카메라 연결 예외 처리

	capture.set(CAP_PROP_FRAME_WIDTH, 400); 	// 카메라 프레임 크기 설정
	capture.set(CAP_PROP_FRAME_HEIGHT, 300);
	capture.set(CAP_PROP_AUTOFOCUS, 0);
	capture.set(CAP_PROP_BRIGHTNESS, 150);

	int zoom = capture.get(CAP_PROP_ZOOM);				// 카메라 속성 가져오기
	int focus = capture.get(CAP_PROP_FOCUS);

	string title = "카메라 원본";							// 윈도우 이름 지정
	namedWindow(title);											// 윈도우 생성
	createTrackbar("zoom", title, &zoom, 10, zoom_bar); 		// 윈도우에 줌 트랙바 추가
	createTrackbar("focus", title, &focus, 40, focus_bar);

	for (;;) {

		Mat frame;
		capture >> frame;										// 카메라 영상받기
		
		Mat x_axis, y_axis, xy_axis;
		flip(frame, x_axis, 0);   // 상하 반전
		flip(frame, y_axis, 1);   // 좌우 반전
		flip(frame, xy_axis, -1); // 상하좌우 반전

		put_string(frame, "zoom: ", Point(10, 240), zoom);		// 줌 값 영상 표시
		put_string(frame, "focus: ", Point(10, 270), focus);	// 포커스 

		imshow(title, frame);
		imshow("상하 반전", x_axis);
		imshow("좌우 반전", y_axis);
		imshow("상하좌우 반전", xy_axis);

		if (waitKey(30) >= 0) break;
	}
	return 0;
}

4.5. 카메라 프레임 동영상 파일 저장

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	VideoCapture capture(0);
	CV_Assert(capture.isOpened());

	double fps = 29.97; // 초당 프레임 수
	int  delay = cvRound(1000.0 / fps); // 프레임간 지연시간
	Size size(640, 360); // 동영상 파일 해상도
	int  fourcc = VideoWriter::fourcc('D', 'X', '5', '0'); // 압축 코덱 설정

	capture.set(CAP_PROP_FRAME_WIDTH, size.width); // 해상도 설정
	capture.set(CAP_PROP_FRAME_HEIGHT, size.height);

	cout << "width x height : " << size << endl;
	cout << "VideoWriterfourcc : " << fourcc << endl;
	cout << "delay : " << delay << endl;
	cout << "fps : " << fps << endl;

	VideoWriter  writer;
	writer.open("../image/video_file.avi", fourcc, fps, size);
	CV_Assert(writer.isOpened());

	Mat frame;

	for (;;) {
		
		capture.read(frame);
		//capture >> frame; 		// 카메라 영상받기
		//writer << frame; 		// 프레임을 동영상으로 저장
		writer.write(frame);

		imshow("카메라 영상보기", frame);
		if (waitKey(delay) >= 0)
			break;
	}
	return 0;
}

4.6. 비디오 파일 읽기

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

void put_string(Mat& frame, string text, Point pt, int value)
{
	text += to_string(value);
	Point shade = pt + Point(2, 2);
	int font = FONT_HERSHEY_PLAIN;
	putText(frame, text, shade, font, 1.8, Scalar(0, 0, 0), 2);
	putText(frame, text, pt, font, 1.8, Scalar(120, 200, 90), 2);
}

int main()
{
	VideoCapture capture;
	capture.open("../image/video_file.avi");
	CV_Assert(capture.isOpened());

	double frame_rate = capture.get(CAP_PROP_FPS);
	int delay = 1000 / frame_rate;
	int frmae_cnt = 0;
	Mat  frame;

	while (capture.read(frame))
	{
		if (waitKey(delay) >= 0) break;

		if (frmae_cnt < 100);
		else if (frmae_cnt < 200)	frame -= Scalar(0, 0, 100); // 영상이 전체적으로 붉은빛이 사라짐
		else if (frmae_cnt < 300) 	frame += Scalar(100, 0, 0); // 영상이 전체적으로 푸른빛이 생겨짐
		else if (frmae_cnt < 400)	frame = frame * 1.5; // 영상이 전체적으로 대비가 높아짐
		else if (frmae_cnt < 500)	frame = frame * 0.5; // 영상이 전체적으로 대비가 낮아짐

		put_string(frame, "frmae_cnt ", Point(20, 50), frmae_cnt++);
		imshow("동영상 파일읽기", frame);
	}
	return 0;
}

4.7. FileStorage 클래스

FileStorage(): 생성자
bool open()
    - string source: 개방할 동영상 파일 이름
    - string filename: 개방할 동영상 파일 이름
    - int flags: 연산 모드
        1) READ(0): 읽기 전용 열기
        2) WRITE(1): 쓰기 전용 열기
        3) APPEND(2): 추가 전용 열기
        4) MEMORY(4): source로부터 데이터를 읽고, 외부버퍼에 저장

    - string& encoding: 저장 데이터의 문자 인코딩 방식을 지정

bool isOpend(): 클래스에 지정된 파일이 열려 있는지 확인하여 열려있으면 true를 반환한다.

bool release(): 파일을 닫고, 모든 메모리 버퍼를 해제한다.

void writeRaw(): 다중의 숫자들을 저장한다. 데이터를 raw 파일로 저장한다.
    - string& fmt: 배열 원소의 자료형에 대한 명세
    - uchar* vec: 저장될 배열의 포인터
    - size_t len: 저장할 원소의 개수

4.8. FileNode 클래스

FileNode()
    - CvFileStorage* fs: 파일 저장 구조에 대한 포인터
    - CvFileStorage* node: 생성되는 파일 노드를 위한 초기화에 사용되는 파일 노드

string name(): 노드 이름을 반환한다.

size_t size(): 노드에서 원소의 개수를 반환한다.
    - int type()
        1) None(0): empty node
        2) INT(1): 정수형
        3) REAL(2): 부동 소수형
        4) FLOAT(2): 부동 소수형
        5) STR(3): 문자열 utf-8 인코딩
        6) STRING(3): 문자열 utf-8 인코딩
        7) REF(4): size_t 크기의 정수형
        8) SEQ(5): 시퀀스
        9) MAP(6): 매핑

bool empty(): 노드가 비어있는지 확인한다.

bool isNamed(): 노드가 이름이 있는지 확인한다.

bool isNone(): 노드가 "none" 객체인지 확인한다.

bool isInt(), bool isReal(): 노드 타입이 정수형, 실수형인지 확인한다.

bool isString(): 노드타입이 문자형인지 확인한다.

bool isMap(), bool isSeq(): 노드의 종류가 매핑인지, 시퀀스인지 확인한다.

operator <<: 연산자 메서드, 템플릿 타입으로 데이터를 저장한다.
    - FileStorage& fs: 파일 저장 구조에 대한 포인터
    - _Tp& value: 저장하고자 하는 데이터(템플릿 자료형)
    - vector<_Tp>& vec: 저장하고자 하는 벡터 데이터
    
operator >>: 파일 스토리지로부터 데이터를 읽는다.
    - FileNode& n: 읽은 데이터가 저장되는 노드
    - _TP& value: 파일 스토리지로부터 읽은 데이터
    - vector<_Tp>& vec: 파일 스토리지로부터 읽은 벡터 데이터

4.9. XML/YAML 파일 저장

  • 스트림 연산자(«) 이용
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	FileStorage fs("test.xml", FileStorage::WRITE);
	string name = "이정진";
	fs << "name" << name;
	fs << "age" << 20;
	fs << "university" << "숭실대학교";
	// 시퀀스 노드(배열 처럼 저장)
	fs << "picture" << "[" << "mine1.jpg" << "mine2.jpg" << "mine3.jpg" << "]";
	
	// 매핑 노드
	fs << "hardware" << "{";
	fs << "cpu" << 25;
	fs << "mainboard" << 10;
	fs << "ram" << 6 << "}";

	int  data[] = { 1, 2, 3, 4, 5 , 6 };
	vector <int> vec(data, data + sizeof(data) / sizeof(float));
	fs << "vector" << vec;
	Mat m(2, 3, CV_32S, data);
	fs << "Mat" << m;

	Point2d  pt(10.5, 200);
	Rect     rect(pt, Size(100, 200));
	fs << "Point" << pt;
	fs << "Rect" << rect;

	fs.release();
	return 0;
}

4.10. XML/YAML 파일 읽기

  • 스트림 연산자(») 이용
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	FileStorage fs("test.xml", FileStorage::READ); // 읽기모드로 연결
	CV_Assert(fs.isOpened());

	string name, university, picture;
	int age;
	fs["university"] >> university; // 노드 접근 방법
	fs["name"] >> name;
	fs["age"] >> age;
	cout << "university " << university << endl;
	cout << "name " << name << endl;
	cout << "age " << age << endl;

	// 컬렉션 노드 가져오기
	FileNode node_pic = fs["picture"]; // 시퀀스 노드
	FileNode node_hd = fs["hardware"]; // 매핑 노드

	try {
		if (node_pic.type() != FileNode::SEQ)
			CV_Error(Error::StsError, "시퀀스 노드가 아닙니다.");
		if (!node_hd.isMap())
			CV_Error(Error::StsError, "매핑 노드가 아닙니다.");
	}
	catch (Exception& e) {
		exit(1);
	}

	cout << "[picture]  ";
	// 시퀀스 노드 - 인덱스로 접근
	cout << (string)node_pic[0] << ", ";
	cout << (string)node_pic[1] << ", ";
	cout << (string)node_pic[2] << endl << endl;

	// 매핑 노드 - 키값으로 접근
	cout << "[hardware]" << endl;
	cout << "  cpu  " << (int)node_hd["cpu"] << endl;
	cout << "  mainboard  " << (int)node_hd["mainboard"] << endl;
	cout << "  ram  " << (int)node_hd["ram"] << endl;

	Point pt;
	Rect  rect;
	Mat   mat;
	vector<float> vec;
	fs["vector"] >> vec;

	// 행렬 데이터 접근
	fs["Point"] >> pt;
	fs["Rect"] >> rect;
	fs["Mat"] >> mat;

	cout << "[vec] = " << ((Mat)vec).t() << endl;
	cout << "[pt] = " << pt << endl;
	cout << "[rect] = " << rect << endl << endl;
	cout << "[mat] = " << endl << mat << endl;

	fs.release();
	return 0;
}
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	FileStorage fs_r("test.xml", FileStorage::READ);
	CV_Assert(fs_r.isOpened());

	FileNode node_pic = fs_r["picture"];			// 시퀀스 노드
	vector<Mat> images;
	for (int i = 0; i < node_pic.size(); i++)
	{
		Mat tmp = imread("../image/" + (string)node_pic[i], IMREAD_UNCHANGED);
		CV_Assert(tmp.data);
		images.push_back(tmp);
		imshow(node_pic[i], images[i]);
	}
	
	FileStorage fs_w("result.xml", FileStorage::WRITE);
	CV_Assert(fs_w.isOpened());

	vector<double> mean, dev;
	for (int i = 0; i < images.size(); i++) {
		string pic_name = ((string)node_pic[i]).substr(0,5); // 파일 이름만 가져오기

		meanStdDev(images[i], mean, dev); // 평균과 표준편차를 벡터로 변환
		fs_w << pic_name + "mmean" << "["; // 시퀀스 노드로 저장
		for (int j = 0; j < (int)mean.size(); j++) { // 각 채널 평균은 원소로 저장
			fs_w << mean[j];
		}
		fs_w << "]";
		fs_w << pic_name + "ddev" << dev;
	}

	fs_r.release();
	fs_w.release();
	waitKey(100);
	return 0;
}

4.11. 심화 실습 1

인터넷 등에서 네 개 칼라 영사의 jpg 파일을 구해서 XML 문서를 만들어서 각 파일명들을 시퀀스 노드로 저장하는 코드로 작성

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	
	FileStorage fs("test2.xml", FileStorage::WRITE);
	fs << "picture" << "[";
	for (int i = 1; i < 4; i++) {
//		string pci_name = ((string)node_pic[i]).sub_str(0, 4);
		string filename = "m" + to_string(i) + ".jpg";
		Mat gray2color = imread(filename, IMREAD_COLOR); // RGB가 들어가서, 채널이 3개
		fs << filename;
	}

	fs << "]";

	fs.release();
	return 0;
}

4.12. 심화 실습 2

심화 실습 #1에서 생성된 XML 문서를 읽어서 4가지 컬러 영상의 영상들의 채널별 화소평균과 표준편차를 계산하여 XML 형식으로 저장하는 코드를 작성하시오.

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
	FileStorage fs_r("test2.xml", FileStorage::READ);
	CV_Assert(fs_r.isOpened());

	FileNode node_pic = fs_r["picture"];			// 시퀀스 노드
	vector<Mat> images;
	for (int i = 0; i < node_pic.size(); i++)
	{
		Mat tmp = imread("../image/" + (string)node_pic[i], IMREAD_UNCHANGED);
		CV_Assert(tmp.data);
		images.push_back(tmp);
		imshow(node_pic[i], images[i]);
	}

	FileStorage fs_w("result2.xml", FileStorage::WRITE);
	CV_Assert(fs_w.isOpened());

	vector<double> mean, dev;
	for (int i = 0; i < images.size(); i++) {
		string pic_name = ((string)node_pic[i]).substr(0, 1); // 파일 이름만 가져오기

		meanStdDev(images[i], mean, dev); // 평균과 표준편차를 벡터로 변환
		fs_w << pic_name + "mmean" << "["; // 시퀀스 노드로 저장
		for (int j = 0; j < (int)mean.size(); j++) { // 각 채널 평균은 원소로 저장
			fs_w << mean[j];
		}
		fs_w << "]";
		fs_w << pic_name + "ddev" << dev;
	}

	fs_r.release();
	fs_w.release();
	waitKey(100);
	return 0;
}

Tags: OpenCV

Comments:

comments powered by Disqus

© 2021 - MH.Ji. All rights reserved
Built using Jekyll