针对sun数据集遍历所有xml解析包含指定类的坐标写入到txt文件并保存相关图片

title: SUN数据集中提取包含飞机类别图片并计算坐标写入到txt文件中
tags: C++,boost,libxml2,数据处理,搬砖日常

针对sun数据集遍历所有xml解析包含指定类的坐标写入到txt文件并保存相关图片

数据集官网:SUN database

数据集网盘下载查找:聚数力

参考博客:C++ boost库 遍历读取文件夹文件

参考博客:C++ 简单读写文本文件、统计文件的行数、读取文件数据到数组

参考博客:C++复制文件(一)

参考博客:XML 解析中,如何排除控制字符

参考博客:sed处理替换字符中的特殊字符sed替换所有目录下特定文件

1.下载完的数据集标注文件是子目录下对应类别中包含xml文件的结构,需要递归访问到所有xml并把路径保存到vector中,对应方法如下:

namespace fs = boost::filesystem;
//定义函数recursion_xml,递归遍历指定目录下包含xml的全部文件夹
void recursion_xml(fs::path src_path,vector<string> &saveXmlsPath)
{
    fs::directory_iterator end;             //迭代器的终点
    int i = 0;
    for (fs::directory_iterator dir(src_path); dir!=end; dir++)
    {
       
        string xmlFilePath = dir->path().string();
        if (dir->path().extension() == ".xml") {
            cout << ".xml文件全路径:" << xmlFilePath << endl;
            saveXmlsPath.push_back(xmlFilePath);
        }
        if (fs::is_directory(*dir)) 
        recursion_xml(*dir,saveXmlsPath); //检查路径是否与目录对应(是文件夹则递归)
    }
}

2.在解析xml文件,发现特殊字符不能解析报PCDATA invalid Char value 26 in Entity错误,查了很多博客给出了特殊字符表,和简单的正则表达式,并没有给出具体解决办法。最初想到的是重新写入到xml文件,问了leader有什么结合正则对文本的处理方法,给出建议是使用sed命令,查找了相关博客写了一个简单脚本如下:

#!/bin/bash
oldstr="potters wheel" #里面包含特殊字符不能复制到命令行,但是可以在文本中复制
newstr="potters_wheel"
sed -i "s/$oldstr/$newstr/g" `find . -name *.xml` #注意find命令符号为左上角ESC下面的键

3.SUN中的xml文件是没有经过格式化,所以获取到标签中的内容会存在多个换行(首部换行,尾部两个换行),针对这个问题查找libxml2中有没有预先格式化方法未果,换种思路对内容处理写了简单的方法如下:

//去掉前一个后两个换行符
void remove(char *str){
    int len = strlen(str);
    for(int i=0 ; i<len-2 ; i++){
        str[i] = str[i+1];
    }
    str[len-2] = 0;
}

4.xml解析使用的是libxml2库中的方法,根据xml文件格式进行解析,因为SUN中标注的是物体的轮廓,需要计算最小x,y,最大x,y作为两个坐标点,具体代码如下:

bool parseXmlForSUNDataSet(string xmlPath,string fileName,string saveTxtsPath) {
	xmlDocPtr doc = NULL;
	xmlNodePtr root_node = NULL;
	xmlNodePtr pstXmlNode = NULL;  //object
    xmlNodePtr pstXmlSubNode = NULL; // name || polygon
    xmlNodePtr pstXmlPtNode = NULL; // username || pt
    xmlNodePtr tempNode = NULL; // x || y
    bool isContainedPlane = false;
    const char* xmlFilePath = xmlPath.c_str(); //string转const char*

	doc = xmlReadFile(xmlFilePath, NULL, XML_PARSE_NOBLANKS);
	if (!doc) {
		fprintf(stderr, "配置文件读取错误\n");
		return isContainedPlane;
	}

	root_node = xmlDocGetRootElement(doc);
    if (!root_node) {
        fprintf(stderr, "配置文件读取根节点错误\n");
		return isContainedPlane;
    }
    pstXmlNode = root_node->xmlChildrenNode;
    while (NULL != pstXmlNode) {
        if ((!xmlStrcmp(pstXmlNode->name, (const xmlChar *)"object"))) {
            pstXmlSubNode = pstXmlNode->xmlChildrenNode;
            while (NULL != pstXmlSubNode) {
                if ((!xmlStrcmp(pstXmlSubNode->name, (const xmlChar *)"name"))) {
                    char *temp = (char *)xmlNodeGetContent(pstXmlSubNode);
                    remove(temp);
                    if(!strcmp(temp,(char *)"airplane") || !strcmp(temp,(char *)"airplane occluded") || !strcmp(temp,(char *)"airplane crop") || !strcmp(temp,(char *)"airplane part")){
                        printf("find airplane || airplane occluded || airplane crop || airplane part\n");
                    }else{
                        break;
                    }
                    
                }
                if ((!xmlStrcmp(pstXmlSubNode->name, (const xmlChar *)"polygon"))) {
                    //cout<<"==polygon=="<<endl;
                    pstXmlPtNode = pstXmlSubNode->xmlChildrenNode;
                    int min_x,min_y; //以最小的x,y作为左上坐标
                    int max_x,max_y; //以最大的x,y作为右下坐标
                    bool isFirst = true; //是否第一次读坐标
                    while(NULL != pstXmlPtNode){
                        if(!xmlStrcmp(pstXmlPtNode->name, (const xmlChar *)"pt")){
                            tempNode = pstXmlPtNode->xmlChildrenNode;
                            char *x = (char *)xmlNodeGetContent(tempNode);
                            remove(x);
                            //printf("====x:%s===\n",x);
                            int temp_x = atoi(x); //获取当前Pt节点中x的值转int
                            tempNode = tempNode->next;
                            char *y = (char *)xmlNodeGetContent(tempNode);
                            remove(y);
                            int temp_y = atoi(y); //获取当前Pt节点中y的值转int

                            if(isFirst){
                                min_x = temp_x;
                                min_y = temp_y;
                                max_x = temp_x;
                                max_y = temp_y;
                                isFirst = false;
                            }else{
                                //判断更新坐标
                                if(temp_x < min_x){
                                    min_x = temp_x;
                                }
                                if(temp_x > max_x){
                                    max_x = temp_x;
                                }
                                if(temp_y < min_y){
                                    min_y = temp_y;
                                }
                                if(temp_y > max_y){
                                    max_y = temp_y;
                                }
                            }
                        }
                        pstXmlPtNode = pstXmlPtNode->next;
                    }
                    //如果坐标点全不为0,写入到txt文本中
                    if(min_x != 0 && min_y != 0 && max_x != 0 && max_y != 0){
                        string coordinate = ""+ to_string(min_x) + " " + to_string(min_y) + " " + to_string(max_x) + " " + to_string(max_y);
                        string saveTxt = saveTxtsPath + fileName +".txt";
                        isContainedPlane = true; //说明当前解析的xml文件里面有飞机
                        //写文件操作
                        ofstream in;
                        in.open(saveTxt,ios::app); //ios::app打开文件移动到文件尾
                        in << coordinate <<"\n";
                        in.close();
                    }
                    
                }
                pstXmlSubNode = pstXmlSubNode->next;
            }
        }
        pstXmlNode = pstXmlNode->next;
    }
	
	if (doc) {
		xmlFreeDoc(doc);
	}
    return isContainedPlane;

}

5.解析完xml文件需要把筛选出的对应图片保存到指定目录,具体代码如下:

//定义函数recursion_image,递归遍历指定目录下寻找对应名称的图片文件
void recursion_image(fs::path src_path,string fileName,string saveImagesPath)
{
    fs::directory_iterator end;             //迭代器的终点
    int i = 0;
    for (fs::directory_iterator dir(src_path); dir!=end; dir++)
    {
        string imageFilePath = dir->path().string();
        fs::path filePath(imageFilePath);
        string imageFileName = filePath.stem().string(); //截取单独文件名称不含后缀
        if (!(imageFileName.compare(fileName))) {
            cout << ".jpg文件全路径:" << imageFilePath << endl;
            string saveImagePath = saveImagesPath + fileName+".jpg";
            int result = CopyFile(((char *)imageFilePath.data()),(char *)(saveImagePath.data()));
            if(result == 1){
                cout<<"copy file success"<<endl;
            }else{
                cout<<"copy file fail"<<endl;
            }
        }
        if (fs::is_directory(*dir)) 
        recursion_image(*dir,fileName,saveImagesPath); //检查路径是否与目录对应(是文件夹则递归)
    }
}

//复制文件,将筛选的图片复制到指定目录下
int CopyFile(char *SourceFile, char *NewFile)
{
	std::ifstream in;
	std::ofstream out;
 
	try
	{
		in.open(SourceFile, std::ios::binary);//打开源文件
		if (in.fail())//打开源文件失败
		{
			std::cout << "Error 1: Fail to open the source file." << std::endl;
			in.close();
			out.close();
			return 0;
		}
		out.open(NewFile, std::ios::binary);//创建目标文件 
		if (out.fail())//创建文件失败
		{
			std::cout << "Error 2: Fail to create the new file." << std::endl;
			out.close();
			in.close();
			return 0;
		}
		else//复制文件
		{
			out << in.rdbuf();
			out.close();
			in.close();
			return 1;
		}
	}
	catch (std::exception e)
	{
	}
}

6.代码github地址

代码地址待提交

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值