QT+Yolov8 推理部署,ONNX模型 ,实例分割+目标检测

发布于:2025-08-15 ⋅ 阅读:(13) ⋅ 点赞:(0)

QT+Yolov8 实例分割、目标检测推理。QT源码。

程序准备/版本:QT creator QT6.8 编译器:MSVC2022 opencv:4.7 onnxruntime:1.16.0 cpu版本

QT+yolo推理部署

程序部分源码:

#include "aitoolinterface.h"
#include "ui_aitoolinterface.h"
#include <QDebug>
#include <QDateTime>
#include <QFileInfo>

AIToolInterface::AIToolInterface(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::AIToolInterface)
    , m_yoloModel(nullptr)
    , m_modelLoaded(false)
    , m_yoloModel2(nullptr)
    , m_modelLoaded2(false)
{
    ui->setupUi(this);

    // 初始化状态
    ui->label_status->setText("状态: 未加载模型");
    ui->label_status->setStyleSheet("color: red;");

    // 禁用测试按钮,直到模型加载
    ui->pushButton_test->setEnabled(false);
    ui->pushButton_test_2->setEnabled(false);
    logMessage("AI分割检测工具已启动");
}

AIToolInterface::~AIToolInterface()
{
    if (m_yoloModel) {
        delete m_yoloModel;
        m_yoloModel = nullptr;
    }
    if (m_yoloModel2) {
        delete m_yoloModel2;
        m_yoloModel2 = nullptr;
    }
    delete ui;
}

void AIToolInterface::on_pushButton_browseModel_clicked()
{
    QString modelPath = QFileDialog::getOpenFileName(this,
                                                     "选择ONNX模型文件",
                                                     "./Bin/x64/Models/",
                                                     "ONNX Files (*.onnx);;All Files (*)");

    if (!modelPath.isEmpty()) {
        ui->lineEdit_modelPath->setText(modelPath);
        logMessage("已选择模型文件: " + modelPath);
    }
}

void AIToolInterface::on_pushButton_loadModel_clicked()
{
    QString modelPath = ui->lineEdit_modelPath->text();
    if (modelPath.isEmpty()) {
        QMessageBox::warning(this, "警告", "请先选择模型文件路径");
        return;
    }

    // 检查文件是否存在
    QFileInfo fileInfo(modelPath);
    if (!fileInfo.exists()) {
        QMessageBox::critical(this, "错误", "模型文件不存在: " + modelPath);
        return;
    }

    try {
        // 创建YOLO模型实例
        if (m_yoloModel) {
            delete m_yoloModel;
        }

        m_yoloModel = new Yolov8SegOnnx();

        // 加载模型
        logMessage("正在加载模型...");
        bool success = m_yoloModel->ReadModel(modelPath.toStdString(), false, 0, false);

        if (success) {
            m_modelLoaded = true;
            ui->label_status->setText("状态: 模型已加载");
            ui->label_status->setStyleSheet("color: green;");
            ui->pushButton_test->setEnabled(true);
            logMessage("模型加载成功!");
        } else {
            m_modelLoaded = false;
            ui->label_status->setText("状态: 模型加载失败");
            ui->label_status->setStyleSheet("color: red;");
            ui->pushButton_test->setEnabled(false);
            logMessage("模型加载失败!");
            delete m_yoloModel;
            m_yoloModel = nullptr;
        }
    } catch (const std::exception& e) {
        m_modelLoaded = false;
        ui->label_status->setText("状态: 模型加载异常");
        ui->label_status->setStyleSheet("color: red;");
        ui->pushButton_test->setEnabled(false);
        logMessage("模型加载异常: " + QString(e.what()));
        if (m_yoloModel) {
            delete m_yoloModel;
            m_yoloModel = nullptr;
        }
    }
}

void AIToolInterface::on_pushButton_browseImage_clicked()
{
    QString imagePath = QFileDialog::getOpenFileName(this,
                                                     "选择图片文件",
                                                     "./Bin/x64/Pic/",
                                                     "Image Files (*.jpg *.jpeg *.png *.bmp);;All Files (*)");

    if (!imagePath.isEmpty()) {
        ui->lineEdit_imagePath->setText(imagePath);
        m_currentImagePath = imagePath;

        // 加载并显示图片
        m_currentImage = cv::imread(imagePath.toStdString());
        if (m_currentImage.empty()) {
            QMessageBox::warning(this, "警告", "无法加载图片: " + imagePath);
            logMessage("图片加载失败: " + imagePath);
            return;
        }

        // 检查图片是否有效
        if (m_currentImage.rows <= 0 || m_currentImage.cols <= 0) {
            QMessageBox::warning(this, "警告", "图片尺寸无效: " + imagePath);
            logMessage("图片尺寸无效: " + imagePath);
            m_currentImage = cv::Mat();
            return;
        }

        logMessage(QString("已加载图片: %1 (尺寸: %2x%3)").arg(imagePath).arg(m_currentImage.cols).arg(m_currentImage.rows));
        displayImage(m_currentImage, ui->label_originalImage, "原始图片");
    }
}

void AIToolInterface::on_pushButton_test_clicked()
{
    if (!m_modelLoaded || !m_yoloModel) {
        QMessageBox::warning(this, "警告", "请先加载模型");
        logMessage("检测失败: 模型未加载");
        return;
    }

    if (m_currentImage.empty()) {
        QMessageBox::warning(this, "警告", "请先选择图片");
        logMessage("检测失败: 未选择图片");
        return;
    }

    // 额外检查图片有效性
    if (m_currentImage.rows <= 0 || m_currentImage.cols <= 0) {
        QMessageBox::warning(this, "警告", "当前图片无效,请重新选择");
        logMessage("检测失败: 当前图片无效");
        return;
    }

    try {
        logMessage("开始检测...");

        // 执行检测
        std::vector<OutputParams> results;
        bool success = m_yoloModel->OnnxDetect(m_currentImage, results);

        if (success) {
            logMessage(QString("检测完成,发现 %1 个目标").arg(results.size()));
            qDebug()<<"准备绘制结果";
            // 绘制检测结果
            cv::Mat resultImage = drawDetectionResult(m_currentImage, results);
            qDebug()<<"绘制结果完成";
            cv::imwrite("D:/OtherFiles/QTFile/AI_Solder/AI_Solder_v0/Bin/x64/Pic/result.jpg",resultImage);
            // 显示结果
            displayImage(resultImage, ui->label_resultImage, "检测结果");
            qDebug()<<"显示结果完成";
            // 输出检测详情
            for (size_t i = 0; i < results.size(); ++i) {
                const auto& result = results[i];
                QString className = QString::fromStdString(m_yoloModel->_className[result.id]);
                logMessage(QString("目标 %1: %2, 置信度: %3, 位置: (%4,%5,%6,%7)")
                               .arg(i + 1)
                               .arg(className)
                               .arg(result.confidence, 0, 'f', 3)
                               .arg(result.box.x)
                               .arg(result.box.y)
                               .arg(result.box.width)
                               .arg(result.box.height));
            }
        } else {
            logMessage("检测失败!");
            QMessageBox::warning(this, "错误", "检测失败");
        }

    } catch (const std::exception& e) {
        logMessage("检测异常: " + QString(e.what()));
        QMessageBox::critical(this, "错误", "检测过程中发生异常: " + QString(e.what()));
    }
}

//检测模型加载---
void AIToolInterface::on_pushButton_browseModel_2_clicked()
{
    QString modelPath = QFileDialog::getOpenFileName(this,
                                                     "选择ONNX模型文件",
                                                     "./Bin/x64/Models/",
                                                     "ONNX Files (*.onnx);;All Files (*)");

    if (!modelPath.isEmpty()) {
        ui->lineEdit_modelPath_2->setText(modelPath);
        logMessage("已选择模型文件: " + modelPath);
    }
}


void AIToolInterface::on_pushButton_browseImage_2_clicked()
{
    QString imagePath = QFileDialog::getOpenFileName(this,
                                                     "选择图片文件",
                                                     "./Bin/x64/Pic/",
                                                     "Image Files (*.jpg *.jpeg *.png *.bmp);;All Files (*)");

    if (!imagePath.isEmpty()) {
        ui->lineEdit_imagePath_2->setText(imagePath);
        m_currentImagePath2 = imagePath;

        // 加载并显示图片
        m_currentImage2 = cv::imread(imagePath.toStdString());
        if (m_currentImage2.empty()) {
            QMessageBox::warning(this, "警告", "无法加载图片: " + imagePath);
            logMessage("图片加载失败: " + imagePath);
            return;
        }

        // 检查图片是否有效
        if (m_currentImage2.rows <= 0 || m_currentImage2.cols <= 0) {
            QMessageBox::warning(this, "警告", "图片尺寸无效: " + imagePath);
            logMessage("图片尺寸无效: " + imagePath);
            m_currentImage2 = cv::Mat();
            return;
        }

        logMessage(QString("已加载图片: %1 (尺寸: %2x%3)").arg(imagePath).arg(m_currentImage2.cols).arg(m_currentImage2.rows));
        displayImage(m_currentImage2, ui->label_originalImage, "原始图片");
    }
}


void AIToolInterface::on_pushButton_loadModel_2_clicked()
{
    QString modelPath = ui->lineEdit_modelPath_2->text();
    if (modelPath.isEmpty()) {
        QMessageBox::warning(this, "警告", "请先选择模型文件路径");
        return;
    }

    // 检查文件是否存在
    QFileInfo fileInfo(modelPath);
    if (!fileInfo.exists()) {
        QMessageBox::critical(this, "错误", "模型文件不存在: " + modelPath);
        return;
    }

    try {
        // 创建YOLO模型实例
        if (m_yoloModel2) {
            delete m_yoloModel2;
        }

        m_yoloModel2 = new Yolov8Onnx();
        qDebug()<<"实例化检测模型";
        // 加载模型
        logMessage("正在加载模型...");
        bool success = m_yoloModel2->ReadModel(modelPath.toStdString(), false, 0, false);

        if (success) {
            m_modelLoaded2 = true;
            ui->label_status_2->setText("状态: 模型已加载");
            ui->label_status_2->setStyleSheet("color: green;");
            ui->pushButton_test_2->setEnabled(true);
            logMessage("模型加载成功!");
        } else {
            m_modelLoaded2 = false;
            ui->label_status_2->setText("状态: 模型加载失败");
            ui->label_status_2->setStyleSheet("color: red;");
            ui->pushButton_test_2->setEnabled(false);
            logMessage("模型加载失败!");
            delete m_yoloModel2;
            m_yoloModel2 = nullptr;
        }
    } catch (const std::exception& e) {
        m_modelLoaded2 = false;
        ui->label_status_2->setText("状态: 模型加载异常");
        ui->label_status_2->setStyleSheet("color: red;");
        ui->pushButton_test_2->setEnabled(false);
        logMessage("模型加载异常: " + QString(e.what()));
        if (m_yoloModel2) {
            delete m_yoloModel2;
            m_yoloModel2 = nullptr;
        }
    }
}


void AIToolInterface::on_pushButton_test_2_clicked()
{
    if (!m_modelLoaded2 || !m_yoloModel2) {
        QMessageBox::warning(this, "警告", "请先加载模型");
        logMessage("检测失败: 模型未加载");
        return;
    }

    if (m_currentImage2.empty()) {
        QMessageBox::warning(this, "警告", "请先选择图片");
        logMessage("检测失败: 未选择图片");
        return;
    }

    // 额外检查图片有效性
    if (m_currentImage2.rows <= 0 || m_currentImage2.cols <= 0) {
        QMessageBox::warning(this, "警告", "当前图片无效,请重新选择");
        logMessage("检测失败: 当前图片无效");
        return;
    }

    try {
        logMessage("开始检测...");

        // 执行检测
        std::vector<OutputParams> results;
        bool success = m_yoloModel2->OnnxDetect(m_currentImage2, results);

        if (success) {
            logMessage(QString("检测完成,发现 %1 个目标").arg(results.size()));
            qDebug()<<"准备绘制结果";
            // 绘制检测结果 - 使用专门的目标检测绘制函数
            cv::Mat resultImage = drawDetectionResult2(m_currentImage2, results);
            qDebug()<<"绘制结果完成";
            cv::imwrite("D:/OtherFiles/QTFile/AI_Solder/AI_Solder_v0/Bin/x64/Pic/result2.jpg",resultImage);
            // 显示结果
            displayImage(resultImage, ui->label_DetecResult, "检测结果");
            qDebug()<<"显示结果完成";
            // 输出检测详情
            for (size_t i = 0; i < results.size(); ++i) {
                const auto& result = results[i];
                QString className = QString::fromStdString(m_yoloModel2->_className[result.id]);
                logMessage(QString("目标 %1: %2, 置信度: %3, 位置: (%4,%5,%6,%7)")
                               .arg(i + 1)
                               .arg(className)
                               .arg(result.confidence, 0, 'f', 3)
                               .arg(result.box.x)
                               .arg(result.box.y)
                               .arg(result.box.width)
                               .arg(result.box.height));
            }
        } else {
            logMessage("检测失败!");
            QMessageBox::warning(this, "错误", "检测失败");
        }

    } catch (const std::exception& e) {
        logMessage("检测异常: " + QString(e.what()));
        QMessageBox::critical(this, "错误", "检测过程中发生异常: " + QString(e.what()));
    }
}







//日志打印
void AIToolInterface::logMessage(const QString& message)
{
    QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss");
    ui->textEdit_log->append(QString("[%1] %2").arg(timestamp, message));

    // 自动滚动到底部
    QTextCursor cursor = ui->textEdit_log->textCursor();
    cursor.movePosition(QTextCursor::End);
    ui->textEdit_log->setTextCursor(cursor);
}

void AIToolInterface::displayImage(const cv::Mat& image, QLabel* label, const QString& text)
{
    if (image.empty()) {
        label->setText(text.isEmpty() ? "无图片" : text);
        return;
    }

    // 转换为QImage
    QImage qimg = cvMatToQImage(image);

    // 缩放图片以适应标签大小
    QPixmap pixmap = QPixmap::fromImage(qimg);
    QPixmap scaledPixmap = pixmap.scaled(label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);

    label->setPixmap(scaledPixmap);
    label->setAlignment(Qt::AlignCenter);
}
//绘制分割结果--:在黑色背景图像上绘制分割掩膜
cv::Mat AIToolInterface::drawDetectionResult(const cv::Mat& image, const std::vector<OutputParams>& results)
{
    // 创建黑色背景图片
    cv::Mat resultImage = cv::Mat::zeros(image.size(), CV_8UC3);
    qDebug()<<"绘制1111111111111111";

    // 定义颜色
    // 随机数生成器初始化
    static std::mt19937 rng(std::time(0));
    static std::uniform_int_distribution<int> dist(50, 205); // 避免太暗或太亮的颜色
    // 颜色缓存(避免重复生成)
    static std::unordered_map<int, cv::Scalar> colorMap;

    // 创建累积掩码,用于收集所有目标的掩码
    cv::Mat accumulatedMask = cv::Mat::zeros(image.size(), CV_8UC3);

    for (const auto& result : results) {


        // 自动生成或获取颜色
        if (colorMap.find(result.id) == colorMap.end()) {
            // 生成高对比度颜色(确保在图像中可见)
            colorMap[result.id] = cv::Scalar(
                dist(rng),       // B
                dist(rng),       // G
                dist(rng)        // R
                );
        }
        cv::Scalar color = colorMap[result.id];

        // // 绘制边界框---可选
        // if (result.box.area() > 0) {
        //     cv::rectangle(resultImage, result.box, color, 2);

        //     // 绘制标签
        //     std::string className = m_yoloModel->_className[result.id];
        //     std::string label = className + ":" + std::to_string(result.confidence);
        //     qDebug()<<"绘制3333333333333";
        //     int baseline = 0;
        //     cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseline);
        //     qDebug()<<"绘制44444444444444";
        //     cv::Point textPos(result.box.x, result.box.y - 5);
        //     if (textPos.y < labelSize.height) {
        //         textPos.y = result.box.y + labelSize.height;
        //     }
        //     qDebug()<<"绘制55555555555555";
        //     cv::putText(resultImage, label, textPos, cv::FONT_HERSHEY_SIMPLEX, 0.5, color, 1);
        // }

        // 绘制分割掩码
        if (result.boxMask.rows > 0 && result.boxMask.cols > 0) {
            cv::Mat mask = result.boxMask.clone();

            // 创建一个与原始图像相同尺寸的掩码
            cv::Mat fullMask = cv::Mat::zeros(image.size(), CV_8UC1);

            // 将小掩码复制到正确的位置
            cv::Rect roi = result.box;
            if (roi.x >= 0 && roi.y >= 0 &&
                roi.x + roi.width <= fullMask.cols &&
                roi.y + roi.height <= fullMask.rows) {
                mask.copyTo(fullMask(roi));
            }

            // 转换为彩色掩码
            cv::Mat coloredMask;
            cv::cvtColor(fullMask, coloredMask, cv::COLOR_GRAY2BGR);
            qDebug()<<"绘制6666666666666";

            // 应用颜色
            coloredMask.setTo(color, fullMask);
            qDebug()<<"绘制77777777777777";

            // 将当前掩码累积到总掩码上
            cv::add(accumulatedMask, coloredMask, accumulatedMask);
            qDebug()<<"绘制888888888888";
        }
    }

    // 最后将累积的掩码叠加到结果图像上
    cv::addWeighted(resultImage, 1.0, accumulatedMask, 1, 0, resultImage);

    return resultImage;
}

//绘制目标检测结果--:在原图上直接绘制边界框和置信度
cv::Mat AIToolInterface::drawDetectionResult2(const cv::Mat& image, const std::vector<OutputParams>& results)
{
    // 克隆原图,避免修改原始图像
    cv::Mat resultImage = image.clone();

    // 随机数生成器初始化
    static std::mt19937 rng(std::time(0));
    static std::uniform_int_distribution<int> dist(50, 205); // 避免太暗或太亮的颜色
    // 颜色缓存(避免重复生成)
    static std::unordered_map<int, cv::Scalar> colorMap;



    for (const auto& result : results) {

        // 自动生成或获取颜色
        if (colorMap.find(result.id) == colorMap.end()) {
            // 生成高对比度颜色(确保在图像中可见)
            colorMap[result.id] = cv::Scalar(
                dist(rng),       // B
                dist(rng),       // G
                dist(rng)        // R
                );
        }
        cv::Scalar color = colorMap[result.id];

        // 绘制边界框
        if (result.box.area() > 0) {
            // 绘制矩形框
            cv::rectangle(resultImage, result.box, color, 2);

            // 绘制标签背景
            std::string className = m_yoloModel2->_className[result.id];
            std::string label = className + ":" + std::to_string(result.confidence);

            int baseline = 0;
            cv::Size labelSize = cv::getTextSize(label, cv::FONT_HERSHEY_SIMPLEX, 1, 4, &baseline);

            // 计算标签位置
            cv::Point textPos(result.box.x, result.box.y - 5);
            if (textPos.y < labelSize.height) {
                textPos.y = result.box.y + labelSize.height;
            }

            // // 绘制标签背景矩形
            // cv::Rect labelRect(textPos.x - 2, textPos.y - labelSize.height - 2,
            //                  labelSize.width + 4, labelSize.height + 4);
            // cv::rectangle(resultImage, labelRect, color, -1); // 填充矩形

            // 绘制标签文字(白色)
            cv::putText(resultImage, label, textPos, cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 255, 255), 4);
        }
    }

    return resultImage;
}

QImage AIToolInterface::cvMatToQImage(const cv::Mat& mat)
{
    if (mat.type() == CV_8UC3) {
        // BGR to RGB
        cv::Mat rgb;
        cv::cvtColor(mat, rgb, cv::COLOR_BGR2RGB);

        QImage img((uchar*)rgb.data, rgb.cols, rgb.rows, rgb.step, QImage::Format_RGB888);
        return img.copy();
    } else if (mat.type() == CV_8UC1) {
        // Grayscale
        QImage img((uchar*)mat.data, mat.cols, mat.rows, mat.step, QImage::Format_Grayscale8);
        return img.copy();
    } else {
        return QImage();
    }
}
void AIToolInterface::Recv_Img(const QPixmap &p){

}



效果图:

在这里插入图片描述
在这里插入图片描述
更多源码私信获取


网站公告

今日签到

点亮在社区的每一天
去签到