0%

Qt使用ONNX调用YOLOv8模型

Qt配置onnx_runtime

首先,onnx_runtime官方也给编译好的release版本,下载即可。但是在qt中配置有一个坑。
在这里插入图片描述
在Qt Creator中正常添加外部库,但是你会发现构建会找不到onnxruntime.lib,这是如果你替换成全路径,即把注释的部分换成下面的lib路径,直接指明onnxruntime.lib。这时构建成功,可以include <onnxruntime_cxx_api.h>,但是在运行你会遇到应用程序无法启动。
在这里插入图片描述
根据百度把onnxruntime.dll复制到.exe目录下。

OK,启动成功。

opencv读取视频流

居中显示,随意拉伸。
在这里插入图片描述
在这里插入图片描述
实现居中的逻辑:

1
// 调整QImage的大小以匹配QLabel的大小 QPixmap scaledPixmap = QPixmap::fromImage(qimg).scaled(ui->Origin_Video->size(), Qt::KeepAspectRatio, Qt::FastTransformation);

而在界面当中需要对窗口随意拉伸,这是就需要界面允许缩放。修改QLabel的属性:
在这里插入图片描述
修改成minimum,并给定最小宽度和高度。(还不知道原因,等有空学习一下)

最后opencv读取视频流并拉取每一帧显示在QLabel中,这里采用的是用一个Qtimer,定时去获取视频帧。

1
// 创建定时器,每隔一定时间显示下一帧 timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::showNextFrame); timer->start(33); // 设置帧率为30FPS,即每隔33毫秒显示一帧

完整代码如下:

1
// 在槽函数中处理视频的加载和显示 void MainWindow::on_actionvideo_triggered() { camera->stop(); viewfinder->close(); QString curPath = QDir::homePath(); QString dlgTitle = "选择视频文件"; QString filter = "视频文件(*.wmv *.mp4);;所有文件(*.*)"; QString aFile = QFileDialog::getOpenFileName(this, dlgTitle, curPath, filter); if (aFile.isEmpty()) { return; } ui->dir_Edit->setText(aFile); currentSource = File; // 更新当前视频源为视频文件 displayVideo(); // 显示视频 } // 根据当前视频源显示视频的函数 void MainWindow::displayVideo() { if (currentSource == File) { std::string video_path = ui->dir_Edit->text().toLocal8Bit().constData(); cap.open(video_path); if (!cap.isOpened()) { qDebug() << "Error: Unable to open the video file"; return; } // 创建定时器,每隔一定时间显示下一帧 timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::showNextFrame); timer->start(33); // 设置帧率为30FPS,即每隔33毫秒显示一帧 } else if (currentSource == Camera) { // 创建定时器,每隔一定时间显示下一帧 timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &MainWindow::viewfinderchange); timer->start(33); // 设置帧率为30FPS,即每隔33毫秒显示一帧 // cameras = QCameraInfo::availableCameras(); //获取所有相机的列表 // camera = new QCamera(cameras[0]); //camera指向指定的摄像头 camera->setCaptureMode(QCamera::CaptureStillImage); //设定捕获模式 camera->setViewfinder(viewfinder); //设置取景器 camera->start(); } } // 显示下一帧的槽函数 void MainWindow::showNextFrame() { cv::Mat frame; cap >> frame; // 从视频流中获取一帧 if (frame.empty()) { cap.set(cv::CAP_PROP_POS_FRAMES, 0); // 如果视频结束,重新开始播放 cap >> frame; } currentFrame = frame; // 保存当前帧 displayCurrentFrame(); // 显示当前帧 } void MainWindow::displayCurrentFrame() { // 将OpenCV帧转换为QImage QImage qimg(currentFrame.data, currentFrame.cols, currentFrame.rows, currentFrame.step, QImage::Format_RGB888); qimg = qimg.rgbSwapped(); // 将格式从BGR转换为RGB // 调整QImage的大小以匹配QLabel的大小 QPixmap scaledPixmap = QPixmap::fromImage(qimg).scaled(ui->Origin_Video->size(), Qt::KeepAspectRatio, Qt::FastTransformation); // 将调整大小后的图像居中显示在QLabel中 centerImageInLabel(ui->Origin_Video, scaledPixmap); }

QCamra

居中显示,随意拉伸
在这里插入图片描述
QCamera其实同理,中间拉伸也用了一个QTimer定时获取QLabel的size。
QCamera的使用包括初始化一个camera和设置取景器viewfinder,viewfinder的作用就是控制图像在空间中的展示。

1
void MainWindow::on_actioncamera_triggered() { cameras = QCameraInfo::availableCameras(); //获取所有相机的列表 //qDebug() << "this is camera: "; if (cameras.count() > 0) { for(const QCameraInfo &cameraInfo:cameras) { qDebug() << cameraInfo.description(); } camera = new QCamera(cameras.at(0)); //初始化实例化一个相机对象 } //设置取景器 viewfinder = new QCameraViewfinder(ui->Origin_Video); camera->setViewfinder(viewfinder); centerCameraViewfinderInLabel(viewfinder, ui->Origin_Video); camera->start(); //开启相机 //设置默认摄像头参数 QCameraViewfinderSettings set; // set.setResolution(640, 480); //设置显示分辨率 set.setMaximumFrameRate(30); //设置帧率 camera->setViewfinderSettings(set); stopVideo(); ui->Origin_Video->setPixmap(QPixmap("")); currentSource = Camera; // 更新当前视频源为摄像头 viewfinder->show(); displayVideo(); // 显示视频 }

yolov8 onnx 推理

1
void MainWindow::on_actionTest_triggered() { // std::string projectBasePath = "./"; // Set your ultralytics base path QString qs = QCoreApplication::applicationDirPath(); std::string projectBasePath = qs.toLocal8Bit().constData(); bool runOnGPU = false; // Note that in this example the classes are hard-coded and 'classes.txt' is a place holder. Inference inf(projectBasePath + "/moust_best.onnx", cv::Size(640, 640), "mouse.txt", runOnGPU); std::string video_path = ui->dir_Edit->text().toLocal8Bit().constData(); // 读取视频文件 // cv::VideoCapture cap(projectBasePath + "/video/video.mp4"); cv::VideoCapture cap(video_path); if (!cap.isOpened()) { std::cout << "Error opening video file" << std::endl; return ; } cv::Mat frame; while (cap.read(frame)) { // 推断开始... std::vector<Detection> output = inf.runInference(frame); int detections = output.size(); std::cout << "Number of detections:" << detections << std::endl; for (int i = 0; i < detections; ++i) { Detection detection = output[i]; cv::Rect box = detection.box; cv::Scalar color = detection.color; // Detection box cv::rectangle(frame, box, color, 2); // Detection box text std::string classString = detection.className + ' ' + std::to_string(detection.confidence).substr(0, 4); cv::Size textSize = cv::getTextSize(classString, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0); cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20); cv::rectangle(frame, textBox, color, cv::FILLED); cv::putText(frame, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0); } // 推断结束... // 仅用于预览 float scale = 0.8; cv::resize(frame, frame, cv::Size(frame.cols*scale, frame.rows*scale)); cv::imshow("Inference", frame); if (cv::waitKey(1) == 27) { break; } } cap.release(); cv::destroyAllWindows(); }

多线程(onnx推理线程和界面主线程)

在这里插入图片描述
摄像头与onnx互不干扰,说明主界面线程与onnx推理是分开线程进行的,ok!

######################### 2024 05 09 更新 ##############################################