Compare commits
2 Commits
33e34aa125
...
3.1.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 392bc98ebf | |||
| 0866b9cd56 |
@ -305,10 +305,14 @@ void DepthCameraOperation::OpenDepthCamera_getDepthValue()
|
||||
auto vid = devInfo->getVid();
|
||||
|
||||
config->enableVideoStream(OB_STREAM_DEPTH, 640, 480, 15, OB_FORMAT_Y16);
|
||||
config->enableVideoStream(OB_STREAM_COLOR, 640, 480, 15, OB_FORMAT_YUYV);
|
||||
config->setFrameAggregateOutputMode(OB_FRAME_AGGREGATE_OUTPUT_ALL_TYPE_FRAME_REQUIRE);
|
||||
|
||||
m_pipe->enableFrameSync();
|
||||
|
||||
// Create a format converter filter.
|
||||
auto formatConverter = std::make_shared<ob::FormatConvertFilter>();
|
||||
|
||||
m_pipe->start(config);
|
||||
|
||||
// Drop several frames
|
||||
@ -322,7 +326,10 @@ void DepthCameraOperation::OpenDepthCamera_getDepthValue()
|
||||
record = true;
|
||||
QString fileNamePrefix = AppSettings::instance().depthCameraDataFolder() + QDir::separator() + "Gemini336L";
|
||||
|
||||
double depthValue_all = 0.0;
|
||||
// 增量平均所需的变量
|
||||
cv::Mat avgRgbMat, avgDepthMat;
|
||||
int avgFrameCount = 0;
|
||||
|
||||
for (size_t i = 0; i < m_averageNumberOfTimes; i++)
|
||||
{
|
||||
if(frameIndex==0)
|
||||
@ -342,25 +349,56 @@ void DepthCameraOperation::OpenDepthCamera_getDepthValue()
|
||||
|
||||
// 彩色和深度图像
|
||||
auto depthFrame = frameSet->getFrame(OB_FRAME_DEPTH)->as<ob::DepthFrame>();
|
||||
auto colorFrame = frameSet->getFrame(OB_FRAME_COLOR)->as<ob::ColorFrame>();
|
||||
|
||||
//是否需要保存深度图像????????
|
||||
// Convert the color frame to RGB format.
|
||||
if (colorFrame->format() != OB_FORMAT_RGB) {
|
||||
if (colorFrame->format() == OB_FORMAT_MJPG) {
|
||||
formatConverter->setFormatConvertType(FORMAT_MJPG_TO_RGB);
|
||||
}
|
||||
else if (colorFrame->format() == OB_FORMAT_UYVY) {
|
||||
formatConverter->setFormatConvertType(FORMAT_UYVY_TO_RGB);
|
||||
}
|
||||
else if (colorFrame->format() == OB_FORMAT_YUYV) {
|
||||
formatConverter->setFormatConvertType(FORMAT_YUYV_TO_RGB);
|
||||
}
|
||||
else {
|
||||
std::cout << "Color format is not support!" << std::endl;
|
||||
continue;
|
||||
}
|
||||
colorFrame = formatConverter->process(colorFrame)->as<ob::ColorFrame>();
|
||||
}
|
||||
// Processed the color frames to BGR format, use OpenCV to save to disk.
|
||||
formatConverter->setFormatConvertType(FORMAT_RGB_TO_BGR);
|
||||
colorFrame = formatConverter->process(colorFrame)->as<ob::ColorFrame>();
|
||||
|
||||
//用于测试:保存深度图像
|
||||
//saveDepthFrame(depthFrame, frameIndex, fileNamePrefix.toStdString());
|
||||
//saveColorFrame(colorFrame, frameIndex, fileNamePrefix.toStdString());
|
||||
|
||||
cv::Mat colorMat(colorFrame->height(), colorFrame->width(), CV_8UC3, colorFrame->data());
|
||||
cv::Mat rgbMat;
|
||||
cv::cvtColor(colorMat, rgbMat, cv::COLOR_BGR2RGB);
|
||||
//m_colorImage = QImage(rgbMat.data, rgbMat.cols, rgbMat.rows, static_cast<int>(rgbMat.step), QImage::Format_RGB888).copy();
|
||||
|
||||
|
||||
cv::Mat depthMat(depthFrame->height(), depthFrame->width(), CV_16UC1, depthFrame->data());
|
||||
|
||||
//裁剪边缘区域
|
||||
int cropRows = static_cast<int>(depthMat.rows * (1 - m_percentageOfEffectiveArea) / 2);
|
||||
int cropCols = static_cast<int>(depthMat.cols * (1 - m_percentageOfEffectiveArea) / 2);
|
||||
cv::Rect roi(cropCols, cropRows,
|
||||
depthMat.cols - 2 * cropCols,
|
||||
depthMat.rows - 2 * cropRows);
|
||||
cv::Mat depthRoi = depthMat(roi);
|
||||
// 增量平均计算
|
||||
if (avgFrameCount == 0)
|
||||
{
|
||||
avgRgbMat = cv::Mat::zeros(rgbMat.size(), CV_32FC3);
|
||||
avgDepthMat = cv::Mat::zeros(depthMat.size(), CV_32F);
|
||||
}
|
||||
avgFrameCount++;
|
||||
cv::Mat rgbFloat;
|
||||
rgbMat.convertTo(rgbFloat, CV_32FC3);
|
||||
avgRgbMat = avgRgbMat + (rgbFloat - avgRgbMat) / avgFrameCount;
|
||||
|
||||
cv::Mat depthMatTmp;
|
||||
depthMat.convertTo(depthMatTmp, CV_32F);
|
||||
avgDepthMat = avgDepthMat + (depthMatTmp - avgDepthMat) / avgFrameCount;
|
||||
|
||||
//计算平均深度值并累加
|
||||
cv::Scalar meanDepth = cv::mean(depthRoi);
|
||||
double depthValue = meanDepth[0] / 1000.0; // 转换为米
|
||||
depthValue_all += depthValue;
|
||||
std::cout << "Depth value: " << depthValue << " m, accumulated: " << depthValue_all << std::endl;
|
||||
|
||||
cv::Mat depthMat8U;
|
||||
depthMat.convertTo(depthMat8U, CV_8UC1, 255.0 / 4096.0);
|
||||
@ -375,13 +413,51 @@ void DepthCameraOperation::OpenDepthCamera_getDepthValue()
|
||||
|
||||
frameIndex++;
|
||||
}
|
||||
m_pipe->stop();
|
||||
|
||||
// 对累积平均后的图像进行处理
|
||||
double depthValue;
|
||||
if (avgFrameCount > 0)
|
||||
{
|
||||
cv::Mat avgRgbResult, avgDepthResult;
|
||||
avgRgbMat.convertTo(avgRgbResult, CV_8UC3);
|
||||
avgDepthMat.convertTo(avgDepthResult, CV_16UC1);
|
||||
|
||||
// 保存平均结果图像
|
||||
std::vector<int> pngParams;
|
||||
pngParams.push_back(cv::IMWRITE_PNG_COMPRESSION);
|
||||
pngParams.push_back(0);
|
||||
pngParams.push_back(cv::IMWRITE_PNG_STRATEGY);
|
||||
pngParams.push_back(cv::IMWRITE_PNG_STRATEGY_DEFAULT);
|
||||
cv::imwrite(getTestFilePath("_AvgRGB_").toStdString(), avgRgbResult, pngParams);
|
||||
cv::imwrite(getTestFilePath("_AvgDepth_").toStdString(), avgDepthResult, pngParams);
|
||||
|
||||
// 创建掩膜,排除深度值为0的区域
|
||||
cv::Mat mask = avgDepthResult != 0;
|
||||
|
||||
// 保存掩膜
|
||||
cv::Mat mask8U;
|
||||
mask.convertTo(mask8U, CV_8UC1, 255.0);
|
||||
std::string maskName = fileNamePrefix.toStdString() + "_Mask_" + std::to_string(mask.cols) + "x" + std::to_string(mask.rows) + ".png";
|
||||
cv::imwrite(maskName, mask8U, pngParams);
|
||||
|
||||
if (m_depthAlgorithm == 0)
|
||||
{
|
||||
depthValue = processAveragedImages_roiAvg(avgDepthResult, mask);
|
||||
}
|
||||
else if (m_depthAlgorithm == 1)
|
||||
{
|
||||
depthValue = processAveragedImages_depthRangePercentage(avgDepthResult, mask);
|
||||
}
|
||||
else if (m_depthAlgorithm == 2)
|
||||
{
|
||||
depthValue = processAveragedImages_segmentation(avgRgbResult, avgDepthResult, mask);
|
||||
}
|
||||
}
|
||||
|
||||
//计算平均深度值
|
||||
double depthValue_avg = depthValue_all / m_averageNumberOfTimes;
|
||||
std::cout << "Average depth value: " << depthValue_avg << " m" << std::endl;
|
||||
emit DepthValueSignal(depthValue_avg);
|
||||
|
||||
m_pipe->stop();
|
||||
std::cout << "Depth value: " << depthValue << " m" << std::endl;
|
||||
emit DepthValueSignal(depthValue);
|
||||
|
||||
delete m_pipe;
|
||||
m_pipe = nullptr;
|
||||
@ -389,6 +465,97 @@ void DepthCameraOperation::OpenDepthCamera_getDepthValue()
|
||||
record = false;
|
||||
}
|
||||
|
||||
double DepthCameraOperation::processAveragedImages_roiAvg(const cv::Mat& avgDepthResult, const cv::Mat& mask)
|
||||
{
|
||||
//裁剪边缘区域
|
||||
int cropRows = static_cast<int>(avgDepthResult.rows * (1 - m_percentageOfEffectiveArea) / 2);
|
||||
int cropCols = static_cast<int>(avgDepthResult.cols * (1 - m_percentageOfEffectiveArea) / 2);
|
||||
cv::Rect roi(cropCols, cropRows,
|
||||
avgDepthResult.cols - 2 * cropCols,
|
||||
avgDepthResult.rows - 2 * cropRows);
|
||||
cv::Mat depthRoi = avgDepthResult(roi);
|
||||
cv::Mat maskRoi = mask(roi);
|
||||
//计算平均深度值,使用掩膜排除深度值为0的区域
|
||||
cv::Scalar meanDepth = cv::mean(depthRoi, maskRoi);
|
||||
double depthValue = meanDepth[0] / 1000.0; // 转换为米
|
||||
|
||||
return depthValue;
|
||||
}
|
||||
|
||||
double DepthCameraOperation::processAveragedImages_depthRangePercentage(const cv::Mat& avgDepthResult, const cv::Mat& mask)
|
||||
{
|
||||
// 找出最大最小值
|
||||
double minVal, maxVal;
|
||||
cv::minMaxLoc(avgDepthResult, &minVal, &maxVal, nullptr, nullptr, mask);
|
||||
|
||||
// 检查是否有有效数据
|
||||
if (minVal == std::numeric_limits<double>::max())
|
||||
{
|
||||
std::cout << "Warning: No valid depth pixels found in masked region!" << std::endl;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// 返回最小值乘以m_depthRangePercentage
|
||||
double depthValue = minVal * m_depthRangePercentage / 1000.0; // 转换为米
|
||||
|
||||
return depthValue;
|
||||
}
|
||||
|
||||
double DepthCameraOperation::processAveragedImages_segmentation(const cv::Mat& avgRgbResult, const cv::Mat& avgDepthResult, const cv::Mat& mask)
|
||||
{
|
||||
// 转换到 HSV 颜色空间进行植被分割
|
||||
cv::Mat hsvMat;
|
||||
cv::cvtColor(avgRgbResult, hsvMat, cv::COLOR_RGB2HSV);
|
||||
|
||||
// 分离通道
|
||||
std::vector<cv::Mat> hsvChannels;
|
||||
cv::split(hsvMat, hsvChannels);
|
||||
cv::Mat hue = hsvChannels[0];
|
||||
cv::Mat sat = hsvChannels[1];
|
||||
cv::Mat val = hsvChannels[2];
|
||||
|
||||
// 定义绿色植被的Hue范围 (OpenCV中Hue范围是0-180,实际绿色约35-90度)
|
||||
// 扩展范围以覆盖不同光照条件下的绿色
|
||||
cv::Mat hueMask1 = (hue >= 35) & (hue <= 85);
|
||||
cv::Mat satMask = sat > 30; // 饱和度阈值,去除灰色区域
|
||||
cv::Mat valMask = val > 50; // 亮度阈值,去除过暗区域
|
||||
|
||||
// 组合条件生成植被掩膜
|
||||
cv::Mat vmask;
|
||||
cv::bitwise_and(hueMask1, satMask, vmask);
|
||||
cv::bitwise_and(vmask, valMask, vmask);
|
||||
|
||||
// 结合深度掩膜,计算两个mask的交集
|
||||
cv::Mat combinedMask;
|
||||
cv::bitwise_and(mask, vmask, combinedMask);
|
||||
|
||||
// 保存 vmask 和 combinedMask 到 exe 所在文件夹的文件夹
|
||||
cv::Mat vmask8U, combinedMask8U;
|
||||
vmask.convertTo(vmask8U, CV_8UC1, 255.0);
|
||||
combinedMask.convertTo(combinedMask8U, CV_8UC1, 255.0);
|
||||
cv::imwrite(getTestFilePath("vmask").toStdString(), vmask8U);
|
||||
cv::imwrite(getTestFilePath("combinedMask").toStdString(), combinedMask8U);
|
||||
|
||||
// 计算 avgRgbResult 在 combinedMask 区域内的平均深度值
|
||||
double depthValue = 0.0;
|
||||
if (cv::countNonZero(combinedMask) > 0) {
|
||||
cv::Scalar meanDepth = cv::mean(avgDepthResult, combinedMask);
|
||||
depthValue = meanDepth[0] / 1000.0; // 转换为米
|
||||
} else {
|
||||
std::cout << "Warning: No valid pixels in combined mask!" << std::endl;
|
||||
}
|
||||
|
||||
return depthValue;
|
||||
}
|
||||
|
||||
QString DepthCameraOperation::getTestFilePath(const QString& fileName)
|
||||
{
|
||||
QString testFolder = QCoreApplication::applicationDirPath() + QDir::separator() + "depthValueTest";
|
||||
QDir().mkpath(testFolder);
|
||||
QString timestamp = QString::number(QDateTime::currentMSecsSinceEpoch());
|
||||
return testFolder + QDir::separator() + fileName + "_" + timestamp + ".png";
|
||||
}
|
||||
|
||||
void DepthCameraOperation::saveDepthFrame(const std::shared_ptr<ob::DepthFrame> depthFrame, const uint32_t frameIndex, std::string fileNamePrefix_)
|
||||
{
|
||||
std::vector<int> params;
|
||||
|
||||
@ -5,8 +5,10 @@
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QImage>
|
||||
#include <Qthread>
|
||||
#include <QThread>
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
//#include <QLabel>
|
||||
#include <QFileDialog>
|
||||
|
||||
@ -35,8 +37,10 @@ public:
|
||||
|
||||
void setCaptureInterval(int captureIntervalSeconds);
|
||||
|
||||
void setDepthAlgorithm(int depthAlgorithm) { m_depthAlgorithm = depthAlgorithm; }
|
||||
void setAverageNumberOfTimes(double averageNumberOfTimes) { m_averageNumberOfTimes = averageNumberOfTimes; }
|
||||
void setPercentageOfEffectiveArea(double percentageOfEffectiveArea) { m_percentageOfEffectiveArea = percentageOfEffectiveArea; }
|
||||
void setDepthRangePercentage(double depthRangePercentage) { m_depthRangePercentage = depthRangePercentage; }
|
||||
|
||||
private:
|
||||
ob::Pipeline* m_pipe;
|
||||
@ -53,8 +57,16 @@ private:
|
||||
|
||||
int m_captureIntervalMilliseconds;
|
||||
|
||||
int m_depthAlgorithm;
|
||||
double m_averageNumberOfTimes;
|
||||
double m_percentageOfEffectiveArea;
|
||||
double m_depthRangePercentage;
|
||||
|
||||
double processAveragedImages_roiAvg(const cv::Mat& avgDepthResult, const cv::Mat& mask);
|
||||
double processAveragedImages_depthRangePercentage(const cv::Mat& avgDepthResult, const cv::Mat& mask);
|
||||
double processAveragedImages_segmentation(const cv::Mat& avgRgbResult, const cv::Mat& avgDepthResult, const cv::Mat& mask);
|
||||
|
||||
QString getTestFilePath(const QString& fileName);
|
||||
|
||||
public slots:
|
||||
void OpenDepthCamera();
|
||||
@ -101,5 +113,4 @@ signals:
|
||||
private:
|
||||
Ui::DepthCameraClass ui;
|
||||
QThread* m_DepthCameraThread;
|
||||
|
||||
};
|
||||
|
||||
@ -682,6 +682,9 @@ void HPPA::initTimedDataCollection()
|
||||
connect(m_omc_LiftingPlatform, &OneMotorControl_LiftingPlatform::sequenceComplete, m_tdc, &TimedDataCollection::subTaskCompleted);
|
||||
connect(m_omc_LiftingPlatform, &OneMotorControl_LiftingPlatform::back2OriginSignal_TimedDataCollection, m_tdc, &TimedDataCollection::onBack2Origin);
|
||||
|
||||
//自动调焦
|
||||
connect(m_tdc, &TimedDataCollection::AutoFocusSignals, this, &HPPA::onAutoFocus_TimedDataCollection);
|
||||
|
||||
m_tdc->show();
|
||||
}
|
||||
|
||||
@ -782,7 +785,8 @@ void HPPA::onStartTimedDataCollection(int camType)
|
||||
|
||||
void HPPA::onObtainTargetDepthInformation(SubTask subTaskParams)
|
||||
{
|
||||
m_tmc->run4_ObtainTargetDepthInfo(m_depthCameraWindow, subTaskParams.depthType, subTaskParams.depthInfoX, subTaskParams.depthInfoY, subTaskParams.averageNumberOfTimes, subTaskParams.percentageOfEffectiveArea);
|
||||
m_tmc->run4_ObtainTargetDepthInfo(m_depthCameraWindow, subTaskParams.depthAlgorithm, subTaskParams.depthType, subTaskParams.depthInfoX, subTaskParams.depthInfoY,
|
||||
subTaskParams.averageNumberOfTimes, subTaskParams.percentageOfEffectiveArea, subTaskParams.depthRangePercentage);
|
||||
}
|
||||
|
||||
void HPPA::onLiftingPlatform(SubTask subTaskParams)
|
||||
@ -790,6 +794,13 @@ void HPPA::onLiftingPlatform(SubTask subTaskParams)
|
||||
m_omc_LiftingPlatform->run();
|
||||
}
|
||||
|
||||
void HPPA::onAutoFocus_TimedDataCollection(SubTask subTaskParams)
|
||||
{
|
||||
m_tmc->setImager(m_Imager);
|
||||
//先使用subTaskParams.autoFocusMotorConfigFilePath替换文件oneMotorConfigFile_focus.cfg
|
||||
m_tmc->run5_AutoFocus(subTaskParams.autoFocusX, subTaskParams.autoFocusY);
|
||||
}
|
||||
|
||||
void HPPA::onTimedDataCollection()
|
||||
{
|
||||
QAction* checkedScenario = m_ScenarioActionGroup->checkedAction();
|
||||
|
||||
@ -452,6 +452,7 @@ public Q_SLOTS:
|
||||
void onStartTimedDataCollection(int camType);
|
||||
void onObtainTargetDepthInformation(SubTask subTaskParams);
|
||||
void onLiftingPlatform(SubTask subTaskParams);
|
||||
void onAutoFocus_TimedDataCollection(SubTask subTaskParams);
|
||||
|
||||
void onStretchedImageReady(int fileNumber, const QString& filePath, QPixmap& pixmap);
|
||||
void onStretchProcessingError(int fileNumber, const QString& filePath, const QString& error);
|
||||
|
||||
@ -131,70 +131,70 @@ QVariant TaskTreeModel::data(const QModelIndex& index, int role) const
|
||||
if (node->nodeType == TreeNodeType::Task && node->taskData) {
|
||||
const TimedTask& task = *node->taskData;
|
||||
switch (index.column()) {
|
||||
case ColName:
|
||||
return QString::fromLocal8Bit("定时任务 %1").arg(task.id);
|
||||
case ColScheduledTime:
|
||||
return task.scheduledTime.toString("yyyy-MM-dd HH:mm:ss");
|
||||
case ColCountdown: {
|
||||
if (task.status == TaskStatus::Finished || task.status == TaskStatus::Running)
|
||||
{
|
||||
return QString::fromLocal8Bit("0");
|
||||
case ColName:
|
||||
return QString::fromLocal8Bit("定时任务 %1").arg(task.id);
|
||||
case ColScheduledTime:
|
||||
return task.scheduledTime.toString("yyyy-MM-dd HH:mm:ss");
|
||||
case ColCountdown: {
|
||||
if (task.status == TaskStatus::Finished || task.status == TaskStatus::Running)
|
||||
{
|
||||
return QString::fromLocal8Bit("0");
|
||||
}
|
||||
qint64 seconds = QDateTime::currentDateTime().secsTo(task.scheduledTime);
|
||||
if (seconds < 0)
|
||||
{
|
||||
return QString::fromLocal8Bit("已超时");
|
||||
}
|
||||
return formatCountdown(seconds);
|
||||
}
|
||||
qint64 seconds = QDateTime::currentDateTime().secsTo(task.scheduledTime);
|
||||
if (seconds < 0)
|
||||
{
|
||||
return QString::fromLocal8Bit("已超时");
|
||||
case ColStartTime:
|
||||
return task.startTime.isValid() ?
|
||||
task.startTime.toString("HH:mm:ss") : "-";
|
||||
case ColEndTime:
|
||||
return task.endTime.isValid() ?
|
||||
task.endTime.toString("HH:mm:ss") : "-";
|
||||
case ColDuration:
|
||||
return formatDuration(task.durationMinutes);
|
||||
case ColEstimatedDuration:
|
||||
return formatDuration(task.estimatedDurationMinutes);
|
||||
case ColStatus:
|
||||
return statusToString(task.status);
|
||||
case ColProgress: {
|
||||
int finished = 0;
|
||||
for (const auto& sub : task.subTasks) {
|
||||
if (sub.status == TaskStatus::Finished) finished++;
|
||||
}
|
||||
return QString("%1/%2").arg(finished).arg(task.subTasks.size());
|
||||
}
|
||||
return formatCountdown(seconds);
|
||||
}
|
||||
case ColStartTime:
|
||||
return task.startTime.isValid() ?
|
||||
task.startTime.toString("HH:mm:ss") : "-";
|
||||
case ColEndTime:
|
||||
return task.endTime.isValid() ?
|
||||
task.endTime.toString("HH:mm:ss") : "-";
|
||||
case ColDuration:
|
||||
return formatDuration(task.durationMinutes);
|
||||
case ColEstimatedDuration:
|
||||
return formatDuration(task.estimatedDurationMinutes);
|
||||
case ColStatus:
|
||||
return statusToString(task.status);
|
||||
case ColProgress: {
|
||||
int finished = 0;
|
||||
for (const auto& sub : task.subTasks) {
|
||||
if (sub.status == TaskStatus::Finished) finished++;
|
||||
}
|
||||
return QString("%1/%2").arg(finished).arg(task.subTasks.size());
|
||||
}
|
||||
case ColPath:
|
||||
return task.savePath;
|
||||
case ColPath:
|
||||
return task.savePath;
|
||||
}
|
||||
}
|
||||
else if (node->nodeType == TreeNodeType::SubTask && node->subTaskData) {
|
||||
const SubTask& subTask = *node->subTaskData;
|
||||
switch (index.column()) {
|
||||
case ColName:
|
||||
return subTaskTypeToString(subTask.type);
|
||||
case ColScheduledTime:
|
||||
return "-";
|
||||
case ColCountdown:
|
||||
return "-";
|
||||
case ColStartTime:
|
||||
return subTask.startTime.isValid() ?
|
||||
subTask.startTime.toString("HH:mm:ss") : "-";
|
||||
case ColEndTime:
|
||||
return subTask.endTime.isValid() ?
|
||||
subTask.endTime.toString("HH:mm:ss") : "-";
|
||||
case ColDuration:
|
||||
return formatDuration(subTask.durationMinutes);
|
||||
case ColEstimatedDuration:
|
||||
return formatDuration(subTask.estimatedDurationMinutes);
|
||||
case ColStatus:
|
||||
return statusToString(subTask.status);
|
||||
case ColProgress:
|
||||
return "-";
|
||||
case ColPath:
|
||||
return "-";
|
||||
case ColName:
|
||||
return subTaskTypeToString(subTask.type);
|
||||
case ColScheduledTime:
|
||||
return "-";
|
||||
case ColCountdown:
|
||||
return "-";
|
||||
case ColStartTime:
|
||||
return subTask.startTime.isValid() ?
|
||||
subTask.startTime.toString("HH:mm:ss") : "-";
|
||||
case ColEndTime:
|
||||
return subTask.endTime.isValid() ?
|
||||
subTask.endTime.toString("HH:mm:ss") : "-";
|
||||
case ColDuration:
|
||||
return formatDuration(subTask.durationMinutes);
|
||||
case ColEstimatedDuration:
|
||||
return formatDuration(subTask.estimatedDurationMinutes);
|
||||
case ColStatus:
|
||||
return statusToString(subTask.status);
|
||||
case ColProgress:
|
||||
return "-";
|
||||
case ColPath:
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,6 +114,7 @@ void TimedDataCollection::setupConnections()
|
||||
|
||||
connect(m_scheduler, &TaskScheduler::ObtainingDepthInformationSignals, this, &TimedDataCollection::ObtainingDepthInformationSignals);
|
||||
connect(m_scheduler, &TaskScheduler::LiftingPlatformSignals, this, &TimedDataCollection::LiftingPlatformSignals);
|
||||
connect(m_scheduler, &TaskScheduler::AutoFocusSignals, this, &TimedDataCollection::AutoFocusSignals);
|
||||
|
||||
connect(m_scheduler, &TaskScheduler::switchHalogenLampSignal,
|
||||
this, &TimedDataCollection::switchHalogenLampSignal);
|
||||
@ -419,6 +420,7 @@ void TimedDataCollection::readTimedTaskFromFile(const QString& filePath)
|
||||
double totalEstimatedMinutes = 0.0;
|
||||
for (int j = 0; j < loadedTasks[i].subTasks.size(); ++j)
|
||||
{
|
||||
loadedTasks[i].subTasks[j].durationMinutes = 0.0; // 初始化实际耗时为0
|
||||
QString pathLineFilePath = loadedTasks[i].subTasks[j].pathLineFilePath;
|
||||
if (!pathLineFilePath.isEmpty())
|
||||
{
|
||||
@ -429,6 +431,7 @@ void TimedDataCollection::readTimedTaskFromFile(const QString& filePath)
|
||||
}
|
||||
double slrTimeMinute = 135 / 60;
|
||||
loadedTasks[i].estimatedDurationMinutes = totalEstimatedMinutes + loadedTasks[i].HalogenLampPreheatingTime_Minute + slrTimeMinute;
|
||||
loadedTasks[i].durationMinutes = 0.0; // 初始化实际耗时为0
|
||||
}
|
||||
|
||||
m_taskModel->setTasks(loadedTasks);
|
||||
|
||||
@ -54,6 +54,7 @@ Q_SIGNALS:
|
||||
|
||||
void ObtainingDepthInformationSignals(SubTask info);
|
||||
void LiftingPlatformSignals(SubTask info);
|
||||
void AutoFocusSignals(SubTask info);
|
||||
|
||||
void switchHalogenLampSignal(int state);
|
||||
void switchD65LampSignal(int state);
|
||||
|
||||
@ -121,6 +121,23 @@ SubTaskType TimedDataCollectionDataStructuresReaderWriter::stringToSubTaskType(c
|
||||
return SubTaskType::SingleLensReflex;
|
||||
}
|
||||
|
||||
QString TimedDataCollectionDataStructuresReaderWriter::hyperImagerTypeToString(HyperImagerType type)
|
||||
{
|
||||
switch (type) {
|
||||
case HyperImagerType::Pika_L: return "Pika_L";
|
||||
case HyperImagerType::Pika_NIR: return "Pika_NIR";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
HyperImagerType TimedDataCollectionDataStructuresReaderWriter::stringToHyperImagerType(const QString& str)
|
||||
{
|
||||
if (str == "Pika_L") return HyperImagerType::Pika_L;
|
||||
if (str == "Pika_NIR") return HyperImagerType::Pika_NIR;
|
||||
|
||||
return HyperImagerType::Pika_L;
|
||||
}
|
||||
|
||||
// ==================== SubTask序列化 ====================
|
||||
|
||||
QJsonObject TimedDataCollectionDataStructuresReaderWriter::subTaskToJson(const SubTask& subTask)
|
||||
@ -138,15 +155,18 @@ QJsonObject TimedDataCollectionDataStructuresReaderWriter::subTaskToJson(const S
|
||||
obj["defaultRenderBand"] = subTask.defaultRenderBand;
|
||||
obj["captureIntervalSeconds"] = subTask.captureIntervalSeconds;
|
||||
|
||||
obj["autoFocusHyperImagerType"] = hyperImagerTypeToString(subTask.autoFocusHyperImagerType);
|
||||
obj["autoFocusMotorConfigFilePath"] = subTask.autoFocusMotorConfigFilePath;
|
||||
obj["autoFocusX"] = subTask.autoFocusX;
|
||||
obj["autoFocusY"] = subTask.autoFocusY;
|
||||
|
||||
obj["depthAlgorithm"] = subTask.depthAlgorithm;
|
||||
obj["depthInfoX"] = subTask.depthInfoX;
|
||||
obj["depthInfoY"] = subTask.depthInfoY;
|
||||
obj["averageNumberOfTimes"] = subTask.averageNumberOfTimes;
|
||||
obj["percentageOfEffectiveArea"] = subTask.percentageOfEffectiveArea;
|
||||
obj["depthType"] = subTask.depthType;
|
||||
obj["depthRangePercentage"] = subTask.depthRangePercentage;
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -164,15 +184,18 @@ bool TimedDataCollectionDataStructuresReaderWriter::jsonToSubTask(const QJsonObj
|
||||
subTask.defaultRenderBand = json["defaultRenderBand"].toInt();
|
||||
subTask.captureIntervalSeconds = json["captureIntervalSeconds"].toDouble();
|
||||
|
||||
subTask.autoFocusHyperImagerType = stringToHyperImagerType(json["autoFocusHyperImagerType"].toString());
|
||||
subTask.autoFocusMotorConfigFilePath = json["autoFocusMotorConfigFilePath"].toString();
|
||||
subTask.autoFocusX = json["autoFocusX"].toDouble();
|
||||
subTask.autoFocusY = json["autoFocusY"].toDouble();
|
||||
|
||||
subTask.depthAlgorithm = json["depthAlgorithm"].toInt();
|
||||
subTask.depthInfoX = json["depthInfoX"].toDouble();
|
||||
subTask.depthInfoY = json["depthInfoY"].toDouble();
|
||||
subTask.averageNumberOfTimes = json["averageNumberOfTimes"].toInt();
|
||||
subTask.percentageOfEffectiveArea = json["percentageOfEffectiveArea"].toDouble();
|
||||
subTask.depthType = json["depthType"].toInt();
|
||||
subTask.depthRangePercentage = json["depthRangePercentage"].toDouble();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -466,84 +489,102 @@ void TaskExecutor::executeNextSubTask()
|
||||
|
||||
switch (subTask.type)
|
||||
{
|
||||
case SubTaskType::ObtainingDepthInformation:
|
||||
{
|
||||
//(1)移动到指定位置并通过深度相机获取深度信息(2)调整升降板高度(白板+调焦板)(3)回到零位(0,0)
|
||||
emit switchD65LampSignal(1);
|
||||
emit ObtainingDepthInformationSignals(subTask);
|
||||
break;
|
||||
}
|
||||
case SubTaskType::AutoFocus:
|
||||
{
|
||||
//先判断高光谱相机类型,然后发送相机参数hyperCamParm连接相机
|
||||
case SubTaskType::ObtainingDepthInformation:
|
||||
{
|
||||
//(1)移动到指定位置并通过深度相机获取深度信息(2)调整升降板高度(白板+调焦板)(3)回到零位(0,0)
|
||||
emit switchD65LampSignal(1);
|
||||
emit ObtainingDepthInformationSignals(subTask);
|
||||
break;
|
||||
}
|
||||
case SubTaskType::AutoFocus:
|
||||
{
|
||||
switch (subTask.autoFocusHyperImagerType)
|
||||
{
|
||||
case HyperImagerType::Pika_L:
|
||||
m_camType = 0;
|
||||
m_currentFolder = makeSubTaskDataFolder("L");
|
||||
emit hyperCamParm(m_camType, subTask.frameRate, subTask.exposureTime, m_currentFolder, "L");
|
||||
|
||||
//执行自动调焦任务
|
||||
break;
|
||||
}
|
||||
case SubTaskType::LiftingPlatform:
|
||||
{
|
||||
//执行升降平台任务
|
||||
emit LiftingPlatformSignals(subTask);
|
||||
break;
|
||||
}
|
||||
case SubTaskType::HyperSpectual400_1000nm:
|
||||
{
|
||||
m_camType = 0;
|
||||
m_currentFolder = makeSubTaskDataFolder("L");
|
||||
emit hyperCamParm(m_camType, subTask.frameRate, subTask.exposureTime, m_currentFolder, "L");
|
||||
break;
|
||||
case HyperImagerType::Pika_NIR:
|
||||
m_camType = 1;
|
||||
m_currentFolder = makeSubTaskDataFolder("NIR");
|
||||
emit hyperCamParm(m_camType, subTask.frameRate, subTask.exposureTime, m_currentFolder, "NIR");
|
||||
|
||||
emit motorParm(subTask.pathLineFilePath);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
emit switchHalogenLampSignal(1);
|
||||
|
||||
// 打开卤素灯预热
|
||||
emit switchHalogenLampSignal(1);
|
||||
printMsgAndTime("open HalogenLamp");
|
||||
double sleepTimeSecond = m_task.HalogenLampPreheatingTime_Minute * 60;
|
||||
QTimer::singleShot(sleepTimeSecond * 1000, this, &TaskExecutor::emitRecordSignal);
|
||||
//执行自动调焦任务
|
||||
QTimer::singleShot(3 * 1000, this, &TaskExecutor::emitAutoFocusSignal);
|
||||
break;
|
||||
}
|
||||
case SubTaskType::LiftingPlatform:
|
||||
{
|
||||
//执行升降平台任务
|
||||
emit LiftingPlatformSignals(subTask);
|
||||
break;
|
||||
}
|
||||
case SubTaskType::HyperSpectual400_1000nm:
|
||||
{
|
||||
m_camType = 0;
|
||||
m_currentFolder = makeSubTaskDataFolder("L");
|
||||
emit hyperCamParm(m_camType, subTask.frameRate, subTask.exposureTime, m_currentFolder, "L");
|
||||
|
||||
break;
|
||||
}
|
||||
case SubTaskType::HyperSpectual1000_1700nm:
|
||||
{
|
||||
m_camType = 1;
|
||||
m_currentFolder = makeSubTaskDataFolder("NIR");
|
||||
emit hyperCamParm(m_camType, subTask.frameRate, subTask.exposureTime, m_currentFolder, "NIR");
|
||||
emit motorParm(subTask.pathLineFilePath);
|
||||
|
||||
emit motorParm(subTask.pathLineFilePath);
|
||||
// 打开卤素灯预热
|
||||
emit switchHalogenLampSignal(1);
|
||||
printMsgAndTime("open HalogenLamp");
|
||||
double sleepTimeSecond = m_task.HalogenLampPreheatingTime_Minute * 60;
|
||||
QTimer::singleShot(sleepTimeSecond * 1000, this, &TaskExecutor::emitRecordSignal);
|
||||
|
||||
QTimer::singleShot(3 * 1000, this, &TaskExecutor::emitRecordSignal);
|
||||
break;
|
||||
}
|
||||
case SubTaskType::HyperSpectual1000_1700nm:
|
||||
{
|
||||
m_camType = 1;
|
||||
m_currentFolder = makeSubTaskDataFolder("NIR");
|
||||
emit hyperCamParm(m_camType, subTask.frameRate, subTask.exposureTime, m_currentFolder, "NIR");
|
||||
|
||||
break;
|
||||
}
|
||||
case SubTaskType::SingleLensReflex:
|
||||
{
|
||||
m_camType = 2;
|
||||
m_currentFolder = makeSubTaskDataFolder("SLR");
|
||||
emit camParm(m_camType, 3, m_currentFolder);
|
||||
emit motorParm(subTask.pathLineFilePath);
|
||||
|
||||
emit motorParm(subTask.pathLineFilePath);
|
||||
QTimer::singleShot(3 * 1000, this, &TaskExecutor::emitRecordSignal);
|
||||
|
||||
emit switchD65LampSignal(1);
|
||||
break;
|
||||
}
|
||||
case SubTaskType::SingleLensReflex:
|
||||
{
|
||||
m_camType = 2;
|
||||
m_currentFolder = makeSubTaskDataFolder("SLR");
|
||||
emit camParm(m_camType, 3, m_currentFolder);
|
||||
|
||||
emit switchSlrSignal(1);
|
||||
emit motorParm(subTask.pathLineFilePath);
|
||||
|
||||
QTimer::singleShot(3 * 1000, this, &TaskExecutor::emitRecordSignal);
|
||||
emit switchD65LampSignal(1);
|
||||
|
||||
break;
|
||||
}
|
||||
case SubTaskType::DepthCamera:
|
||||
{
|
||||
m_camType = 3;
|
||||
m_currentFolder = makeSubTaskDataFolder("DepthCamera");
|
||||
emit camParm(m_camType, 3, m_currentFolder);
|
||||
emit switchSlrSignal(1);
|
||||
|
||||
emit motorParm(subTask.pathLineFilePath);
|
||||
QTimer::singleShot(3 * 1000, this, &TaskExecutor::emitRecordSignal);
|
||||
|
||||
emit switchD65LampSignal(1);
|
||||
break;
|
||||
}
|
||||
case SubTaskType::DepthCamera:
|
||||
{
|
||||
m_camType = 3;
|
||||
m_currentFolder = makeSubTaskDataFolder("DepthCamera");
|
||||
emit camParm(m_camType, 3, m_currentFolder);
|
||||
|
||||
QTimer::singleShot(3 * 1000, this, &TaskExecutor::emitRecordSignal);
|
||||
emit motorParm(subTask.pathLineFilePath);
|
||||
|
||||
break;
|
||||
}
|
||||
emit switchD65LampSignal(1);
|
||||
|
||||
QTimer::singleShot(3 * 1000, this, &TaskExecutor::emitRecordSignal);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
ensurePreTaskLighting();
|
||||
}
|
||||
@ -553,6 +594,12 @@ void TaskExecutor::emitRecordSignal()
|
||||
emit startRecordSignal(m_camType);
|
||||
}
|
||||
|
||||
void TaskExecutor::emitAutoFocusSignal()
|
||||
{
|
||||
SubTask& subTask = m_task.subTasks[m_currentSubTaskIndex];
|
||||
emit AutoFocusSignals(subTask);
|
||||
}
|
||||
|
||||
// ==================== TaskScheduler 实现 ====================
|
||||
|
||||
TaskScheduler::TaskScheduler(QObject* parent)
|
||||
@ -729,6 +776,7 @@ void TaskScheduler::executeTask(TimedTask& task)
|
||||
|
||||
connect(m_currentExecutor, &TaskExecutor::ObtainingDepthInformationSignals, this, &TaskScheduler::ObtainingDepthInformationSignals);
|
||||
connect(m_currentExecutor, &TaskExecutor::LiftingPlatformSignals, this, &TaskScheduler::LiftingPlatformSignals);
|
||||
connect(m_currentExecutor, &TaskExecutor::AutoFocusSignals, this, &TaskScheduler::AutoFocusSignals);
|
||||
|
||||
connect(m_currentExecutor, &TaskExecutor::switchHalogenLampSignal, this, &TaskScheduler::switchHalogenLampSignal);
|
||||
connect(m_currentExecutor, &TaskExecutor::switchD65LampSignal, this, &TaskScheduler::switchD65LampSignal);
|
||||
|
||||
@ -30,6 +30,10 @@ enum class SubTaskType {
|
||||
AutoFocus, // 自动对焦
|
||||
LiftingPlatform // 升降平台
|
||||
};
|
||||
enum class HyperImagerType {
|
||||
Pika_L,
|
||||
Pika_NIR
|
||||
};
|
||||
|
||||
// ==================== 统一子任务封装 ====================
|
||||
|
||||
@ -51,13 +55,16 @@ struct SubTask {
|
||||
int captureIntervalSeconds = 5; // 单反/深度相机用
|
||||
|
||||
//任务ObtainingDepthInformation所需的x和y坐标
|
||||
int depthAlgorithm = 0;//0:深度图像的范围(percentageOfEffectiveArea)平均,1:深度范围(depthRangePercentage)的百分比,2:通过彩色图像分割植被区域的深度图像,然后平均
|
||||
int depthType = 0;//0表示植被深度,1表示白板/调焦版深度
|
||||
double depthInfoX = 0.0;
|
||||
double depthInfoY = 0.0;
|
||||
int averageNumberOfTimes = 1; //任务ObtainingDepthInformation所需的平均次数
|
||||
double percentageOfEffectiveArea = 50.0; //深度图像的有效范围百分比
|
||||
double depthRangePercentage = 80.0; //深度范围的百分比
|
||||
|
||||
//高光谱自动调焦
|
||||
HyperImagerType autoFocusHyperImagerType;//取值范围:L、NIR
|
||||
QString autoFocusMotorConfigFilePath;//马达配置文件
|
||||
double autoFocusX = 0.0;
|
||||
double autoFocusY = 0.0;
|
||||
@ -124,6 +131,9 @@ private:
|
||||
|
||||
static QString subTaskTypeToString(SubTaskType type);
|
||||
static SubTaskType stringToSubTaskType(const QString& str);
|
||||
|
||||
static QString hyperImagerTypeToString(HyperImagerType type);
|
||||
static HyperImagerType stringToHyperImagerType(const QString& str);
|
||||
};
|
||||
|
||||
// ==================== 任务执行器 ====================
|
||||
@ -168,6 +178,7 @@ signals:
|
||||
|
||||
void ObtainingDepthInformationSignals(SubTask info);
|
||||
void LiftingPlatformSignals(SubTask info);
|
||||
void AutoFocusSignals(SubTask info);
|
||||
|
||||
void switchHalogenLampSignal(int state);
|
||||
void switchD65LampSignal(int state);
|
||||
@ -179,6 +190,7 @@ public slots:
|
||||
void onError(const QString& error);
|
||||
|
||||
void emitRecordSignal();
|
||||
void emitAutoFocusSignal();
|
||||
|
||||
private:
|
||||
QString m_currentFolder;
|
||||
@ -240,6 +252,7 @@ signals:
|
||||
|
||||
void ObtainingDepthInformationSignals(SubTask info);
|
||||
void LiftingPlatformSignals(SubTask info);
|
||||
void AutoFocusSignals(SubTask info);
|
||||
|
||||
void switchHalogenLampSignal(int state);
|
||||
void switchD65LampSignal(int state);
|
||||
|
||||
@ -210,12 +210,14 @@ void TwoMotorControl::onBack2Origin2()
|
||||
emit back2OriginSignal_TimedDataCollection();
|
||||
}
|
||||
|
||||
void TwoMotorControl::run4_ObtainTargetDepthInfo(DepthCameraWindow* window, int depthType, double depthInfoX, double depthInfoY, int averageNumberOfTimes, double percentageOfEffectiveArea)
|
||||
void TwoMotorControl::run4_ObtainTargetDepthInfo(DepthCameraWindow* window, double depthAlgorithm,int depthType, double depthInfoX, double depthInfoY, int averageNumberOfTimes, double percentageOfEffectiveArea, double depthRangePercentage)
|
||||
{
|
||||
m_depthType = depthType;
|
||||
|
||||
window->m_DepthCameraOperation->setDepthAlgorithm(depthAlgorithm);
|
||||
window->m_DepthCameraOperation->setAverageNumberOfTimes(averageNumberOfTimes);
|
||||
window->m_DepthCameraOperation->setPercentageOfEffectiveArea(percentageOfEffectiveArea);
|
||||
window->m_DepthCameraOperation->setDepthRangePercentage(depthRangePercentage);
|
||||
|
||||
m_ObtainTargetDepthInfoCoordinator = new TwoMotor1PosCoordinator(m_multiAxisController);
|
||||
connect(m_ObtainTargetDepthInfoCoordinator, &TwoMotor1PosCoordinator::ArrivalSignal, window, &DepthCameraWindow::OpenDepthCamera_getDepthValue);
|
||||
@ -232,6 +234,25 @@ void TwoMotorControl::run4_ObtainTargetDepthInfo(DepthCameraWindow* window, int
|
||||
m_ObtainTargetDepthInfoCoordinator->moveToTarget(depthInfoX, depthInfoY, xmotor_move_speed, ymotor_move_speed);
|
||||
}
|
||||
|
||||
void TwoMotorControl::run5_AutoFocus(double autoFocusX, double autoFocusY)
|
||||
{
|
||||
m_focusWindow = new focusWindow(this, m_Imager);
|
||||
m_focusWindow->onConnectMotor();
|
||||
m_focusWindow->show();
|
||||
|
||||
//自动调焦的协调器添加功能:先归零
|
||||
m_autoFocusCoordinator = new TwoMotor1PosCoordinator(m_multiAxisController);
|
||||
connect(m_autoFocusCoordinator, &TwoMotor1PosCoordinator::ArrivalSignal, m_focusWindow, &focusWindow::onAutoFocus);
|
||||
connect(m_focusWindow, &focusWindow::AutoFocusFinishedSignal, m_autoFocusCoordinator, &TwoMotor1PosCoordinator::back2origin);
|
||||
connect(m_focusWindow, &focusWindow::AutoFocusFinishedSignal, this, &TwoMotorControl::sequenceComplete, Qt::UniqueConnection);
|
||||
connect(m_autoFocusCoordinator, &TwoMotor1PosCoordinator::back2OriginSignal, this, &TwoMotorControl::onBack2Origin4);
|
||||
|
||||
|
||||
double xmotor_move_speed = ui.xmotor_move_speed_lineEdit->text().toDouble();
|
||||
double ymotor_move_speed = ui.ymotor_move_speed_lineEdit->text().toDouble();
|
||||
m_autoFocusCoordinator->moveToTarget(autoFocusX, autoFocusY, xmotor_move_speed, ymotor_move_speed);
|
||||
}
|
||||
|
||||
void TwoMotorControl::saveDepthValue(double depthValue)
|
||||
{
|
||||
if (m_depthType == 0)//0表示植被深度,1表示白板/调焦版深度
|
||||
@ -251,6 +272,16 @@ void TwoMotorControl::onBack2Origin3()
|
||||
emit back2OriginSignal_TimedDataCollection();
|
||||
}
|
||||
|
||||
void TwoMotorControl::onBack2Origin4()
|
||||
{
|
||||
m_focusWindow->deleteLater();
|
||||
m_focusWindow = nullptr;
|
||||
|
||||
m_autoFocusCoordinator->deleteLater();
|
||||
m_autoFocusCoordinator = nullptr;
|
||||
emit back2OriginSignal_TimedDataCollection();
|
||||
}
|
||||
|
||||
void TwoMotorControl::run()
|
||||
{
|
||||
if (getState())
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
|
||||
#include "DepthValueLogger.h"
|
||||
|
||||
#include "focusWindow.h"
|
||||
|
||||
#define PI 3.1415926
|
||||
|
||||
class TwoMotorControl : public QDialog, public MotorWindowBase
|
||||
@ -84,10 +86,12 @@ public Q_SLOTS:
|
||||
|
||||
void run2(SingleLensReflexCameraWindow* w);
|
||||
void run3(DepthCameraWindow* window);
|
||||
void run4_ObtainTargetDepthInfo(DepthCameraWindow* window, int depthType, double depthInfoX, double depthInfoY, int averageNumberOfTimes, double percentageOfEffectiveArea);
|
||||
void run4_ObtainTargetDepthInfo(DepthCameraWindow* window, double depthAlgorithm, int depthType, double depthInfoX, double depthInfoY, int averageNumberOfTimes, double percentageOfEffectiveArea, double depthRangePercentage);
|
||||
void run5_AutoFocus(double autoFocusX, double autoFocusY);
|
||||
void onBack2Origin2();
|
||||
void saveDepthValue(double depthValue);
|
||||
void onBack2Origin3();
|
||||
void onBack2Origin4();
|
||||
|
||||
void stop_record();
|
||||
|
||||
@ -117,6 +121,7 @@ private:
|
||||
TwoMotionCaptureCoordinator* m_coordinator = nullptr;
|
||||
TwoMotionCaptureCoordinator* m_coordinator_TimedDataCollection = nullptr;
|
||||
TwoMotor1PosCoordinator* m_ObtainTargetDepthInfoCoordinator = nullptr;
|
||||
TwoMotor1PosCoordinator* m_autoFocusCoordinator = nullptr;
|
||||
|
||||
DarkAndWhiteCaptureCoordinator* m_darkCaptureCoordinator = nullptr;
|
||||
DarkAndWhiteCaptureCoordinator* m_whiteCaptureCoordinator = nullptr;
|
||||
@ -125,4 +130,6 @@ private:
|
||||
IrisMultiMotorController* m_multiAxisController = nullptr;
|
||||
|
||||
int m_depthType;
|
||||
|
||||
focusWindow* m_focusWindow = nullptr;
|
||||
};
|
||||
|
||||
@ -288,7 +288,7 @@ QPushButton:pressed
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>版本:3.1.2</string>
|
||||
<string>版本:3.1.3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -299,7 +299,7 @@ void focusWindow::connectMotor(bool isNotification)//需要修改这个函数
|
||||
if (m_coordinator)
|
||||
{
|
||||
disconnect(&m_MotionCaptureCoordinatorThread, SIGNAL(finished()), m_coordinator, SLOT(deleteLater()));
|
||||
disconnect(this, SIGNAL(startStepMotion(double, int, double, double)), m_coordinator, SLOT(startStepMotion(double, int, double, double)));
|
||||
disconnect(this, SIGNAL(startStepMotionSignal(double, int, double, double)), m_coordinator, SLOT(startStepMotion(double, int, double, double)));
|
||||
disconnect(m_coordinator, SIGNAL(progressChanged(int)), this, SLOT(onAutoFocusProgress(int)));
|
||||
disconnect(m_coordinator, SIGNAL(sequenceComplete()), this, SLOT(onAutoFocusFinished()));
|
||||
|
||||
@ -343,7 +343,7 @@ void focusWindow::connectMotor(bool isNotification)//需要修改这个函数
|
||||
m_coordinator = new MotionCaptureCoordinator(m_multiAxisController, m_Imager);
|
||||
m_coordinator->moveToThread(&m_MotionCaptureCoordinatorThread);
|
||||
connect(&m_MotionCaptureCoordinatorThread, SIGNAL(finished()), m_coordinator, SLOT(deleteLater()));
|
||||
connect(this, SIGNAL(startStepMotion(double, int, double, double)), m_coordinator, SLOT(startStepMotion(double, int, double, double)));
|
||||
connect(this, SIGNAL(startStepMotionSignal(double, int, double, double)), m_coordinator, SLOT(startStepMotion(double, int, double, double)));
|
||||
connect(m_coordinator, SIGNAL(progressChanged(int)), this, SLOT(onAutoFocusProgress(int)));
|
||||
connect(m_coordinator, SIGNAL(sequenceComplete()), this, SLOT(onAutoFocusFinished()));
|
||||
m_MotionCaptureCoordinatorThread.start();
|
||||
@ -475,7 +475,7 @@ void focusWindow::onAutoFocus()
|
||||
//获取马达最大位置
|
||||
std::vector<double> maxRangeLocations = m_multiAxisController->getMaxPos();
|
||||
double maxPos = maxRangeLocations[0];
|
||||
emit startStepMotion(m_dSpeed, m_iStepSize, 0, maxPos);
|
||||
emit startStepMotionSignal(m_dSpeed, m_iStepSize, 0, maxPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -649,26 +649,48 @@ void focusWindow::moveAfterAutoFocus(int motorID, double location)
|
||||
|
||||
std::cout << "\n已经到达位置:" << location << std::endl;
|
||||
|
||||
double tmp = abs(location - m_goodPos) / m_goodPos * 100;
|
||||
if (tmp < 5 || m_goodPos == 0)
|
||||
double errorRate = getErrorRate(m_goodPos, location);
|
||||
if (errorRate < 5|| m_moveRetryCount > MAX_MOVE_RETRY)
|
||||
{
|
||||
m_isMoveAfterAutoFocus = false;
|
||||
m_moveRetryCount = 0;
|
||||
if (!m_isAutoFocusSuccess)
|
||||
{
|
||||
showMessageBox(QString::fromLocal8Bit("纹理较弱,自动调焦效果不佳!请使用调焦纸进行自动调焦!"));
|
||||
qDebug() << "纹理较弱,自动调焦效果不佳!请使用调焦纸进行自动调焦!";
|
||||
//showMessageBox(QString::fromLocal8Bit("纹理较弱,自动调焦效果不佳!请使用调焦纸进行自动调焦!"));
|
||||
}
|
||||
else
|
||||
{
|
||||
showMessageBox(QString::fromLocal8Bit("自动调焦成功!"));
|
||||
qDebug() << "自动调焦成功!";
|
||||
//showMessageBox(QString::fromLocal8Bit("自动调焦成功!"));
|
||||
}
|
||||
emit AutoFocusFinishedSignal(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_moveRetryCount++;
|
||||
qDebug() << "自动调焦后目标马达位置,重试次数:" << m_moveRetryCount;
|
||||
//移动马达到最佳位置
|
||||
emit move2LocSignal(0, (double)m_goodPos, m_dSpeed, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
double focusWindow::getErrorRate(double targetLoc, double actualLoc)
|
||||
{
|
||||
double targetLocTmp;
|
||||
if (targetLoc == 0)
|
||||
{
|
||||
targetLocTmp = 0.001;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetLocTmp = targetLoc;
|
||||
}
|
||||
double errorRate = abs(targetLoc - actualLoc) / targetLocTmp * 100;
|
||||
|
||||
return errorRate;
|
||||
}
|
||||
|
||||
void focusWindow::getGaussianInitParam(const std::vector<double>& pos, const std::vector<double>& index, double& a_init, double& mu_init, double& sigma_init, double& c_init)
|
||||
{
|
||||
auto minmax_element = std::minmax_element(index.begin(), index.end());
|
||||
@ -819,11 +841,15 @@ MotionCaptureCoordinator::MotionCaptureCoordinator(
|
||||
, m_currentPos(0)
|
||||
, m_endPos(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, &MotionCaptureCoordinator::zeroStart,
|
||||
m_motorCtrl, &IrisMultiMotorController::zeroStart);
|
||||
|
||||
connect(m_motorCtrl, &IrisMultiMotorController::motorStopSignal,
|
||||
this, &MotionCaptureCoordinator::handlePositionReached);
|
||||
//connect(m_motorCtrl, &IrisMultiMotorController::moveFailed,
|
||||
@ -860,15 +886,37 @@ void MotionCaptureCoordinator::startStepMotion(double speed, int stepInterval, d
|
||||
m_speed = speed;
|
||||
m_iStepInterval = stepInterval;
|
||||
m_iStepIntervalRealTime = 1;
|
||||
m_currentPos = startPos;
|
||||
m_startPos = startPos;
|
||||
m_endPos = endPos;
|
||||
m_posInternal = (endPos - startPos) / stepInterval;
|
||||
|
||||
m_isRunning = true;
|
||||
m_isZeroing = true;
|
||||
|
||||
// 先执行归零操作
|
||||
emit zeroStart(0);
|
||||
qDebug() << "MotionCaptureCoordinator::startStepMotion: Zeroing started.";
|
||||
}
|
||||
|
||||
void MotionCaptureCoordinator::startMotionSequence()
|
||||
{
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
|
||||
m_currentPos = m_startPos;
|
||||
m_isZeroing = false;
|
||||
qDebug() << "MotionCaptureCoordinator::startMotionSequence: Zeroing complete. Starting motion sequence.";
|
||||
|
||||
processNextPosition();
|
||||
}
|
||||
|
||||
void MotionCaptureCoordinator::handleZeroComplete(int motorID, double pos)
|
||||
{
|
||||
if (!m_isRunning || !m_isZeroing) return;
|
||||
|
||||
// 归零完成,开始分步运动
|
||||
startMotionSequence();
|
||||
}
|
||||
|
||||
void MotionCaptureCoordinator::stopStepMotion()
|
||||
{
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
@ -911,6 +959,13 @@ void MotionCaptureCoordinator::handlePositionReached(int motorID, double pos)
|
||||
{
|
||||
if (!m_isRunning) return;
|
||||
|
||||
// 如果正在等待归零完成,调用归零完成处理
|
||||
if (m_isZeroing)
|
||||
{
|
||||
handleZeroComplete(motorID, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&m_dataMutex);
|
||||
|
||||
//验证马达运动位置是否到达指定位置
|
||||
|
||||
@ -70,14 +70,17 @@ signals:
|
||||
void errorOccurred(const QString& error);
|
||||
void moveTo(int, double, double, int);
|
||||
void getFocusIndexSobel();
|
||||
void zeroStart(int motorID);
|
||||
|
||||
private slots:
|
||||
void handlePositionReached(int motorID, double pos);
|
||||
void handleCaptureComplete(double index);
|
||||
void handleError(const QString& error);
|
||||
void handleZeroComplete(int motorID, double pos);
|
||||
|
||||
private:
|
||||
void processNextPosition();
|
||||
void startMotionSequence();
|
||||
|
||||
IrisMultiMotorController* m_motorCtrl;
|
||||
ImagerOperationBase* m_cameraCtrl;
|
||||
@ -86,6 +89,7 @@ private:
|
||||
|
||||
double m_posInternal;
|
||||
double m_currentPos;
|
||||
double m_startPos;
|
||||
double m_endPos;
|
||||
bool m_isRunning;
|
||||
double m_speed;
|
||||
@ -93,6 +97,7 @@ private:
|
||||
int m_iStepInterval;
|
||||
int m_iStepIntervalRealTime;
|
||||
int m_counter;
|
||||
bool m_isZeroing;
|
||||
};
|
||||
|
||||
class focusWindow:public QDialog
|
||||
@ -134,6 +139,8 @@ private:
|
||||
double m_goodPos;
|
||||
bool m_isAutoFocusSuccess;
|
||||
bool m_isMoveAfterAutoFocus = false;
|
||||
int m_moveRetryCount = 0;
|
||||
static constexpr int MAX_MOVE_RETRY = 3;
|
||||
void getGaussianInitParam(const std::vector<double>& pos, const std::vector<double>& index, double& a_init, double& mu_init, double& sigma_init, double& c_init);
|
||||
void gaussian_fit(const std::vector<double>& x_data, const std::vector<double>& y_data, double& a, double& mu, double& sigma, double& c);
|
||||
|
||||
@ -142,6 +149,8 @@ private:
|
||||
void connectMotor(bool isNotification);
|
||||
|
||||
void showMessageBox(QString msg, QString title = QString::fromLocal8Bit("提示"));
|
||||
|
||||
double getErrorRate(double targetLoc, double actualLoc);
|
||||
|
||||
public Q_SLOTS:
|
||||
void onConnectMotor();
|
||||
@ -176,8 +185,10 @@ signals:
|
||||
void zeroStartSignal(int);
|
||||
void testConnectivitySignal(int, int);
|
||||
|
||||
void startStepMotion(double speed, int stepInterval = 100, double startPos = 0, double endPos = -1);
|
||||
void startStepMotionSignal(double speed, int stepInterval = 100, double startPos = 0, double endPos = -1);
|
||||
void closeSignal();
|
||||
|
||||
void AutoFocusFinishedSignal(int status);
|
||||
};
|
||||
|
||||
class WorkerThread2 : public QThread
|
||||
|
||||
Reference in New Issue
Block a user