当前位置: 首页 > news >正文

AI质检数据准备利器:基于Qt/QML 5.14的图像批量裁剪工具开发实战

目录

    • 一、前言:为何需要这个工具?
    • 二、工具目标与功能
    • 三、技术架构
    • 四、详细实现步骤
        • 4.1. C++ 后端 (`ImageCropper`) 搭建
        • 4.2. 项目配置 (`main.cpp` 和 `.pro`)
        • 4.3. QML 前端界面 (`main.qml`)
    • 五、使用流程
    • 六、总结与展望

一、前言:为何需要这个工具?

在人工智能,特别是计算机视觉领域,流传着一句金科玉律:“Garbage In, Garbage Out”(垃圾进,垃圾出)。这句话精准地指出了数据质量对于模型性能的决定性作用。在工业质检领域,启动一个AI检测项目前,我们面临的首要挑战并非是选择哪个高深的算法,而是如何高效、准确地构建一个高质量的训练数据集

传统的数据采集方式,往往是先拍摄大量包含良品与次品的巨幅图像,然后再由算法工程师或标注员手动从中截取出一个个包含待检测特征的独立样本(ROI,Region of Interest)。这个过程不仅重复、枯燥,而且极易出错,严重影响了整个项目的研发效率。

为了解决这一痛点,本文将完整地分享一个实用小工具的开发过程。这个工具旨在极大简化从大图中批量提取训练样本的工作流,它允许用户:

  1. 载入一张高分辨率的工业图像。
  2. 通过鼠标在图像上直观地框选出目标区域。
  3. 一键将框选区域裁剪并保存为独立的 JPG 图片。
  4. 自动对保存的图片进行递增编号(1.jpg, 2.jpg, …),便于管理。

这篇博客将带领您,用 Qt 5.14 和 QML 技术,从零开始打造这款属于我们自己的“AI 质检数据准备利器”。

二、工具目标与功能

我们的目标是创建一个简洁、直观、高效的桌面应用,其核心功能如下:

  1. 加载图像:支持从本地文件系统加载任意尺寸的 JPG, PNG, BMP 图像。
  2. 框选区域:允许用户在显示的图像上,通过鼠标拖拽的方式,自由绘制矩形选框。
  3. 一键裁剪保存:提供一个“裁剪并保存”按钮,点击后立即截取选框内的图像。
  4. 自动命名:将截取的图像以 1.jpg, 2.jpg, 3.jpg… 的序列自动命名,并保存在程序运行目录下的 output 文件夹中。
  5. 用户反馈:在成功保存后,向用户提供清晰的反馈,例如显示“已保存为: 10.jpg”。

三、技术架构

我们将采用稳定且解耦的前后端分离架构:

  • 前端 (QML):负责所有用户交互界面。使用 QML 来实现图像的显示、加载按钮、保存按钮以及鼠标的矩形绘制逻辑。
  • 后端 (C++):负责所有的核心业务逻辑。它将处理图像的加载、根据 QML 传来的坐标进行精确裁剪、管理自增序号以及执行文件保存操作。

Qt版本:Qt 5.14
编译器:VS 2017 64bit
编译工具:qmake

四、详细实现步骤

使用qt创建一个名为croptool的qt quick application项目。

4.1. C++ 后端 (ImageCropper) 搭建

首先,我们需要一个专门负责裁剪和保存逻辑的 C++ 类。

imagecropper.h

#ifndef IMAGECROPPER_H
#define IMAGECROPPER_H#include <QObject>
#include <QImage>
#include <QRect>
#include <QSize>
#include <QUrl>
#include <QString>class ImageCropper : public QObject
{Q_OBJECT
public:explicit ImageCropper(QObject *parent = nullptr);signals:// 定义一个信号,用于在保存成功后通知 QML,并传递保存的文件名void saveSuccess(const QString &filename);public slots:// QML 将调用此槽函数来执行裁剪和保存操作void cropAndSaveImage(const QUrl &imageUrl, const QRect &viewRect, const QSize &containerSize);private:int m_saveCounter; // 用于保存自增的文件序号
};#endif // IMAGECROPPER_H

imagecropper.cpp

#include "imagecropper.h"
#include <QDebug>
#include <QDir>ImageCropper::ImageCropper(QObject *parent) : QObject(parent), m_saveCounter(1)
{// 在构造函数中,检查并创建用于存放结果的 output 文件夹QDir dir;if (!dir.exists("output")) {dir.mkpath("output");}
}void ImageCropper::cropAndSaveImage(const QUrl &imageUrl, const QRect &viewRect, const QSize &containerSize)
{if (!imageUrl.isLocalFile() || !viewRect.isValid()) {qWarning() << "Invalid image URL or selection rectangle.";return;}QImage image(imageUrl.toLocalFile());if (image.isNull()) {qWarning() << "Failed to load image.";return;}// --- 坐标系映射逻辑  ---QSize sourceSize = image.size();QSize scaledSize = sourceSize;scaledSize.scale(containerSize, Qt::KeepAspectRatio);double offsetX = (containerSize.width() - scaledSize.width()) / 2.0;double offsetY = (containerSize.height() - scaledSize.height()) / 2.0;double scaleFactor = (double)sourceSize.width() / (double)scaledSize.width();QRect sourceRect(static_cast<int>((viewRect.x() - offsetX) * scaleFactor),static_cast<int>((viewRect.y() - offsetY) * scaleFactor),static_cast<int>(viewRect.width() * scaleFactor),static_cast<int>(viewRect.height() * scaleFactor));sourceRect = sourceRect.intersected(image.rect());if (!sourceRect.isValid()) {qWarning() << "Calculated source rectangle is invalid.";return;}// --- 核心裁剪与保存逻辑 ---QImage croppedImage = image.copy(sourceRect);// 构造保存路径和文件名QString savePath = QString("output/%1.jpg").arg(m_saveCounter);// 保存图片,可以指定 JPG 格式的压缩质量 (范围 0-100, -1 为默认)if (croppedImage.save(savePath, "JPG", 80)) {qDebug() << "Successfully saved to" << savePath;// 发送成功信号emit saveSuccess(savePath);// 文件序号自增m_saveCounter++;} else {qWarning() << "Failed to save image to" << savePath;}
}
4.2. 项目配置 (main.cpp.pro)

main.cpp
main.cpp 中,我们实例化 ImageCropper 并将其暴露给 QML。

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "imagecropper.h" // 包含我们的新类int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);QQmlApplicationEngine engine;ImageCropper imageCropper;engine.rootContext()->setContextProperty("imageCropper", &imageCropper);const QUrl url(QStringLiteral("qrc:/main.qml"));engine.load(url);return app.exec();
}

.pro 文件
确保 imagecropper.himagecropper.cpp 被添加到了项目文件中。

# ...
HEADERS += \imagecropper.hSOURCES += \main.cpp \imagecropper.cpp
# ...
4.3. QML 前端界面 (main.qml)

前端界面力求简洁,核心就是一个图像显示区和几个操作按钮。

import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Dialogs 1.2ApplicationWindow {id: windowvisible: truewidth: 1024height: 768title: qsTr("AI质检数据批量裁剪工具")// 用于显示保存成功后的提示信息property string statusText: ""Connections {target: imageCropperfunction onSaveSuccess(filename) {statusText = `已保存: ${filename}`// 让提示信息在2秒后自动消失statusClearTimer.start()}}Timer {id: statusClearTimerinterval: 2000onTriggered: statusText = ""}// 主布局Column {anchors.fill: parentspacing: 10// 顶部工具栏Row {anchors.horizontalCenter: parent.horizontalCenterspacing: 20padding: 10Button {text: "加载图像"onClicked: fileDialog.open()}Button {id: saveButtontext: "裁剪并保存"enabled: selectionRect.visible && selectionRect.width > 0 && selectionRect.height > 0onClicked: {imageCropper.cropAndSaveImage(mainImage.source,Qt.rect(selectionRect.x, selectionRect.y, selectionRect.width, selectionRect.height),Qt.size(imageContainer.width, imageContainer.height))}}}// 状态栏,用于显示反馈信息Label {anchors.horizontalCenter: parent.horizontalCentertext: statusTextfont.bold: truecolor: "green"}// 图像显示和交互区域Item {id: imageContainerwidth: parent.width - 20height: parent.height - 100anchors.horizontalCenter: parent.horizontalCenterclip: trueRectangle { /* ... 纯色背景 ... */ }Image { id: mainImage; /* ... */ }Rectangle { id: selectionRect; /* ... 用户绘制的选框 ... */ }// MouseAreaMouseArea {anchors.fill: parentproperty var startPoint: Qt.point(0, 0)onPressed: (mouse) => {startPoint = Qt.point(mouse.x, mouse.y);selectionRect.x = mouse.x;selectionRect.y = mouse.y;selectionRect.width = 0;selectionRect.height = 0;selectionRect.visible = true;}onPositionChanged: (mouse) => {selectionRect.width = mouse.x - startPoint.x;selectionRect.height = mouse.y - startPoint.y;}}}}// 文件选择对话框FileDialog {id: fileDialogtitle: "请选择一张图片"folder: shortcuts.picturesnameFilters: ["Image files (*.jpg *.png *.bmp)"]onAccepted: {mainImage.source = fileDialog.fileUrlwindow.detectedPoints = [] // 加载新图片时清空旧结果}}
}

五、使用流程

  1. 启动程序:打开我们编译好的应用。
  2. 加载图像:点击“加载图像”按钮,选择一张包含多个待检测目标的工业大图。
  3. 框选目标:在图像上,按住鼠标左键并拖动,精确地框住第一个您想要提取的样本。
  4. 保存样本:点击“裁剪并保存”按钮。程序界面会提示“已保存: output/1.jpg”。
  5. 继续操作:接着框选第二个目标,再次点击“裁剪并保存”,程序会提示“已保存: output/2.jpg”。
  6. 重复此过程,直到您将图像上所有需要的样本都提取出来。最终,output 文件夹中就存放了您亲手制作、高质量、已编号的训练数据集!

在这里插入图片描述

六、总结与展望

通过这个简单的实战项目,我们构建了一个在 AI 视觉项目中极其实用的数据预处理工具。它将原本繁琐的手动截图、重命名工作,简化为“框选-点击”的流畅操作,极大地提升了数据集的制作效率。

这个工具仅仅是一个起点,基于这个坚实的框架,我们还可以畅想更多强大的功能:

  • 快捷键操作:例如按 Enter 键代替点击“保存”按钮。
  • 批量加载:支持一次性加载文件夹中的所有图片,通过“上一张/下一张”按钮快速切换。
  • 格式导出:在保存 JPG 的同时,可以生成符合 YOLO 或 Pascal VOC 格式的 .txt.xml 标注文件,实现真正的“一次框选,双重产出”。

希望这个小工具的开发过程,能为奋斗在工业质检领域的同行们带来启发,让我们用代码为 AI 的落地扫清障碍。

http://www.lryc.cn/news/616039.html

相关文章:

  • Python 2025:最新技术趋势与展望
  • Text2SQL 自助式数据报表开发(Chat BI)
  • 解决 .NET Core 6.0 + PostgreSQL 网站首次连接缓慢问题
  • 嵌入式软件分层架构的设计原理与实践验证(有限状态机理解及结构体封装理解)
  • spring-ai整合PGVector实现RAG
  • WinForm之TreeView控件
  • Baumer高防护相机如何通过YoloV8深度学习模型实现道路坑洼的检测识别(C#代码UI界面版)
  • [激光原理与应用-223]:机械 - 机加厂加工机械需要2D还是3D图?
  • jvm有哪些垃圾回收器,实际中如何选择?
  • 本地WSL部署接入 whisper + ollama qwen3:14b 总结字幕校对增强版
  • Code Exercising Day 10 of “Code Ideas Record“:StackQueue part02
  • 低版本 IntelliJ IDEA 使用高版本 JDK 语言特性的问题
  • IDEA 如何导入系统设置
  • 基于ECharts的智慧社区数据可视化
  • IDEA 快捷编辑指南
  • IntelliJ IDEA 2025.2 重磅发布
  • OneCode 3.0 可视化功能全面分析:从开发者到用户的全场景解析
  • [激光原理与应用-214]:设计 - 皮秒紫外激光器 - 电控设计,高精度、高可靠性与智能化的全链路方案
  • 【渲染流水线】[几何阶段]-[归一化NDC]以UnityURP为例
  • SpringMVC的知识点总结
  • JDBC的连接过程(超详细)
  • 【Python 工具人快餐 · 第 6 份】
  • Redis缓存穿透、缓存击穿、缓存雪崩
  • 社交与职场中的墨菲定律
  • 故障诊断 | VMD-CNN-LSTM西储大学轴承故障诊断附MATLAB代码
  • vscode uv 发布一个python包:编辑、调试与相对路径导包
  • K8s四层负载均衡-service
  • 《Qt————Tcp通讯》
  • 【自动化运维神器Ansible】playbook案例解析:Tags组件实现任务选择性执行
  • 从DDPM对比学习Diffusion Policy:生成模型到策略学习的演进