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){
}
效果图:
更多源码私信获取