[jsoncpp] json parser for C++

spring·2020년 11월 9일
0

1. 설치

매우 쉽다. 아래의 명령으로 github에서 repository를 받은 후에 파이썬 스크립트를 실행하기만 하면 된다.

git clone https://github.com/open-source-parsers/jsoncpp
python amalgamate.py

dist 폴더 밑에 헤더파일과 소스파일(cpp) 이 생성되는데, 프로젝트에 포함만 시키면 된다.
header only가 아닌게 아쉽지만 (cpp 파일 하나가 5천줄 정도 되니..)

2. 파싱

아래는 MS COCO 2017의 instances_train2017.json 을 파싱하는 예시이다. 파일이 매우 커서 파싱하는데 수십초가 걸린다.

이 라이브러리는 파일을 입력으로 받지 않고, JSON 문자열 자체를 받으므로 우선 파일을 문자열로 모두 가져와야 한다.

아래의 코드로 파일의 모든 문자열을 std::string 에 담을 수 있다.

std::ifstream fin;
fin.open("instances_train2017.json", std::ios::in);
std::string str;
str.assign(std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>());

이렇게 얻은 문자열을 아래의 코드로 파싱을 한다.

Json::Reader reader;
Json::Value root;
if (reader.parse(str, root) == false) {
	std::cerr << "Failed to parse Json : " << reader.getFormattedErrorMessages() << std::endl;
	return 1;
}

우선 해당 Json은 파일의 크기가 너무 커서 아래의 함수로 대략적인 구조를 살펴 볼 수 있다.

std::vector<std::string> Json::Value::getMemberNames()

MS COCO 2017의 Json은 아래 5개의 멤버가 있는데 이중 annotationsimages 만 보면 된다.

annotations
categories
images
info
licenses

(MS는 굳이 tagged images를 제공하는데 JSON을 써야 했을까?)

images 멤버의 예

{
        "coco_url" : "http://images.cocodataset.org/train2017/000000421681.jpg",
        "date_captured" : "2013-11-18 08:56:23",
        "file_name" : "000000421681.jpg",
        "flickr_url" : "http://farm4.staticflickr.com/3441/3942180902_5ee1fb9149_z.jpg",
        "height" : 375,
        "id" : 421681,
        "license" : 4,
        "width" : 500
}

annotations 멤버의 예

{
        "area" : 827.60005000000001,
        "bbox" :
        [
                465.31,
                216.49000000000001,
                26.440000000000001,
                46.159999999999997
        ],
        "category_id" : 18,
        "id" : 4430,
        "image_id" : 390348,
        "iscrowd" : 0,
        "segmentation" :
        [
                [
                        465.31,
                        224.47,
                        ...중략...
                        465.94,
                        225.09999999999999
                ]
        ]
}

복잡한데, 한 images에 여러개의 annotation이 있으니, 해당 JSON의 image->id 와 annotations->image_id 를 매핑시켜서 저장해야 한다.

#include<iostream>
#include<fstream>
#include<map>
#include"json\json.h"
class BboxInfo {
public:
	int label;			//category_id
	float x, y, w, h;	//bbox

};
class ImageInfo {
public:
	std::string file;
	std::vector<BboxInfo> bbox;
	ImageInfo(std::string _file) {
		this->file = _file;
	}
};
int CvtJSON2Tsp() {
	std::string annotation_file = "D:/LocalDrive/77.Project(PhD)/01.신도컴퓨터/01.dataset/02.COCO/annotations_trainval2017/instances_train2017.json";
	std::string image_dir = "D:/LocalDrive/77.Project(PhD)/01.신도컴퓨터/01.dataset/02.COCO/train2017/";

	std::ifstream fin;
	fin.open(annotation_file, std::ios::in);

	std::string str;
	str.assign(std::istreambuf_iterator<char>(fin), std::istreambuf_iterator<char>());


	Json::Reader reader;
	Json::Value root;
	if (reader.parse(str, root) == false) {
		std::cerr << "Failed to parse Json : " << reader.getFormattedErrorMessages() << std::endl;
		return 1;
	}

	std::map<int, int> coco80label;
	int c = 0;
	Json::Value categories = root["categories"];
	std::fstream fout("coco2017label.txt", std::ios::out);
	for (auto it = categories.begin(); it != categories.end(); it++) {
		fout << (*it)["name"].asString() << std::endl;
		int coco_label = (*it)["id"].asInt();
		coco80label.insert(std::make_pair(coco_label, c++));
	}
	fout.close();

	std::map<int/*id*/, ImageInfo> data;
	Json::Value images = root["images"];
	for (auto it = images.begin(); it != images.end(); it++) {
		int id = (*it)["id"].asInt();
		std::string file = (*it)["file_name"].asString();
		data.insert(std::make_pair(id, ImageInfo(file)));
	}
	
	Json::Value anno = root["annotations"];
	for (auto it = anno.begin(); it != anno.end(); it++) {
		BboxInfo bbox;
		bbox.x = (*it)["bbox"][0].asFloat();
		bbox.y = (*it)["bbox"][1].asFloat();
		bbox.w = (*it)["bbox"][2].asFloat();
		bbox.h = (*it)["bbox"][3].asFloat();
		bbox.label = (*it)["category_id"].asInt();
		int id = (*it)["image_id"].asInt();
		data.find(id)->second.bbox.push_back(bbox);
	}

    /*
    Do Something...
    */
	return 0;
}
profile
Researcher & Developer @ NAVER Corp | Designer @ HONGIK Univ.

0개의 댓글