#include "widget.h" #include "ui_widget.h" #include #include #include #include #include #include // #include #include #include #include #include #include #include #include #include using namespace std; // 硬编码参数值 int file_delay = 1200; // 延迟时间(毫秒) int file_encoder = 12000; // 行频 int file_valve = 200; // 喷阀触发频率 //下位机参数 int lowmac_dp = 350; //偏振延迟时间 int lowmac_sm = 1200; //吹气量 valve/12 = 吹气时间ms int lowmac_ts = 8; //模板匹配阈值 int lowmac_sg = 60; //偏振绿色通道大小阈值 int lowmac_td = 7; //偏振红色通道差值 Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { this->isCamRunning = false; ui->setupUi(this); // 确认 tabWidget 是 QTabWidget 类型 QTabWidget* tab = qobject_cast(ui->tabWidget); if (tab) { QTabBar* tabBar = tab->tabBar(); if (tabBar) { tabBar->hide(); // 隐藏 TabBar } else { qWarning() << "tabBar() 返回 nullptr!"; } } else { qWarning() << "ui->tabWidget 不是 QTabWidget 类型!"; } ui->camera_0_img->setScaledContents(false); ui->camera_1_img->setScaledContents(false); iniOnnx(); iniColor(); iniLowMac(); iniCamera(); // 初始化存储工作者和线程 storageWorker = new StorageWorker(); storageWorker->moveToThread(&storageThread); connect(&storageThread, &QThread::started, storageWorker, &StorageWorker::process); connect(this, &Widget::destroyed, &storageThread, &QThread::quit); connect(&storageThread, &QThread::finished, storageWorker, &QObject::deleteLater); storageThread.start(); // 启动显示定时器,每秒检查一次 QTimer* timer = new QTimer(this); connect(timer, &QTimer::timeout, this, &Widget::refreshImage); timer->start(50); // 每50毫秒秒刷新一次界面 ui->tabWidget->setCurrentIndex(1); } Widget::~Widget() { // 停止存储线程 g_storageQueue.stop(); storageThread.quit(); storageThread.wait(); // 现有清理代码... DestoryCamera(); DestoryLowMac(); delete ui; } void Widget::refreshImage() { // refresh Image 1 and image 0 refreshSingleImage(0, this->ui->mtx_0_overlay->isChecked(), this->ui->dl_0_overlay->isChecked(), this->ui->img_0_mirror->isChecked()); refreshSingleImage(1, this->ui->mtx_1_overlay->isChecked(), this->ui->dl_1_overlay->isChecked(), this->ui->img_1_mirror->isChecked()); // refresh buttons this->ui->btn_start->setEnabled(!this->isCamRunning); this->ui->btn_stop->setEnabled(this->isCamRunning); this->ui->btn_take_photos->setEnabled(this->isCamRunning); // refresh checkouts this->ui->dl_enable_0->setEnabled(!this->isCamRunning); this->ui->dl_enable_1->setEnabled(!this->isCamRunning); this->ui->tra_enable_0->setEnabled(!this->isCamRunning); this->ui->tra_enable_1->setEnabled(!this->isCamRunning); // QDateTime now = QDateTime::currentDateTime(); ui->label_currentDateTime->setText(now.toString("yyyy-MM-dd hh:mm:ss")); if (this->isCamRunning) { // 计算启动至今的秒数差 qint64 elapsedSeconds = this->startTime.secsTo(now); // 转化为小时、分钟、秒 qint64 hours = elapsedSeconds / 3600; qint64 minutes = (elapsedSeconds % 3600) / 60; qint64 seconds = elapsedSeconds % 60; // 将“总运行时间”的小时/分钟分别更新到 UI ui->label_totalHours->setText(QString::number(hours)); // e.g. "482" ui->label_totalMinutes->setText(QString::number(minutes)); // e.g. "52" // 拼接“今日运行时长”一句话 (根据实际需求,可直接使用上面 hours、minutes、seconds) QString todayRunStr = QString("今日运行时长:%1小时%2分钟%3秒") .arg(hours) .arg(minutes) .arg(seconds); ui->label_todayRunningTime->setText(todayRunStr); } else { // 如果设备没在运行,UI可以显示默认内容或保持不变 // 下面示例:将运行时长重置为 0 ui->label_totalHours->setText("0"); ui->label_totalMinutes->setText("0"); ui->label_todayRunningTime->setText("今日运行时长:0小时0分钟0秒"); } // refresh info QString info; if(this->isCamRunning) { if(SaveImg_Flag==1) { info = "存图中!!"; } else { info = "运行"; }; }else { info = "停止"; }; this->ui->lab_info->setText(info); ui->label_valve_actions->setText(QLocale(QLocale::English).toString(g_valveActionCount)); } void Widget::refreshSingleImage(int camera_id, bool overlay_traditional_result, bool overlay_dl_result, bool mirror) { // 验证摄像头ID的有效性 if (camera_id < 0 || camera_id >= 2) { // 假设只有两个摄像头 qWarning() << "Invalid Camera ID:" << camera_id; return; } // 定义每个摄像头对应的 QLabel QLabel* cameraLabels[2] = { ui->camera_0_img, ui->camera_1_img }; // 定义每个摄像头对应的变量数组 QMutex* dispPicMutexes[2] = { &gDispPicMutex0, &gDispPicMutex1 }; MIL_ID dispCurrentPicIds[2] = { gDispCurrentPicId0, gDispCurrentPicId1 }; // 获取当前摄像头的数据 QMutex* currentDispMutex = dispPicMutexes[camera_id]; MIL_ID current_id; { QMutexLocker locker(currentDispMutex); current_id = dispCurrentPicIds[camera_id]; } if (current_id == 0) return; // 将MIL图像转换为OpenCV Mat cv::Mat img = ImageUtils::mil2Mat(current_id); if (img.empty()) { qWarning() << "Failed to convert MIL image to Mat for Camera ID:" << camera_id; return; } if (current_id == 0) return; std::vector> dl_mask; std::vector> traditional_mask; { QMutexLocker locker(&g_detection_result[camera_id].mutex); dl_mask = g_detection_result[camera_id].dl_mask; traditional_mask = g_detection_result[camera_id].traditional_mask; } // 如果需要叠加结果,处理掩码 if (overlay_dl_result && g_dl_enable[camera_id]) { if (!dl_mask.empty() && !dl_mask[0].empty()) { // 将二维 vector 转换为 cv::Mat int rows = dl_mask.size(); int cols = dl_mask[0].size(); cv::Mat dl_mask_mat(rows, cols, CV_8U); for (int i = 0; i < rows; ++i) { if (dl_mask[i].size() != cols) { qWarning() << "Inconsistent mask row size for dl_mask in Camera ID:" << camera_id; dl_mask_mat.release(); break; } memcpy(dl_mask_mat.ptr(i), dl_mask[i].data(), cols); } if (!dl_mask_mat.empty()) { // 调整掩膜尺寸以匹配图像尺寸 if (dl_mask_mat.size() != img.size()) { cv::resize(dl_mask_mat, dl_mask_mat, img.size(), 0, 0, cv::INTER_NEAREST); } // 确保掩膜为二值图像 cv::threshold(dl_mask_mat, dl_mask_mat, 128, 255, cv::THRESH_BINARY); // 创建绿色掩膜 cv::Mat green_overlay(img.size(), img.type(), cv::Scalar(0, 255, 0)); green_overlay.setTo(cv::Scalar(0, 255, 0), dl_mask_mat); // 叠加掩膜到图像 cv::addWeighted(green_overlay, 0.5, img, 1.0, 0, img); } } } if (overlay_traditional_result & g_traditional_enable[camera_id]) { if (!traditional_mask.empty() && !traditional_mask[0].empty()) { // 将二维 vector 转换为 cv::Mat int rows = traditional_mask.size(); int cols = traditional_mask[0].size(); cv::Mat traditional_mask_mat(rows, cols, CV_8U); for (int i = 0; i < rows; ++i) { if (traditional_mask[i].size() != cols) { qWarning() << "Inconsistent mask row size for traditional_mask in Camera ID:" << camera_id; traditional_mask_mat.release(); break; } memcpy(traditional_mask_mat.ptr(i), traditional_mask[i].data(), cols); } if (!traditional_mask_mat.empty()) { // 调整掩膜尺寸以匹配图像尺寸 if (traditional_mask_mat.size() != img.size()) { cv::resize(traditional_mask_mat, traditional_mask_mat, img.size(), 0, 0, cv::INTER_NEAREST); } // 确保掩膜为二值图像 cv::threshold(traditional_mask_mat, traditional_mask_mat, 128, 255, cv::THRESH_BINARY); // 创建红色掩膜 cv::Mat red_overlay(img.size(), img.type(), cv::Scalar(0, 0, 255)); red_overlay.setTo(cv::Scalar(0, 0, 255), traditional_mask_mat); // 叠加掩膜到图像 cv::addWeighted(red_overlay, 0.5, img, 1.0, 0, img); } } } // 如果需要镜像处理 if (mirror) { cv::flip(img, img, 1); // 水平翻转 } // 将OpenCV Mat转换为QPixmap QPixmap pixmap = ImageUtils::mat2QPixmap(img); if (pixmap.isNull()) { qWarning() << "Failed to convert Mat to QPixmap for Camera ID:" << camera_id; return; } // 高质量缩放图像 QSize labelSize = cameraLabels[camera_id]->size(); QPixmap scaledPixmap = pixmap.scaled(labelSize); // 更新UI标签 cameraLabels[camera_id]->setPixmap(scaledPixmap); } void Widget::on_pushButton_2_clicked() { SaveImg_Flag = 1; } void Widget::on_btn_stop_clicked() { DestoryLowMac(); this->isCamRunning = false; // 恢复显示的图片 { QMutexLocker locker(&gDispPicMutex0); gDispCurrentPicId0 = 0; } ui->camera_0_img->clear(); { QMutexLocker locker(&gDispPicMutex1); gDispCurrentPicId1 = 0; } ui->camera_1_img->clear(); } void Widget::on_btn_start_clicked() { g_dl_enable[0] = !this->ui->dl_enable_0->isChecked(); g_dl_enable[1] = !this->ui->dl_enable_0->isChecked(); g_traditional_enable[0] = !this->ui->tra_enable_0->isChecked(); g_traditional_enable[1] = !this->ui->tra_enable_1->isChecked(); this->isCamRunning = true; this->startTime = QDateTime::currentDateTime(); // 从这里开始计算设备运行时间 // 热身两个工作者 for(int i = 0; i < 2; ++i) { g_runner_array[i]->warm_up(); } // 启动检测工作者线程 for(int i = 0; i < 2; ++i) { g_recognitionRunning[i]->store(true); g_recognitionThread[i] = new std::thread(detectionWorker, i); // 获取线程的本地句柄 HANDLE hThread = static_cast(g_recognitionThread[i]->native_handle()); // 设置线程优先级为最高 if(SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST)) { std::cout << "DL Thread " << i << " set highest thread priority。" << std::endl; } else { std::cerr << "SET thread " << i << " failed, error code:" << GetLastError() << std::endl; } } Start_camera(); } void Widget::on_btn_take_photos_pressed() { SaveImg_Flag = true; } void Widget::on_btn_take_photos_released() { SaveImg_Flag = false; } void Widget::on_btn_quit_clicked() { // // 停止检测工作者线程 // for(int i = 0; i < 2; ++i) // { // g_recognitionRunning[i]->store(false); // g_img_Queue[i]->stop(); // 停止队列以唤醒线程 // } // // 等待检测工作者线程结束 // for(int i = 0; i < 2; ++i) // { // if(g_recognitionThread[i] && g_recognitionThread[i]->joinable()) // { // g_recognitionThread[i]->join(); // delete g_recognitionThread[i]; // g_recognitionThread[i] = nullptr; // } // if(g_recognitionRunning[i]) // { // delete g_recognitionRunning[i]; // g_recognitionRunning[i] = nullptr; // } // } DestoryCamera(); DestoryLowMac(); qApp->quit(); } void Widget::on_btn_set_lower_clicked() { // 硬编码参数值 file_delay = ui->spinbox_delaytime->text().toInt(); // 延迟时间(毫秒) file_encoder = ui->spinbox_encoder->text().toInt(); // 编码器值++ file_valve = ui->spinbox_valve->text().toInt(); // 阀门通道 } void Widget::on_btn_set_valve_clicked() { ui->tabWidget->setCurrentIndex(2); } void Widget::on_btn_tab3_backtab2_clicked() { ui->tabWidget->setCurrentIndex(1); } void Widget::on_btn_live_clicked() { ui->tabWidget->setCurrentIndex(1); } void Widget::on_btn_tab3_backtab2_2_clicked() { ui->tabWidget->setCurrentIndex(3); } void Widget::on_btn_settings_clicked() { ui->tabWidget->setCurrentIndex(2); }