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

文章目录

  • 前言
  • 一、在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的Rectcv::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_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic: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*648m_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的Rectcv::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_OBJECTpublic: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.rhkb.cn/news/314142.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt绘制边框有阴影兼容性问题

在Qt开发过程中&#xff0c;有时候我们要显示一个有阴影的对话框&#xff0c;这时一般采用自定义实现&#xff0c;然而最近在开发时软件时&#xff0c;Win11上显示正常&#xff0c;Win10或其他Win11电脑显示不正常&#xff0c;存在兼容性问题吗&#xff1f; 下面是具体的源码 …

PPSSPPSDL for Mac v1.17.1 PSP游戏模拟器(附500款游戏) 激活版

PPSSPPSDL for Mac是一款模拟器软件&#xff0c;它允许用户在Mac上运行PSP&#xff08;PlayStation Portable&#xff09;游戏。通过这款模拟器&#xff0c;用户可以体验到高清甚至更高的分辨率的游戏画面&#xff0c;同时还能够升级纹理以提升清晰度&#xff0c;并启用后处理着…

nvm下载的node没有npm

nvm下载的node没有npm 相信大家最近可能发现自己使用的nvm下载nodejs没有npm了。 会出现这种情况&#xff1a; C:\Users\89121>nvm install 15 Downloading node.js version 15.14.0 (64-bit)... Complete Downloading npm version 7.7.6... Download failed. Rolling Bac…

WebGL开发框架比较

WebGL开发框架提供了一套丰富的工具和API&#xff0c;使得在Web浏览器中创建和操作3D图形变得更加容易。以下是一些流行的WebGL开发框架及其各自的优缺点。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.Three.js 优点&#xff1a…

ThingsBoard服务端使用RPC通过网关给设备发送消息

一、概述 1、发送服务器端网关RPC 二、案例&#xff1a; 1、建立设备与网关之间的通讯 2、查看设备和网关是否在线状态啊 3、通过 仪表盘&#xff0c;创建设备A的模拟RPC调用的窗口链接 4、在客户端的网关设备上订阅RPC网关的主题信息 5、通过服务端的窗口&#xff0c;发…

电脑提示丢失iutils.dll怎么办?一分钟教你搞定dll丢失问题

在计算机世界中&#xff0c;DLL&#xff08;Dynamic Link Library&#xff0c;动态链接库&#xff09;文件扮演着至关重要的角色&#xff0c;它们如同乐高积木中的基础模块&#xff0c;不同程序通过调用这些模块来实现各种功能。其中&#xff0c;iutils.dll就是这样一款不可或缺…

苹果电脑装虚拟机好用吗 苹果电脑装虚拟机要钱吗 Parallels对mac的损害 Parallels占用多大空间 PD19

在当今数字化的时代&#xff0c;人们对电脑系统跨设备互联的需求越来越高。作为拥有广泛用户群体的苹果电脑&#xff0c;许多用户会有在Mac系统中运行其他操作系统的需求。在这种情况下&#xff0c;安装虚拟机是一个较好的解决方案。那么接下来就给大家介绍苹果电脑装虚拟机好用…

Neo-reGeorg明文流量

Neo-reGeorg 1 同IP对&#xff0c;同一个URI&#xff0c;第一个TCP流是“GET”请求&#xff0c;随后的TCP流请求为“POST”。&#xff08;jsp\jspx\php&#xff09; 2 第一个TCP流中&#xff0c;GET只有一个会话。&#xff08;jsp\jspx\php&#xff09;&#xff0c;响应body79…

Echarts-知识图谱

Echarts-知识图谱 demo地址 打开CodePen 效果 思路 1. 生成根节点 2. 根据子节点距离与根节点的角度关系&#xff0c;生成子节点坐标&#xff0c;进而生成子节点 3. 从子节点上按角度生成对应的子节点 4. 递归将根节点与每一层级子节点连线核心代码 定义节点配置 functio…

10.接口自动化测试学习-Pytest框架(2)

1.mark标签 如果在每一个模块&#xff0c;每一个类&#xff0c;每一个方法和用例之前都加上mark标签&#xff0c;那么在pytest运行时就可以只运行带有该mark标签的模块、类、接口。 这样可以方便我们执行自动化时&#xff0c;自主选择执行全部用例、某个模块用例、某个流程用…

区块链技术与应用学习笔记(10-11节)——北大肖臻课程

目录 10.分岔 ①什么是分叉&#xff1f; ②导致分叉的原因&#xff1f; ③在比特币新共识规则发布会会导致什么分叉&#xff1f; 什么是硬分叉&#xff1f; 硬分叉例子&#xff1f; 什么是软分叉&#xff1f; 软分叉和硬分叉区别&#xff1f; 软分叉实例 11.问答 转…

茴香豆:搭建你的RAG智能助理-作业三

本次课程由书生浦语社区贡献者【北辰】老师讲解【茴香豆&#xff1a;搭建你的 RAG 智能助理】课程。分别是&#xff1a; RAG 基础介绍茴香豆产品简介使用茴香豆搭建RAG知识库实战 课程视频&#xff1a;https://www.bilibili.com/video/BV1QA4m1F7t4/ 课程文档&#xff1a;ht…

Scala的函数至简原则

对于scala语言来说&#xff0c;函数的至简原则是它的一大特色。下面让我们一起来看看分别有什么吧&#xff01; 函数至简原则&#xff1a;能省则省&#xff01; 初始函数 def test(name:String):String{return name }1、return可以省略&#xff0c;Scala会使用函数体的最后一…

Docker in Docker的原理与实战

Docker in Docker&#xff08;简称DinD&#xff09;是一种在Docker容器内部运行另一个Docker实例的技术。这种技术允许用户在一个隔离的Docker容器中创建、管理和运行其他Docker容器&#xff0c;从而提供了更灵活和可控的部署选项。以下是DinD的主要特点&#xff1a; 隔离性&am…

Linux下载及安装OpenSSL

文章目录 前言一、OpenSSL下载二、OpenSSL安装1.上传下载好的安装包到服务器2.解压3.切换目录4.配置config5.编译6.安装7.备份旧版本OpenSSL7.创建软链接8.添加OpenSSL动态链接库9.更新库缓存10.查看OpenSSL版本验证安装是否成功 前言 一般系统会自带有OpenSSL&#xff0c;我们…

ssm和springboot项目运行与部署

java项目可以打成jar包或者war包&#xff0c;以前SSM前后端不分离的项目&#xff0c;一般都是打成war包&#xff0c;现在前后端分离springboot项目&#xff0c;一般都是打成jar包&#xff0c;当然前后端分离springboot项目打成war包也可以。 总结&#xff1a; ssm项目 前后端分…

详细解析什么是期权交易的获利方法

期权交易的获利方法 在期权交易之前进行充分的准备工作和风险评估是至关重要的。其中行情结构、策略方法、预期收益和风险评估&#xff0c;是期权交易成功的关键要素。它们能帮助我们更好地制定交易计划&#xff0c;控制风险&#xff0c;并追求稳定的利润。以下是对这四点的详…

SpringBoot+MyBatis-Plus+jsqlparser实现多租户功能

前言 多租户技术&#xff08;multi-tenancy technology&#xff09;是一种软件架构技术&#xff0c;它允许在单个系统实例上为多个用户或组织提供服务&#xff0c;同时确保这些用户之间数据的隔离性。在多租户架构中&#xff0c;每个租户&#xff08;可以是个人用户、企业、组…

Golang | Leetcode Golang题解之第46题全排列

题目&#xff1a; 题解&#xff1a; func permute(nums []int) [][]int {var (n len(nums)dfs func(vals []int) // 已选择数 排列为vals 后续回溯继续选择 直至选完ans [][]int)dfs func(vals []int) {//边界if len(vals) n {ans append(ans, vals)}//转移 枚举选哪个f…

LM1875L-TB5-T 音频功率放大器 PDF中文资料_参数_引脚图

LM1875L-TB5-T 规格信息&#xff1a; 商品类型音频功率放大器 音频功率放大器的类型- 输出类型1-Channel (Mono) 作业电压16V ~ 60V 输出功率25W x 1 4Ω 额外特性过流保护,热保护 UTC LM1875是一款单片功率放大器&#xff0c;可为消费类音频应 用提供极低失真和高品质的…