6 Commits

Author SHA1 Message Date
0562e8592c add,山地所贡嘎山11:
完成整个采集流程
2026-09-15 14:58:27 +08:00
9bc2133e24 add,山地所贡嘎山10:
优化rgb相机控制,添加采集视频和照片的逻辑;
2026-09-10 17:11:44 +08:00
e552dc2ed5 add,山地所贡嘎山9:
1、解析gps数据;
2、帧率与旋转速度计算器:给定帧率、高度、FOV、传感器像元数,计算旋转速度;
3、修改采集逻辑:(1)采集gps(2)高光谱曝光:以10hz为下限,在保证曝光质量的前提下提高帧率;(3)fodis曝光;(4)以步骤2中的帧率为基础,结合高度FOV等信息计算旋转速度;(5)采集数据;
2026-09-08 16:25:05 +08:00
2e7bf50737 fix:IR和IR L
1、IR L采集到的一帧有异常,本来14位,最大值应该为16383,但是最右边有异常大的值(大概50000),导致自动曝光失败;
2、IR(以前的NIR)和IR L都是14位的,修改WriteHdr();
2026-09-07 09:16:51 +08:00
1a64fb32e3 fix
1、默认渲染波段:可见光、近红外,采集中、打开文件;
2026-09-04 10:27:57 +08:00
ebc39f9f9d add,山地所贡嘎山8:
1、在类GonggashanTaskExecutor中状态机协调控制整个采集流程;
2026-09-03 15:50:29 +08:00
34 changed files with 2871 additions and 239 deletions

View File

@ -120,6 +120,41 @@ void AppSettings::setFiberImagerDataFolder(const QString& path)
m_settings.setValue("General/FiberImagerDataFolder", path);
}
QString AppSettings::rgbCameraDataFolder() const
{
QString path = m_settings.value("RgbCamera/RgbCameraDataFolder", "D:").toString();
if (path.isEmpty())
{
return QCoreApplication::applicationDirPath() + "/CapturedRgbCameraData/";
}
return path;
}
void AppSettings::setRgbCameraDataFolder(const QString& path)
{
m_settings.setValue("RgbCamera/RgbCameraDataFolder", path);
}
QString AppSettings::rgbCameraFileName() const
{
return m_settings.value("RgbCamera/FileName", "test_rgb_data").toString();
}
void AppSettings::setRgbCameraFileName(const QString& name)
{
m_settings.setValue("RgbCamera/FileName", name);
}
QString AppSettings::fodisCameraFileName() const
{
return m_settings.value("FodisCamera/FileName", "test_fodis_data").toString();
}
void AppSettings::setFodisCameraFileName(const QString& name)
{
m_settings.setValue("FodisCamera/FileName", name);
}
double AppSettings::scanSpeed() const
{
return m_settings.value("OneMotorControl/ScanSpeed", kDefaultScanSpeed).toDouble();

View File

@ -38,6 +38,16 @@ public:
QString FiberImagerDataFolder() const;
void setFiberImagerDataFolder(const QString& path);
QString rgbCameraDataFolder() const;
void setRgbCameraDataFolder(const QString& path);
// RGB相机文件名
QString rgbCameraFileName() const;
void setRgbCameraFileName(const QString& name);
QString fodisCameraFileName() const;
void setFodisCameraFileName(const QString& name);
// 扫描速度
double scanSpeed() const;
void setScanSpeed(double value);

View File

@ -1,4 +1,5 @@
#include "CaptureCoordinator.h"
#include <algorithm>
TwoMotionCaptureCoordinator::TwoMotionCaptureCoordinator(
IrisMultiMotorController* motorCtrl,
@ -458,7 +459,9 @@ OneMotionCaptureCoordinator::OneMotionCaptureCoordinator(
connect(this, &OneMotionCaptureCoordinator::stopRecordHSISignal,
m_cameraCtrl, &ImagerOperationBase::stop_record);
connect(m_cameraCtrl, &ImagerOperationBase::RecordFinishedSignal_WhenFrameNumberMeet,
this, &OneMotionCaptureCoordinator::handleCaptureCompleteWhenFrameNumberMeet);
this, &OneMotionCaptureCoordinator::handleHyperImagerCaptureComplete);
connect(m_cameraCtrl, &ImagerOperationBase::RecordFinishedSignal_WhenFrameNumberNotMeet,
this, &OneMotionCaptureCoordinator::handleHyperImagerCaptureComplete);
}
OneMotionCaptureCoordinator::~OneMotionCaptureCoordinator()
@ -497,12 +500,9 @@ void OneMotionCaptureCoordinator::stopStepMotion()
{
m_cameraCtrl->stop_record();
}
emit stopMotorSignal(0);
m_isHypercamStopRecord = true;
}
void OneMotionCaptureCoordinator::handleCaptureCompleteWhenFrameNumberMeet()
void OneMotionCaptureCoordinator::handleHyperImagerCaptureComplete()
{
emit stopMotorSignal(0);
m_isHypercamStopRecord = true;
@ -587,13 +587,11 @@ void OneMotionCaptureCoordinator::handleMotorStoped(int motorID, double pos)
}
move2LocBeforeStart();
emit sequenceComplete_cam_stop_before_motorback(0);
emit sequenceCompleteSignal_hyperImagerStopRecord(0);
}
else
{
// emit sequenceComplete last: the slot connected to it may delete this object,
// so no member access is allowed after this point.
emit sequenceComplete(0);
emit sequenceCompleteSignal_motorBack2Origin(0);
}
}
@ -972,3 +970,265 @@ double OneMotionCoordinator::getErrorRate(double targetLoc, double actualLoc)
return errorRate;
}
//---------------------------------------------------------------------------------------------------------------------------------------------
OneMotorMultiPosCoordinator::OneMotorMultiPosCoordinator(
IrisMultiMotorController* motorCtrl,
ImagerOperationBase* cameraCtrl,
QObject* parent)
: QObject(parent)
, m_motorCtrl(motorCtrl)
, m_cameraCtrl(cameraCtrl)
, m_currentPos(0)
, m_isRunning(false)
, m_isZeroing(false)
{
//这些信号槽是按照逻辑顺序的
connect(this, SIGNAL(moveTo(int, double, double, int)),
m_motorCtrl, SLOT(moveTo(int, double, double, int)));
connect(this, &OneMotorMultiPosCoordinator::zeroStart,
m_motorCtrl, &IrisMultiMotorController::zeroStart);
connect(m_motorCtrl, &IrisMultiMotorController::motorStopSignal,
this, &OneMotorMultiPosCoordinator::handlePositionReached);
//connect(m_motorCtrl, &IrisMultiMotorController::moveFailed,
// this, &OneMotorMultiPosCoordinator::handleError);
connect(this, &OneMotorMultiPosCoordinator::startAutoExposureSignal,
m_cameraCtrl, &ImagerOperationBase::auto_exposure);
connect(m_cameraCtrl, &ImagerOperationBase::autoExposureSignal,
this, &OneMotorMultiPosCoordinator::onAutoExposureFinished);
//connect(m_cameraCtrl, &ImagerOperationBase::captureFailed,
// this, &OneMotorMultiPosCoordinator::handleError);
}
OneMotorMultiPosCoordinator::~OneMotorMultiPosCoordinator()
{
}
void OneMotorMultiPosCoordinator::startStepMotion(double speed, std::vector<double> locations)
{
QMutexLocker locker(&m_dataMutex);
if (locations.empty())
{
emit sequenceComplete(1);
return;
}
if (m_isRunning)
{
emit errorOccurred("Sequence already running");
return;
}
m_locations = locations;
m_counter = 0;
m_positionData.clear();
m_speed = speed;
m_iStepIntervalRealTime = 1;
m_isRunning = true;
m_isZeroing = true;
// 先执行归零操作
emit zeroStart(0);
qDebug() << "OneMotorMultiPosCoordinator::startStepMotion: Zeroing started.";
}
void OneMotorMultiPosCoordinator::startMotionSequence()
{
QMutexLocker locker(&m_dataMutex);
m_isZeroing = false;
qDebug() << "OneMotorMultiPosCoordinator::startMotionSequence: Zeroing complete. Starting motion sequence.";
processNextPosition();
}
void OneMotorMultiPosCoordinator::handleZeroComplete(int motorID, double pos)
{
if (!m_isRunning || !m_isZeroing) return;
// 归零完成,开始分步运动
startMotionSequence();
}
void OneMotorMultiPosCoordinator::stopStepMotion()
{
QMutexLocker locker(&m_dataMutex);
m_isRunning = false;
emit sequenceStopped();
}
QVector<PositionsLogData> OneMotorMultiPosCoordinator::getAllPositionData() const
{
//QMutexLocker locker(&m_dataMutex);
return m_positionData;
}
bool OneMotorMultiPosCoordinator::saveToCsv(const QString& filename)
{
//QMutexLocker locker(&m_dataMutex);
QFile file(filename);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
{
return false;
}
QTextStream out(&file);
out << "Timestamp,targetPosition,ActualPosition,exposureTime\n";
for (const auto& data : m_positionData)
{
out << data.timestamp.toString("yyyy-MM-dd HH:mm:ss.zzz") << ","
<< QString::number(data.targetPosition, 'f', 4) << ","
<< QString::number(data.actualPosition, 'f', 4) << ","
<< QString::number(data.exposureTime, 'f', 4) << "\n";
}
file.close();
return true;
}
void OneMotorMultiPosCoordinator::handlePositionReached(int motorID, double pos)
{
if (!m_isRunning) return;
// 如果正在等待归零完成,调用归零完成处理
if (m_isZeroing)
{
handleZeroComplete(motorID, pos);
return;
}
QMutexLocker locker(&m_dataMutex);
//验证马达运动位置是否到达指定位置
//if (pos != m_currentPos) return;
// 记录位置信息
PositionsLogData data;
data.targetPosition = m_currentPos;
data.actualPosition = pos;
data.timestamp = QDateTime::currentDateTime();
data.frameRate = 10;
m_positionData.append(data);
// 开始自动曝光
m_cameraCtrl->setFramerate(m_positionData.last().frameRate);
emit startAutoExposureSignal();
}
void OneMotorMultiPosCoordinator::onAutoExposureFinished(double exposureTime)
{
if (!m_isRunning) return;
QMutexLocker locker(&m_dataMutex);
// 在保证曝光质量的前提下,尽量提高帧率,帧率下限为10hz
m_positionData.last().exposureTime = exposureTime;
std::cout << "第" << m_counter << "次曝光:" << std::endl;
std::cout << "目标位置:" << m_positionData.last().targetPosition << std::endl;
std::cout << "实际位置:" << m_positionData.last().actualPosition << std::endl;
std::cout << "曝光时间:" << m_positionData.last().exposureTime << std::endl;
// 如果曝光时间过低(< 2ms),基于曝光时间计算新的帧率并重新进行自动曝光
if (exposureTime < 2.0) {
int currentFrameRate = m_positionData.last().frameRate;
// 基于曝光时间计算理论最大帧率:fps = 1000 / exposureTime(ms)
// 乘以0.8作为安全系数,确保不会达到极限
int newFrameRate = static_cast<int>((1000.0 / exposureTime) * 0.8);
// 设置合理的帧率范围:[currentFrameRate + 10, min(200, newFrameRate)]
newFrameRate = std::max(currentFrameRate + 10, newFrameRate);
newFrameRate = std::min(newFrameRate, 200);
m_positionData.last().frameRate = newFrameRate;
std::cout << "曝光时间过低,基于曝光时间计算新帧率:" << currentFrameRate << " -> " << newFrameRate << std::endl;
std::cout << "(曝光时间 " << exposureTime << "ms -> 理论最大帧率 " << (1000.0 / exposureTime) << "fps)" << std::endl;
m_cameraCtrl->setFramerate(newFrameRate);
}
processNextPosition();
}
void OneMotorMultiPosCoordinator::handleError(const QString& error)
{
QMutexLocker locker(&m_dataMutex);
m_isRunning = false;
emit errorOccurred(error);
}
void OneMotorMultiPosCoordinator::processNextPosition()
{
if (!m_isRunning) return;
if (m_locations.empty())
{
m_isRunning = false;
emit sequenceComplete(0);
// 找到曝光时间最大的那组数据
double maxExposureTime = 0.0;
double maxFrameRate = 10;
for (const auto& data : m_positionData) {
if (data.exposureTime > maxExposureTime) {
maxExposureTime = data.exposureTime;
maxFrameRate = data.frameRate;
}
}
std::cout << "自动曝光完成,使用最大曝光时间参数:" << std::endl;
std::cout << " 曝光时间:" << maxExposureTime << "ms" << std::endl;
std::cout << " 帧率:" << maxFrameRate << "Hz" << std::endl;
emit hyperAutoExposureDoneSignal(maxExposureTime, maxFrameRate);
m_cameraCtrl->setIntegrationTime(maxExposureTime);
m_cameraCtrl->setFramerate(maxFrameRate);
return;
}
m_currentPos = m_locations.front();
m_locations.erase(m_locations.begin());
emit moveTo(0, m_currentPos, m_speed, 1000);
}

View File

@ -141,11 +141,11 @@ public slots:
void startStepMotion(OneMotionCapturePathLine pathLine);
void stopStepMotion();
void handleCaptureCompleteWhenFrameNumberMeet();
void handleHyperImagerCaptureComplete();
signals:
void sequenceComplete_cam_stop_before_motorback(int);
void sequenceComplete(int);
void sequenceCompleteSignal_hyperImagerStopRecord(int);
void sequenceCompleteSignal_motorBack2Origin(int);
void errorOccurred(const QString& error);
void moveTo(int, double, double, int);
void moveSignal(int, bool, double, int);
@ -300,3 +300,75 @@ private:
int m_retryTimes;
bool m_reached;
};
// 数据记录结构体
struct PositionsLogData
{
double targetPosition; // 目标位置
double actualPosition; // 实际马达位置
double frameRate; // 帧率
double exposureTime; //
QDateTime timestamp; // 时间戳
PositionsLogData(double target = 0, double actual = 0.0, double exposure = 0.0, double frameRate = 10.0)
: targetPosition(target), actualPosition(actual), frameRate(frameRate),
exposureTime(exposure ), timestamp(QDateTime::currentDateTime()) {
}
};
// 协调控制器
class OneMotorMultiPosCoordinator : public QObject
{
Q_OBJECT
public:
OneMotorMultiPosCoordinator(IrisMultiMotorController* motorCtrl,
ImagerOperationBase* cameraCtrl,
QObject* parent = nullptr);
~OneMotorMultiPosCoordinator();
QVector<PositionsLogData> getAllPositionData() const;
bool saveToCsv(const QString& filename);
public slots:
void startStepMotion(double speed, std::vector<double> locations);
void stopStepMotion();
signals:
void progressChanged(int progress);
void sequenceComplete(int status);
void sequenceStopped();
void errorOccurred(const QString& error);
void moveTo(int, double, double, int);
void startAutoExposureSignal();
void zeroStart(int motorID);
void hyperAutoExposureDoneSignal(double exposureTime, double frameRate);
private slots:
void handlePositionReached(int motorID, double pos);
void onAutoExposureFinished(double exposureTime);
void handleError(const QString& error);
void handleZeroComplete(int motorID, double pos);
private:
void processNextPosition();
void startMotionSequence();
IrisMultiMotorController* m_motorCtrl;
ImagerOperationBase* m_cameraCtrl;
QVector<PositionsLogData> m_positionData;
mutable QMutex m_dataMutex;
double m_currentPos;
bool m_isRunning;
double m_speed;
int m_iStepInterval;
int m_iStepIntervalRealTime;
int m_counter;
bool m_isZeroing;
std::vector<double> m_locations;
};

View File

@ -50,7 +50,7 @@ private:
public slots:
virtual void recordDark(QString path) = 0;
virtual void recordTarget(int recordTimes, QString path) = 0;
virtual void recordTarget2csv(int recordTimes, QString path) = 0;
virtual void autoExpose() = 0;
signals:

View File

@ -27,10 +27,9 @@ FodisWindow::FodisWindow(QWidget* parent)
connect(this->ui.dataFolderBtn, SIGNAL(clicked()), this, SLOT(onSelectDataFolder()));
// 初始化数据保存路径显示(从 AppSettings 恢复或使用默认)
ui.dataFolderLineEdit->setText(AppSettings::instance().depthCameraDataFolder());
connect(ui.fileNameLineEdit, &QLineEdit::textChanged, this, &FodisWindow::onFileNameChanged);
setDataFolder(AppSettings::instance().FiberImagerDataFolder());
loadSettings();
}
FodisWindow::~FodisWindow()
@ -41,6 +40,12 @@ FodisWindow::~FodisWindow()
m_JinspFiberImagerOperation = nullptr;
}
void FodisWindow::loadSettings()
{
ui.dataFolderLineEdit->setText(AppSettings::instance().FiberImagerDataFolder());
ui.fileNameLineEdit->setText(AppSettings::instance().fodisCameraFileName());
}
void FodisWindow::onSelectDataFolder()
{
QString dir = QFileDialog::getExistingDirectory(this,
@ -59,6 +64,17 @@ void FodisWindow::setDataFolder(QString dir)
}
}
void FodisWindow::setFileName(QString name)
{
ui.fileNameLineEdit->setText(name);
AppSettings::instance().setFodisCameraFileName(name);
}
void FodisWindow::onFileNameChanged(const QString& text)
{
AppSettings::instance().setFodisCameraFileName(text);
}
void FodisWindow::setCaptureInterval(int captureIntervalSeconds)
{
m_JinspFiberImagerOperation->setCaptureInterval(captureIntervalSeconds);
@ -69,11 +85,24 @@ void FodisWindow::openFiberImager()
openFiberImager_expose_record("pos_null");
}
void FodisWindow::openFiberImager_expose_record(QString posInfo)
void FodisWindow::openFiberImager_expose_record(QString posInfo, QString dataFolder)
{
setDataFolder(dataFolder);
setFileName(posInfo);
if (!m_JinspFiberImagerOperation->getRecordStatus())
{
emit openFiberImagerSignal(posInfo);
QString folder = AppSettings::instance().FiberImagerDataFolder();
QDir dir(folder);
if (!dir.exists())
{
dir.mkpath(".");
}
QString eventPrefix = posInfo.isEmpty() ? "default" : posInfo;
QString dateStr = QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss");
QString m_qstrFullFileName = folder + QDir::separator() + dateStr + "_" + eventPrefix + "_fodis_data.dat";
emit openFiberImagerSignal(m_qstrFullFileName);
}
}

View File

@ -31,15 +31,17 @@ public:
public Q_SLOTS:
void openFiberImager();
void openFiberImager_expose_record(QString posInfo);
void openFiberImager_expose_record(QString posInfo, QString dataFolder = "");
void onCamOpened();
void closeFiberImager();
void onCamClosed();
void onSelectDataFolder();
void onFileNameChanged(const QString& text);
void setFileName(QString name);
signals:
void openFiberImagerSignal(QString posInfo);
void openFiberImagerSignal(QString filePath);
void PlotSpectralSignal();
void FiberImagerClosedSignal();
@ -53,4 +55,6 @@ private:
QThread* m_FiberImagerThread;
JinspFiberImager* m_JinspFiberImagerOperation;
void loadSettings();
};

File diff suppressed because it is too large Load Diff

View File

@ -10,34 +10,108 @@
#include <QFile>
#include <QTextStream>
#include <QDir>
#include <QStateMachine>
#include <QSignalTransition>
#include <QFinalState>
#include <QTimer>
#include <QSerialPort>
#include <QElapsedTimer>
#include "ui_gonggashanCtl.h"
#include "CommunicationViaTCP.h"
#include "AppSettings.h"
class GonggashanTaskScheduler : public QObject
class GsbGpsParse;
class GsbGpsReader;
// ============ 执行阶段枚举 ============
enum class GonggaShanExecPhase {
Idle, // 空闲
Preparation, // 准备阶段
HyperExposure, // 高光谱传感器曝光
FiberExposure, // 光纤光谱仪曝光
GpsAcquisition, // 获取GPS位置
MotorCalc, // 计算马达速度
DataCollection, // 开始采集:高光谱、FODIS、rgb相机
Completed // 完成
};
// ============ 任务执行器 ============
class GonggashanTaskExecutor : public QObject
{
Q_OBJECT
Q_OBJECT
public:
enum class TaskState
{
Idle,
Running
};
explicit GonggashanTaskExecutor(QObject* parent = nullptr);
~GonggashanTaskExecutor();
explicit GonggashanTaskScheduler(QObject* parent = nullptr);
~GonggashanTaskScheduler();
bool isRunning() const { return m_machine && m_machine->isRunning() && !m_machine->configuration().isEmpty(); }
bool isTaskRunning() const;
void setTaskRunning(bool running);
public slots:
void start(const QString& posInfo);
void stop();
Q_SIGNALS:
void taskStateChanged(TaskState state);
void onHyperExposureComplete(double exposureTime, double frameRate);
void onFiberExposureComplete(double exposureTime);
void onGpsAcquired(double latitude, double longitude, double altitude);
void onMotorSpeedCalculated(double rotationSpeed);
void onRcordComplete();
signals:
void hyperAutoExposureSignal_gonggashan();
void fiberExposureRecordSignal(QString posInfo, QString dataFolder);
void gpsAcquisitionSignal();
void motorSpeedSignal(double speed);
void startCollectionSignal(const QString& posInfo, const QString& gpsData, double m_motorRotationSpeed, QString dataFolder);
void calculateMotorSpeedSignal(double hyperFrameRate);
void finished(bool success);
void errorOccurred(const QString& error);
// ---- 以下为状态机内部信号,供 transition 使用 ----
void taskStartRequested(const QString& posInfo);
void preparationComplete();
void hyperExposureDone();
void fiberExposureDone();
void gpsAcquisitionDone();
void motorSpeedDone();
void dataCollectionDone();
void stopRequested();
protected:
virtual bool preparationImpl();
virtual bool hyperExposureImpl(int exposureTime);
virtual bool fiberExposureImpl(int exposureTime);
virtual bool gpsAcquisitionImpl(double lat, double lon, double alt);
virtual bool motorCalcImpl();
private slots:
void onFinalStateEntered();
private:
TaskState m_taskState;
void buildStateMachine();
QState* currentState() const;
void leavePhase(QState* state);
QString m_posInfo;
QString m_gpsData;
double m_motorRotationSpeed = 0.0;
double m_hyperExposureTime = 0;
double m_hyperFrameRate = 0;
double m_fiberExposureTime = 0;
bool m_stopRequested = false;
QString m_todayDataFolder; // 当天日期的数据保存文件夹
// Qt State Machine
QStateMachine* m_machine = nullptr;
QState* m_hyperExposureState = nullptr;
QState* m_fiberExposureState = nullptr;
QState* m_gpsAcquisitionState = nullptr;
QState* m_motorCalcState = nullptr;
QState* m_dataCollectionState = nullptr;
QFinalState* m_finalState = nullptr;
};
class GonggaShanRecordCtl : public QDialog
@ -55,8 +129,18 @@ public Q_SLOTS:
void onFiberImagerExposureCompleteSignal(int exposureTime);
Q_SIGNALS:
// Emitted when user changes any of the R/G/B wavelength values
void startRcordSignal(QString posInfo);
void gpsAcquisitionDoneSignal_gonggashan(double lat, double lon, double alt);
void calculateMotorSpeedDoneSignal_gonggashan(double rotationSpeed);
void hyperAutoExposureSignal_gonggashan();
void hyperAutoExposureDoneSignal_gonggashan(double exposureTime, double frameRate);
void fiberExposureRecordSignal_gonggashan(QString posInfo, QString dataFolder);
void fiberExposureDoneSignal_gonggashan(double exposureTime);
void startRcordSignal_gonggashan(const QString& posInfo, const QString& gpsData, double m_motorRotationSpeed, QString dataFolder);
void recordFinishedSignal_gonggashan();
private Q_SLOTS:
void startListen();
@ -64,13 +148,202 @@ private Q_SLOTS:
void startRecord(int position);
// GonggashanTaskExecutor 反馈槽
void onTaskExecutorFinished(bool success);
void onGpsAcquisition();
void onCalculateMotorSpeed(double hyperFrameRate);
private:
void logStatus(const QString& message, bool isHearderBlankLine = false, bool isTailBlankLine = false);
Ui::gongga_control ui;
QPointer<MotorParams::CommunicationViaTCP> tcpServer6005;
GonggashanTaskScheduler* m_taskScheduler;
GonggashanTaskExecutor* m_taskExecutor; // 新增
QString m_logFilePath;
QFile m_logFile;
QTextStream m_logStream;
// GPS 串口读取器
GsbGpsReader* m_gpsReader = nullptr;
};
// ============ USB GPS NMEA-0183 解析器 (G6301, COM17) ============
class GsbGpsParse : public QObject
{
Q_OBJECT
public:
// GPS数据有效性状态
enum class GpsStatus {
Invalid, // 无效或未定位
Valid, // 有效定位
Differential // 差分定位
};
// GPS解析数据结构
struct GpsData {
QString rawNmea; // 原始NMEA语句
QString utcTime; // UTC时间 (HHMMSS.SSS)
QString utcDate; // UTC日期 (DDMMYY)
double latitude = 0.0; // 纬度 (十进制度)
double longitude = 0.0; // 经度 (十进制度)
double altitude = 0.0; // 高程 (米)
int satelliteCount = 0; // 卫星数目
GpsStatus status = GpsStatus::Invalid; // 定位状态
bool isValid = false; // 综合有效性
bool hasGga = false; // 是否已收到GPGGA语句
bool hasRmc = false; // 是否已收到GPRMC语句
};
explicit GsbGpsParse(QObject* parent = nullptr);
~GsbGpsParse() = default;
// 设置最小卫星数目要求 (默认4颗)
void setMinSatelliteCount(int count) { m_minSatelliteCount = count; }
int minSatelliteCount() const { return m_minSatelliteCount; }
// 解析单条NMEA语句
bool parseNmeaSentence(const QString& sentence);
// 获取当前解析的GPS数据
const GpsData& gpsData() const { return m_gpsData; }
// 检查GPS数据是否有效
bool isGpsDataValid() const;
// 格式化输出
QString toDisplayString() const;
// 获取状态描述
QString statusDescription() const;
signals:
void gpsDataUpdated(const GpsData& data);
void gpsStatusChanged(bool isValid);
private:
// 解析 $GPGGA - GPS定位数据
bool parseGpgga(const QStringList& fields);
// 解析 $GPRMC - 推荐最小定位数据
bool parseGprmc(const QStringList& fields);
// NMEA校验
bool validateNmeaChecksum(const QString& sentence);
// 转换纬度格式 (DDMM.MMMM -> DD.DDDDD)
double convertLatitude(const QString& degMin, const QString& direction);
// 转换经度格式 (DDDMM.MMMM -> DDD.DDDDD)
double convertLongitude(const QString& degMin, const QString& direction);
// 检查数据有效性
void validateData();
GpsData m_gpsData;
int m_minSatelliteCount = 4; // 默认要求至少4颗卫星
QString m_pendingTime;
QString m_pendingDate;
};
// ============ USB GPS 串口读取器 (G6301, COM17) ============
class GsbGpsReader : public QObject
{
Q_OBJECT
public:
explicit GsbGpsReader(QObject* parent = nullptr);
~GsbGpsReader();
// 打开指定串口
bool openPort(const QString& portName, qint32 baudRate = 4800);
// 关闭串口
void closePort();
// 串口是否打开
bool isPortOpen() const;
// 获取GPS解析器(用于设置参数如最小卫星数)
GsbGpsParse* gpsParser() { return &m_parser; }
const GsbGpsParse* gpsParser() const { return &m_parser; }
// 获取GPS数据
GsbGpsParse::GpsData currentGpsData() const;
// 等待有效GPS数据 (带超时)
bool waitForValidData(int timeoutMs = 30000);
signals:
void gpsDataReady(const GsbGpsParse::GpsData& data);
void portOpened();
void portClosed();
void portError(const QString& error);
private slots:
void onReadyRead();
private:
GsbGpsParse m_parser;
QSerialPort* m_serialPort;
};
// ============ 帧率与旋转速度计算器 ============
class FramerateRotateSpeedCal : public QObject
{
Q_OBJECT
public:
explicit FramerateRotateSpeedCal(QObject* parent = nullptr);
~FramerateRotateSpeedCal() = default;
// 设置参数
void setAltitude(double altitude) { m_altitude = altitude; }
void setVerticalFov(double fovDegrees) { m_verticalFov = fovDegrees; }
void setVerticalPixels(int pixels) { m_verticalPixels = pixels; }
void setFramerate(double framerate) { m_framerate = framerate; }
// 获取参数
double altitude() const { return m_altitude; }
double verticalFov() const { return m_verticalFov; }
int verticalPixels() const { return m_verticalPixels; }
double framerate() const { return m_framerate; }
// 计算垂直航向分辨率(地面分辨率)
// 基于高度、视场角和像素个数
// 公式: resolution = 2 * altitude * tan(FOV/2) / pixels
double calculateVerticalGroundResolution();
// 计算速度
// 基于帧率和垂直航向分辨率
// 公式: speed = framerate * ground_resolution
double calculateSpeed();
// 计算旋转速度
// 基于高度和计算出的速度
// 公式: rotation_speed = speed / (2 * PI * altitude)
double calculateRotationSpeed();
// 一站式计算:设置参数后一次性计算所有结果
double calculate(double altitude, double verticalFov, int verticalPixels, double framerate);
// 获取计算结果
double groundResolution() const { return m_groundResolution; }
double speed() const { return m_speed; }
double rotationSpeed() const { return m_rotationSpeedDegPerSec; }
signals:
void calculationCompleted(double groundResolution, double speed, double rotationSpeed);
private:
double m_pi = 3.14159265358979323846; // 圆周率
double m_altitude = 0.0; // 高度(米)
double m_verticalFov = 0.0; // 垂直视场角(度)
int m_verticalPixels = 0; // 垂直方向像素个数
double m_framerate = 0.0; // 帧率(Hz)
double m_groundResolution = 0.0; // 垂直航向分辨率(米/像素)
double m_speed = 0.0; // 速度(米/秒)
double m_rotationSpeedDegPerSec = 0.0; // 旋转速度(度/秒)
};

View File

@ -1112,22 +1112,47 @@ void HPPA::initControlTabwidget()
void HPPA::setupGonggashanAutoRecordConnection()
{
connect(m_gonggaShanRecordCtl, &GonggaShanRecordCtl::startRcordSignal, this, &HPPA::onGonggashanRecord);
connect(m_gonggaShanRecordCtl, &GonggaShanRecordCtl::hyperAutoExposureSignal_gonggashan, this, &HPPA::onGonggashanHyperAutoExposure);
connect(m_omc, &OneMotorControl::hyperAutoExposureDoneSignal_gonggashan, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::hyperAutoExposureDoneSignal_gonggashan);
connect(m_gonggaShanRecordCtl, &GonggaShanRecordCtl::fiberExposureRecordSignal_gonggashan, this, &HPPA::onGonggashanFiberAutoExposureRecord);
connect(m_fodisWindow, &FodisWindow::startExposureSignal, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::onFiberImagerStartExposureSignal);
connect(m_fodisWindow, &FodisWindow::exposureCompleteSignal, this, &HPPA::onStartRecordStep1);
connect(m_fodisWindow, &FodisWindow::exposureCompleteSignal, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::onFiberImagerExposureCompleteSignal);
connect(m_fodisWindow, &FodisWindow::exposureCompleteSignal, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::fiberExposureDoneSignal_gonggashan);
connect(m_omc, &OneMotorControl::sequenceComplete, m_fodisWindow, &FodisWindow::closeFiberImager);
connect(m_omc, &OneMotorControl::sequenceComplete, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::onRcordFinished);
connect(m_gonggaShanRecordCtl, &GonggaShanRecordCtl::startRcordSignal_gonggashan, this, &HPPA::onGonggashanRecord);
connect(m_omc, &OneMotorControl::sequenceCompleteSignal_hyperImagerStopRecord, m_rgbCameraControlWindow, &rgbCameraWindow::toggleTakePhoto);
connect(m_omc, &OneMotorControl::sequenceCompleteSignal_hyperImagerStopRecord, m_fodisWindow, &FodisWindow::closeFiberImager);
connect(m_omc, &OneMotorControl::sequenceCompleteSignal_hyperImagerStopRecord, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::recordFinishedSignal_gonggashan);
//connect(m_omc, &OneMotorControl::sequenceComplete_motorBack2Origin, m_gonggaShanRecordCtl, &GonggaShanRecordCtl::);
}
void HPPA::onGonggashanRecord(QString posInfo)
void HPPA::onGonggashanHyperAutoExposure()
{
//连接马达和光谱仪
m_omc->connectMotor(false);
if (!testImagerVality())
{
onconnect();
}
m_omc->setImager(m_Imager);
m_omc->multiPosHyperAutoExposure();
}
void HPPA::onGonggashanFiberAutoExposureRecord(QString posInfo, QString dataFolder)
{
//设置数据存储路径和文件名
m_fodisWindow->openFiberImager_expose_record(posInfo, dataFolder);
}
void HPPA::onGonggashanRecord(const QString& posInfo, const QString& gpsData, double motorRotationSpeed, QString dataFolder)
{
//设置文件名
//AppSettings::instance().setFrameRate(f);
//AppSettings::instance().setIntegrationTime(e);
//AppSettings::instance().setDataFolder(filePath);
AppSettings::instance().setDataFolder(dataFolder);
QString dateStr = QDateTime::currentDateTime().toString("yyyy-MM-dd_HH-mm-ss");
//QString fi = AppSettings::instance().fileName() + "_" + dateStr;
@ -1137,13 +1162,17 @@ void HPPA::onGonggashanRecord(QString posInfo)
//连接马达和光谱仪
m_omc->connectMotor(false);
m_omc->setScanSpeed(motorRotationSpeed);
if (!testImagerVality())
{
onconnect();
}
m_fodisWindow->openFiberImager_expose_record(posInfo);
onStartRecordStep1();
//采集rgb图像
m_rgbCameraControlWindow->toggleTakePhoto();
}
void HPPA::recordFromRobotArm(int fileCounter)
@ -1645,6 +1674,7 @@ void HPPA::createGonggaRotatingPlatformScenario()
m_tabManager->showTab(m_hic);
m_tabManager->showTab(m_ic);
m_tabManager->showTab(m_rgbCameraControlWindow);
m_tabManager->showTab(m_fodisWindow);
m_tabManager->showTab(m_omc);
m_tabManager->showTab(m_gonggaShanRecordCtl);
@ -2571,7 +2601,7 @@ void HPPA::onconnect()
connect(m_Imager, SIGNAL(RecordDarlFinishSignal()), this, SLOT(recordDarkFinish()));
// Connect LayerFileCreated from imager to HPPA slot
connect(m_Imager, SIGNAL(LayerFileCreated(QString,QString,int)), this, SLOT(onLayerCreatedFromFile(QString,QString,int)));
connect(m_Imager, SIGNAL(LayerFileCreated(QString,QString,int, QString)), this, SLOT(onLayerCreatedFromFile(QString,QString,int, QString)));
connect(this->ui.actionOpenDirectory, SIGNAL(triggered()), this, SLOT(onActionOpenDirectory()));
@ -3083,22 +3113,22 @@ void WorkerThread3::run()
emit AutoFocusFinishedSignal();
}
void HPPA::onLayerCreatedFromFile(const QString& baseName, const QString& filePath, int fileIndex)
void HPPA::onLayerCreatedFromFile(const QString& baseName, const QString& filePath, int fileIndex, const QString& hyperimagerTppe)
{
if (!m_LayerTreeModel || !m_RasterGroup) return;
if (ui.mAction3DPlantPhenotypeScenario->isChecked())
{
//addLayer(baseName, filePath, false, false);
addLayer(baseName, filePath, false);
addLayer(baseName, filePath, false, true, hyperimagerTppe);
}
else
{
addLayer(baseName, filePath, false);
addLayer(baseName, filePath, false, true, hyperimagerTppe);
}
}
void HPPA::addLayer(const QString& baseName, const QString& filePath,bool refresh, bool isAddImage)
void HPPA::addLayer(const QString& baseName, const QString& filePath,bool refresh, bool isAddImage, const QString& hyperimagerTppe)
{
// Create MapLayer first and attach it to a LayerTreeLayerNode
RasterLayer* ml = new RasterLayer(baseName, filePath);
@ -3115,14 +3145,41 @@ void HPPA::addLayer(const QString& baseName, const QString& filePath,bool refres
if (isAddImage)
{
newImage(ml, RasterImageLayer::RendererType::Multiband, node, refresh);
newImage(ml, RasterImageLayer::RendererType::Multiband, node, refresh, hyperimagerTppe);
}
}
void HPPA::newImage(RasterLayer* ml, RasterImageLayer::RendererType type, LayerTreeNode* parent, bool refresh)
void HPPA::newImage(RasterLayer* ml, RasterImageLayer::RendererType type, LayerTreeNode* parent, bool refresh, const QString& hyperimagerTppe)
{
QWidget* mapcavasContainer = onCreateTab(ml->name());
RasterImageLayer* rasterImageLayer = new RasterImageLayer(ml, type);
RasterImageLayer* rasterImageLayer;
//当边采集边显示时,需要修改rasterImageLayer默认渲染波段
if (hyperimagerTppe== "visibleLight")
{
rasterImageLayer = new RasterImageLayer(ml, type, false);
auto params = rasterImageLayer->multibandParams();
params.rWave = 665;
params.gWave = 560;
params.bWave = 490;
rasterImageLayer->setMultibandParams(params);
}
else if (hyperimagerTppe == "nearInfrared")
{
rasterImageLayer = new RasterImageLayer(ml, type, false);
auto params = rasterImageLayer->multibandParams();
params.rWave = 1500;
params.gWave = 1300;
params.bWave = 1100;
rasterImageLayer->setMultibandParams(params);
}
else//当打开影像文件时,rasterImageLayer默认渲染波段由头文件决定
{
rasterImageLayer = new RasterImageLayer(ml, type, true);
}
QList<Mapcavas*> mapcavas = mapcavasContainer->findChildren<Mapcavas*>();
if (mapcavas.size() == 0)
{

View File

@ -430,9 +430,9 @@ public Q_SLOTS:
void createMicroscopicMotionControlScenario();
void onCreated3DModelOneMotor();
void addLayer(const QString& baseName, const QString& filePath, bool refresh, bool isAddImage = true);
void newImage(RasterLayer* ml, RasterImageLayer::RendererType, LayerTreeNode* parent, bool refresh=true);
void onLayerCreatedFromFile(const QString& baseName, const QString& filePath, int fileIndex);
void addLayer(const QString& baseName, const QString& filePath, bool refresh, bool isAddImage = true, const QString& hyperimagerTppe="");
void newImage(RasterLayer* ml, RasterImageLayer::RendererType, LayerTreeNode* parent, bool refresh=true, const QString& hyperimagerTppe="");
void onLayerCreatedFromFile(const QString& baseName, const QString& filePath, int fileIndex, const QString& hyperimagerTppe);
void removeLayerByTreeIndex();
void removeLayerByNode(LayerTreeNode* node);
void showColorImageByTreeIndex();
@ -457,7 +457,9 @@ public Q_SLOTS:
void onStretchedImageReady(int fileNumber, const QString& filePath, QPixmap& pixmap);
void onStretchProcessingError(int fileNumber, const QString& filePath, const QString& error);
void onGonggashanRecord(QString posInfo);
void onGonggashanRecord(const QString& posInfo, const QString& gpsData, double motorRotationSpeed, QString dataFolder);
void onGonggashanHyperAutoExposure();
void onGonggashanFiberAutoExposureRecord(QString posInfo, QString dataFolder);
protected:
void closeEvent(QCloseEvent* event) override;

View File

@ -160,6 +160,7 @@
<ClCompile Include="recordFrameCounter.cpp" />
<ClCompile Include="resononImager.cpp" />
<ClCompile Include="ResononNirImager.cpp" />
<ClCompile Include="RgbCameraCaptureCoordinator.cpp" />
<ClCompile Include="RgbCameraOperation.cpp" />
<ClCompile Include="rgbCameraWindow.cpp" />
<ClCompile Include="RobotArmControl.cpp" />
@ -289,6 +290,7 @@
<ClInclude Include="ResononNirImager.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="stdafx.h" />
<QtMoc Include="RgbCameraCaptureCoordinator.h" />
<QtMoc Include="RgbCameraOperation.h" />
<QtMoc Include="resononImager.h" />
<QtMoc Include="QMotorDoubleSlider.h" />

View File

@ -68,11 +68,12 @@ double ImagerOperationBase::auto_exposure()
imagerStopCollect();
emit autoExposureSignal();
double exposureTime = getIntegrationTime();
emit autoExposureSignal(exposureTime);
//std::cout << "自动曝光:" << getIntegrationTime() << std::endl;
//std::cout << "自动曝光:" << exposureTime << std::endl;
return getIntegrationTime();
return exposureTime;
}
void ImagerOperationBase::focus()
@ -251,7 +252,7 @@ void ImagerOperationBase::start_record()
m_FileName2Save2 = m_FileName2Save + "_" + std::to_string(m_FileSavedCounter) + ".bil";
QString baseName = QString::fromStdString(getFileNameFromPath(m_FileName2Save2));
QString filePath = QString::fromStdString(m_FileName2Save2);
emit LayerFileCreated(baseName, filePath, m_FileSavedCounter);
emit LayerFileCreated(baseName, filePath, m_FileSavedCounter, "visibleLight");
FILE* m_fImage = fopen(m_FileName2Save2.c_str(), "w+b");
@ -313,7 +314,7 @@ void ImagerOperationBase::start_record()
fprintf(hTimesFile, "%ll\n", timeOs);
//将rgb波段提取出来,以便在界面中显示
m_RgbImage->FillRgbImage(buffer);//??????????????????????????????????????????????????????????????????????????????????????????????????????
m_RgbImage->FillRgbImage(buffer, 121, 79, 40);//??????????????????????????????????????????????????????????????????????????????????????????????????????
//std::cout << "第" << m_iFrameCounter << "帧写了" << x << "个unsigned short。" << std::endl;

View File

@ -117,11 +117,11 @@ signals:
void testImagerStatus();//表示可以测试相机连接状态:是否连接,并反映到界面上
void autoExposureSignal();
void autoExposureSignal(double exposureTime);
// 新增:当一组影像文件(.bil/.hdr)写入完成后发出(会从采集线程发出,Qt 会做 queued connection)
void ImageFileSaved(const QString& path, int fileIndex);
// 修改:不再直接发送 MapLayer*,而是发送文件名与文件路径,UI 层负责创建 MapLayer 对象并管理生命周期
void LayerFileCreated(const QString& baseName, const QString& filePath, int fileIndex);
void LayerFileCreated(const QString& baseName, const QString& filePath, int fileIndex, const QString& hyperimagerTppe);
};

View File

@ -2,7 +2,8 @@
// Created by 73505 on 2023/5/7.
//
#include <algorithm>
#include <QFile>
#include <QDir>
#include "JinspFiberImager.h"
JinspFiberImager::JinspFiberImager(bool bIsUSBMode, std::string ucPortNumber, std::string strDeviceName)
@ -132,7 +133,7 @@ void JinspFiberImager::recordDark(QString path)
outfile.close();
}
void JinspFiberImager::recordTarget(int recordTimes, QString path)
void JinspFiberImager::recordTarget2csv(int recordTimes, QString path)
{
//获取设备信息
DeviceAttribute attribute;
@ -318,10 +319,8 @@ void JinspFiberImager::setCaptureInterval(int captureIntervalSeconds)
m_captureIntervalMilliseconds = captureIntervalSeconds * 1000;
}
void JinspFiberImager::OpenFiberImagerAndRecord(QString posInfo)
void JinspFiberImager::OpenFiberImagerAndRecord(QString filePath)
{
m_posInfo = posInfo;
//连接光谱仪
QString SN;
QString pixelCount;
@ -335,13 +334,25 @@ void JinspFiberImager::OpenFiberImagerAndRecord(QString posInfo)
emit exposureCompleteSignal(m_iExposureTime);
//采集
QFile qfData(filePath);
bool bRes = qfData.open(QFile::WriteOnly | QIODevice::Append);
if (!bRes)
{
printf("WriteData QFile open Failed");
return;
}
m_record = true;
DataFrame data;
while (m_record)
{
recordTarget(1, AppSettings::instance().FiberImagerDataFolder());
singleShot(data);
qfData.write((char*)&data, sizeof(DataFrame));
qfData.flush();
QThread::msleep(m_captureIntervalMilliseconds);
}
qfData.close();
std::cout << "close.........." << std::endl;
m_FiberSpectrometer->Close();

View File

@ -61,10 +61,10 @@ private:
public slots:
void recordDark(QString path);
void recordTarget(int recordTimes, QString path);
void recordTarget2csv(int recordTimes, QString path);
void autoExpose();
void OpenFiberImagerAndRecord(QString posInfo);
void OpenFiberImagerAndRecord(QString filePath);
signals:
void sendExposureTimeSignal(int exposureTime);

View File

@ -42,6 +42,11 @@ void OneMotorControl::onConnectMotor()
connectMotor(true);
}
void OneMotorControl::setScanSpeed(double speed)
{
ui.speed_lineEdit->setText(QString::number(speed));
}
void OneMotorControl::connectMotor(bool isNotification)
{
if (getMotorsConnectionStatus())
@ -225,16 +230,20 @@ void OneMotorControl::run()
{
if (m_coordinator)//当高光谱相机停止采集后,马达还未回到原点时,上次任务的m_coordinator还没有被销毁
{
onSequenceComplete(0);
onSequenceComplete_motorBack2Origin(0);
}
qRegisterMetaType<OneMotionCapturePathLine>("OneMotionCapturePathLine");
m_coordinator = new OneMotionCaptureCoordinator(m_multiAxisController, m_Imager);
m_coordinator->setObjectName("testOneMotionCaptureCoordinator");
connect(this, SIGNAL(start(OneMotionCapturePathLine)), m_coordinator, SLOT(startStepMotion(OneMotionCapturePathLine)));
connect(this, SIGNAL(stopStepMotionSignal()), m_coordinator, SLOT(stopStepMotion()));
connect(m_coordinator, SIGNAL(sequenceComplete(int)), this, SLOT(onSequenceComplete(int)));
connect(this, &OneMotorControl::start, m_coordinator, &OneMotionCaptureCoordinator::startStepMotion);
connect(this, &OneMotorControl::stopStepMotionSignal, m_coordinator, &OneMotionCaptureCoordinator::stopStepMotion);
connect(m_coordinator, &OneMotionCaptureCoordinator::sequenceCompleteSignal_hyperImagerStopRecord,
this, &OneMotorControl::sequenceCompleteSignal_hyperImagerStopRecord);
connect(m_coordinator, &OneMotionCaptureCoordinator::sequenceCompleteSignal_motorBack2Origin,
this, &OneMotorControl::onSequenceComplete_motorBack2Origin);
OneMotionCapturePathLine tmp;
tmp.speedRecord = ui.speed_lineEdit->text().toDouble();
@ -248,13 +257,37 @@ void OneMotorControl::stop()
emit stopStepMotionSignal();
}
void OneMotorControl::onSequenceComplete(int state)
void OneMotorControl::multiPosHyperAutoExposure()
{
emit sequenceComplete();
//所有该自动曝光的位置
std::vector<double> maxRangeLocations = m_multiAxisController->getMaxPos();
double maxPos = maxRangeLocations[0];
disconnect(this, SIGNAL(start(OneMotionCapturePathLine)), m_coordinator, SLOT(startStepMotion(OneMotionCapturePathLine)));
disconnect(this, SIGNAL(stopStepMotionSignal()), m_coordinator, SLOT(stopStepMotion()));
disconnect(m_coordinator, SIGNAL(sequenceComplete(int)), this, SLOT(onSequenceComplete(int)));
std::vector<double> locations;
locations.push_back(maxPos * 0.2);
locations.push_back(maxPos * 0.5);
locations.push_back(maxPos * 0.8);
//创建协调器,并连接信号槽
m_coordinator_gonggashan_autoexpose = new OneMotorMultiPosCoordinator(m_multiAxisController, m_Imager);
//connect(this, SIGNAL(stopStepMotionSignal()), m_coordinator_gonggashan_autoexpose, SLOT(stopStepMotion()));
connect(m_coordinator_gonggashan_autoexpose, &OneMotorMultiPosCoordinator::sequenceComplete, this, &OneMotorControl::onSequenceComplete_gonggashan_autoexpose);
connect(m_coordinator_gonggashan_autoexpose, &OneMotorMultiPosCoordinator::hyperAutoExposureDoneSignal, this, &OneMotorControl::hyperAutoExposureDoneSignal_gonggashan);
m_coordinator_gonggashan_autoexpose->startStepMotion(ui.speed_lineEdit->text().toDouble(), locations);
}
void OneMotorControl::onSequenceComplete_gonggashan_autoexpose(int state)
{
emit multiPosAutoexposeSequenceCompleteSignal();
m_coordinator_gonggashan_autoexpose->deleteLater();
}
void OneMotorControl::onSequenceComplete_motorBack2Origin(int state)
{
emit sequenceComplete_motorBack2Origin();
// Use deleteLater() instead of delete: this slot may have been called directly
// from OneMotionCaptureCoordinator's call stack (direct connection), so deleting

View File

@ -26,6 +26,8 @@ public:
void run();
void stop();
void multiPosHyperAutoExposure();
void record_dark();
void record_white();
@ -33,6 +35,8 @@ public:
void connectMotor(bool isNotification);
void setScanSpeed(double speed);
public Q_SLOTS:
void onConnectMotor();
@ -46,7 +50,8 @@ public Q_SLOTS:
void onxMotorLeft();
void onxMotorStop();
void onSequenceComplete(int state);
void onSequenceComplete_motorBack2Origin(int state);
void onSequenceComplete_gonggashan_autoexpose(int state);
signals:
void moveSignal(int, bool, double, int);
@ -62,9 +67,16 @@ signals:
void stopStepMotionSignal();
void sequenceComplete();
void sequenceComplete_motorBack2Origin();
void broadcastLocationSignal(std::vector<double>);
void hyperAutoExposureDoneSignal_gonggashan(double exposureTime, double frameRate);
void multiPosAutoexposeSequenceCompleteSignal();
void sequenceCompleteSignal_hyperImagerStopRecord(int);
private:
Ui::OneMotorControl_UI ui;
@ -78,6 +90,8 @@ private:
DarkAndWhiteCaptureCoordinator* m_whiteCaptureCoordinator = nullptr;
bool m_xMotorConnectionStatus = false;
QPointer<OneMotorMultiPosCoordinator> m_coordinator_gonggashan_autoexpose;
};
class OneMotorControl_LiftingPlatform : public QDialog, public MotorWindowBase
@ -128,6 +142,7 @@ signals:
void broadcastLocationSignal(std::vector<double>);
private:
Ui::OneMotorControl_UI ui;

View File

@ -5,11 +5,39 @@
#include "MultibandRasterRenderer.h"
#include "SinglebandRasterRenderer.h"
RasterImageLayer::RasterImageLayer(RasterLayer* layer, RendererType type)
RasterImageLayer::RasterImageLayer(RasterLayer* layer, RendererType type, bool initRenderParamsFromFile)
: m_layer(layer)
, m_rendererType(type)
, m_rendererInitialized(false)
{
if (initRenderParamsFromFile)
{
// 此处根据头文件(打开影像文件)修改默认渲染波段,只对打开文件的情况下有效;
// 当边采集边显示时(initRenderParamsFromFile设置为false),应该在实例化RasterImageLayer后调用setMultibandParams设置默认渲染波段
try
{
if (!m_layer) return;
std::vector<double> wavelengths = m_layer->bandWavelengths();
if (wavelengths.empty()) return;
std::sort(wavelengths.begin(), wavelengths.end());
double m_minWave = wavelengths.front();
double m_maxWave = wavelengths.back();
if (m_minWave > 800 && m_maxWave > 1600)
{
m_multibandParams.rWave = 1500;
m_multibandParams.gWave = 1300;
m_multibandParams.bWave = 1100;
}
}
catch (const std::exception&)
{
}
}
}
void RasterImageLayer::ensureRenderer()

View File

@ -19,7 +19,7 @@ public:
Singleband
};
RasterImageLayer(RasterLayer* layer, RendererType type);
RasterImageLayer(RasterLayer* layer, RendererType type, bool initRenderParamsFromFile);
~RasterImageLayer();
void ensureRenderer();

View File

@ -104,20 +104,25 @@ void ResononNirImager::setSpectraBin(int new_spectral_bin)
double ResononNirImager::auto_exposure()
{
//第一步:先设置曝光时间为在当前帧率情况下最大
double x = 1 / getFramerate() * 1000;//获取最大毫秒曝光时间
double f = getFramerate();
double x = 1 / f * 1000;//获取最大毫秒曝光时间
std::cout << f << "hz帧率下,最大曝光时间为" << x << std::endl;
reConnectImage();
setIntegrationTime(x);
//第二步:通过循环寻找最佳曝光时间
imagerStartCollect();
double tmpTime;
while (true)
{
getFrame(buffer);
if (GetMaxValue(buffer, m_FrameSize) >= 4095)
if (GetMaxValue(buffer, m_FrameSize) >= 16383)
{
setIntegrationTime(getIntegrationTime() * 0.8);
std::cout << "自动曝光-----------" << std::endl;
tmpTime = getIntegrationTime() * 0.8;
setIntegrationTime(tmpTime);
std::cout << "自动曝光-----------:" << tmpTime << std::endl;
}
else
{
@ -128,9 +133,28 @@ double ResononNirImager::auto_exposure()
reConnectImage();
//imagerStopCollect();
//std::cout << "自动曝光:" << getIntegrationTime() << std::endl;
double exposureTime = getIntegrationTime();
emit autoExposureSignal(exposureTime);
return getIntegrationTime();
//std::cout << "自动曝光:" << exposureTime << std::endl;
return exposureTime;
}
unsigned short ResononNirImager::GetMaxValue(unsigned short* dark, int number)
{
unsigned int max = 0;
for (size_t i = 0; i < number; i++)
{
if (dark[i] > 16383) continue;//IR L为14位
if (dark[i] > max)
{
max = dark[i];
}
}
std::cout << "本帧最大值为" << max << std::endl;
return max;
}
double ResononNirImager::getWavelengthAtBand(int band)
@ -290,7 +314,7 @@ void ResononNirImager::start_record()
m_FileName2Save2 = m_FileName2Save + "_" + std::to_string(m_FileSavedCounter) + ".bil";
QString baseName = QString::fromStdString(getFileNameFromPath(m_FileName2Save2));
QString filePath = QString::fromStdString(m_FileName2Save2);
emit LayerFileCreated(baseName, filePath, m_FileSavedCounter);
emit LayerFileCreated(baseName, filePath, m_FileSavedCounter, "nearInfrared");
FILE* m_fImage = fopen(m_FileName2Save2.c_str(), "w+b");
@ -352,7 +376,8 @@ void ResononNirImager::start_record()
fprintf(hTimesFile, "%ll\n", timeOs);
//将rgb波段提取出来,以便在界面中显示
m_RgbImage->FillRgbImage(buffer);//??????????????????????????????????????????????????????????????????????????????????????????????????????
//需要修改函数m_RgbImage->FillRgbImage,接收参数:提取rgb的偏移,参数由相机类型、单帧大小、波长范围等确定
m_RgbImage->FillRgbImage(buffer, 172, 112, 52);//??????????????????????????????????????????????????????????????????????????????????????????????????????
//std::cout << "第" << m_iFrameCounter << "帧写了" << x << "个unsigned short。" << std::endl;
@ -408,7 +433,7 @@ void ResononNirImager::WriteHdr()
outfile << "ENVI\n";
outfile << "interleave = bil\n";
outfile << "data type = 12\n";
outfile << "bit depth = 12\n";
outfile << "bit depth = 14\n";
outfile << "byte order = 0\n";
outfile << "samples = " << getSampleCount() << "\n";
outfile << "bands = " << getBandCount() << "\n";

View File

@ -38,6 +38,8 @@ public:
void WriteHdr();
protected:
unsigned short GetMaxValue(unsigned short* dark, int number);
private:
void reConnectImage();

View File

@ -0,0 +1,246 @@
#include "stdafx.h"
#include "RgbCameraCaptureCoordinator.h"
#include "RgbCameraOperation.h"
#include "AppSettings.h"
RgbCameraCaptureCoordinator::RgbCameraCaptureCoordinator(RgbCameraOperation* rgbCamera, QObject* parent)
: QObject(parent)
, m_rgbCamera(rgbCamera)
, m_captureMode(None)
, m_isCapturing(false)
, m_isCameraOpened(false)
, m_pendingOpenCamera(false)
, m_pendingMode(None)
{
if (m_rgbCamera)
{
connect(m_rgbCamera, &RgbCameraOperation::CamOpenedSignal,
this, &RgbCameraCaptureCoordinator::onCameraOpened);
connect(m_rgbCamera, &RgbCameraOperation::CamClosedSignal,
this, &RgbCameraCaptureCoordinator::onCameraClosed);
connect(m_rgbCamera, &RgbCameraOperation::VideoRecordingStartedSignal,
this, &RgbCameraCaptureCoordinator::onVideoRecordingStarted);
connect(m_rgbCamera, &RgbCameraOperation::VideoRecordingStoppedSignal,
this, &RgbCameraCaptureCoordinator::onVideoRecordingStopped);
connect(this, &RgbCameraCaptureCoordinator::openCameraSignal,
m_rgbCamera, &RgbCameraOperation::OpenCamera, Qt::QueuedConnection);
connect(this, &RgbCameraCaptureCoordinator::closeCameraSignal,
m_rgbCamera, &RgbCameraOperation::CloseCamera, Qt::QueuedConnection);
connect(this, &RgbCameraCaptureCoordinator::startVideoRecordingSignal,
m_rgbCamera, &RgbCameraOperation::startVideoRecording, Qt::QueuedConnection);
connect(this, &RgbCameraCaptureCoordinator::stopVideoRecordingSignal,
m_rgbCamera, &RgbCameraOperation::stopVideoRecording, Qt::QueuedConnection);
connect(this, &RgbCameraCaptureCoordinator::startPhotoSaveTimerSignal,
m_rgbCamera, &RgbCameraOperation::startPhotoSaveTimer, Qt::QueuedConnection);
connect(this, &RgbCameraCaptureCoordinator::stopPhotoSaveTimerSignal,
m_rgbCamera, &RgbCameraOperation::stopPhotoSaveTimer, Qt::QueuedConnection);
}
}
RgbCameraCaptureCoordinator::~RgbCameraCaptureCoordinator()
{
cleanup();
}
void RgbCameraCaptureCoordinator::startVideoCapture()
{
QMutexLocker locker(&m_dataMutex);
if (m_isCapturing)
{
if (m_captureMode == Video)
{
//emit errorOccurred(QStringLiteral("视频正在采集中"));
return;
}
else
{
//emit errorOccurred(QStringLiteral("正在执行其他采集操作"));
return;
}
}
m_pendingOpenCamera = true;
m_pendingMode = Video;
openCamera();
}
void RgbCameraCaptureCoordinator::stopVideoCapture()
{
QMutexLocker locker(&m_dataMutex);
if (m_captureMode != Video || !m_isCapturing)
{
//emit errorOccurred(QStringLiteral("视频未在采集中"));
return;
}
if (m_rgbCamera)
{
emit stopVideoRecordingSignal();
closeCamera();
}
}
void RgbCameraCaptureCoordinator::startPhotoCapture()
{
QMutexLocker locker(&m_dataMutex);
if (m_isCapturing)
{
if (m_captureMode == Photo)
{
//emit errorOccurred(QStringLiteral("照片正在采集中"));
return;
}
else
{
//emit errorOccurred(QStringLiteral("正在执行其他采集操作"));
return;
}
}
m_pendingOpenCamera = true;
m_pendingMode = Photo;
openCamera();
}
void RgbCameraCaptureCoordinator::stopPhotoCapture()
{
QMutexLocker locker(&m_dataMutex);
if (m_captureMode != Photo || !m_isCapturing)
{
return;
}
emit stopPhotoSaveTimerSignal();
emit captureStopped(Photo);
closeCamera();
}
void RgbCameraCaptureCoordinator::openCamera()
{
if (!m_rgbCamera)
{
emit cameraOpenFailed(QStringLiteral("RGB相机对象未初始化"));
return;
}
if (m_isCameraOpened)
{
onCameraOpened();
return;
}
emit openCameraSignal();
}
void RgbCameraCaptureCoordinator::closeCamera()
{
if (!m_rgbCamera)
{
return;
}
if (m_isCameraOpened)
{
emit closeCameraSignal();
}
else
{
m_isCameraOpened = false;
m_isCapturing = false;
m_captureMode = None;
}
}
void RgbCameraCaptureCoordinator::cleanup()
{
QMutexLocker locker(&m_dataMutex);
if (m_rgbCamera && m_isCapturing)
{
if (m_captureMode == Video)
{
emit stopVideoRecordingSignal();
}
}
closeCamera();
}
void RgbCameraCaptureCoordinator::onCameraOpened()
{
//QMutexLocker locker(&m_dataMutex);
m_isCameraOpened = true;
emit cameraOpened();
qDebug() << "Camera opened. Pending open camera: " << m_pendingOpenCamera << ", Pending mode: " << m_pendingMode;
if (!m_pendingOpenCamera)
{
return;
}
qDebug() << "Camera opened. Pending open camera: --------------------------------------------";
m_pendingOpenCamera = false;
CaptureMode mode = m_pendingMode;
m_pendingMode = None;
m_captureMode = mode;
m_isCapturing = true;
if (mode == Video)
{
emit startVideoRecordingSignal();
}
else if (mode == Photo)
{
emit captureStarted(Photo);
emit startPhotoSaveTimerSignal();
}
}
void RgbCameraCaptureCoordinator::onCameraClosed()
{
QMutexLocker locker(&m_dataMutex);
m_isCameraOpened = false;
CaptureMode previousMode = m_captureMode;
if (m_isCapturing)
{
emit captureStopped(previousMode);
}
m_isCapturing = false;
m_captureMode = None;
emit cameraClosed();
}
void RgbCameraCaptureCoordinator::onVideoRecordingStarted()
{
QMutexLocker locker(&m_dataMutex);
emit captureStarted(Video);
}
void RgbCameraCaptureCoordinator::onVideoRecordingStopped()
{
QMutexLocker locker(&m_dataMutex);
emit captureStopped(Video);
emit stopVideoRecordingSignal();
closeCamera();
}
void RgbCameraCaptureCoordinator::onPhotoCaptured()
{
}

View File

@ -0,0 +1,69 @@
#pragma once
#include <QObject>
#include <QTimer>
#include <QMutex>
class RgbCameraOperation;
class RgbCameraCaptureCoordinator : public QObject
{
Q_OBJECT
public:
enum CaptureMode { None = 0, Video, Photo };
Q_ENUM(CaptureMode)
explicit RgbCameraCaptureCoordinator(RgbCameraOperation* rgbCamera, QObject* parent = nullptr);
~RgbCameraCaptureCoordinator();
// 视频采集控制
Q_INVOKABLE void startVideoCapture();
Q_INVOKABLE void stopVideoCapture();
// 照片采集控制
Q_INVOKABLE void startPhotoCapture();
Q_INVOKABLE void stopPhotoCapture();
// 状态查询
CaptureMode getCurrentMode() const { return m_captureMode; }
bool isCapturing() const { return m_isCapturing; }
bool isCameraOpened() const { return m_isCameraOpened; }
Q_SIGNALS:
void captureStarted(CaptureMode mode);
void captureStopped(CaptureMode mode);
void cameraOpened();
void cameraClosed();
void cameraOpenFailed(const QString& error);
void errorOccurred(const QString& error);
void photoCaptured(const QString& filePath);
void openCameraSignal();
void closeCameraSignal();
void startVideoRecordingSignal();
void stopVideoRecordingSignal();
void startPhotoSaveTimerSignal();
void stopPhotoSaveTimerSignal();
public Q_SLOTS:
void openCamera();
void closeCamera();
private Q_SLOTS:
void onCameraOpened();
void onCameraClosed();
void onVideoRecordingStarted();
void onVideoRecordingStopped();
void onPhotoCaptured();
void cleanup();
private:
RgbCameraOperation* m_rgbCamera;
CaptureMode m_captureMode;
bool m_isCapturing;
bool m_isCameraOpened;
bool m_pendingOpenCamera;//指示相机是否是带目的的打开:带目的打开true(打开就要录像+m_pendingMode),还是手动打开(false,不带目的)
CaptureMode m_pendingMode;
mutable QMutex m_dataMutex;
};

View File

@ -1,15 +1,55 @@
#include "stdafx.h"
#include "RgbCameraOperation.h"
#include "AppSettings.h"
RgbCameraOperation::RgbCameraOperation()
{
cam = nullptr;
m_ImageProcessor = new ImageProcessor();
m_func = nullptr;
m_videoWriter = nullptr;
m_isRecording = false;
m_captureTimer = new QTimer(this);
connect(m_captureTimer, &QTimer::timeout, this, &RgbCameraOperation::onCaptureFrame);
m_photoSaveTimer = new QTimer(this);
m_photoSaveTimer->setSingleShot(false);
connect(m_photoSaveTimer, &QTimer::timeout, this, &RgbCameraOperation::onPhotoSaveTimeout);
}
RgbCameraOperation::~RgbCameraOperation()
{
if (m_photoSaveTimer != nullptr)
{
m_photoSaveTimer->stop();
//delete m_photoSaveTimer;
m_photoSaveTimer = nullptr;
}
if (m_captureTimer != nullptr)
{
m_captureTimer->stop();
//delete m_captureTimer;
m_captureTimer = nullptr;
}
if (m_videoWriter != nullptr)
{
if (m_isRecording)
{
m_videoWriter->release();
}
delete m_videoWriter;
m_videoWriter = nullptr;
}
if (cam != nullptr)
{
cam->release();
delete cam;
cam = nullptr;
}
}
void RgbCameraOperation::OpenCamera()
@ -17,21 +57,80 @@ void RgbCameraOperation::OpenCamera()
std::cout << "打开摄像头+++++++++++++++++++++++++++++++++++++++++++" << std::endl;
cam = new cv::VideoCapture(0);
// 设置摄像头分辨率为最高可用分辨率
int width = 640;//1920
int height = 480;//1080
cam->set(cv::CAP_PROP_FRAME_WIDTH, width);
cam->set(cv::CAP_PROP_FRAME_HEIGHT, height);
// 验证实际设置的分辨率
double actualWidth = cam->get(cv::CAP_PROP_FRAME_WIDTH);
double actualHeight = cam->get(cv::CAP_PROP_FRAME_HEIGHT);
std::cout << "摄像头分辨率设置为: " << actualWidth << "x" << actualHeight << std::endl;
record = true;
while (record)
{
//std::cout << "采集影像+++++++++++++++++++++++++++++++++++++++++++" << std::endl;
cam->read(frame);
m_qImage = m_ImageProcessor->Mat2QImage(frame);
// 使用定时器定期获取图像,不再阻塞线程
m_captureTimer->start(33); // ~30fps
m_frameCounter = 0;
}
emit PlotSignal();
void RgbCameraOperation::onCaptureFrame()
{
if (!record || cam == nullptr || !cam->isOpened())
{
return;
}
cam->release();
cam->read(frame);
m_frameCounter++;
if (m_frameCounter == 1)
{
emit CamOpenedSignal();
}
emit CamOpenedSignal();
// 保存视频逻辑:如果正在录制,将当前帧写入视频
if (m_isRecording && m_videoWriter != nullptr && !frame.empty())
{
m_videoWriter->write(frame);
}
m_qImage = m_ImageProcessor->Mat2QImage(frame.clone());
emit PlotSignal();
}
void RgbCameraOperation::onPhotoSaveTimeout()
{
if (m_qImage.isNull())
{
std::cerr << "保存图片失败:当前没有可用的图像数据" << std::endl;
return;
}
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss_zzz");
QString prefix = AppSettings::instance().rgbCameraFileName();
QString fileName = QString("%1_rgb_%2.jpg").arg(prefix).arg(timestamp);
QString fullPath = AppSettings::instance().rgbCameraDataFolder() + QDir::separator() + fileName;
if (m_qImage.save(fullPath, "JPG", 95))
{
std::cout << "图片已保存: " << fullPath.toStdString() << std::endl;
emit photoSavedSignal(fullPath);
}
else
{
std::cerr << "保存图片失败: " << fullPath.toStdString() << std::endl;
}
}
void RgbCameraOperation::startPhotoSaveTimer()
{
m_photoSaveTimer->start(2000); // 每2秒保存一张照片
}
void RgbCameraOperation::stopPhotoSaveTimer()
{
m_photoSaveTimer->stop();
}
void RgbCameraOperation::OpenCamera_callback()
@ -66,5 +165,117 @@ void RgbCameraOperation::CloseCamera()
record = false;
// 停止定时器
m_captureTimer->stop();
m_photoSaveTimer->stop();
// 停止视频录制
if (m_isRecording)
{
stopVideoRecording();
}
// 释放摄像头
if (cam != nullptr)
{
cam->release();
//delete cam;
cam = nullptr;
}
emit CamClosedSignal();
}
void RgbCameraOperation::saveImage()
{
if (m_qImage.isNull())
{
std::cerr << "保存图片失败:当前没有可用的图像数据" << std::endl;
return;
}
// 生成带时间戳的文件名
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss_zzz");
QString prefix = AppSettings::instance().rgbCameraFileName();
QString fileName = QString("%1_rgb_%2.jpg").arg(prefix).arg(timestamp);
QString fullPath = AppSettings::instance().rgbCameraDataFolder() + QDir::separator() + fileName;
// 保存为 JPG 格式
if (m_qImage.save(fullPath, "JPG", 95))
{
std::cout << "图片已保存: " << fullPath.toStdString() << std::endl;
}
else
{
std::cerr << "保存图片失败: " << fullPath.toStdString() << std::endl;
}
}
void RgbCameraOperation::startVideoRecording()
{
if (m_isRecording)
{
std::cout << "视频已经在录制中" << std::endl;
return;
}
if (frame.empty())
{
std::cerr << "开始录制失败:当前没有可用的帧数据" << std::endl;
return;
}
// 释放旧的 VideoWriter
if (m_videoWriter != nullptr)
{
delete m_videoWriter;
m_videoWriter = nullptr;
}
// 生成带时间戳的视频文件名
QString timestamp = QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss");
QString prefix = AppSettings::instance().rgbCameraFileName();
QString fileName = QString("%1_video_%2.avi").arg(prefix).arg(timestamp);
QString fullPath = AppSettings::instance().rgbCameraDataFolder() + QDir::separator() + fileName;
// 获取视频编码器和帧大小
int fourcc = cv::VideoWriter::fourcc('M', 'J', 'P', 'G'); // Motion-JPEG 编码器
cv::Size frameSize = frame.size();
// 创建 VideoWriter
m_videoWriter = new cv::VideoWriter(fullPath.toStdString(), fourcc, 30.0, frameSize);
if (!m_videoWriter->isOpened())
{
std::cerr << "创建视频写入器失败: " << fullPath.toStdString() << std::endl;
delete m_videoWriter;
m_videoWriter = nullptr;
return;
}
m_isRecording = true;
std::cout << "开始录制视频: " << fullPath.toStdString() << std::endl;
emit VideoRecordingStartedSignal();
}
void RgbCameraOperation::stopVideoRecording()
{
if (!m_isRecording)
{
std::cout << "视频没有在录制" << std::endl;
return;
}
if (m_videoWriter != nullptr)
{
m_videoWriter->release();
//delete m_videoWriter;
m_videoWriter = nullptr;
}
m_isRecording = false;
std::cout << "停止录制视频" << std::endl;
emit VideoRecordingStoppedSignal();
}

View File

@ -4,6 +4,9 @@
#include <QObject>
#include <QTimer>
#include <QDir>
#include <QDateTime>
#include <QCoreApplication>
#include <opencv2/opencv.hpp>
#include <QImage>
@ -24,6 +27,12 @@ public:
QImage m_qImage;
void setCallback(void(*func)());
// 保存图片和视频的公共接口
void saveImage(); // 保存当前帧为图片
void startVideoRecording(); // 开始视频录制
void stopVideoRecording(); // 停止视频录制
bool isRecording() const { return m_isRecording; } // 获取录制状态
private:
cv::Mat frame;
cv::VideoCapture *cam;
@ -34,15 +43,33 @@ private:
bool record;
// 保存图片和视频相关
cv::VideoWriter* m_videoWriter;
bool m_isRecording;
int m_frameCounter;
// 定时器获取图像
QTimer* m_captureTimer;
// 定时器保存照片
QTimer* m_photoSaveTimer;
private slots:
void onCaptureFrame();
void onPhotoSaveTimeout();
Q_SIGNALS:
void PlotSignal();
void CamOpenedSignal();
void CamClosedSignal();
void VideoRecordingStartedSignal(); // 录制开始信号
void VideoRecordingStoppedSignal(); // 录制停止信号
void photoSavedSignal(const QString& filePath); // 照片保存成功信号
public slots:
void OpenCamera();
void OpenCamera_callback();//不使用信号而使用回调函数来通知界面刷新视频
void CloseCamera();
signals:
void PlotSignal();
void CamOpenedSignal();
void CamClosedSignal();
void startPhotoSaveTimer();
void stopPhotoSaveTimer();
};
#endif // !RGBCAMERAOPERATION_H

View File

@ -288,7 +288,7 @@ QPushButton:pressed
}</string>
</property>
<property name="text">
<string>版本:3.1.3</string>
<string>版本:3.1.4</string>
</property>
</widget>
</item>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>477</height>
<width>438</width>
<height>330</height>
</rect>
</property>
<property name="sizePolicy">
@ -83,7 +83,7 @@ QPushButton:pressed
</property>
</spacer>
</item>
<item row="1" column="1">
<item row="1" column="1" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QPushButton" name="close_btn">
@ -113,33 +113,7 @@ QPushButton:pressed
</item>
</layout>
</item>
<item row="2" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>135</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>135</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1">
<item row="2" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
@ -184,7 +158,71 @@ QPushButton:pressed
</item>
</layout>
</item>
<item row="4" column="1">
<item row="3" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>135</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="styleSheet">
<string notr="true">QLabel {
color: rgb(255, 255, 255);
}</string>
</property>
<property name="text">
<string>文件名</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="fileNameLineEdit">
<property name="styleSheet">
<string notr="true">QLineEdit {
background-color: #142D7F;
color: #e6eeff;
border: 1px solid #2f6bff;
border-radius: 6px;
padding: 4px 8px;
min-width: 70px;
min-height: 20px;
font-size: 13px;
}</string>
</property>
<property name="text">
<string>test</string>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>135</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>

View File

@ -64,7 +64,7 @@ void CImage::SetRgbImageWidthAndHeight(int BandCount, int Sample, int FrameNumbe
//std::cout << "rgb影像内存地址为:" << m_QRgbImage << std::endl;
}
void CImage::FillRgbImage(unsigned short *datacube)
void CImage::FillRgbImage(unsigned short *datacube, int rBandNumber, int gBandNumber, int bBandNumber)
{
//uchar==unsigned char,内存大小:1个字节,范围为0-255
//uchar * imagebits24 = m_QRgbImage->bits();
@ -78,9 +78,9 @@ void CImage::FillRgbImage(unsigned short *datacube)
//std::cout << "rgb图像写入数据帧数:" << j << std::endl;
//取值:一帧影像中,从左到右的rgb像元值
r = *(datacube + 121 * m_iSampleNumber + j);
g = *(datacube + 79 * m_iSampleNumber + j);
b = *(datacube + 40 * m_iSampleNumber + j);
r = *(datacube + rBandNumber * m_iSampleNumber + j);
g = *(datacube + gBandNumber * m_iSampleNumber + j);
b = *(datacube + bBandNumber * m_iSampleNumber + j);
//将像元值赋值到cv::Mat中,操作像元值:https://zhuanlan.zhihu.com/p/51842288
//int dataType = m_matRgbImage->type();//当数据类型为CV_16UC3时,返回18

View File

@ -21,7 +21,7 @@ public:
CImage(QWidget* pParent = NULL);
//~CImage();
void SetRgbImageWidthAndHeight(int BandCount, int Sample, int FrameNumber);
void FillRgbImage(unsigned short *datacube);
void FillRgbImage(unsigned short *datacube, int rBandNumber, int gBandNumber, int bBandNumber);
void FillFocusGrayImage(unsigned short *datacube);
void FillFocusGrayQImage(unsigned short * datacube);

View File

@ -109,7 +109,7 @@ QImage ImageProcessor::Mat2QImage(cv::Mat cvImg)//https://www.cnblogs.com/annt/p
QImage::Format_RGB888);
}
return qImg;
return qImg.copy(); // 返回独立数据副本,避免cvImg被覆盖导致QImage数据失效
}
cv::Mat ImageProcessor::CStretchDeal(const cv::Mat img, const uint minnum, const uint maxnum)

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>416</width>
<height>219</height>
<width>489</width>
<height>329</height>
</rect>
</property>
<property name="sizePolicy">
@ -70,18 +70,50 @@ QPushButton:pressed
}</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>18</width>
<height>100</height>
</size>
</property>
</spacer>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="styleSheet">
<string notr="true">QLabel {
color: rgb(255, 255, 255);
}</string>
</property>
<property name="text">
<string>数据路径</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="dataFolderLineEdit">
<property name="styleSheet">
<string notr="true">QLineEdit {
background-color: #142D7F;
color: #e6eeff;
border: 1px solid #2f6bff;
border-radius: 6px;
padding: 4px 8px;
min-width: 70px;
min-height: 20px;
font-size: 13px;
}</string>
</property>
<property name="text">
<string>D:\</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="dataFolderBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer">
@ -96,6 +128,19 @@ QPushButton:pressed
</property>
</spacer>
</item>
<item row="0" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>18</width>
<height>100</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
@ -165,7 +210,7 @@ QPushButton:pressed
</property>
</spacer>
</item>
<item row="2" column="1">
<item row="4" column="1">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -178,6 +223,44 @@ QPushButton:pressed
</property>
</spacer>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="styleSheet">
<string notr="true">QLabel {
color: rgb(255, 255, 255);
}</string>
</property>
<property name="text">
<string>文件名</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="fileNameLineEdit">
<property name="styleSheet">
<string notr="true">QLineEdit {
background-color: #142D7F;
color: #e6eeff;
border: 1px solid #2f6bff;
border-radius: 6px;
padding: 4px 8px;
min-width: 70px;
min-height: 20px;
font-size: 13px;
}</string>
</property>
<property name="text">
<string>test</string>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>

View File

@ -1,8 +1,11 @@
#include "rgbCameraWindow.h"
#include <Qthread>
#include "rgbCameraWindow.h"
#include <QThread>
#include <QFileDialog>
#include "AppSettings.h"
rgbCameraWindow::rgbCameraWindow(QWidget* parent)
: QDialog(parent)
, m_captureCoordinator(nullptr)
{
ui.setupUi(this);
@ -11,22 +14,190 @@ rgbCameraWindow::rgbCameraWindow(QWidget* parent)
m_RgbCamera->moveToThread(m_RgbCameraThread);
m_RgbCameraThread->start();
connect(ui.open_rgb_camera_btn, SIGNAL(clicked()), m_RgbCamera, SLOT(OpenCamera()));//ʹ<><CAB9><EFBFBD>ź<EFBFBD>֪ͨ<CDA8><D6AA><EFBFBD>̣߳<DFB3>ui<75>̣߳<DFB3>ˢ<EFBFBD><CBA2><EFBFBD><EFBFBD>Ƶ <20><> <20>ɹ<EFBFBD><C9B9><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǽ<EFBFBD><C7BD>濨<EFBFBD><E6BFA8>
connect(m_RgbCamera, SIGNAL(PlotSignal()), this, SIGNAL(PlotRgbImageSignal()));
//m_RgbCamera->setCallback(onPlotRgbImage);
//connect(this->ui.open_rgb_camera_btn, SIGNAL(clicked()), m_RgbCamera, SLOT(OpenCamera_callback()));//ʹ<>ûص<C3BB><D8B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ˢ<EFBFBD><CBA2><EFBFBD><EFBFBD><EFBFBD>̣߳<DFB3>ui<75>̣߳<DFB3><CCA3>ϵ<EFBFBD><CFB5><EFBFBD>Ƶ <20><> ʧ<><CAA7>
connect(ui.close_rgb_camera_btn, SIGNAL(clicked()), this, SLOT(onCloseRgbCamera()));//<2F>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD>
connect(ui.open_rgb_camera_btn, &QPushButton::clicked,
this, [this]() { if (m_captureCoordinator) m_captureCoordinator->openCamera(); });
connect(m_RgbCamera, SIGNAL(CamClosedSignal()), this, SIGNAL(CamClosedSignal()));
connect(ui.close_rgb_camera_btn, &QPushButton::clicked,
this, [this]() { if (m_captureCoordinator) m_captureCoordinator->closeCamera(); });
connect(this->ui.dataFolderBtn, SIGNAL(clicked()), this, SLOT(onSelectDataFolder()));
connect(this->ui.take_video_btn, &QPushButton::clicked,
this, [this]() {
if (m_captureCoordinator)
{
if (!m_captureCoordinator->isCapturing())
m_captureCoordinator->startVideoCapture();
else if (m_captureCoordinator->getCurrentMode() == RgbCameraCaptureCoordinator::Video)
m_captureCoordinator->stopVideoCapture();
}
});
connect(this->ui.take_photo_btn, &QPushButton::clicked,
this, [this]() {
if (m_captureCoordinator)
{
if (!m_captureCoordinator->isCapturing())
m_captureCoordinator->startPhotoCapture();
else if (m_captureCoordinator->getCurrentMode() == RgbCameraCaptureCoordinator::Photo)
m_captureCoordinator->stopPhotoCapture();
}
});
connect(ui.fileNameLineEdit, &QLineEdit::textChanged, this, &rgbCameraWindow::onFileNameChanged);
setupCaptureCoordinator();
loadSettings();
}
rgbCameraWindow::~rgbCameraWindow()
{
delete m_captureCoordinator;
m_captureCoordinator = nullptr;
m_RgbCameraThread->quit();
m_RgbCameraThread->wait();
delete m_RgbCamera;
delete m_RgbCameraThread;
}
void rgbCameraWindow::onCloseRgbCamera()
void rgbCameraWindow::toggleTakePhoto()
{
//std::cout << "<22>ر<EFBFBD><D8B1><EFBFBD>Ƶ+++++++++++++++++++++++++++++++++++++++++++" << std::endl;
m_RgbCamera->CloseCamera();
emit ui.take_photo_btn->clicked();
}
void rgbCameraWindow::setupCaptureCoordinator()
{
m_captureCoordinator = new RgbCameraCaptureCoordinator(m_RgbCamera, this);
connect(m_captureCoordinator, &RgbCameraCaptureCoordinator::captureStarted,
this, &rgbCameraWindow::onCaptureStarted);
connect(m_captureCoordinator, &RgbCameraCaptureCoordinator::captureStopped,
this, &rgbCameraWindow::onCaptureStopped);
connect(m_captureCoordinator, &RgbCameraCaptureCoordinator::cameraOpened,
this, &rgbCameraWindow::onCameraOpened);
connect(m_captureCoordinator, &RgbCameraCaptureCoordinator::cameraClosed,
this, &rgbCameraWindow::onCameraClosed);
connect(m_captureCoordinator, &RgbCameraCaptureCoordinator::cameraOpenFailed,
this, &rgbCameraWindow::onCameraOpenFailed);
connect(m_captureCoordinator, &RgbCameraCaptureCoordinator::photoCaptured,
this, &rgbCameraWindow::onPhotoCaptured);
connect(m_RgbCamera, &RgbCameraOperation::photoSavedSignal,
this, &rgbCameraWindow::onPhotoSaved);
}
void rgbCameraWindow::startVideoCapture()
{
if (m_captureCoordinator)
{
m_captureCoordinator->startVideoCapture();
}
}
void rgbCameraWindow::stopVideoCapture()
{
if (m_captureCoordinator)
{
m_captureCoordinator->stopVideoCapture();
}
}
void rgbCameraWindow::startPhotoCapture()
{
if (m_captureCoordinator)
{
m_captureCoordinator->startPhotoCapture();
}
}
void rgbCameraWindow::stopPhotoCapture()
{
if (m_captureCoordinator)
{
m_captureCoordinator->stopPhotoCapture();
}
}
void rgbCameraWindow::onCaptureStarted(RgbCameraCaptureCoordinator::CaptureMode mode)
{
if (mode == RgbCameraCaptureCoordinator::Video)
{
ui.take_video_btn->setText(QString::fromLocal8Bit("停止录制"));
}
else if (mode == RgbCameraCaptureCoordinator::Photo)
{
ui.take_photo_btn->setText(QString::fromLocal8Bit("采集中..."));
}
}
void rgbCameraWindow::onCaptureStopped(RgbCameraCaptureCoordinator::CaptureMode mode)
{
if (mode == RgbCameraCaptureCoordinator::Video)
{
ui.take_video_btn->setText(QString::fromLocal8Bit("录制视频"));
}
else if (mode == RgbCameraCaptureCoordinator::Photo)
{
ui.take_photo_btn->setText(QString::fromLocal8Bit("拍照"));
}
}
void rgbCameraWindow::onCameraOpened()
{
}
void rgbCameraWindow::onCameraClosed()
{
}
void rgbCameraWindow::onCameraOpenFailed(const QString& error)
{
//QMessageBox::warning(this, QString::fromLocal8Bit("相机打开失败"), error);
}
void rgbCameraWindow::onPhotoCaptured(const QString& filePath)
{
}
void rgbCameraWindow::onPhotoSaved(const QString& filePath)
{
// 可在此更新界面,例如显示已保存的照片数量
std::cout << "照片已保存: " << filePath.toStdString() << std::endl;
}
void rgbCameraWindow::loadSettings()
{
QString folder = AppSettings::instance().rgbCameraDataFolder();
setDataFolder(folder);
setFileName(AppSettings::instance().rgbCameraFileName());
}
void rgbCameraWindow::onSelectDataFolder()
{
QString dir = QFileDialog::getExistingDirectory(this,
QString::fromLocal8Bit("选择数据保存路径"),
ui.dataFolderLineEdit->text());
setDataFolder(dir);
}
void rgbCameraWindow::setDataFolder(QString dir)
{
if (!dir.isEmpty())
{
ui.dataFolderLineEdit->setText(dir);
}
}
void rgbCameraWindow::setFileName(QString name)
{
ui.fileNameLineEdit->setText(name);
}
void rgbCameraWindow::onFileNameChanged(const QString& text)
{
AppSettings::instance().setRgbCameraFileName(text);
}

View File

@ -1,4 +1,4 @@
#pragma once
#pragma once
#include <QDialog>
#include <QNetworkRequest>
@ -8,6 +8,7 @@
#include "ui_rgbCamera.h"
#include "RgbCameraOperation.h"
#include "RgbCameraCaptureCoordinator.h"
class rgbCameraWindow : public QDialog
{
@ -19,16 +20,43 @@ public:
RgbCameraOperation* m_RgbCamera;
public Q_SLOTS:
void onCloseRgbCamera();
// 视频采集控制
void startVideoCapture();
void stopVideoCapture();
signals:
// 照片采集控制
void startPhotoCapture();
void stopPhotoCapture();
public Q_SLOTS:
void onSelectDataFolder();
void onFileNameChanged(const QString& text);
void toggleTakePhoto();
Q_SIGNALS:
void PlotRgbImageSignal();
void CamClosedSignal();
private Q_SLOTS:
// 协调器信号处理
void onCaptureStarted(RgbCameraCaptureCoordinator::CaptureMode mode);
void onCaptureStopped(RgbCameraCaptureCoordinator::CaptureMode mode);
void onCameraOpened();
void onCameraClosed();
void onCameraOpenFailed(const QString& error);
void onPhotoCaptured(const QString& filePath);
void onPhotoSaved(const QString& filePath);
private:
Ui::rgbCameraClass ui;
QThread* m_RgbCameraThread;//rgb<67><62><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ȡͼ<C8A1><CDBC><EFBFBD>߳<EFBFBD>
QThread* m_RgbCameraThread;//rgb相机获取图像线程
RgbCameraCaptureCoordinator* m_captureCoordinator;
void setDataFolder(QString dir);
void setFileName(QString name);
void loadSettings();
void saveSettings();
void setupCaptureCoordinator();
};