매우 쉽다. 아래의 명령으로 github에서 repository를 받은 후에 파이썬 스크립트를 실행하기만 하면 된다.
git clone https://github.com/open-source-parsers/jsoncpp
python amalgamate.py
dist 폴더 밑에 헤더파일과 소스파일(cpp) 이 생성되는데, 프로젝트에 포함만 시키면 된다.
header only가 아닌게 아쉽지만 (cpp 파일 하나가 5천줄 정도 되니..)
아래는 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개의 멤버가 있는데 이중 annotations 와 images 만 보면 된다.
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;
}