一个简单的YOLO格式图片标注工具 Qt界面实现

自己无聊的时候做的一个简单的YOLO标注Qt界面,我也不知道为啥要做这个哈哈哈,没有labelimg好用还可能有bug。还是把代码贴出来吧,反正总共也就三四百行的代码规模还指望能做多少功能呢。
该代码实现功能是YOLO格式图片标注,打开一个存放图片的文件夹,框选矩形框后标注得到的txt文件存放在图片所在文件夹内。

dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = nullptr);
    ~Dialog();

    static int label;

private slots:
    void on_pushButton_yes_clicked();

    void on_pushButton_no_clicked();

private:
    Ui::Dialog *ui;
};

#endif // DIALOG_H

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPaintEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QVector>
#include <QPoint>
#include <QAction>
#include <QColor>
#include <QMenu>
#include <QFileDialog>
#include <QTextStream>

#include "dialog.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    QVector<QRect> rects;//矩形集合
    static QVector<int> labels;//矩形集合
    QPoint pos;

protected:
    void paintEvent(QPaintEvent *);//重写窗体重绘事件
    void mousePressEvent(QMouseEvent *);//重写鼠标按下事件
    void mouseReleaseEvent(QMouseEvent *);//重写鼠标释放事件
    void mouseMoveEvent(QMouseEvent *);//重写鼠标移动事件
    void keyPressEvent(QKeyEvent *e); //重写按键事件

private:
    Ui::MainWindow *ui;
    int ispress = 0; //左键按下标志
    int isopen = 0; //打开图片标志
    QPixmap pixmap;
    Dialog *dialog;
    QString picsPath;
    QStringList imagesList;
    float width_ratio = 1.0f;
    float height_ratio = 1.0f;
    int num = 0;

private slots:
    void OpenPic(); //打开图片
    void SaveTxt(); //保存图片
    void NextPic(); //下张图片
    void ShowPic(QString picPath); //显示图片
};

#endif // MAINWINDOW_H

dialog.cpp

#include "dialog.h"
#include "ui_dialog.h"
#include "mainwindow.h"
#include <QDebug>
#include <QMessageBox>
#include <QTimer>

int Dialog::label = -1;

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    setAttribute(Qt::WA_QuitOnClose, false);
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::on_pushButton_yes_clicked()
{
    if (ui->lineEdit->text().isEmpty())
    {
        QMessageBox *box = new QMessageBox(QMessageBox::Warning, "warning", "Please input uint number!", QMessageBox::Ok);
        box->exec();
        return;
    }

    label = ui->lineEdit->text().toUInt();
    MainWindow::labels.push_back(label);

    this->close();
    qDebug()<<label;
}

void Dialog::on_pushButton_no_clicked()
{
    this->close();
}

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <iostream>

QVector<int> MainWindow::labels ={};

#define OFFSET 45

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ispress = false; //初始鼠标左键未按下
    this->centralWidget()->setMouseTracking(true);//开启鼠标实时追踪,监听鼠标移动事件,默认只有按下时才监听
    this->setMouseTracking(true);

    QAction *openAction = new QAction;
    openAction->setShortcut(QKeySequence(tr("Ctrl+O")));
    ui->toolBar->addAction(openAction);
    connect(openAction, SIGNAL(triggered()), this, SLOT(OpenPic()));

    QAction *saveAction = new QAction;
    saveAction->setShortcut(QKeySequence(tr("Ctrl+S")));
    ui->toolBar->addAction(saveAction);
    connect(saveAction, SIGNAL(triggered()), this, SLOT(SaveTxt()));

    QAction *nextAction = new QAction;
    nextAction->setShortcut(QKeySequence(tr("Right")));
    ui->toolBar->addAction(nextAction);
    connect(nextAction, SIGNAL(triggered()), this, SLOT(NextPic()));

    dialog = new Dialog;
    ui->statusbar->showMessage("Ctrl+O打开图像文件夹 鼠标左键拖动矩形标注框 右箭头按键换下一张图片");
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::paintEvent(QPaintEvent *)
{
    if(isopen == 0)//不是打开图片的,每一次新建一个空白的画布
    {
        pixmap = QPixmap(size());
        pixmap.fill(Qt::white);
    }
    QPixmap pix = pixmap.scaled(this->width(),this->height(),Qt::KeepAspectRatio);
    QPainter p(&pix);

    QPen pen;
    pen.setColor(QColor(0,0,255));
    pen.setWidth(2);
    p.setPen(pen);
    for(int i = 0; i <rects.size(); ++i)
    {
       p.drawRect(rects.at(i));
    }

    pen.setColor(QColor(255,0,0));
    p.setPen(pen);
    p.drawLine(0, pos.y(), width(), pos.y());
    p.drawLine(pos.x(), OFFSET, pos.x(), height());
    p.end();
    p.begin(this);//将当前窗体作为画布
    p.drawPixmap(0, 0, pix);//将pixmap画到窗体
}

void MainWindow::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton)//当鼠标左键按下
    {
        ispress = true;//左键按下标志
        QRect rect;//鼠标按下,矩形开始
        rects.append(rect);//将新矩形添加到矩形集合
        QRect& lastRect = rects.last();//拿到新矩形
        lastRect.setTopLeft(e->pos());//记录鼠标的坐标(新矩形的左上角坐标)
    }
}

void MainWindow::mouseMoveEvent(QMouseEvent *e)
{
    if(ispress)
    {
        QRect& lastRect = rects.last();//拿到最后添加的矩形
        lastRect.setBottomRight(e->pos());//更新矩形的右下角坐标
    }

    pos = e->pos();
    update();//触发窗体重绘
}

void MainWindow::mouseReleaseEvent(QMouseEvent *e)
{
    if(ispress)
    {
        QRect& lastRect = rects.last();//拿到新矩形
        lastRect.setBottomRight(e->pos());//不是拖拽时,更新矩形的右下角坐标)
        ispress = false;
    }

    qDebug()<<rects.last();
    dialog->show();
}

void MainWindow::SaveTxt()
{
    QString picPath = picsPath + "/" + imagesList[num];
    qDebug() << picPath;

    QString fileName = picPath.replace(".bmp", ".txt").replace(".jpg", ".txt").replace(".png", ".txt");
    QFile file(fileName);
    file.open(QIODevice::WriteOnly);
    QTextStream stream(&file);

    if (fileName.length() > 0)
    {
        qDebug()<<labels << rects;

        for(int i=0;i<labels.size();++i)
        {
            float x = 1.0f * rects[i].x() / this->width();
            float y = 1.0f *(rects[i].y() - OFFSET) / this->height();
            float width = abs(1.0f * rects[i].width() / this->width());
            float height = abs(1.0f * rects[i].height() / this->height());
            stream << labels[i] << " " << x << " " << y << " " << width << " " << height <<"\n";
        }
    }

    file.close();
}

QStringList getImageList(QString path)
{
    QStringList imagesList;
    QDir dir(path);
    if (!dir.exists())
    {
        qDebug() << "path is non-existent!";
        return imagesList;
    }
    dir.setFilter(QDir::Files | QDir::NoSymLinks);
    QStringList filters;
    filters << "*.bmp" << "*.jpg" << "*png";
    dir.setNameFilters(filters);
    imagesList = dir.entryList();
    return imagesList;
}

void MainWindow::ShowPic(QString picsPath)
{
    QString picPath = picsPath + "/" + imagesList[num];
    ui->statusbar->showMessage(picPath+"  "+QString::number(num+1)+"/"+QString::number(imagesList.size()));

    QPixmap pix;
    pix.load(picPath);//加载图片
    width_ratio = (float)pix.width()/this->width();
    height_ratio = (float)pix.height()/this->height();

    pix = pix.scaled(this->width(),this->height(),Qt::KeepAspectRatio);
    QPainter p(&pixmap);
    p.drawPixmap(0, OFFSET, pix);//添加工具栏的空间
    isopen = 1;//设置文件打开标志
    update();//触发窗体重绘,将图片画到窗体
}

void MainWindow::OpenPic()
{
    picsPath = QFileDialog::getExistingDirectory(this,tr("打开"),"");
    if(picsPath.isEmpty())
        return;

    imagesList = getImageList(picsPath);
    qDebug()<<imagesList;

    ShowPic(picsPath);
}

void MainWindow::NextPic()
{
    if(num+1>=imagesList.size())
        return;

    SaveTxt();
    rects.clear();
    labels.clear();
    pixmap = QPixmap(size());//新建pixmap
    pixmap.fill(Qt::white);//背景色填充为白色
    num++;

    ShowPic(picsPath);
}

void MainWindow::keyPressEvent(QKeyEvent *e) //按键事件
{
    if (e->key() == Qt::Key_Z && e->modifiers() == Qt::ControlModifier) //Ctrl+Z撤销
    {
        if(rects.size()>0)
        {
            rects.pop_back();
            update();
        }
    }
    else if (e->key() == Qt::Key_S && e->modifiers() == Qt::ControlModifier) //Ctrl+S保存
    {
        SaveTxt();
    }
}

最终效果如下:
之前放的动图不知道为啥违规了,NND再放一张图片。
在这里插入图片描述
源代码见:Qt开发的labelimg-yolo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

给算法爸爸上香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值