【实例分割/目标检测】coco大Json文件转labelme小Json文件

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

“第一次写博客,记录一下吧”

当时写这个脚本是为了方便评估模型(当时用的是tensorrt,c++),直接将solov2推理的结果保存成了labelme格式的Json文件{(所以这次的代码也是c++代码哈)也是为了方便标数据,labelme打开可以直接微调坐标成标签},后来想通过json文件评估模型,就把coco的大json文件转换成labelme格式的json文件,方便以后用c++推理然后评估。所以废话不多说直接上代码

一、Zbase64

使用labelme需要的图片编码

1.Zbase64.h

#pragma once

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

class ZBase64
{
public:
	/*编码
	DataByte
		[in]输入的数据长度,以字节为单位
	*/	
	string Encode(const unsigned char* Data, int DataByte);
	/*解码
	DataByte
		[in]输入的数据长度,以字节为单位
	OutByte
		[out]输出的数据长度,以字节为单位,请不要通过返回值计算
		输出数据的长度
	*/
	string Decode(const char* Data, int DataByte, int& OutByte);

	//string img_to_base64(std::string img_path);
};

2.Zbase64.cpp

#include "ZBase64.h"


string ZBase64::Encode(const unsigned char* Data, int DataByte)
{
	//编码表
	const char EncodeTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	//返回值
	string strEncode;
	unsigned char Tmp[4] = { 0 };
	int LineLength = 0;
	for (int i = 0; i < (int)(DataByte / 3); i++)
	{
		Tmp[1] = *Data++;
		Tmp[2] = *Data++;
		Tmp[3] = *Data++;
		strEncode += EncodeTable[Tmp[1] >> 2];
		strEncode += EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
		strEncode += EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
		strEncode += EncodeTable[Tmp[3] & 0x3F];
		if (LineLength += 4, LineLength == 76) { strEncode += "\r\n"; LineLength = 0; }
	}
	//对剩余数据进行编码
	int Mod = DataByte % 3;
	if (Mod == 1)
	{
		Tmp[1] = *Data++;
		strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
		strEncode += EncodeTable[((Tmp[1] & 0x03) << 4)];
		strEncode += "==";
	}
	else if (Mod == 2)
	{
		Tmp[1] = *Data++;
		Tmp[2] = *Data++;
		strEncode += EncodeTable[(Tmp[1] & 0xFC) >> 2];
		strEncode += EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
		strEncode += EncodeTable[((Tmp[2] & 0x0F) << 2)];
		strEncode += "=";
	}

	return strEncode;
}

string ZBase64::Decode(const char* Data, int DataByte, int& OutByte)
{
	//解码表
	const char DecodeTable[] =
	{
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		62, // '+'
		0, 0, 0,
		63, // '/'
		52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'
		0, 0, 0, 0, 0, 0, 0,
		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
		13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'
		0, 0, 0, 0, 0, 0,
		26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
		39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'
	};
	//返回值
	string strDecode;
	int nValue;
	int i = 0;
	while (i < DataByte)
	{
		if (*Data != '\r' && *Data != '\n')
		{
			nValue = DecodeTable[*Data++] << 18;
			nValue += DecodeTable[*Data++] << 12;
			strDecode += (nValue & 0x00FF0000) >> 16;
			OutByte++;
			if (*Data != '=')
			{
				nValue += DecodeTable[*Data++] << 6;
				strDecode += (nValue & 0x0000FF00) >> 8;
				OutByte++;
				if (*Data != '=')
				{
					nValue += DecodeTable[*Data++];
					strDecode += nValue & 0x000000FF;
					OutByte++;
				}
			}
			i += 4;
		}
		else// 回车换行,跳过
		{
			Data++;
			i++;
		}
	}
	return strDecode;
}

二、主函数

注意!!!

1.coco_class.txt对应的内容如下

0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 11
11 13
12 14
13 15
14 16
15 17
16 18
17 19
18 20
19 21
20 22
21 23
22 24
23 25
24 27
25 28
26 31
27 32
28 33
29 34
30 35
31 36
32 37
33 38
34 39
35 40
36 41
37 42
38 43
39 44
40 46
41 47
42 48
43 49
44 50
45 51
46 52
47 53
48 54
49 55
50 56
51 57
52 58
53 59
54 60
55 61
56 62
57 63
58 64
59 65
60 67
61 70
62 72
63 73
64 74
65 75
66 76
67 77
68 78
69 79
70 80
71 81
72 82
73 84
74 85
75 86
76 87
77 88
78 89
79 90

2.主函数main.cpp

注意!!!
CocoJson2LabelmeJson()中需要更改的路径有

json_val2017 // 要保存的json文件目录
img_val2017 // coco图片目录
txt_path // coco标签对应字典

main()中需要更改的路径有

image_path //coco图片路径
json_path //coco大json文件路径

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <math.h>
#include <iostream>
#include <windows.h>
#include <stdio.h>
#include <algorithm>
#include <map>
#include <numeric>

#include <json/json.h>
#include "Zbase64.h"
#include <dirent.h>
#include <istream>
#include <fstream>
//#include "torch/torch.h"
//#include <torch/script.h>
#include "ZBase64.h"

using namespace std;


int read_files_in_dir(const char *p_dir_name, std::vector<std::string> &file_names) 
{
	DIR *p_dir = opendir(p_dir_name);
	if (p_dir == nullptr) {
		return -1;
	}
	struct dirent* p_file = nullptr;
	while ((p_file = readdir(p_dir)) != nullptr) {
		if (strcmp(p_file->d_name, ".") != 0 &&
			strcmp(p_file->d_name, "..") != 0) {
			std::string cur_file_name(p_file->d_name);
			file_names.push_back(cur_file_name);
		}
	}

	closedir(p_dir);
	return 0;
}


void CocoJson2LabelmeJson(const std::string & file, int & w, int & h, std::vector<std::string> file_names)
{
	std::string txt_path = "D:/vspy_obj/evaluation/evaluate/coco_class.txt";     // coco的id转换txt文件
	std::ifstream infile;
	infile.open(txt_path, ios::in);

	std::map<std::string, std::string> key_info;
	if (infile.is_open())
	{
		std::string line;
		while (getline(infile, line))
		{
			key_info.insert(pair<std::string, std::string>(line.substr(line.rfind(" ") + 1, line.size()), line.substr(0, line.find_first_of(" "))));
		}
		infile.close();
	}
	else
	{
		std::cerr << "Error loading the class names!\n";
	}


	Json::CharReaderBuilder reader;
	Json::Value root, annotations, points;

	std::ifstream ifs;
	std::cout << "loading  " << file << "  file ..." << std::endl;
	ifs.open(file, ios::binary);

	if (!ifs.is_open())
	{
		std::cout << "Error opening json file\n";
		return;
	}

	std::string json_val2017 = "D:/dataset/coco2017/val2017_json/";     // 要保存的文件目录
	std::string img_val2017 = "D:/dataset/coco2017/val2017/";           // coco图片目录

	JSONCPP_STRING errs;
	bool res = Json::parseFromStream(reader, ifs, &root, &errs);

	if (!res || !errs.empty())
	{
		std::cout << "parseJson error! " << errs << std::endl;
		return;
	}

	auto names = root.getMemberNames();

	annotations = root["annotations"];

	for (int j = 0; j < names.size(); j++)
	{
		std::cout << names[j] << std::endl;
	}

	for (size_t i = 0; i < file_names.size(); i++)
	{
		std::cout << "处理第 "<< i << " 张图片" << std::endl;
		int index = 0;
		int num_obj = 0;
		for (int j = 0; j < annotations.size(); j++)
		{
			int length = annotations[j]["image_id"].asString().size();
			std::string zero = "0";
			std::string zeros = "";
			for (int k = 0; k < 12 - length; k++)
			{
				zeros += zero;
			}
			if (file_names[i] == zeros + annotations[j]["image_id"].asString() + ".jpg")
			{
				bool iscrowd = false;

				std::string json_path = json_val2017 + zeros + annotations[j]["image_id"].asString() + ".json";
				std::string image_path = img_val2017 + file_names[i];
				cv::Mat img = cv::imread(image_path);

				///<
				if (index == 0)
					if (!write_base642json(img, json_path))
						std::cout << "convert jpg2base64 successfull !" << std::endl;

				///<
				Json::CharReaderBuilder s_reader;
				Json::Value s_root, s_shape, s_shape_rect, points;
				std::ifstream s_ifs;
				s_ifs.open(json_path, ios::binary);

				if (!s_ifs.is_open())
				{
					std::cout << "Error opening json file\n";
					return;
				}

				JSONCPP_STRING s_errs;
				bool res = Json::parseFromStream(s_reader, s_ifs, &s_root, &s_errs);

				if (!res || !s_errs.empty())
				{
					std::cout << "parseJson error! " << s_errs << std::endl;
					return;
				}

				Json::StreamWriterBuilder writerBuilder;

				if (index == 0)
				{
					s_root["imageHeight"] = Json::Value(img.rows);
					s_root["imageWidth"] = Json::Value(img.cols);
					s_root["imagePath"] = Json::Value(image_path);

				}

				if (annotations[j]["iscrowd"] == 0)
				{
					int s_index = 0;
					int anno_num = annotations[j]["segmentation"][0].size();
					for (int m = 0; m < anno_num; m++)
					{
						Json::Value point;
						point[m % 2] = annotations[j]["segmentation"][0][m];
						point[m % 2 + 1] = annotations[j]["segmentation"][0][m + 1];
						points[s_index] = point;
						m++;
						s_index++;
					}
					s_shape["label"] = key_info[annotations[j]["category_id"].asString()];
					s_shape["points"] = points;
					s_shape["group_id"] = Json::nullValue;
					s_shape["shape_type"] = "polygon";
				}
				else if (annotations[j]["iscrowd"] == 1)
				{
					iscrowd = true;

					int mask_w = annotations[j]["segmentation"]["size"][1].asInt();
					int mask_h = annotations[j]["segmentation"]["size"][0].asInt();
					cv::Mat mask = cv::Mat::zeros(cv::Size(mask_w, mask_h), CV_8UC1);

					int sum_count = 0;
					int anno_num = annotations[j]["segmentation"]["counts"].size();
					for (int m = 0; m < anno_num - 1; m++)
					{
						int point_x;
						int point_y;

						sum_count += annotations[j]["segmentation"]["counts"][m].asInt();
						point_x = sum_count / mask_h;
						point_y = sum_count % mask_h;
						sum_count += annotations[j]["segmentation"]["counts"][m + 1].asInt();
						int index_mask = annotations[j]["segmentation"]["counts"][m + 1].asInt();
						for (int n = 0; n < index_mask; n++)
						{
							int ratio = (point_y + n) / mask_h;
							int yu = (point_y + n) % mask_h;
							if (point_y + n >= mask_h)
							{
								mask.at<uchar>(yu, point_y + ratio) = 255;  //
							}
							else
							{
								mask.at<uchar>(point_y + n, point_x) = 255;
							}
							if (n % 256 == 0)
							{
								//cv::imshow("mask", mask);
								//cv::waitKey(1);
							}
						}
						m++;
					}
					std::vector<std::vector<cv::Point>> contours;
					cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
					//cv::imshow("mask", mask);
					//cv::waitKey();
					if (contours.size())
					{
						for (int k = 0; k < contours.size(); k++)
						{
							Json::Value points_s;

							int s_index = 0;
							if (contours[k].size() > 10)
							{
								for (size_t l = 0; l < contours[k].size(); l++)
								{
									Json::Value point;

									point[0] = contours[k][l].x;
									point[1] = contours[k][l].y;
									points_s[s_index] = point;
									s_index++;
								}
								s_shape["group_id"] = 0;
								s_shape["points"] = points_s;
								s_shape["shape_type"] = "polygon";
								s_shape["label"] = key_info[annotations[j]["category_id"].asString()];
								s_root["shapes"][num_obj] = s_shape;
								num_obj++;
							}

						}
					}

				}

				s_shape_rect["label"] = key_info[annotations[j]["category_id"].asString()];
				s_shape_rect["points"][0][0] = annotations[j]["bbox"][0];
				s_shape_rect["points"][0][1] = annotations[j]["bbox"][1];
				s_shape_rect["points"][1][0] = Json::Value(annotations[j]["bbox"][0].asFloat() + annotations[j]["bbox"][2].asFloat());
				s_shape_rect["points"][1][1] = Json::Value(annotations[j]["bbox"][1].asFloat() + annotations[j]["bbox"][3].asFloat());

				s_shape_rect["shape_type"] = "rectangle";

				if (!iscrowd)
				{
					s_root["shapes"][num_obj] = s_shape;
					s_root["shapes"][num_obj + 1] = s_shape_rect;
				}
				else
				{
					s_root["shapes"][num_obj] = s_shape_rect;
				}

				std::ofstream os;
				os.open(json_path, std::ios::out);
				if (!os.is_open())
				{
					std::cout << "Error opening json file\n";
					return;
				}
				std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
				jsonWriter->write(s_root, &os);

				os.close();

				//if (index == 0)
				//	std::cout << "" << i << " --> " << file_names[i] << std::endl;
				//std::cout << annotations[j]["category_id"] << "  ";
				index++;
				if (!iscrowd)
				{
					num_obj += 2;
				}
				else
				{
					num_obj += 1;
				}
			}
		}
	}
}

int main()
{
	///< 实例分割  coco大json文件转为以图片为单位的小单位文件
	
	int image_w;
	int image_h;
	std::string image_path = "D:/dataset/coco2017/val2017/";
	std::string json_path = "D:/dataset/coco2017/annotations/instances_val2017.json";
	
	std::vector<std::string> file_names;
	//cv::glob(image_path, file_names);
	if (read_files_in_dir(image_path.c_str(), file_names) < 0)
	{
		std::cerr << "read failed" << std::endl;
		return -1;
	}
	
	CocoJson2LabelmeJson(json_path, image_w, image_h, file_names);
	std::cout << "转换完成" << std::endl;
	
	return 0;
}

三、效果展示

1.将从coco大json文件导出的小json文件复制到coco2017图片目录中

将图片复制进val2017

2.激活conda-labelme环境,输入labelme

在这里插入图片描述

3.进入labelme,选择opendir选择相应文件夹

在这里插入图片描述

4.既可以查看到转换完的labelme小标签

在这里插入图片描述
在这里插入图片描述

总结

配置release跑的会快一些,处理掩码部分可能有一些误差(可修改缩小),代码写的不简洁请谅解,有问题的话可以在下面评论,我会相应修改。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值