替换VOC数据集中图片路径

简介: 在一次做项目的时候,团队分配任务做数据集,用labelimage来打标,汇总数据时发现xml中的图片路径各不相同,于是就写了这个工具来修改xml中的图片路径。

前言

在一次做项目的时候,团队分配任务做数据集,用labelimage来打标,汇总数据时发现xml中的图片路径各不相同,于是就写了这个工具来修改xml中的图片路径。

原理

先打开一个xml文件,观察一下它的结构

<annotation>
    <folder>zhua_qu</folder>
    <filename>2021_03_16_16_42_11_296.jpg</filename>
    <path>D:\xyolo\images\train\2021_03_16_16_42_11_296.jpg</path>
    <source>
        <database>Unknown</database>
    </source>
    <size>
        <width>640</width>
        <height>480</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>zhua_qu</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>389</xmin>
            <ymin>225</ymin>
            <xmax>522</xmax>
            <ymax>359</ymax>
        </bndbox>
    </object>
</annotation>

可以发现图片文件名在\<filename\>和\</filename\>标签之间,图片路径在\<path\>和\</path\>标签之间,理论上来说我们只要将\<path\>和\</path\>之间的图片路径替换成我们想要的就可以了

这个路径我们怎么生成,将要替换成的路径加上文件名就可以了,在这里我们不用考虑c++烦人的中文乱码问题,就算我们要替换成中文路径,但是我们不考虑读取这些路径,直接替换xml中的图片路径即可。

到这里,替换的问题已经有思路了,然后我们再考虑批处理的问题,这里我用了<io.h>这个库来对指定的目录进行遍历,如果遇到目录就用递归的方法继续遍历,对检测到的文件进行过滤,留下xml文件的路径,压入一个vector容器中,后面pop出来调用替换的函数或者类来处理。

代码

main.cpp

#include <iostream>
#include <vector>
#include <string>
#include "replace.h"
#include "getfiles.h"


void help()
{
    // 显示帮助
    std::cout << std::endl;
    std::cout << "This tool replaces the file path of the image in the XML file in the dataset" << std::endl << std::endl;
    std::cout << "Usage: " << std::endl;
    std::cout << "\tmain.exe [DataSet Path] [Replace Path]" << std::endl << std::endl;
    std::cout << "For example: " << std::endl;
    std::cout << "\t.\\main.exe C:\\Users\\17740\\Desktop\\DataSet\\ D:\\xyolo\\images\\train\\" 
    << std::endl << std::endl;
}

int main(int argc, char **argv)
{
    // 显示帮助
    if(argc == 1 || (argc == 2 &&(strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)))
    {
        help();
        return 0;
    }

    std::cout << std::endl;

    std::string filePath = argv[1];
    std::string replace = argv[2];
    std::vector<std::string> files;

    // 规范化字符串
    if(filePath.find_last_of("\\") == filePath.length() - 1)
    {
        filePath.pop_back();
    }
    if(replace.find_last_of("\\") < replace.length() - 1)
    {
        replace.push_back('\\');
    }
    
    // 获取目录中的全部xml文件的路径
    getFiles(filePath, files);
    
    // 遍历容器中的文件路径
    int size = files.size();
    for (int i = 0; i < size; i++)
    {
        std::cout << files[i].c_str() << std::endl;
        Replace r(replace, files[i]);
        r.work();
    }
    
    return 0;
}

replace.h

#include <iostream>
#include <fstream>
#include <string>


class Replace
{
private:
    std::string str;
    std::fstream f;
    char buf[1024];

private:
    std::string replace;
    std::string xmlname;
    std::string filename;
    std::string filename_label_start;
    std::string filename_label_end;
    std::string path_label_start;
    std::string path_label__end;
    int filename_pos_start;
    int filename_pos_end;
    int filenameLength;
    int path_pos_start;
    int path_pos__end;
    int pathLength;

public:
    bool work();

public:
    Replace(const std::string& replace, const std::string& xmlname);

};

replace.cpp

#include "replace.h"


Replace::Replace(const std::string& replace, const std::string& xmlname)
{
    this->replace = replace;
    this->xmlname = xmlname;
    filename_label_start = "<filename>";
    filename_label_end = "</filename>";
    path_label_start = "<path>";
    path_label__end = "</path>";
    filename_pos_start = 0;
    filename_pos_end = 0;
    filenameLength = 0;
    path_pos_start = 0;
    path_pos__end = 0;
    pathLength = 0;
}

bool Replace::work()
{
    f.open(xmlname.c_str());

    if(!f.is_open())
    {
        return false;
    }

    while(!f.eof())
    {
        f.getline(buf, 1024);
        str.append(buf);
        str.append("\n");
    }

    str.pop_back();

    // 从filname标签中截取filename
    filename_pos_start = str.find(filename_label_start.c_str());
    filename_pos_end = str.find(filename_label_end.c_str());
    filenameLength = filename_pos_end - filename_pos_start - filename_label_start.length();
    filename = str.substr(filename_pos_start + filename_label_start.length(), filenameLength);

    // 将path标签中的文件路径替换为定义的路径加上文件名
    path_pos_start = str.find(path_label_start.c_str());
    path_pos__end = str.find(path_label__end.c_str());
    pathLength = path_pos__end - path_pos_start - path_label_start.length();
    str.replace(path_pos_start + path_label_start.length(), pathLength, (replace + filename).c_str());

    f.clear();
    f.seekp(0, std::ios::beg);
    f << str;
    f.close();

    return true;
}

getfiles.h

#include <iostream>
#include <vector>
#include <string>
#include <io.h>

void getFiles(std::string path, std::vector<std::string>& files)
{
    long long hFile = 0;
    struct _finddata_t fileinfo;
    std::string p;

    if((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) !=  -1)
    {
        do
        {
            if((fileinfo.attrib &  _A_SUBDIR))
            {
                if(strcmp(fileinfo.name,".") != 0  &&  strcmp(fileinfo.name,"..") != 0)
                {
                    getFiles(p.assign(path).append("\\").append(fileinfo.name), files);
                }
            }
            else
            {
                std::string s = fileinfo.name;
                size_t lastPos = s.find_last_of(".");
                if(strcmp(s.substr(lastPos + 1).c_str(), "xml") == 0)
                {
                    files.push_back(p.assign(path).append("\\").append(fileinfo.name));
                    //files.push_back(p.assign(path).append(fileinfo.name));
                }
            }
        }while(_findnext(hFile, &fileinfo)  == 0);
        _findclose(hFile);
    }
}

使用方法

VocFilePathRepalce.exe [DataSet Path] [Replace Path]

VocFilePathRepalce.exe [数据集所在的文件路径] [替换xml中的图片路径]

例子:

.\VocFilePathRepalce.exe F:\DataSet\ D:\xyolo\images\train\

改进思路

  • 使用了<io.h>这个库,这个库只在windows能稳定使用,在linux上用不了,要针对linux对获取xml文件的函数进行改进
  • 单线程处理xml文件,当数据集一大,就替换的很慢,所以考虑引入多线程并行处理,根据运行的机器的cpu线程数来动态分配线程,对保存xml文件路径的vector容器进行划分,分配给每个线程处理,加快替换速度。
目录
相关文章
|
4月前
|
数据处理 开发工具 git
coco2017数据集转换为yolo格式(记录过程)
最近做一个yolov5的落地应用项目,用的anylabeling打标,需要将coco2017的数据集转为yolo格式,故写下记录过程!
|
存储 算法 索引
RLE格式分割标注文件表示
RLE格式分割标注文件表示
967 0
|
XML JSON 数据中心
目标检测VOC数据集标注XML文件转EasyDL数据集标注Json格式
目标检测VOC数据集标注XML文件转EasyDL数据集标注Json格式
目标检测VOC数据集标注XML文件转EasyDL数据集标注Json格式
|
3月前
|
编解码 计算机视觉 异构计算
【CV大模型SAM(Segment-Anything)】如何一键分割图片中所有对象?并对不同分割对象进行保存?
【CV大模型SAM(Segment-Anything)】如何一键分割图片中所有对象?并对不同分割对象进行保存?
|
23天前
|
XML 数据格式 Python
将xml标签转换为txt(voc格式转换为yolo方便进行训练)
该文章提供了一个Python脚本,用于将VOC格式的XML标签文件转换为YOLO训练所需的TXT格式,包括修改数据集类别、输入图像与标注文件夹地址、转换过程和结果展示。
将xml标签转换为txt(voc格式转换为yolo方便进行训练)
|
1月前
|
机器学习/深度学习
加载CNN保存模型
【8月更文挑战第10天】加载CNN保存模型。
39 12
|
22天前
|
Python
从bag包中提取图片和点云数据为pcd格式点云文件
从bag包中提取图片和点云数据为pcd格式点云文件
12 0
|
23天前
|
Python
python 随机划分图片数据集以及移动标注
这篇文章提供了一个Python脚本,用于随机划分图片数据集为训练集和测试集,并将对应的标注文件移动到相应的子文件夹中,以减少训练使用的数据量。
|
4月前
|
XML 数据格式 Python
python挑出训练集里图片对应的xml文件,方便统计标签框的类别与数目_python 统计voc2007xml中某一类别框个数(1)
python挑出训练集里图片对应的xml文件,方便统计标签框的类别与数目_python 统计voc2007xml中某一类别框个数(1)
|
4月前
安装并使用labelImg标注数据集,yolo,VOC格式
安装并使用labelImg标注数据集,yolo,VOC格式
159 0