当前位置:   article > 正文

Qt编写的标注文件校正小工具(源文件jpg,目标文件xml)_qt 标定工具

qt 标定工具

背景描述:

图像识别领域中,当需要计算机能够识别图片场景中的新目标种类时,需要进行大量的目标物图片样本的采集,标注和训练。这其中,大批量图片样本的人工标注则是最繁琐也最容易出错的一个环节,因此,原始的jpg图片样本与标注后生成的xml文件是否能一一对应则显得尤为重要;

功能概述 :

在这里插入图片描述

注:图片校正小工具主要针对jpg原始图片和标注后生成的xml文件进行数目与名称校正(也可在代码中修改对比的后缀后用于同名但后缀不同的任何两个类别的文件数目校正),其功能包含:

一、点击界面JPG按钮、XML按钮分别选择对应格式文件所在的文件夹,在“文件位置”下方Label中展示打开的文件夹位置,“初始数目”各自对应的文件数量,并建立“log”日志文件,记录操作过程,建立备份的“backup”文件夹,用于后续的删除文件备份;
二、点击开始按钮,在线程中对jpg文件夹与xml文件夹中文件进行比对和处理,显示处理进度,并保证最终两个文件夹下的内容一一对应;
三、点击记录查询按钮:在打开的新页面中展示文件的处理时间和处理过程;

实例实验:

一、原始存在差异的jpg文件夹与xml文件夹内容展示;

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

二、处理后jpg文件夹、xml文件夹,log日志文件、backup(用于备份删除过的文件,便于后续有需要时恢复)文件夹内容展示;

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

程序源码:

主窗口Widget类

一、头文件:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QStringList>
#include<QFileInfoList>
#include "checkoutfilethread.h"
#include"logrecord.h"

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
public:
    void setBackUpDir();
private slots:
    void on_JPGButton_clicked();
    void on_XMLButton_clicked();
    void on_startButton_clicked();
    void on_recordQueryButton_clicked();
private:
    Ui::Widget *ui;
    CheckOutFileThread workThread;

public:
    QString m_jpgFilePath;
    QString m_xmlFilePath;
};

#endif // WIDGET_H
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

二、源文件

#include "widget.h"
#include "ui_widget.h"
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QDateTime>
#include <QByteArray>
#include <QTextCodec>

const int CUT_SIZE = 4;

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle(tr("JPG&XML"));
    this->setWindowIcon(QIcon(":/new/images/xml.ico"));

    connect(&workThread, &CheckOutFileThread::sendProcessValueRange, this, [&](int maxlVal) {
        ui->progressBar->setRange(0, maxlVal);
    });
    connect(&workThread, &CheckOutFileThread::sendProgressSignal, this, [&](int value) {
        if(value == 0) {
            ui->dealShowLabel->setText(tr("源文件与目标文件完整对应,未作处理"));
        } else {
            ui->progressBar->setValue(value);
            ui->dealShowLabel->setText(tr("处理完毕,源文件与目标文件完整对应"));
        }
    });
    connect(&workThread, &CheckOutFileThread::sendResaultCount, this, [&](int jpgNum, int xmlNum) {
        ui->finalJpgNumLabel->setText(QString("%1").arg(jpgNum));
        ui->finalXmlNumLabel->setText(QString("%1").arg(xmlNum));
    });
    connect(&workThread, &CheckOutFileThread::sendLogFileCreat, this,
            [&](QString txt){ ui->logShowLabel->setText(txt); });
}

Widget::~Widget()
{
    if (workThread.isRunning()) {
        workThread.killTimer(0);
    }
    delete ui;
}

void Widget::setBackUpDir()
{
    QDir recoverDir("./");  // 也可设置为QCoreApplication::applicationDirPath()
    if (!recoverDir.exists("backup")) {
        recoverDir.mkdir("backup");
        ui->dealShowLabel->setText("备份文件夹建立成功!");
    }
}

void Widget::on_JPGButton_clicked()
{
    QString jpgFileDir = QFileDialog::getExistingDirectory(this, "JPG文件目录", "/");
    if (jpgFileDir.isEmpty()) {
        ui->dealShowLabel->setText(tr("文件夹路径获取失败!"));
        return;
    }
    ui->locationJpgLabel->setText(jpgFileDir);

    // 记录对文件的操作时间,在exe目录下新建文件夹,用于备份删除过的源文件,并记录操作log日志;
    setBackUpDir();
    QFile JpgXmlLog("log.txt");
    bool openSucceed = JpgXmlLog.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append);
    openSucceed ? ui->logShowLabel->setText(tr("日志文件创建成功")) :
                  ui->logShowLabel->setText(tr("日志文件创建失败"));
    QByteArray JpgFileOp = QTextCodec::codecForName("UTF-8")->fromUnicode("JPG目录冗余文件删除时间: ");
    JpgXmlLog.write(JpgFileOp);
    JpgXmlLog.write((QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") + "\n").toLatin1().data());

    // 遍历目录文件,移动所有非jpg格式图片至备份的backup目录中
    int delCount = 0;
    QDir JpgDir(jpgFileDir);
    QStringList moveOfJpg = JpgDir.entryList();
    foreach (QString delOther, moveOfJpg) {
        if (QString::compare(delOther.right(CUT_SIZE), ".jpg") != 0) { // CUT_SIZE = 4
            QString srcDirFile = jpgFileDir + "/" + delOther;
            QString backupDirFile = QString("backup") + "/" + delOther;
          // QString backupDirFile = QCoreApplication::applicationDirPath() + "/" + "backup" + "/" + delOther;
            QFile::copy(srcDirFile, backupDirFile);
            JpgXmlLog.write((delOther + "\n").toLatin1().data());
            JpgDir.remove(delOther);
            delCount++;
        }
    }
    JpgXmlLog.close();

    ui->initJpgNumLabel->setText(QString::number(JpgDir.count() - delCount));
    m_jpgFilePath = jpgFileDir;
}

void Widget::on_XMLButton_clicked()
{
    QString xmlFileDir=QFileDialog::getExistingDirectory(this,"XML文件目录","/");
    if (xmlFileDir.isEmpty()) {
        ui->dealShowLabel->setText(tr("文件夹路径获取失败!"));
        return;
    }
    ui->locationXmlLabel->setText(xmlFileDir);

    QFile JpgXmlLog("log.txt");
    bool openSucceed = JpgXmlLog.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append);
    openSucceed ? ui->logShowLabel->setText(tr("日志文件创建成功")) :
                  ui->logShowLabel->setText(tr("日志文件创建失败"));
    QByteArray XmlFileOp=QTextCodec::codecForName("UTF-8")->fromUnicode("XML目录冗余文件删除时间:");
    JpgXmlLog.write(XmlFileOp);
    JpgXmlLog.write((QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")+"\n").toLatin1().data());

    int delCount=0;
    QDir XmlDir(xmlFileDir);
    QStringList delOutOfXml=XmlDir.entryList();
    foreach (QString delOther, delOutOfXml) {
        if(QString::compare(delOther.right(CUT_SIZE), ".xml") != 0) {
            QString srcDirFile = xmlFileDir + "/" + delOther;
            QString backupDirFile = QString("backup") + "/" + delOther;
            // QString backupDirFile = QCoreApplication::applicationDirPath() + "/" + "backup" + "/" + delOther;
            QFile::copy(srcDirFile, backupDirFile);
            JpgXmlLog.write((delOther + "\n").toLatin1().data());
            XmlDir.remove(delOther);
            delCount++;
        }
    }
    JpgXmlLog.close();

    ui->initXmlNumLabel->setText(QString::number(XmlDir.count() - delCount));
    m_xmlFilePath = xmlFileDir;
}

void Widget::on_startButton_clicked()
{
    if(m_jpgFilePath.isEmpty() || m_xmlFilePath.isEmpty() || workThread.isRunning()) {
        ui->logShowLabel->setText("路径异常或正在处理,请重试...");
        return;
    }

    workThread.setParent(this);
    workThread.InitFilePath(m_jpgFilePath, m_xmlFilePath);
    workThread.start();
}

void Widget::on_recordQueryButton_clicked()
{
    LogRecord RecordShow;
    RecordShow.exec();
}
  • 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
  • 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
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149

三、界面文件
在这里插入图片描述

线程处理类

一、头文件

#ifndef CHECKOUTFILETHREAD_H
#define CHECKOUTFILETHREAD_H

#include <QThread>
#include <QFileInfoList>

class CheckOutFileThread : public QThread
{
    Q_OBJECT
public:
    CheckOutFileThread() {};
protected:
    virtual void run();
signals:
    void sendLogFileCreat(QString txt);
    void sendProgressSignal(int value);
    void sendProcessValueRange(int max);
    void sendResaultCount(int jpgNum, int xmlNum);
private:
    QFileInfoList m_jpgs;
    QFileInfoList m_xmls;
    QString m_jpgPath;
    QString m_xmlPath;
public:
    void InitFilePath(QString jpgsDir, QString xmlsDir);
};

#endif // CHECKOUTFILETHREAD_H
  • 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
  • 26
  • 27
  • 28

二、源文件

#include "checkoutfilethread.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDateTime>
#include <QCoreApplication>
#include <QTextCodec>
#include <QByteArray>

void CheckOutFileThread::run()
{
    QDir jpgDir(m_jpgPath, "*.jpg");
    QDir xmlDir(m_xmlPath, "*.xml");
    QStringList jpgFiles = jpgDir.entryList();
    QStringList xmlFiles = xmlDir.entryList();

    // 打开日志文件记录操作,在exe目录下的backup文件夹中将删除后的文件备份
    QFile JpgXmlLog("log.txt");
    bool openSucceed = JpgXmlLog.open(QIODevice::WriteOnly | QIODevice::Text |QIODevice::Append);
    openSucceed ? emit sendLogFileCreat(tr("日志文件在线程中打开成功")) :
                  emit sendLogFileCreat(tr("日志文件在线程中打开失败"));
    QByteArray threadFileOp = QTextCodec::codecForName("UTF-8")->fromUnicode("线程操作删除文件时间: ");
    JpgXmlLog.write(threadFileOp);
    JpgXmlLog.write((QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss") + "\n").toLatin1().data());

    // xml目录中若不包含同名对应的jpg文件,则备份后并将其加入删除列表中,在xml文件夹中将其删除
    int index = 0;
    QStringList deleteFiles;
    foreach(QString jpg, jpgFiles) {
        QString tempJpg = jpg;
        int jpgPosition = tempJpg.lastIndexOf(".jpg");
        QString fileName = tempJpg.replace(jpgPosition, 4, ".xml");
        if (!xmlFiles.contains(fileName)) {
            QString srcDirFile = m_jpgPath + "/" + jpg;
            QString backupDirFile = QString("backup") + "/" +jpg;
           // QString backupDirFile = QCoreApplication::applicationDirPath() + "/" + "backup"+ "/" +jpg;
            QFile::copy(srcDirFile, backupDirFile);
            deleteFiles.append(m_jpgPath + "/" + jpg );
            QString logRecord = m_jpgPath + "/" + jpg + "\n";
            JpgXmlLog.write(logRecord.toLatin1().data());
        }
    }

    // 同理,jpg目录中若不包含同名对应的xml文件,则备份该xml文件,并将其加入删除列表中
    foreach(QString xml, xmlFiles) {
        QString tempxml = xml;
        int xmlPosition = tempxml.lastIndexOf(".xml");
        QString fileName = tempxml.replace(xmlPosition, 4, ".jpg");
        if (!jpgFiles.contains(fileName)) {
            QString srcDirFile = m_xmlPath + "/" + xml;
            QString backupDirFile = QString("backup") + "/" + xml;
           // QString backupDirFile = QCoreApplication::applicationDirPath() + "/" + "backup" + "/" + xml;
            QFile::copy(srcDirFile, backupDirFile);
            deleteFiles.append(m_xmlPath + "/" + xml);
            QString logRecord = m_xmlPath + "/" + xml + "\n";
            JpgXmlLog.write(logRecord.toLatin1().data());
        }
    }
    JpgXmlLog.close();

    // 不对应文件删除
    if (deleteFiles.isEmpty()) {
        emit sendProgressSignal(0);
    } else {
        emit sendProcessValueRange(deleteFiles.count());
        foreach (QString deleteFile , deleteFiles) {
            if(jpgDir.exists(deleteFile)) {
                jpgDir.remove(deleteFile);
            }
            if(xmlDir.exists(deleteFile)) {
                xmlDir.remove(deleteFile);
            }
            emit sendProgressSignal(++index);
        }
    }

    // 处理结束后,获取jpg与xml文件夹下文件数量
    QDir jpgDirResult(m_jpgPath, "*.jpg");
    QDir xmlDirResult(m_xmlPath, "*.xml");
    emit sendResaultCount(jpgDirResult.count(), xmlDirResult.count());
}

void CheckOutFileThread::InitFilePath(QString jpgsDir, QString xmlsDir)
{
    m_jpgPath = jpgsDir;
    m_xmlPath = xmlsDir;
}
  • 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
  • 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
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87

记录查询按钮界面类

一、头文件

#ifndef LOGRECORD_H
#define LOGRECORD_H

#include <QDialog>

namespace Ui {
class LogRecord;
}

class LogRecord : public QDialog
{
    Q_OBJECT

public:
    explicit LogRecord(QWidget *parent = 0);
    ~LogRecord();

private:
    Ui::LogRecord *ui;
};

#endif // LOGRECORD_H
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

二、源文件

#include "logrecord.h"
#include "ui_logrecord.h"
#include <QFile>
#include <QTextStream>

LogRecord::LogRecord(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::LogRecord)
{
    ui->setupUi(this);
    this->setWindowTitle(tr("操作日志"));

    QFile readLog("log.txt");
    if (!readLog.open(QIODevice::ReadOnly | QIODevice::Text)) {
        ui->plainTextEdit->appendPlainText("log.txt文件打开失败!");
    }

    QTextStream readTxt(&readLog);
    ui->plainTextEdit->setPlainText(readTxt.readAll());

    readLog.close();   
}

LogRecord::~LogRecord()
{
    delete ui;
}
  • 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
  • 26
  • 27

三、界面文件
在这里插入图片描述
完整代码链接
https://download.csdn.net/download/qq_44896246/87265004

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号