深度学习图像分拣小工具

阅读: 评论:0

深度学习图像分拣小工具

深度学习图像分拣小工具

深度学习图像分拣是个比较繁琐的工作,windwos文件夹预览图像放大倍数有限,而单独地查看图像来回切换各个文件夹很容易乱。因此在空闲尝试做了个小软件。

一、功能需求

  1. 具备类似windows图像查看软件的缩放,拖拽,快捷键切换功能;
  2. 具备一键分拣,即快捷键分拣到对应文件夹功能;
  3. 后续测试中发现快捷键分拣很容易手滑,因此加入了后撤和重做。

二、软件平台

QT5.12.0/C++/OPENCV3.4.0

三、UI界面

主界面包括三个菜单,一图像窗口,一下拉页,一日志窗口。

3.1 菜单UI

3.1.1 打开图像路径

功能:

  1. 打开包含图像的文件夹,读取所有图片后缀文件列表;
  2. 显示第一张。

代码:

QString openFile = QFileDialog::getExistingDirectory(this, "choose src Directory", "/");
Local8Bit().constData();
QStringList imageList;  QString ImgPath; QDir imgDir ;
if (openFile != NULL)
{ImgPath = openFile; 	imgDir.setPath(ImgPath);imageList << "*.bmp" << "*.jpg" << "*.png" << "*.tif";imgDir.setNameFilters(imageList);imgCount = unt();if (imgCount == 0){return;		}else{QString imageName = ImgPath + "/" + imgDir[0];matOpen = Local8Bit().toStdString());}
else
{return;
}

3.1.2 打开图像路径

功能: 选择/创建主路径文件夹。
代码:

QString openFile = QFileDialog::getExistingDirectory(this, "choose src Directory", "/");
Local8Bit().constData();
if (openFile != NULL)
{MainPath = openFile;
}
else
{reutn;
}

3.1.3 创建子类路径

界面:

功能:输入需要分拣的图像类别,’,'分隔,然后根据类别名称新建子类文件夹。
代码:

//输入类以','分割  
//vector <string> classes   类别文件夹名
string tmpClass = ui>lineEdit_classes>text().toLocal8Bit().toStdString();
char *input = (char *)tmpClass.c_str();
char *token = std::strtok(input, ",");
classes.clear();
while (token != NULL)
{classes.push_back(token);token = std::strtok(NULL, ",");
}
//在主路径下创建以子类名称命名的文件夹
if (MainPath != NULL)
{for (int i = 0; i < classes.size(); i++){QString tmpClassPath = MainPath + '/' + QString::fromStdString(classes[i]);QDir *folder = new QDir;folder->mkdir(tmpClassPath);}
}

3.2 编辑UI

3.2.1 撤销/重做

功能:

  1. 撤销即取消图像移动操作;
  2. 重做即取消撤销操作;
  3. 清空日志窗口。

代码:

//当前图像源路径和目标路径添加到hisProcess,撤销即删除已经移动的图像,
//his_count+1,重做即做反操作,his_count-1;
//清空日志不做赘述。//撤销操作
//int his_count;  //历史操作计数
//vector <QStringList> hisProcess;  //历史记录
if (hisProcess.size() != his_count)
{QFile::remove(hisProcess.at(hisProcess.size() - his_count - 1).last());his_count += 1;
}
else
{return;
}//重做操作
if (his_count != 0)
{QFile::copy(hisProcess.at(hisProcess.size() - his_count).first(), hisProcess.at(hisProcess.size() - his_count).last());his_count -= 1;
}
else
{return;
}

3.3 提示menu

提示功能不做赘述,按需自写。

3.4 图像显示UI


图像显示部分起初只是一个单纯的QLabel来显示图像,后来在使用中发现还是有很多不方便,包括显示分拣classes信息(在textbrowser中无法置顶),单独的翻页按钮比较丑等。于是 对QLabel进行了修改。
主要包括以下几个功能:

  1. 显示图像;
  2. 点击左右边界附近切换上下张图;
  3. 图像的拖拽,缩放;
  4. 右键菜单实现翻页、保存。

3.4.1 显示图像

这个就不做赘述了。

3.4.2 点击左右边界附近切换上下张图


从类视图可以看到,imgShow 包含了两个按钮和一个显示ClassFile的QLabel。QT是不允许控件的叠加布局的,查阅了很多,都是建议重写布局方案,感觉太过繁琐。.html 提供了一种通过QWidgets布局的叠加控件方案,我的方案也是在此基础上进行。

  1. 新建一个QWidgets,添加一个QLabel和两个PushButton。两个PushButton先做水平布局,然后再跟QLabel做垂直布局,最后选择QWidgets的栅格布局,效果如图;

  2. 调整垂直布局和水平布局比例;

  3. 按钮删去所有文字,尺寸水平和垂直方向选择perferred(不删文字按钮水平方向无法最小化),背景透明设置 background:transparent,两个按钮中间加入若干spacer布局;

  4. 右键打开*.ui文件,找到新建的QWidget,class修改为QLabel,回到VS后,点击*.ui,重新编译(若使用QT自带的开发,回到软件后会提示已修改,是否重新加载,确认即可),到此widget修改的label已经可以显示图像了。

3.4.3 图像的拖拽,缩放

功能:

  1. 鼠标点击拖动图像;
  2. ‘ctrl’+滚轮缩放图像,右键还原;
  3. 代码参考了很多类似例子,方法可以参考:.

代码:
zoomLabel.h

#include <QObject>
#include <QLabel>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QPainter>
#include <QDebug>
#include <QPixmap>
#include <iostream>class my_zoomLabel :public QLabel
{
public:my_zoomLabel(QWidget *parent = nullptr);~my_zoomLabel();void initPos();void recievePix(QPixmap *pixmap);protected:void paintEvent(QPaintEvent *) override;void mouseMoveEvent(QMouseEvent *ev) override;void mousePressEvent(QMouseEvent *ev) override;void mouseReleaseEvent(QMouseEvent *ev) override;void wheelEvent(QWheelEvent *event) override;void changeWheelValue(QPoint event, int value);private:double m_scaleValue;					 //图片缩放倍数QPointF m_drawPoint;					 //绘图起点QPointF m_mousePoint;					 //鼠标当前位置点QRect m_rectPixmap;						 //被绘图片的矩形范围bool m_isMousePress;				     //鼠标是否按下QPixmap pix;							 //作为图元显示的图片const double EPS = 1e-6;				 //双精度浮点数比较const double SCALE_VALUE = 0.1;const double SCALE_MAX_VALUE = 10.0;const double SCALE_MIN_VALUE = 0.2;
};

zoomLabel.cpp

#include "my_zoomLabel.h"my_zoomLabel::my_zoomLabel(QWidget *parent) :QLabel(parent)
{m_scaleValue = 1.0;m_mousePoint = QPointF(0, 0);m_drawPoint = QPointF(0, 0);m_rectPixmap = QRect(0, 0, 0, 0);m_isMousePress = 0;
}my_zoomLabel::~my_zoomLabel()
{}void my_zoomLabel::paintEvent(QPaintEvent *)
{QPainter painter(this);double width = this->width()*m_scaleValue;double height = this->height()*m_scaleValue;if (!pix.isNull()){QPixmap scalePixmap = pix.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); // 饱满缩放m_rectPixmap = QRect(m_drawPoint.x(), m_drawPoint.y(), width, height);  // 图片区域painter.drawPixmap(m_rectPixmap, scalePixmap);}
}void my_zoomLabel::mouseMoveEvent(QMouseEvent *event)
{if (m_isMousePress){int x = event->pos().x() - m_mousePoint.x();int y = event->pos().y() - m_mousePoint.y();m_mousePoint = event->pos();m_drawPoint = QPointF(m_drawPoint.x() + x, m_drawPoint.y() + y);update();}
}void my_zoomLabel::mousePressEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton){m_isMousePress = true;m_mousePoint = event->pos();}
}void my_zoomLabel::mouseReleaseEvent(QMouseEvent *event)
{if (event->button() == Qt::RightButton){m_drawPoint = QPointF(0, 0);m_scaleValue = 1.0;update();}if (event->button() == Qt::LeftButton) m_isMousePress = false;
}void my_zoomLabel::wheelEvent(QWheelEvent *event)
{if (event->modifiers() == Qt::ControlModifier)  //ctrl+滚轮缩放{int numDegrees = event->delta() / 8; // 滚动角度 - *8就是鼠标滚动的距离int numSteps = numDegrees / 15;     // 滚动步数 - *15就是鼠标滚动的角度changeWheelValue(event->pos(), numSteps);event->accept();}
}void my_zoomLabel::changeWheelValue(QPoint event, int numSteps)
{m_scaleValue += numSteps * SCALE_VALUE;if (m_scaleValue > (SCALE_MAX_VALUE + EPS)){m_scaleValue = SCALE_MAX_VALUE;return;}if (m_scaleValue < (SCALE_MIN_VALUE - EPS)){m_scaleValue = SCALE_MIN_VALUE;return;}if (ains(event)){double x = m_drawPoint.x() - (event.x() - m_drawPoint.x()) / m_rectPixmap.width()*(this->width()*SCALE_VALUE)*numSteps;double y = m_drawPoint.y() - (event.y() - m_drawPoint.y()) / m_rectPixmap.height()*(this->height()*SCALE_VALUE)*numSteps;m_drawPoint = QPointF(x, y);}else{double x = m_drawPoint.x() - (this->width()*SCALE_VALUE)*numSteps / 2;double y = m_drawPoint.y() - (this->height()*SCALE_VALUE)*numSteps / 2;m_drawPoint = QPointF(x, y);}update();
}void my_zoomLabel::initPos()
{//初始化定位点m_scaleValue = 1.0;m_drawPoint = QPointF(0, 0);
}void my_zoomLabel::recievePix(QPixmap * pixmap)
{pix = *pixmap;
}

3.4.4 右键菜单实现翻页、保存

右键菜单通过代码添加,所以没法截图…
功能:包括上一页、下一页、保存图像。
代码:

//声明
QMenu*   imgShow_Menu;							//显示窗口右键菜单
QAction* imgShow_Action01;
QAction* imgShow_Action02;
QAction* imgShow_Action03;//图像窗口右键菜单
imgShow_Action01 = new QAction(QString::fromLocal8Bit("上一张"), this);
imgShow_Action02 = new QAction(QString::fromLocal8Bit("下一张"), this);
imgShow_Action03 = new QAction(QString::fromLocal8Bit("保存图像"), this);
imgShow_Menu = new QMenu(this);
imgShow_Menu->addAction(imgShow_Action01);
imgShow_Menu->addAction(imgShow_Action02);
imgShow_Menu->addAction(imgShow_Action03);
connect(ui.label_imgShow, &QLabel::customContextMenuRequested, this, &imgQuickView::imgShow_Menu_Action01);
connect(imgShow_Action01, &QAction::triggered, this, &imgQuickView::action_preImg);
connect(imgShow_Action02, &QAction::triggered, this, &imgQuickView::action_nextImg);
connect(imgShow_Action03, &QAction::triggered, this, &imgQuickView::action_saveImg);
//上一张
if (imgIndex > 0 && imgIndex <= imgCount && imgDir[0] != NULL)
{imgIndex -= 1;QString preImageName = ImgPath + "/" + imgDir[imgIndex];matOpen = Local8Bit().toStdString());imgShow(&matOpen);
}
else
{return;
}
//下一张
if (imgIndex < imgCount - 1 && imgDir[0] != NULL)
{imgIndex += 1;QString nextImageName = ImgPath + "/" + imgDir[imgIndex];matOpen = Local8Bit().toStdString());imgShow(&matOpen);
}
else
{return;
}
//保存图像
if (pty())
{return;
}
else
{QString tmpPath = QFileDialog::getExistingDirectory(this, "choose src Directory", "/");QString imgSavePath = tmpPath + "/" + QDateTime::currentDateTime().toString("yy_MM_dd_hh_mm_ss") + ".bmp";cv::StdString(), matOpen);
}

3.4 日志窗口

日志窗口采用的textBrowser控件,textBrowser.append()添加所需即可。

四、快捷键

功能:根据输入的class数量,绑定1-9数字键和指定文件夹。
代码:

//移动图像到指定文件夹
void moveImg(int i)
{//复制原图到项目文件夹sourceImg = ImgPath + "/" + imgDir[imgIndex];tagImg =  MainPath + '/' + QString::fromStdString(classes[i]) + "/" + imgDir[imgIndex];QFile::copy(sourceImg, tagImg);//历史记录 //QStringList.first放源路径,QStringList.last放目标路径QStringList tmpQList;tmpQList.append(sourceImg);tmpQList.append(tagImg);hisProcess.push_back(tmpQList);	his_count = 0;
}
void keyPressEvent(QKeyEvent * ev)
{	//撤销操作if (ev->key() == Qt::Key_Z && ev->modifiers() == Qt::ControlModifier){action_cancel();}else if (ev->key() == Qt::Key_Y && ev->modifiers() == Qt::ControlModifier){action_redo();}//对应1-9移动图像到指定文件夹操作if (!pty()){//根据classes数量,将bool数组前n位置1,后续捕捉键盘事件加入bool标志位判断,//即根据输入class的数量激活对应键盘快捷键int classNum = classes.size();for (int i = 0; i < classNum; i++) { keyBool[i] = 1; };if (ev->key() == Qt::Key_1){if (keyBool[0]) { moveImg(0); }else { return; }}else if (ev->key() == Qt::Key_2){if (keyBool[1]) { moveImg(1); }else { return; }}else if (ev->key() == Qt::Key_3){if (keyBool[2]) { moveImg(2); }else { return; }}else if (ev->key() == Qt::Key_4){if (keyBool[3]) { moveImg(3); }else { return; }}else if (ev->key() == Qt::Key_5){if (keyBool[4]) { moveImg(4); }else { return; }}else if (ev->key() == Qt::Key_6){if (keyBool[5]) { moveImg(5); }else { return; }}else if (ev->key() == Qt::Key_7){if (keyBool[6]) { moveImg(6); }else { return; }}else if (ev->key() == Qt::Key_8){if (keyBool[7]) { moveImg(7); }else { return; }}else if (ev->key() == Qt::Key_9){if (keyBool[8]) { moveImg(8); }else { return; }}}
} 

第一次发帖,大佬们轻拍。源码编辑过加密了,so~。 不过关键的思路应该都有了。然后我重写的QLabel 缩放、拖拽都一卡一卡的,不知道哪块有问题,有优化思路的可以留言沟通下。

本文发布于:2024-01-30 16:48:08,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170660449221440.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:小工具   深度   图像
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23