Qt下使用OpenCV截取图像并在QtableWidget表格上显示

news2024/10/6 8:25:00

文章目录

  • 前言
  • 一、在QLabel上显示图片并绘制矩形框
  • 二、保存矩形框数据为CSV文件
  • 三、保存截取图像
  • 四、将截取图像填充到表格
  • 五、图形视图框架显示图像
  • 六、示例完整代码
  • 总结


前言

本文主要讲述了在Qt下使用OpenCV截取绘制的矩形框图像,并将矩形框数据保存为CSV文件,以及在QtableWidget表格上显示截取的图像,其中也使用到了Qt的图形视图框架,下面是示例的详细内容展示,以便大家学习,如有错误之处,欢迎大家批评指正。

项目效果
请添加图片描述


提示:以下是本篇文章正文内容,下面案例可供参考

一、在QLabel上显示图片并绘制矩形框

本文中使用事件过滤器(eventFilter)来处理界面上QLabel部件的绘图事件,使用QPainter在其上显示图片以及绘制矩形框,矩形框的绘制重写了一些QMouseEvent,具体实现见下文示例完整代码。在我之前的文章中还有直线、多边形等绘制的实现,可以查看文章Qt实现在QLabel上显示图片并进行线条/矩形框/多边形的绘制

//更新界面绘图
void MainWindow::updateShowPaint()
{
    //绘制图像
    QPainter painter(ui->lb_showImage);
    if(m_isImageFlag)
    {
        painter.drawPixmap(0,0,m_fitPixmap);
    }
    painter.setFont(QFont(font().family(),12));

    //红色绘制区域矩形框
    if(m_drawRectFlag)
    {
        QPen pen(Qt::red);   //默认实线画笔
        for(int i=0;i<m_rectList.size();i++)
        {
            const QRect &rect = m_rectList.at(i);
            if(i == m_selectedRectIndex)
            {
                pen.setStyle(Qt::DashLine);   //虚线画笔
                painter.setPen(pen);
            }
            else
            {
                pen.setStyle(Qt::SolidLine);   //实线画笔
                painter.setPen(pen);
            }
            painter.drawRect(rect);
        }
        if(m_isPressedFlag)
        {
            pen.setStyle(Qt::DashLine);   //虚线画笔
            painter.setPen(pen);
            painter.drawRect(m_currentRect);
        }
    }
}

二、保存矩形框数据为CSV文件

示例中使用了QList容器来存储绘制的矩形,所以要遍历容器中的矩形数据并保存为CSV文件(逗号分隔符文件),下面是读写CSV文件的实现:

//保存矩形框数据为CSV文件
bool MainWindow::saveRectListToCSV(const QList<QRect> &rectList, const QString &fileName)
{
    QFile file(fileName);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        LOGDEBUG<<fileName<<"打开失败!";
        return false;
    }

    QTextStream out(&file);
    out<<"x,y,width,height"<<endl;   //写入CSV文件的标题行
    for(const auto &rect : rectList)
    {
        out<<rect.x()<<","<<rect.y()<<","<<rect.width()<<","<<rect.height()<<endl;
    }
    file.close();
    return true;
}

//读取CSV文件数据转换为矩形框
QList<QRect> MainWindow::loadRectListFromCSV(const QString &fileName)
{
    QList<QRect> rectList;
    QFile file(fileName);
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        LOGDEBUG<<fileName<<"打开失败!";
        return rectList;
    }

    QTextStream in(&file);
    QString line = in.readLine();   //跳过标题行
    while(!in.atEnd())
    {
        line = in.readLine();
        if(!line.isEmpty())
        {
            QStringList fields = line.split(",");
            if(fields.size() == 4)
            {
                bool ok;
                int x = fields[0].toInt(&ok);
                int y = fields[1].toInt(&ok);
                int width = fields[2].toInt(&ok);
                int height = fields[3].toInt(&ok);
                if(ok)
                {
                    rectList.append(QRect(x,y,width,height));
                }
                else
                {
                    LOGDEBUG<<"矩形转换失败!";
                }
            }
            else
            {
                LOGDEBUG<<"字段数量不正确!";
            }
        }
    }
    file.close();
    return rectList;
}

三、保存截取图像

要保存截取图像,首先在程序运行目录创建了一个保存截取图像的文件夹,然后每次保存时会先删除文件夹内原先的图像文件,再建新的截取文件图像。使用OpenCV来进行截取图像的保存,所以需要将绘制的QRect转换为OpenCV的Rect:

//保存截取的图像
void MainWindow::saveCroppedImage()
{
    //创建保存截取图像文件夹
    QString imageDir = QCoreApplication::applicationDirPath() + "/saveImage/";
    QDir dir(imageDir);
    if(!dir.exists())
    {
        LOGDEBUG<<"保存截取图像文件夹不存在!";
        if(!dir.mkpath(imageDir))
        {
            LOGDEBUG<<imageDir + "文件夹创建失败!";
            return;
        }
    }

    //判断当前显示图像是否正确
    int rows = m_showMat.rows;
    int cols = m_showMat.cols;
    LOGDEBUG<<"图像高:"<<rows<<"   图像宽:"<<cols;
    if((rows == 648) && (cols == 820))   //测试图像分辨率
    {
        //获取目录下所有图像文件的列表
        QStringList filters;
        filters<<"*.jpg";
        QStringList imageFiles = dir.entryList(filters,QDir::Files);

        //遍历列表并删除文件
        for(const QString &file : imageFiles)
        {
            QString filePath = dir.filePath(file);
            QFile::remove(filePath);
        }

        //遍历矩形列表
        for(const QRect &rect : m_rectList)
        {
            //将QRect转换为OpenCV的Rect
            cv::Rect roi(rect.x()*m_scaleNum,rect.y()*m_scaleNum,rect.width()*m_scaleNum,rect.height()*m_scaleNum);

            //截取图像
            cv::Mat croppedImage = m_showMat(roi);

            //保存图像到文件
            QString saveName = QString("saveImage_%1.jpg").arg(m_rectList.indexOf(rect)+1);
            if(!saveName.isEmpty())
            {
                QString saveImage = imageDir + saveName;
                bool success = cv::imwrite(saveImage.toStdString(),croppedImage);
                if(success)
                {
                    LOGDEBUG<<saveName<<"保存成功!";
                }
                else
                {
                    LOGDEBUG<<saveName<<"保存失败!";
                }
            }
        }
        QMessageBox::information(this,"提示","截取图像保存成功!");
    }
    else
    {
        LOGDEBUG<<"保存失败,当前显示图像错误!";
        QMessageBox::information(this,"提示","截取图像保存失败!");
    }
}

四、将截取图像填充到表格

获取保存截取图像的文件夹内的图像,创建一个QLabel来显示图像并将其设置为单元格的小部件,这样就实现了表格显示图像的效果:

//将截取图像填充到表格
void ShowImage::fillTableWidgetWithImages(QTableWidget *tableWidget)
{
    //清除表格内容
    tableWidget->clear();
    tableWidget->setRowCount(0);

    //设置表头和列宽
    tableWidget->setColumnCount(2);
    tableWidget->setHorizontalHeaderLabels(QStringList() << "截取图像" << "时间");
    tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);   //设置列宽拉伸模式
    tableWidget->resizeColumnsToContents();   //列宽自适应内容

    //获取文件夹内文件
    QString imageDir = QCoreApplication::applicationDirPath() + "/saveImage/";
    QDir dir(imageDir);
    QStringList filters;
    filters<<"*.jpg";
    QStringList imageFiles = dir.entryList(filters,QDir::Files);
    for(const QString &fileName : imageFiles)
    {
        //获取最后修改时间
        QString filePath = dir.filePath(fileName);
        QFileInfo fileInfo(filePath);
        QDateTime fileTime = fileInfo.lastModified();

        //获取图像
        cv::Mat image = cv::imread(filePath.toStdString());
        if(!image.empty())
        {
            //插入新行
            int rowCount = tableWidget->rowCount();
            tableWidget->insertRow(rowCount);

            //创建一个QLabel来显示图像
            QLabel *imageLabel = new QLabel(tableWidget);
            QPixmap pixmap = cvMatToQPixmap(image);
            imageLabel->setPixmap(pixmap);
            imageLabel->setAlignment(Qt::AlignCenter);
            tableWidget->setCellWidget(rowCount,0,imageLabel);   //将QLabel设置为单元格的小部件

            //设置时间文本
            QTableWidgetItem *timeItem = new QTableWidgetItem(fileTime.toString("yyyy-MM-dd HH:mm:ss"));
            timeItem->setTextAlignment(Qt::AlignCenter);
            tableWidget->setItem(rowCount,1,timeItem);   //将时间添加到表格的下一列

            //设置图像文件路径到表格项的data角色,方便后续删除操作
            QTableWidgetItem *pathItem = new QTableWidgetItem();
            pathItem->setData(Qt::UserRole,filePath);
            tableWidget->setItem(rowCount,0,pathItem);
        }
    }
}

五、图形视图框架显示图像

双击表格某行实现跳转,可以对截取图像进行缩放查看,在这里有场景、视图和图形项的使用,关于图形视图框架的更多内容可以查看文章Qt学习:图形视图框架的使用

//双击表格跳转
void ShowImage::on_tw_image_cellDoubleClicked(int row,int column)
{
    //检查列数,确保不是点击的表头或其他非数据列
    if(column < ui->tw_image->columnCount())
    {
        //从表格项中获取图像文件路径
        QVariant imagePathVariant = ui->tw_image->item(row,0)->data(Qt::UserRole);
        QString imagePath = imagePathVariant.toString();
        LOGDEBUG<<"选中的图像文件名:"<<imagePath;

        //加载图像
        QPixmap pixmap(imagePath);
        if(!pixmap.isNull())
        {
            //清除场景中的旧内容
            scene->clear();

            //创建QGraphicsPixmapItem并添加到场景中
            QGraphicsPixmapItem *pixmapItem = scene->addPixmap(pixmap);

            //调整场景大小以匹配图像
            scene->setSceneRect(pixmapItem->boundingRect());

            //确保视图适应新内容
            ui->gw_image->fitInView(scene->sceneRect(),Qt::KeepAspectRatio);
        }
        else
        {
            //处理图像加载失败的情况
            LOGDEBUG<<"加载图像失败!";
        }

        //切换页面
        ui->sw_tableImage->setCurrentIndex(1);
    }
}

六、示例完整代码

1.mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "showimage.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    void initWidget();

    void saveCroppedImage();
    bool saveRectListToCSV(const QList<QRect> &rectList, const QString &fileName);
    QList<QRect> loadRectListFromCSV(const QString &fileName);

    void setShowImage();
    void setWidgetEnabled(bool flag);
    void setCurPoint(QPoint *getPoint);

    void updateShowPaint();

protected:
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
    void mouseReleaseEvent(QMouseEvent *event);
    bool eventFilter(QObject *watched, QEvent *event);

private slots:
    void on_pb_drawRect_clicked();
    void on_pb_resetRect_clicked();
    void on_pb_set_clicked();
    void on_pb_open_clicked();
    void on_pb_view_clicked();
    void on_pb_save_clicked();

private:
    Ui::MainWindow *ui;

    bool m_isImageFlag;     //图片是否存在
    bool m_isRegionFlag;    //是否绘图区域
    bool m_isPressedFlag;   //鼠标是否按压
    bool m_isMoveFlag;      //鼠标是否移动
    bool m_drawRectFlag;     //绘制区域标志

    double m_scaleNum;   //缩放比例
    int m_differNumX;    //x偏移值
    int m_differNumY;    //y偏移值
    int m_getNumX;       //x最大值
    int m_getNumY;       //y最大值

    cv::Mat m_showMat;        //界面显示图像
    QPixmap m_fitPixmap;      //界面绘制图像
    QPoint m_startPoint;       //矩形绘制的起点
    QRect m_currentRect;       //当前正在绘制的矩形
    int m_selectedRectIndex;   //表示选中的矩形序号
    QList<QRect> m_rectList;   //存储绘制区域的矩形

    ShowImage *m_showImage;     //截取图像列表界面

};

#endif // MAINWINDOW_H

2.mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->initWidget();
}

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

//初始化界面
void MainWindow::initWidget()
{
    //初始化
    m_isImageFlag = false;
    m_isRegionFlag = false;
    m_isPressedFlag = false;
    m_isMoveFlag = false;
    m_drawRectFlag = false;

    m_scaleNum = 2;   //根据图像与显示界面的大小变化,显示界面是410*324,测试图像大小是820*648
    m_differNumX = 20;
    m_differNumY = 20;
    m_getNumX = 410;
    m_getNumY = 324;

    m_showMat = NULL;
    m_startPoint = QPoint(0,0);
    m_currentRect = QRect();
    m_selectedRectIndex = -1;
    m_rectList.clear();

    //设置界面尺寸
    this->setFixedSize(this->width(),this->height());

    //添加事件过滤器
    ui->lb_showImage->installEventFilter(this);

    //截取图像列表界面
    m_showImage = new ShowImage();

    //设置界面显示图像
    setShowImage();
}

//保存截取的图像
void MainWindow::saveCroppedImage()
{
    //创建保存截取图像文件夹
    QString imageDir = QCoreApplication::applicationDirPath() + "/saveImage/";
    QDir dir(imageDir);
    if(!dir.exists())
    {
        LOGDEBUG<<"保存截取图像文件夹不存在!";
        if(!dir.mkpath(imageDir))
        {
            LOGDEBUG<<imageDir + "文件夹创建失败!";
            return;
        }
    }

    //判断当前显示图像是否正确
    int rows = m_showMat.rows;
    int cols = m_showMat.cols;
    LOGDEBUG<<"图像高:"<<rows<<"   图像宽:"<<cols;
    if((rows == 648) && (cols == 820))   //测试图像分辨率
    {
        //获取目录下所有图像文件的列表
        QStringList filters;
        filters<<"*.jpg";
        QStringList imageFiles = dir.entryList(filters,QDir::Files);

        //遍历列表并删除文件
        for(const QString &file : imageFiles)
        {
            QString filePath = dir.filePath(file);
            QFile::remove(filePath);
        }

        //遍历矩形列表
        for(const QRect &rect : m_rectList)
        {
            //将QRect转换为OpenCV的Rect
            cv::Rect roi(rect.x()*m_scaleNum,rect.y()*m_scaleNum,rect.width()*m_scaleNum,rect.height()*m_scaleNum);

            //截取图像
            cv::Mat croppedImage = m_showMat(roi);

            //保存图像到文件
            QString saveName = QString("saveImage_%1.jpg").arg(m_rectList.indexOf(rect)+1);
            if(!saveName.isEmpty())
            {
                QString saveImage = imageDir + saveName;
                bool success = cv::imwrite(saveImage.toStdString(),croppedImage);
                if(success)
                {
                    LOGDEBUG<<saveName<<"保存成功!";
                }
                else
                {
                    LOGDEBUG<<saveName<<"保存失败!";
                }
            }
        }
        QMessageBox::information(this,"提示","截取图像保存成功!");
    }
    else
    {
        LOGDEBUG<<"保存失败,当前显示图像错误!";
        QMessageBox::information(this,"提示","截取图像保存失败!");
    }
}

//保存矩形框数据为CSV文件
bool MainWindow::saveRectListToCSV(const QList<QRect> &rectList, const QString &fileName)
{
    QFile file(fileName);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        LOGDEBUG<<fileName<<"打开失败!";
        return false;
    }

    QTextStream out(&file);
    out<<"x,y,width,height"<<endl;   //写入CSV文件的标题行
    for(const auto &rect : rectList)
    {
        out<<rect.x()<<","<<rect.y()<<","<<rect.width()<<","<<rect.height()<<endl;
    }
    file.close();
    return true;
}

//读取CSV文件数据转换为矩形框
QList<QRect> MainWindow::loadRectListFromCSV(const QString &fileName)
{
    QList<QRect> rectList;
    QFile file(fileName);
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        LOGDEBUG<<fileName<<"打开失败!";
        return rectList;
    }

    QTextStream in(&file);
    QString line = in.readLine();   //跳过标题行
    while(!in.atEnd())
    {
        line = in.readLine();
        if(!line.isEmpty())
        {
            QStringList fields = line.split(",");
            if(fields.size() == 4)
            {
                bool ok;
                int x = fields[0].toInt(&ok);
                int y = fields[1].toInt(&ok);
                int width = fields[2].toInt(&ok);
                int height = fields[3].toInt(&ok);
                if(ok)
                {
                    rectList.append(QRect(x,y,width,height));
                }
                else
                {
                    LOGDEBUG<<"矩形转换失败!";
                }
            }
            else
            {
                LOGDEBUG<<"字段数量不正确!";
            }
        }
    }
    file.close();
    return rectList;
}

//设置界面显示图像
void MainWindow::setShowImage()
{
    //读取CSV文件数据转换为矩形框
    QString csvFile = QCoreApplication::applicationDirPath() + "/regionRect.csv";
    m_rectList.clear();
    m_rectList = loadRectListFromCSV(csvFile);

    //判断图像文件是否存在
    QImage showImage;
    QString imageName = QCoreApplication::applicationDirPath() + "/testShow.jpg";
    LOGDEBUG<<"图像文件名:"<<imageName;
    QFileInfo fileInfo(imageName);
    if(!fileInfo.isFile())
    {
        LOGDEBUG<<"图像文件不存在";
        return;
    }
    m_showMat = cv::imread(imageName.toStdString());
    showImage.load(imageName);
    QPixmap showPixmap = QPixmap::fromImage(showImage);
    if(showPixmap.isNull())
    {
        LOGDEBUG<<"界面显示图像为空";
        m_isImageFlag = false;
        return;
    }
    else
    {
        m_isImageFlag = true;
        m_fitPixmap = showPixmap.scaled(QSize(m_getNumX,m_getNumY),Qt::KeepAspectRatio,Qt::SmoothTransformation);   //按比例缩放
    }

    //更新界面
    this->update();
}

//设置界面按钮使能
void MainWindow::setWidgetEnabled(bool flag)
{
    m_selectedRectIndex = -1;
    m_drawRectFlag = !flag;
    ui->pb_drawRect->setEnabled(flag);
    ui->pb_resetRect->setEnabled(flag);
    ui->pb_set->setEnabled(flag);
    ui->pb_open->setEnabled(flag);
    ui->pb_view->setEnabled(flag);
    ui->pb_save->setEnabled(flag);
}

//设置绘图超限点
void MainWindow::setCurPoint(QPoint *getPoint)
{
    if(getPoint->x() < 0)
    {
        getPoint->setX(0);
    }
    else if(getPoint->x() > m_getNumX)
    {
        getPoint->setX(m_getNumX);
    }

    if(getPoint->y() < 0)
    {
        getPoint->setY(0);
    }
    else if(getPoint->y() > m_getNumY)
    {
        getPoint->setY(m_getNumY);
    }
}

//更新界面绘图
void MainWindow::updateShowPaint()
{
    //绘制图像
    QPainter painter(ui->lb_showImage);
    if(m_isImageFlag)
    {
        painter.drawPixmap(0,0,m_fitPixmap);
    }
    painter.setFont(QFont(font().family(),12));

    //红色绘制区域矩形框
    if(m_drawRectFlag)
    {
        QPen pen(Qt::red);   //默认实线画笔
        for(int i=0;i<m_rectList.size();i++)
        {
            const QRect &rect = m_rectList.at(i);
            if(i == m_selectedRectIndex)
            {
                pen.setStyle(Qt::DashLine);   //虚线画笔
                painter.setPen(pen);
            }
            else
            {
                pen.setStyle(Qt::SolidLine);   //实线画笔
                painter.setPen(pen);
            }
            painter.drawRect(rect);
        }
        if(m_isPressedFlag)
        {
            pen.setStyle(Qt::DashLine);   //虚线画笔
            painter.setPen(pen);
            painter.drawRect(m_currentRect);
        }
    }
}

//鼠标按压,开始绘制
void MainWindow::mousePressEvent(QMouseEvent *event)
{
    //判断是否开始绘制
    if(!m_drawRectFlag)
    {
        return;
    }

    //判断鼠标是否在绘图区域内
    int xMax = m_getNumX + m_differNumX;
    int yMax = m_getNumY + m_differNumY;
    if((event->pos().x() < 30) || (event->pos().x() > xMax) ||
       (event->pos().y() < 30) || (event->pos().y() > yMax))
    {
        m_isRegionFlag = false;
        return;
    }
    else
    {
        m_isRegionFlag = true;
    }

    //鼠标左键按压开始绘制
    if(event->button() == Qt::LeftButton)
    {
        m_isPressedFlag = true;
        m_isMoveFlag = false;

        //获取起点
        m_startPoint = QPoint(event->pos().x()-m_differNumX,event->pos().y()-m_differNumY);
        m_currentRect = QRect(m_startPoint,QSize());
        this->update();
    }
}

//鼠标移动
void MainWindow::mouseMoveEvent(QMouseEvent *event)
{
    //判断是否开始绘制
    if(!m_drawRectFlag)
    {
        return;
    }

    //判断是否在绘图区域内
    if(!m_isRegionFlag)
    {
        return;
    }

    if(m_isPressedFlag)
    {
        m_isMoveFlag = true;
        QPoint movePoint = QPoint(event->pos().x()-m_differNumX,event->pos().y()-m_differNumY);
        setCurPoint(&movePoint);
        int w = movePoint.x() - m_startPoint.x();
        int h = movePoint.y() - m_startPoint.y();
        m_currentRect.setSize(QSize(w,h));
        this->update();
    }
}

//鼠标松开,绘制完成
void MainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    //判断是否开始绘制
    if(!m_drawRectFlag)
    {
        return;
    }

    //判断是否在绘图区域内
    if(!m_isRegionFlag)
    {
        return;
    }

    //鼠标左键松开
    if(event->button() == Qt::LeftButton)
    {
        m_isPressedFlag = false;

        if(!m_isMoveFlag)
        {
            //判断是否选中矩形
            QVector<int> v_index;
            QPoint m_endPoint = QPoint(event->pos().x()-m_differNumX,event->pos().y()-m_differNumY);
            QList<QRect> curRectList;
            if(m_drawRectFlag)
            {
                curRectList = m_rectList;
            }

            for(int i=0;i<curRectList.size();i++)
            {
                //需要考虑大矩形包含小矩形的情况
                if(curRectList.at(i).contains(m_endPoint))
                {
                    //将矩形号保存起来
                    v_index.push_back(i);
                }
            }

            //通过面积来判断选中的矩形
            int vSize = v_index.size();
            if(vSize == 0)
            {
                m_selectedRectIndex = -1;
            }
            else
            {
                if(vSize == 1)
                {
                    m_selectedRectIndex = v_index[0];
                }
                else
                {
                    m_selectedRectIndex = v_index[0];
                    int smallArea = curRectList[v_index[0]].width() * curRectList[v_index[0]].width();
                    for(int i=0;i<vSize;i++)
                    {
                        int area = curRectList[v_index[i]].width() * curRectList[v_index[i]].width();
                        if(area < smallArea)
                        {
                            m_selectedRectIndex = v_index[i];
                        }
                    }
                }
            }
        }

        //判断矩形大小是否正常
        int width = qAbs(m_currentRect.width());
        int height = qAbs(m_currentRect.height());
        //LOGDEBUG<<"width:"<<width<<"   height:"<<height;
        if((width < 10) && (height < 10))
        {
            this->update();
            return;
        }
        if((width == 0) || (height == 0))
        {
            this->update();
            return;
        }

        //矩形
        if(m_drawRectFlag)
        {
            m_rectList.append(m_currentRect);
        }

        this->update();
    }
    else if(event->button() == Qt::RightButton)
    {
        //界面使能并更新
        setWidgetEnabled(true);
        this->update();
    }
}

//事件过滤
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
    if((watched == ui->lb_showImage) && (event->type() == QEvent::Paint))
    {
        updateShowPaint();
    }
    return QWidget::eventFilter(watched,event);
}

//绘制矩形
void MainWindow::on_pb_drawRect_clicked()
{
    //界面使能
    setWidgetEnabled(false);
    ui->pb_resetRect->setEnabled(true);
    this->update();
}

//重置矩形
void MainWindow::on_pb_resetRect_clicked()
{
    if(m_selectedRectIndex >= 0)
    {
        if(m_drawRectFlag)
        {
            m_rectList.removeAt(m_selectedRectIndex);
        }
        m_selectedRectIndex = -1;
        this->update();
    }
    else
    {
        QMessageBox::information(this,"提示","未选择绘制的矩形!");
    }
}

//设置截取图像
void MainWindow::on_pb_set_clicked()
{
    //保存截取的图像
    saveCroppedImage();
}

//打开截取图像列表
void MainWindow::on_pb_open_clicked()
{
    m_showImage->updateWidget();
    m_showImage->show();
}

//查看绘制好的矩形
void MainWindow::on_pb_view_clicked()
{
    m_selectedRectIndex = -1;
    m_drawRectFlag = true;
    this->update();
}

//保存
void MainWindow::on_pb_save_clicked()
{
    //保存矩形框数据为CSV文件
    QString csvFile = QCoreApplication::applicationDirPath() + "/regionRect.csv";
    LOGDEBUG<<"矩形框数据文件名:"<<csvFile;
    if(saveRectListToCSV(m_rectList,csvFile))
    {
        QMessageBox::information(this,"提示","文件保存成功!");
    }
}

3.mainwindow.ui
请添加图片描述

4.showimage.h

#ifndef SHOWIMAGE_H
#define SHOWIMAGE_H

#include <QWidget>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include <QTime>
#include <QDebug>
#include <QLabel>
#include <QPainter>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QTableWidget>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include "opencv2/opencv.hpp"

#define LOGDEBUG qDebug()<<QTime::currentTime().toString("[hh:mm:ss:zzz]")

namespace Ui {
class ShowImage;
}

class ShowImage : public QWidget
{
    Q_OBJECT

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

    void initWidget();
    void updateWidget();

    QPixmap cvMatToQPixmap(const cv::Mat &mat);
    void fillTableWidgetWithImages(QTableWidget *tableWidget);
    void deleteSelectedImage(QTableWidget *tableWidget);

protected:
    void wheelEvent(QWheelEvent *event);

private slots:
    void on_pb_delete_clicked();
    void on_tw_image_cellDoubleClicked(int row, int column);
    void on_pb_retuen_clicked();

private:
    Ui::ShowImage *ui;

    QGraphicsScene *scene;   //场景

};

#endif // SHOWIMAGE_H

5.showimage.cpp

#include "showimage.h"
#include "ui_showimage.h"

ShowImage::ShowImage(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ShowImage)
{
    ui->setupUi(this);
    this->initWidget();
}

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

//初始化界面
void ShowImage::initWidget()
{
    this->setWindowTitle("图像列表");
    this->setWindowFlags(Qt::WindowCloseButtonHint | Qt::Dialog);   //Qt::WindowStaysOnTopHint始终保持在顶层界面
    this->setWindowModality(Qt::ApplicationModal);
    this->setAttribute(Qt::WA_QuitOnClose,false);
    this->setFixedSize(this->width(),this->height());

    //创建场景
    scene = new QGraphicsScene(this);
    ui->gw_image->setScene(scene);
    ui->gw_image->setRenderHint(QPainter::Antialiasing);
    ui->gw_image->setDragMode(QGraphicsView::ScrollHandDrag);
}

//更新界面显示
void ShowImage::updateWidget()
{
    ui->sw_tableImage->setCurrentIndex(0);
    fillTableWidgetWithImages(ui->tw_image);
}

//将OpenCV图像转换为QPixmap
QPixmap ShowImage::cvMatToQPixmap(const cv::Mat &mat)
{
    QImage showImage;
    if(mat.channels() > 1)
    {
        showImage = QImage((const unsigned char*)(mat.data),mat.cols,mat.rows,mat.step,QImage::Format_RGB888);   //彩色图
    }
    else
    {
        showImage = QImage((const unsigned char*)(mat.data),mat.cols,mat.rows,mat.step,QImage::Format_Indexed8);   //灰度图
    }

    //OpenCV使用BGR顺序,而Qt使用RGB顺序,因此需要交换颜色通道
    return QPixmap::fromImage(showImage.rgbSwapped().scaled(100,100,Qt::KeepAspectRatio,Qt::SmoothTransformation));
}

//将截取图像填充到表格
void ShowImage::fillTableWidgetWithImages(QTableWidget *tableWidget)
{
    //清除表格内容
    tableWidget->clear();
    tableWidget->setRowCount(0);

    //设置表头和列宽
    tableWidget->setColumnCount(2);
    tableWidget->setHorizontalHeaderLabels(QStringList() << "截取图像" << "时间");
    tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);   //设置列宽拉伸模式
    tableWidget->resizeColumnsToContents();   //列宽自适应内容

    //获取文件夹内文件
    QString imageDir = QCoreApplication::applicationDirPath() + "/saveImage/";
    QDir dir(imageDir);
    QStringList filters;
    filters<<"*.jpg";
    QStringList imageFiles = dir.entryList(filters,QDir::Files);
    for(const QString &fileName : imageFiles)
    {
        //获取最后修改时间
        QString filePath = dir.filePath(fileName);
        QFileInfo fileInfo(filePath);
        QDateTime fileTime = fileInfo.lastModified();

        //获取图像
        cv::Mat image = cv::imread(filePath.toStdString());
        if(!image.empty())
        {
            //插入新行
            int rowCount = tableWidget->rowCount();
            tableWidget->insertRow(rowCount);

            //创建一个QLabel来显示图像
            QLabel *imageLabel = new QLabel(tableWidget);
            QPixmap pixmap = cvMatToQPixmap(image);
            imageLabel->setPixmap(pixmap);
            imageLabel->setAlignment(Qt::AlignCenter);
            tableWidget->setCellWidget(rowCount,0,imageLabel);   //将QLabel设置为单元格的小部件

            //设置时间文本
            QTableWidgetItem *timeItem = new QTableWidgetItem(fileTime.toString("yyyy-MM-dd HH:mm:ss"));
            timeItem->setTextAlignment(Qt::AlignCenter);
            tableWidget->setItem(rowCount,1,timeItem);   //将时间添加到表格的下一列

            //设置图像文件路径到表格项的data角色,方便后续删除操作
            QTableWidgetItem *pathItem = new QTableWidgetItem();
            pathItem->setData(Qt::UserRole,filePath);
            tableWidget->setItem(rowCount,0,pathItem);
        }
    }
}

//删除选中的截取图像
void ShowImage::deleteSelectedImage(QTableWidget *tableWidget)
{
    QList<QTableWidgetItem*> selectedItems = tableWidget->selectedItems();
    if(selectedItems.isEmpty())
    {
        QMessageBox::information(this,"提示","请选择要删除的图像!");
        return;
    }

    //获取选中的行索引
    int row = tableWidget->row(selectedItems.first());

    //从表格项中获取图像文件路径
    QVariant imagePathVariant = tableWidget->item(row,0)->data(Qt::UserRole);
    QString imagePath = imagePathVariant.toString();

    //删除图像文件
    QFile::remove(imagePath);

    //删除表格行
    tableWidget->removeRow(row);
}

//滚轮事件
void ShowImage::wheelEvent(QWheelEvent *event)
{
    if(event->angleDelta().y() > 0)
    {
        //滚轮向上滚动,放大
        ui->gw_image->scale(1.1, 1.1);
    }
    else
    {
        //滚轮向下滚动,缩小
        ui->gw_image->scale(1 / 1.1, 1 / 1.1);
    }
}

//删除
void ShowImage::on_pb_delete_clicked()
{
    deleteSelectedImage(ui->tw_image);
}

//双击表格跳转
void ShowImage::on_tw_image_cellDoubleClicked(int row,int column)
{
    //检查列数,确保不是点击的表头或其他非数据列
    if(column < ui->tw_image->columnCount())
    {
        //从表格项中获取图像文件路径
        QVariant imagePathVariant = ui->tw_image->item(row,0)->data(Qt::UserRole);
        QString imagePath = imagePathVariant.toString();
        LOGDEBUG<<"选中的图像文件名:"<<imagePath;

        //加载图像
        QPixmap pixmap(imagePath);
        if(!pixmap.isNull())
        {
            //清除场景中的旧内容
            scene->clear();

            //创建QGraphicsPixmapItem并添加到场景中
            QGraphicsPixmapItem *pixmapItem = scene->addPixmap(pixmap);

            //调整场景大小以匹配图像
            scene->setSceneRect(pixmapItem->boundingRect());

            //确保视图适应新内容
            ui->gw_image->fitInView(scene->sceneRect(),Qt::KeepAspectRatio);
        }
        else
        {
            //处理图像加载失败的情况
            LOGDEBUG<<"加载图像失败!";
        }

        //切换页面
        ui->sw_tableImage->setCurrentIndex(1);
    }
}

//返回
void ShowImage::on_pb_retuen_clicked()
{
    ui->sw_tableImage->setCurrentIndex(0);
}

6.showimage.ui
请添加图片描述


总结

在这个示例中,我们可以看到其中使用了事件过滤器来刷新绘图界面,使用了OPenCV来实现对图像的截取,使用图形视图框架来对图像进行扩大缩小等,其实有些功能在我之前的文章中就有实现过,但在这里发现了之前实现的需求存在一些不足,并对其进行了优化。在学习的过程中不仅可以发现过去的不足,而且能解锁新的知识,让我们一起加油努力呀~


hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1627478.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

气膜仓库:现代化仓储新选择—轻空间

气膜仓库&#xff0c;作为现代化仓储的新选择&#xff0c;越来越受到人们的青睐。相比传统料仓&#xff0c;气膜仓库具有诸多优势&#xff0c;使其成为各行各业的首选储存解决方案。 1. 高效节能 气膜仓库的建设周期短&#xff0c;基础简单&#xff0c;安装快捷&#xff0c;能耗…

C#命名空间常用函数

在C#中&#xff0c;不同命名空间下有各种常用函数&#xff0c;下面列举一些常见的函数及其对应的命名空间&#xff1a; System命名空间&#xff1a; Console.WriteLine()&#xff1a;用于向控制台输出信息。Convert.ToInt32()&#xff1a;用于将其他数据类型转换为整数类型。 S…

Kafka 3.x.x 入门到精通(05)——对标尚硅谷Kafka教程

Kafka 3.x.x 入门到精通&#xff08;05&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.5 存储消息2.6 消费消息2.6.1 消费消息的基本步骤2.6.2 消费消息的基本代码2.6.3 消费消息的基本原理2.6.3.1消费者组2.6.3.1.1 消费…

凹凸技术揭秘·羚珑智能设计平台·逐梦设计数智化

从技术和功能形态层面&#xff0c;我们把设计数智化分成了两个方向&#xff0c;一个方向是「模板化设计」&#xff0c;另一个方向是「程序化设计」。 2、模板化设计— 「模板化设计」的核心目标&#xff1a;是实现线下设计物料的数字化&#xff0c;在数字化设计资产的基础之上…

WildCard开通GitHub Copilot

更多AI内容请关注我的专栏&#xff1a;《体验AI》 期待您的点赞&#x1f44d;收藏⭐评论✍ WildCard开通GitHub Copilot GitHub Copilot 简介主要功能工作原理 开通过程1、注册Github账号2、准备一张信用卡或虚拟卡3、进入github copilot页4、选择试用5、选择支付方式6、填写卡…

C语言:插入排序

插入排序 1.解释2.步骤3.举例分析示例结果分析 1.解释 插入排序是一种简单直观的排序算法&#xff0c;它的工作原理是通过构建有序序列&#xff0c;对于未排序数据&#xff0c;在已排序序列中从后向前扫描&#xff0c;找到相应位置并插入。插入排序在实现上&#xff0c;通常采…

SSH新功能揭秘:远程工作提升指南【AI写作】

首先&#xff0c;这篇文章是基于笔尖AI写作进行文章创作的&#xff0c;喜欢的宝子&#xff0c;也可以去体验下&#xff0c;解放双手&#xff0c;上班直接摸鱼~ 按照惯例&#xff0c;先介绍下这款笔尖AI写作&#xff0c;宝子也可以直接下滑跳过看正文~ 笔尖Ai写作&#xff1a;…

如何实现直播声卡反向给手机充电功能呢?

在数字化时代的浪潮中&#xff0c;声卡作为多媒体系统的核心组件&#xff0c;扮演着声波与数字信号相互转换的关键角色。它不仅能够将来自各类音源的原始声音信号转换为数字信号&#xff0c;进而输出到各类声响设备&#xff0c;更能够通过音乐设备数字接口(MIDI)发出合成乐器的…

多家企业机密数据遭Lockbit3.0窃取,亚信安全发布《勒索家族和勒索事件监控报告》

本周态势快速感知 本周全球共监测到勒索事件87起&#xff0c;与上周相比勒索事件大幅下降。美国依旧为受勒索攻击最严重的国家&#xff0c;占比45%。 本周Cactus是影响最严重的勒索家族&#xff0c;Lockbit3.0和Bianlian恶意家族紧随其后&#xff0c;从整体上看Lockbit3.0依旧…

BERT-CRF 微调中文 NER 模型

文章目录 数据集模型定义数据集预处理BIO 标签转换自定义Dataset拆分训练、测试集 训练验证、测试指标计算推理其它相关参数CRF 模块 数据集 CLUE-NER数据集&#xff1a;https://github.com/CLUEbenchmark/CLUENER2020/blob/master/pytorch_version/README.md 模型定义 imp…

如何安全进行速卖通自养号测评操作?

对于新加入的卖家而言&#xff0c;进行销量测评显得尤为关键。速卖通平台上的新店往往难以获得活动的扶持&#xff0c;且初始流量相当有限。因此&#xff0c;开店的首要任务便是积极展开测评工作&#xff0c;努力积累初始的评论和销售记录。测评的益处颇为显著&#xff0c;它不…

Android Dalvik虚拟机JNI方法的注册过程分析

Dalvik虚拟机在调用一个成员函数的时候&#xff0c;如果发现该成员函数是一个JNI方法&#xff0c;那么就会直接跳到它的地址去执行。也就是说&#xff0c;JNI方法是直接在本地操作系统上执行的&#xff0c;而不是由Dalvik虚拟机解释器执行。由此也可看出&#xff0c;JNI方法是A…

数据结构(九)---并查集

目录 1.集合 2.集合的相关操作 (1)查(Find)&#xff1a; •Find操作的优化 (2)并(Union)&#xff1a; •Union操作的优化 1.集合 数据元素之间的逻辑关系可以为集合&#xff0c;树形关系&#xff0c;线性关系&#xff0c;图关系。对于集合而言&#xff0c;一个集合可以划…

微信小程序:8.WXSS

WXSS和CSS的关系 WXSS具有CSS大部分特性&#xff0c;同时&#xff0c;WXSS还对CSS进行扩充以及修改&#xff0c;适应微信小程序的开发。 与CSS相比&#xff0c;WXSS扩展的特性有&#xff1a; rpx尺寸单位imprt样式导入 rpx尺寸单位 rpx是微信小程序中独有的&#xff0c;用来…

第三节课,后端登录【1】.2--本人

一、视频链接 网址&#xff1a; 后端用户脱敏和session-CSDN直播 二、代码开始 2.1 新建一个request参数。完成用户登录态键 快捷建&#xff0c; 全局变量 代码&#xff1a; // 3.记录用户的登录态/*** 这段代码是Java Web开发中的一部分&#xff0c;用于在会话&#xff08…

面试:finalize

一、概述 将资源释放和清理放在finalize方法中非常不好&#xff0c;非常影响性能&#xff0c;严重时甚至会引起OOM&#xff08;Out Of Memory&#xff09;&#xff0c;从Java9开始就被标注为Deprecated&#xff0c;不建议被使用了。 二、两个重要的队列 1、unfinalized 队列 当…

为什么 Facebook 不使用 Git?

在编程的世界里&#xff0c;Git 就像水一样常见&#xff0c;以至于我们认为它是创建和管理代码更改的唯一可行的工具。 前 Facebook 员工&#xff0c;2024 年 首先&#xff0c;我为什么关心&#xff1f; 我致力于构建 Graphite&#xff0c;它从根本上受到 Facebook 内部工具的…

FSMC读取FPGA的FIFO

一、硬件说明 FSMC配置 单片机的代码如下&#xff1a; #define VALUE_ADDRESS_AD1 (__IO uint16_t *)0x60400000while (1){if(!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_8)) //数据非空{data *(__IO uint16_t *)VALUE_ADDRESS_AD1;data2 *(__IO uint16_t *)VALUE_ADDRESS_AD1…

网上订餐系统,基于 SpringBoot+Vue+MySQL 开发的前后端分离的网上订餐系统设计实现

目录 一. 前言 二. 功能模块 2.1. 用户功能模块的实现 2.2. 管理员功能模块的实现 三. 部分代码实现 四. 源码下载 一. 前言 随着我国经济的飞速发展&#xff0c;人们的生活速度明显加快&#xff0c;在餐厅吃饭排队的情况到处可见&#xff0c;近年来由于新兴IT行业的空前…

python的turtle库画直线

1.画一条直线 让画笔从(0,0)划到&#xff08;100,100&#xff09;&#xff0c;在turtle中画笔是一只小乌龟。 import turtle turtle.setup(800,800,0,0)#turtle.setup(width,height,startx,starty)来设置窗口初始位置及大小 turtle.goto(100,100)2.画一条折线 left和right使小…