diff --git a/HPPA/CaptureCoordinator.cpp b/HPPA/CaptureCoordinator.cpp index b7fddfd..9679c23 100644 --- a/HPPA/CaptureCoordinator.cpp +++ b/HPPA/CaptureCoordinator.cpp @@ -97,6 +97,11 @@ void TwoMotionCaptureCoordinator::getLocBeforeStart() loop.exec(); + std::vector pos; + pos.push_back(0); + pos.push_back(0); + m_locBeforeStart = pos; + disconnect(conn); } diff --git a/HPPA/HPPA.cpp b/HPPA/HPPA.cpp index b151139..22f3fec 100644 --- a/HPPA/HPPA.cpp +++ b/HPPA/HPPA.cpp @@ -553,6 +553,20 @@ HPPA::HPPA(QWidget* parent) } )"); + m_ImageStretchThread = new QThread(this); + m_ImageStretchWorker = new ImageStretchWorker(); + m_ImageStretchWorker->moveToThread(m_ImageStretchThread); + // 注册QPixmap类型用于跨线程信号槽 + qRegisterMetaType("QPixmap&"); + qRegisterMetaType("cv::Mat"); + qRegisterMetaType("cv::Mat"); + connect(m_ImageStretchWorker, &ImageStretchWorker::imageReady, + this, &HPPA::onStretchedImageReady, Qt::QueuedConnection); + connect(m_ImageStretchWorker, &ImageStretchWorker::processingError, + this, &HPPA::onStretchProcessingError, Qt::QueuedConnection); + m_ImageStretchThread->start(); + + //光谱仪操作 m_Imager = nullptr; m_RecordThread = nullptr; @@ -1182,6 +1196,14 @@ HPPA::~HPPA() { s_instance = nullptr; } + + m_ImageStretchThread->quit(); + m_ImageStretchThread->wait(3000); + delete m_ImageStretchWorker; + m_ImageStretchWorker = nullptr; + delete m_ImageStretchThread; + m_ImageStretchThread = nullptr; + } void HPPA::removeLayerByTreeIndex() @@ -2626,14 +2648,20 @@ void HPPA::onPlotHyperspectralImageRgbImage(int fileNumber, int frameNumber, QSt QList currentImageViewer = currentWidget->findChildren(); currentImageViewer[0]->DisplayFrameNumber(m_Imager->getFrameCounter());//界面中显示已经采集的帧数 - //创建需要显示的图像--opencv版本 - ImageProcessor imageProcessor; - //cv::Mat rgbImage(*m_Imager->getRgbImage()->m_matRgbImage, cv::Range(0, m_Imager->getFrameCounter()), cv::Range::all());//2022.3.18重构的 cv::Mat rgbImage(*m_Imager->getMatRgbImage(), cv::Range(0, m_Imager->getFrameCounter()), cv::Range::all()); - cv::Mat rgbImageStretched = imageProcessor.CStretch(rgbImage, 0.02); + showQuickPreview(fileNumber, filePath, rgbImage); - //在界面中显示图像 - currentImageViewer[0]->SetImage(&QPixmap::fromImage(imageProcessor.Mat2QImage(rgbImageStretched)));//绘制图像 + + if (m_ImageStretchWorker) { + // 深拷贝图像数据,因为原始数据可能在处理完成前被修改 + cv::Mat rgbImageCopy = rgbImage.clone(); + QMetaObject::invokeMethod(m_ImageStretchWorker, "processImage", + Qt::QueuedConnection, + Q_ARG(cv::Mat, rgbImageCopy), + Q_ARG(int, fileNumber), + Q_ARG(QString, filePath), + Q_ARG(float, 0.02f)); + } //20241225 //保存拉伸后的rgb @@ -2652,6 +2680,43 @@ void HPPA::onPlotHyperspectralImageRgbImage(int fileNumber, int frameNumber, QSt //currentImageViewer[0]->SetImage(&QPixmap::fromImage(CuttedImage));//绘制图像 } +void HPPA::showQuickPreview(int fileNumber, const QString& filePath, const cv::Mat& rgbImage) +{ + // 可以显示一个简单缩放的预览图,或者显示"处理中..."的提示 + // 这里简单地跳过,等待拉伸后的图像 + // 如果需要快速预览,可以进行简单的缩放显示 + + /* + QWidget* currentWidget = m_MapLayerStore->widgetForLayer(filePath); + if (!currentWidget) return; + + QList currentImageViewer = currentWidget->findChildren(); + if (currentImageViewer.isEmpty()) return; + + // 显示一个简单的灰度或缩放版本作为预览 + cv::Mat preview; + cv::resize(rgbImage, preview, cv::Size(), 0.25, 0.25); // 缩小到25% + // ... 转换并显示 + */ +} + +void HPPA::onStretchedImageReady(int fileNumber, const QString& filePath, QPixmap& pixmap) +{ + QWidget* currentWidget = m_MapLayerStore->widgetForLayer(filePath); + if (!currentWidget) return; + QList currentImageViewer = currentWidget->findChildren(); + if (currentImageViewer.isEmpty()) return; + // 在界面中显示拉伸后的图像 + currentImageViewer[0]->SetImage(&pixmap); +} +// 处理错误的回调 +void HPPA::onStretchProcessingError(int fileNumber, const QString& filePath, const QString& error) +{ + qDebug() << "Image stretch error for file" << filePath << ":" << error; + // 可以选择显示错误信息或保留之前的图像 +} + + void HPPA::focusPlotSpectralImg(int state) { if (!m_Imager) return; @@ -2966,3 +3031,185 @@ void HPPA::onBandSelectionChanged(double rWave, double gWave, double bWave) mapcavas[0]->freshmap(); } + + +//-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +ImageStretchWorker::ImageStretchWorker(QObject* parent) + : QObject(parent) +{ +} + +ImageStretchWorker::~ImageStretchWorker() +{ +} + +void ImageStretchWorker::processImage(const cv::Mat& rgbImage, int fileNumber, const QString& filePath, float ratio) +{ + try { + if (rgbImage.empty()) { + emit processingError(fileNumber, filePath, "Empty image"); + return; + } + + // 执行耗时的拉伸操作 + cv::Mat rgbImageStretched = CStretch(rgbImage, ratio); + + // 转换为QImage再转为QPixmap + QImage qimg = Mat2QImage(rgbImageStretched); + QPixmap pixmap = QPixmap::fromImage(qimg); + + emit imageReady(fileNumber, filePath, pixmap); + } + catch (const std::exception& e) { + emit processingError(fileNumber, filePath, QString::fromStdString(e.what())); + } +} + +std::vector ImageStretchWorker::CHistogram(const cv::Mat& img) +{ + cv::Mat mimg = img.clone(); + int rows = mimg.rows; + int cols = mimg.cols; + int maxValue = 65535; + + std::vector hisnum(maxValue, 0); + for (int i = 0; i < rows; ++i) + { + for (int j = 0; j < cols; ++j) + { + unsigned short gv = mimg.at(i, j); + if (gv >= maxValue) + { + ++hisnum[0]; + } + else + { + ++hisnum[gv]; + } + } + } + + long int pnum = rows * cols; + std::vector hisp; + hisp.reserve(hisnum.size()); + for (size_t i = 0; i < hisnum.size(); ++i) + { + float p = static_cast(hisnum[i]) / pnum; + hisp.push_back(cv::Point2f(static_cast(i), p)); + } + return hisp; +} + +uint ImageStretchWorker::MaxRatio(const std::vector& data, float ratio) +{ + float maxp = 0; + uint outnum = 0; + for (int i = static_cast(data.size()) - 1; i >= 0; --i) + { + maxp += data[i].y; + if (maxp >= ratio) + { + outnum = i; + break; + } + } + return outnum; +} + +uint ImageStretchWorker::MinRatio(const std::vector& data, float ratio) +{ + float minp = 0; + uint outnum = 0; + for (size_t i = 0; i < data.size(); ++i) + { + minp += data[i].y; + if (minp >= ratio) + { + outnum = static_cast(i); + break; + } + } + return outnum; +} + +cv::Mat ImageStretchWorker::CStretchDeal(const cv::Mat& img, uint minnum, uint maxnum) +{ + cv::Mat mimg = img.clone(); + int rows = mimg.rows; + int cols = mimg.cols; + cv::Mat nimg = cv::Mat::zeros(rows, cols, CV_8U); + + float dertnum = static_cast(maxnum - minnum); + + for (int i = 0; i < rows; ++i) + { + for (int j = 0; j < cols; ++j) + { + unsigned short a = mimg.at(i, j); + if (a <= minnum) + nimg.at(i, j) = 0; + else if (a >= maxnum) + nimg.at(i, j) = 255; + else + nimg.at(i, j) = static_cast(255 * (a - minnum) / dertnum); + } + } + return nimg; +} + +cv::Mat ImageStretchWorker::CStretch(const cv::Mat& img, float ratio) +{ + cv::Mat mimg = img.clone(); + cv::Mat bgr[3]; + cv::split(mimg, bgr); + + std::vector hb = CHistogram(bgr[0]); + std::vector hg = CHistogram(bgr[1]); + std::vector hr = CHistogram(bgr[2]); + + uint minb = MinRatio(hb, ratio); + uint ming = MinRatio(hg, ratio); + uint minr = MinRatio(hr, ratio); + uint maxb = MaxRatio(hb, ratio); + uint maxg = MaxRatio(hg, ratio); + uint maxr = MaxRatio(hr, ratio); + + cv::Mat b = CStretchDeal(bgr[0], minb, maxb); + cv::Mat g = CStretchDeal(bgr[1], ming, maxg); + cv::Mat r = CStretchDeal(bgr[2], minr, maxr); + + cv::Mat newbgr; + newbgr.create(img.rows, img.cols, CV_32FC3); + cv::Mat nbgr[3] = { b, g, r }; + cv::merge(nbgr, 3, newbgr); + return newbgr; +} + +QImage ImageStretchWorker::Mat2QImage(const cv::Mat& cvImg) +{ + QImage qImg; + if (cvImg.channels() == 3) + { + cv::Mat rgbImg; + cv::cvtColor(cvImg, rgbImg, cv::COLOR_BGR2RGB); + qImg = QImage(rgbImg.data, + rgbImg.cols, rgbImg.rows, + static_cast(rgbImg.step), + QImage::Format_RGB888).copy(); // 使用copy()确保数据独立 + } + else if (cvImg.channels() == 1) + { + qImg = QImage(cvImg.data, + cvImg.cols, cvImg.rows, + static_cast(cvImg.step), + QImage::Format_Indexed8).copy(); + } + else + { + qImg = QImage(cvImg.data, + cvImg.cols, cvImg.rows, + static_cast(cvImg.step), + QImage::Format_RGB888).copy(); + } + return qImg; +} diff --git a/HPPA/HPPA.h b/HPPA/HPPA.h index 88c03be..b6398d3 100644 --- a/HPPA/HPPA.h +++ b/HPPA/HPPA.h @@ -88,6 +88,8 @@ #include "PowerControl3D.h" +#include + #define PI 3.1415926 QT_CHARTS_USE_NAMESPACE//QChartView 使用 需要加宏, 否则无法使用 @@ -194,6 +196,26 @@ private: QPixmap m_pixmap; }; +class ImageStretchWorker : public QObject +{ + Q_OBJECT +public: + explicit ImageStretchWorker(QObject* parent = nullptr); + ~ImageStretchWorker(); +public slots: + void processImage(const cv::Mat& rgbImage, int fileNumber, const QString& filePath, float ratio = 0.02f); +signals: + void imageReady(int fileNumber, const QString& filePath, QPixmap& pixmap); + void processingError(int fileNumber, const QString& filePath, const QString& error); +private: + cv::Mat CStretch(const cv::Mat& img, float ratio); + cv::Mat CStretchDeal(const cv::Mat& img, uint minnum, uint maxnum); + std::vector CHistogram(const cv::Mat& img); + uint MaxRatio(const std::vector& data, float ratio); + uint MinRatio(const std::vector& data, float ratio); + QImage Mat2QImage(const cv::Mat& cvImg); +}; + class HPPA : public QMainWindow { Q_OBJECT @@ -325,6 +347,13 @@ private: void initTimedDataCollection(); + QThread* m_ImageStretchThread = nullptr; + ImageStretchWorker* m_ImageStretchWorker = nullptr; + + // 用于临时显示的未拉伸图像(可选,用于快速预览) + void showQuickPreview(int fileNumber, const QString& filePath, const cv::Mat& rgbImage); + + public Q_SLOTS: void onPlotHyperspectralImageRgbImage(int fileNumber, int frameNumber, QString filePath); void focusPlotSpectralImg(int state); @@ -408,6 +437,10 @@ public Q_SLOTS: void setTimedDataCollectionCamParm(int camType, int captureIntervalSeconds, QString folder); void setTimedDataCollectionMotorParm(QString pathLineFilePath); void onStartTimedDataCollection(int camType); + + void onStretchedImageReady(int fileNumber, const QString& filePath, QPixmap& pixmap); + void onStretchProcessingError(int fileNumber, const QString& filePath, const QString& error); + protected: void closeEvent(QCloseEvent* event) override; diff --git a/HPPA/TaskTreeModel.cpp b/HPPA/TaskTreeModel.cpp index a175444..2973124 100644 --- a/HPPA/TaskTreeModel.cpp +++ b/HPPA/TaskTreeModel.cpp @@ -212,13 +212,16 @@ QVariant TaskTreeModel::data(const QModelIndex& index, int role) const TaskStatus status = TaskStatus::Waiting; if (node->nodeType == TreeNodeType::Task && node->taskData) { status = node->taskData->status; + return QBrush(QColor("#E2EDFF")); // 深绿色文字 } else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) { status = node->subTaskData->status; + return QBrush(QColor("#E2EDFF")); // 深绿色文字 } if (status == TaskStatus::Running) { - return QBrush(QColor(0, 100, 0)); // 深绿色文字 + return QBrush(QColor("#E2EDFF")); // 深绿色文字 } + return QBrush(QColor("#E2EDFF")); // 深绿色文字 } else if (role == Qt::FontRole) { QFont font; @@ -615,12 +618,12 @@ QString TaskTreeModel::formatDuration(double minutes) const QColor TaskTreeModel::statusColor(TaskStatus status) const { switch (status) { - case TaskStatus::Waiting: return QColor(255, 255, 230); // 浅黄 - case TaskStatus::Running: return QColor(200, 255, 200); // 浅绿 - case TaskStatus::Finished: return QColor(220, 220, 255); // 浅蓝 - case TaskStatus::Skiped: return QColor(220, 220, 220); // 浅灰 + case TaskStatus::Waiting: return QColor("#121945"); // 浅黄 + case TaskStatus::Running: return QColor("#121945"); // 浅绿 + case TaskStatus::Finished: return QColor("#121945"); // 浅蓝 + case TaskStatus::Skiped: return QColor("#121945"); // 浅灰 } - return QColor(255, 255, 255); + return QColor("#121945"); } QString TaskTreeModel::formatCountdown(qint64 seconds) const diff --git a/HPPA/TimedDataCollection.cpp b/HPPA/TimedDataCollection.cpp index 73cc313..15c0c47 100644 --- a/HPPA/TimedDataCollection.cpp +++ b/HPPA/TimedDataCollection.cpp @@ -77,8 +77,8 @@ void TimedDataCollection::setupUI() // 创建状态标签 ui.statusLabel->setStyleSheet( "QLabel {" - " background-color: #f0f0f0;" - " border: 1px solid #ccc;" + " background-color: #3856B8;" + " border: 1px solid #2f6bff;" " border-radius: 4px;" " padding: 8px;" " font-size: 12px;" @@ -215,11 +215,11 @@ void TimedDataCollection::onErrorOccurred(const QString& error) ui.statusLabel->setText(QString::fromLocal8Bit("错误: %1").arg(error)); ui.statusLabel->setStyleSheet( "QLabel {" - " background-color: #ffebee;" - " border: 1px solid #f44336;" + " background-color: #3856B8;" + " border: 1px solid #2f6bff;" " border-radius: 4px;" " padding: 8px;" - " color: #c62828;" + " color: #e6eeff;" "}" ); @@ -354,11 +354,11 @@ void TimedDataCollection::updateStatusBar() styleSheet = "QLabel {" - " background-color: #e8f5e9;" - " border: 1px solid #4CAF50;" + " background-color: #3856B8;" + " border: 1px solid #2f6bff;" " border-radius: 4px;" " padding: 8px;" - " color: #2e7d32;" + " color: #e6eeff;" " font-weight: bold;" "}"; } @@ -381,8 +381,8 @@ void TimedDataCollection::updateStatusBar() styleSheet = "QLabel {" - " background-color: #f5f5f5;" - " border: 1px solid #ccc;" + " background-color: #3856B8;" + " border: 1px solid #2f6bff;" " border-radius: 4px;" " padding: 8px;" "}"; @@ -466,11 +466,11 @@ void TimedDataCollection::startScheduler() ui.statusLabel->setText(QString::fromLocal8Bit("调度器已启动,等待任务执行...")); ui.statusLabel->setStyleSheet( "QLabel {" - " background-color: #fff3e0;" - " border: 1px solid #ff9800;" + " background-color: #3856B8;" + " border: 1px solid #2f6bff;" " border-radius: 4px;" " padding: 8px;" - " color: #e65100;" + " color: #e6eeff;" "}"); } } @@ -492,11 +492,11 @@ void TimedDataCollection::stopScheduler() ui.statusLabel->setText(QString::fromLocal8Bit("调度器已停止")); ui.statusLabel->setStyleSheet( "QLabel {" - " background-color: #fce4ec;" - " border: 1px solid #e91e63;" + " background-color: #3856B8;" + " border: 1px solid #2f6bff;" " border-radius: 4px;" " padding: 8px;" - " color: #880e4f;" + " color: #e6eeff;" "}"); } } diff --git a/HPPA/TimedDataCollectionDataStructures.cpp b/HPPA/TimedDataCollectionDataStructures.cpp index 490097b..bbf469a 100644 --- a/HPPA/TimedDataCollectionDataStructures.cpp +++ b/HPPA/TimedDataCollectionDataStructures.cpp @@ -206,7 +206,7 @@ bool TimedDataCollectionDataStructuresReaderWriter::jsonToTimedTask(const QJsonO TaskExecutor::TaskExecutor(QObject* parent) : QObject(parent) - , m_currentSubTaskIndex(0) + , m_currentSubTaskIndex(-1) , m_isRunning(false) { } @@ -230,7 +230,7 @@ void TaskExecutor::execute(const TimedTask& task) emit taskUpdated(m_task); - m_currentSubTaskIndex = 0; + m_currentSubTaskIndex = -1; m_isRunning = true; qDebug() << "TaskExecutor: Starting task" << task.id; @@ -322,7 +322,6 @@ void TaskExecutor::onSequenceComplete(int status) case SubTaskType::SingleLensReflex: { emit switchD65LampSignal(0); - emit switchSlrSignal(0); break; } case SubTaskType::DepthCamera: @@ -360,11 +359,21 @@ void TaskExecutor::onSequenceComplete(int status) void TaskExecutor::onBack2Origin() { + // 关闭单反电源 + switch (m_task.subTasks[m_currentSubTaskIndex].type) + { + case SubTaskType::SingleLensReflex: + { + emit switchSlrSignal(0); + break; + } + } + // 检查是否还有更多子任务 - m_currentSubTaskIndex++; - if (m_currentSubTaskIndex < m_task.subTasks.size()) { + int nestSubTaskIndex = m_currentSubTaskIndex + 1; + if (nestSubTaskIndex < m_task.subTasks.size()) { // 执行下一个子任务 - if(m_task.subTasks[m_currentSubTaskIndex].type== SubTaskType::SingleLensReflex) + if(m_task.subTasks[nestSubTaskIndex].type== SubTaskType::SingleLensReflex) { printMsgAndTime("Slr task,for weak up,please wait 135 seconds!"); emit switchSlrSignal(0); @@ -403,7 +412,10 @@ void TaskExecutor::onError(const QString& error) void TaskExecutor::executeNextSubTask() { - if (!m_isRunning || m_currentSubTaskIndex >= m_task.subTasks.size()) { + m_currentSubTaskIndex++; + + if (!m_isRunning || m_currentSubTaskIndex >= m_task.subTasks.size()) + { return; } diff --git a/HPPA/TimedDataCollection_ui.ui b/HPPA/TimedDataCollection_ui.ui index 98f5ceb..d958e1e 100644 --- a/HPPA/TimedDataCollection_ui.ui +++ b/HPPA/TimedDataCollection_ui.ui @@ -6,67 +6,200 @@ 0 0 - 1182 - 576 + 1299 + 658 定时采集 - - - - - - - - true - - - - - - - ... - - - - - - - 开始调度 - - - - - - - 展开 - - - - - - - 停止调度 - - - - - - - 折叠 - - - - - - - 任务统计 - - - Qt::AlignCenter - + + QWidget #contentWidget +{ + background: #192770; + /*border-radius: 8px 8px 8px 8px;*/ + border: 1px solid #2f6bff; +} + +QLineEdit { + background-color: #3856B8; + color: #e6eeff; + border: 1px solid #2f6bff; + border-radius: 6px; + padding: 4px 8px; + min-width: 70px; + min-height: 20px; + font-size: 13px; +} + +QLabel +{ + background: #243A82; + color: rgb(255, 255, 255); + border: 1px solid #2f6bff; + border-radius: 6px; +} + +QPushButton +{ + /*width: 172px; + height: 56px;*/ + font: 10pt "新宋体"; + background-color: qlineargradient( + spread:pad, + x1:0.5, y1:0, x2:0.5, y2:1, + stop:0 #283D86, + stop:1 #0F1A40 + ); + color: white; + border: none; + padding: 8px 8px; + border-radius: 4px; +} +QPushButton:hover +{ + background-color: qlineargradient( + spread:pad, + x1:0, y1:0, x2:1, y2:0, + stop:0 #3A4875, + stop:1 #5F6B91 + ); +} +/* 按下时的效果 */ +QPushButton:pressed +{ + background-color: qlineargradient( + spread:pad, + x1:0, y1:0, x2:1, y2:0, + stop:0 #1A254F, + stop:1 #3A466B + ); + /* 可选:添加下压效果 */ + padding-top: 9px; + padding-bottom: 7px; +} + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + QTreeView +{ + background-color: #121945; + border: 0px solid #444; +} + +QHeaderView::section +{ + background-color: #121945; + color: #ACCDFF; + + padding: 6px; +} + +QScrollBar:vertical { + background: #0b1440; + width: 10px; + border: 1px solid #1f2a5a; +} + +QScrollBar:horizontal { + background: #0b1440; + height: 10px; + border: 1px solid #1f2a5a; +} + +QScrollBar::handle:vertical, +QScrollBar::handle:horizontal { + background: #3a5bd6; + border-radius: 4px; + min-height: 20px; + min-width: 20px; +} + +QScrollBar::handle:vertical:hover, +QScrollBar::handle:horizontal:hover { + background: #4f73ff; +} + + + + + + + + true + + + + + + + ... + + + + + + + 开始调度 + + + + + + + 展开 + + + + + + + 停止调度 + + + + + + + 折叠 + + + + + + + + + + 任务统计 + + + Qt::AlignCenter + + + + diff --git a/HPPA/about.ui b/HPPA/about.ui index 822918d..e13f72e 100644 --- a/HPPA/about.ui +++ b/HPPA/about.ui @@ -288,7 +288,7 @@ QPushButton:pressed } - 版本:3.0.2 + 版本:3.1.0