GrabBag/App/WheelMeasure/WheelMeasureApp/dialogcameralevel.cpp

1195 lines
44 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "dialogcameralevel.h"
#include "ui_dialogcameralevel.h"
#include "WheelMeasurePresenter.h"
#include "PathManager.h"
#include "VrLog.h"
#include "wheelArchHeigthMeasure_Export.h"
#include <QThread>
#include <QApplication>
#include <QLabel>
#include <QListView>
#include <QDoubleValidator>
#include <cmath>
#include <mutex>
#include <vector>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
DialogCameraLevel::DialogCameraLevel(QWidget *parent)
: QDialog(parent)
, ui(new Ui::DialogCameraLevel)
, m_pConfig(nullptr)
, m_pConfigResult(nullptr)
, m_currentCameraIndex(-1)
{
ui->setupUi(this);
// 初始化结果显示区域
ui->label_level_result->setText("请选择相机,然后点击调平计算按钮\n开始相机调平操作");
ui->label_level_result->setAlignment(Qt::AlignCenter);
// 修正模块输入精度限制:标定块标称值 / 凹槽间距 / Y范围均为三位小数
auto* d3Validator = new QDoubleValidator(this);
d3Validator->setDecimals(3);
d3Validator->setNotation(QDoubleValidator::StandardNotation);
ui->edit_calib_nominal_length->setValidator(d3Validator);
ui->edit_calib_groove_spacing->setValidator(d3Validator);
ui->edit_calib_y_min->setValidator(d3Validator);
ui->edit_calib_y_max->setValidator(d3Validator);
// 修正系数八位小数
auto* d8Validator = new QDoubleValidator(this);
d8Validator->setDecimals(8);
d8Validator->setNotation(QDoubleValidator::StandardNotation);
ui->edit_correction_factor->setValidator(d8Validator);
}
DialogCameraLevel::~DialogCameraLevel()
{
// 清理扫描数据缓存
clearScanDataCache();
// 确保恢复Presenter的状态回调
restorePresenterStatusCallback();
delete ui;
}
void DialogCameraLevel::setCameraList(const std::vector<std::pair<std::string, IVrEyeDevice*>>& cameraList,
WheelMeasurePresenter* presenter)
{
m_cameraList = cameraList;
m_presenter = presenter;
LOG_INFO("setCameraList called with %zu cameras\n", cameraList.size());
// 详细记录每个相机的信息
for (size_t i = 0; i < cameraList.size(); ++i) {
const auto& camera = cameraList[i];
LOG_INFO(" Camera %zu: name='%s', device=%s\n",
i + 1,
camera.first.c_str(),
(camera.second != nullptr ? "connected" : "not connected"));
}
// 初始化/重新初始化相机选择框
initializeCameraCombo();
}
void DialogCameraLevel::setConfig(IVrWheelMeasureConfig* config, WheelMeasureConfigResult* configResult)
{
m_pConfig = config;
m_pConfigResult = configResult;
// 重新打开页面时,从文件重新加载配置以获取最新数据
if (m_pConfig && m_pConfigResult) {
QString configPath = PathManager::GetInstance().GetConfigFilePath();
*m_pConfigResult = m_pConfig->LoadConfig(configPath.toStdString());
}
// 如果相机已经选择,重新加载当前相机的标定状态
if (m_currentCameraIndex >= 0 && m_currentCameraIndex < static_cast<int>(m_cameraList.size())) {
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
loadCameraRoiRange(m_currentCameraIndex);
}
}
void DialogCameraLevel::initializeCameraCombo()
{
LOG_INFO("initializeCameraCombo called, camera list size: %zu\n", m_cameraList.size());
ui->combo_camera->clear();
if (m_cameraList.empty()) {
ui->combo_camera->setEnabled(false);
if (!m_presenter) {
ui->label_level_result->setText("Presenter未初始化\n无法获取相机列表");
LOG_ERROR("Presenter is null in initializeCameraCombo\n");
} else {
ui->label_level_result->setText("相机列表为空\n\n可能原因:\n1. 配置文件中未配置相机\n2. 系统正在初始化中\n3. 所有相机连接失败");
LOG_WARNING("Camera list is empty in initializeCameraCombo\n");
}
ui->label_level_result->setAlignment(Qt::AlignCenter);
} else {
LOG_INFO("Adding %zu cameras to combo box\n", m_cameraList.size());
// 添加所有相机到下拉列表
for (size_t i = 0; i < m_cameraList.size(); ++i) {
const auto& camera = m_cameraList[i];
QString cameraName = QString::fromStdString(camera.first);
// 如果相机没有连接,在名称后添加标记
if (camera.second == nullptr) {
cameraName += " [未连接]";
}
ui->combo_camera->addItem(cameraName);
LOG_INFO(" Added camera %zu: %s (device=%s)\n",
i + 1,
camera.first.c_str(),
(camera.second != nullptr ? "OK" : "NULL"));
}
ui->combo_camera->setEnabled(true);
// 获取默认相机索引
int defaultCameraIndex = 0;
if (m_presenter) {
int presenterDefaultIndex = m_presenter->GetDefaultCameraIndex();
LOG_INFO("Presenter default camera index (1-based): %d\n", presenterDefaultIndex);
if (presenterDefaultIndex > 0 && presenterDefaultIndex <= static_cast<int>(m_cameraList.size())) {
defaultCameraIndex = presenterDefaultIndex - 1;
}
}
// 设置默认选中的相机
if (defaultCameraIndex >= 0 && defaultCameraIndex < static_cast<int>(m_cameraList.size())) {
ui->combo_camera->setCurrentIndex(defaultCameraIndex);
m_currentCameraIndex = defaultCameraIndex;
}
// 检查并显示当前选中相机的标定状态
if (m_currentCameraIndex >= 0) {
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
}
}
}
void DialogCameraLevel::on_btn_level_calc_clicked()
{
ui->label_level_result->setAlignment(Qt::AlignLeft);
// 检查是否有可用的相机
if (m_cameraList.empty()) {
QMessageBox::warning(this, "错误", "无可用相机设备!");
return;
}
// 获取选中的相机
int selectedIndex = ui->combo_camera->currentIndex();
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(m_cameraList.size())) {
QMessageBox::warning(this, "错误", "请选择有效的相机!");
return;
}
// 清空之前的结果显示
ui->label_level_result->setText("调平计算中,请稍候...");
// 显示进度提示
ui->btn_level_calc->setEnabled(false);
QApplication::processEvents();
try {
// 执行相机调平
if (performCameraLeveling()) {
// 调平成功
} else {
ui->label_level_result->setText("调平失败!\n\n请检查:\n1. 相机连接是否正常\n2. 地面扫描数据是否充足\n3. 扫描区域是否有足够的地面");
}
} catch (const std::exception& e) {
LOG_ERROR("Camera leveling failed with exception: %s\n", e.what());
QMessageBox::critical(this, "错误", QString("调平过程发生异常:%1").arg(e.what()));
}
// 恢复按钮状态
ui->btn_level_calc->setEnabled(true);
}
void DialogCameraLevel::on_btn_close_clicked()
{
reject();
}
bool DialogCameraLevel::performCameraLeveling()
{
try {
// 获取选中的相机索引
int selectedIndex = ui->combo_camera->currentIndex();
// 先检查索引有效性,再设置回调
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(m_cameraList.size())) {
LOG_ERROR("Invalid camera index: %d\n", selectedIndex);
return false;
}
LOG_INFO("Performing camera leveling with camera %d (index %d)\n", selectedIndex + 1, selectedIndex);
// 1. 设置调平状态回调(在索引检查之后)
setLevelingStatusCallback();
// 2. 清空之前的扫描数据
clearScanDataCache();
// 3. 启动相机扫描地面数据
if (!startCameraScan(selectedIndex)) {
LOG_ERROR("Failed to start camera scan for leveling\n");
restorePresenterStatusCallback(); // 恢复回调
return false;
}
// 4. 等待扫描完成
LOG_INFO("Collecting ground scan data, waiting for swing finish signal...\n");
int waitTime = 0;
const int maxWaitTime = 10000; // 最大等待10秒
const int checkInterval = 100;
while (!m_swingFinished && waitTime < maxWaitTime) {
QThread::msleep(checkInterval);
QApplication::processEvents();
waitTime += checkInterval;
}
// 5. 停止扫描
stopCameraScan(selectedIndex);
if (m_swingFinished) {
LOG_INFO("Camera swing finished signal received, scan completed\n");
} else if (waitTime >= maxWaitTime) {
LOG_WARNING("Timeout waiting for camera swing finish signal\n");
}
// 6. 调用调平算法计算
double planeCalib[9];
double planeHeight;
double invRMatrix[9];
if (!calculatePlaneCalibration(planeCalib, planeHeight, invRMatrix)) {
LOG_ERROR("Failed to calculate plane calibration\n");
restorePresenterStatusCallback(); // 恢复回调
return false;
}
LOG_INFO("Camera leveling calculation completed\n");
// 7. 更新界面显示
updateLevelingResults(planeCalib, planeHeight, invRMatrix);
// 8. 保存结果到配置
int cameraIndex = m_currentCameraIndex + 1; // 转换为1-based索引
QString cameraName;
if (m_currentCameraIndex >= 0 && m_currentCameraIndex < static_cast<int>(m_cameraList.size())) {
cameraName = QString::fromStdString(m_cameraList[m_currentCameraIndex].first);
} else {
cameraName = QString("Camera_%1").arg(cameraIndex);
}
if (!saveLevelingResults(planeCalib, planeHeight, invRMatrix, cameraIndex, cameraName)) {
LOG_ERROR("Failed to save leveling results\n");
restorePresenterStatusCallback(); // 恢复回调
return false;
}
clearScanDataCache();
// 9. 调平完成后恢复回调
restorePresenterStatusCallback();
LOG_INFO("Camera leveling completed successfully\n");
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in performCameraLeveling: %s\n", e.what());
restorePresenterStatusCallback(); // 异常时也恢复回调
return false;
}
}
void DialogCameraLevel::updateLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9])
{
// 更新地面高度编辑框
ui->edit_plane_height->setText(QString::number(planeHeight, 'f', 2));
// 构建显示文本
QString resultText;
resultText += QString("地面高度: %1 mm\n").arg(QString::number(planeHeight, 'f', 2));
// 调平矩阵
resultText += QString("调平矩阵:\n");
for (int i = 0; i < 3; i++) {
resultText += QString("[%1, %2, %3]\n")
.arg(QString::number(planeCalib[i*3], 'f', 4))
.arg(QString::number(planeCalib[i*3+1], 'f', 4))
.arg(QString::number(planeCalib[i*3+2], 'f', 4));
}
resultText += QString("逆旋转矩阵:\n");
for (int i = 0; i < 3; i++) {
resultText += QString("[%1, %2, %3]\n")
.arg(QString::number(invRMatrix[i*3], 'f', 4))
.arg(QString::number(invRMatrix[i*3+1], 'f', 4))
.arg(QString::number(invRMatrix[i*3+2], 'f', 4));
}
ui->label_level_result->setText(resultText);
ui->label_level_result->setAlignment(Qt::AlignLeft | Qt::AlignTop);
}
bool DialogCameraLevel::startCameraScan(int cameraIndex)
{
if (cameraIndex < 0 || cameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_ERROR("Invalid camera index for scan: %d\n", cameraIndex);
return false;
}
IVrEyeDevice* camera = m_cameraList[cameraIndex].second;
if (!camera) {
LOG_ERROR("Camera device is null at index: %d\n", cameraIndex);
return false;
}
// 启动相机检测
int result = camera->StartDetect(&DialogCameraLevel::StaticDetectionCallback, keResultDataType_Position, this);
if (result != 0) {
LOG_ERROR("Failed to start camera detection: %d\n", result);
return false;
}
LOG_INFO("Camera scan started successfully for camera index: %d\n", cameraIndex);
return true;
}
bool DialogCameraLevel::stopCameraScan(int cameraIndex)
{
if (cameraIndex < 0 || cameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_ERROR("Invalid camera index for stop scan: %d\n", cameraIndex);
return false;
}
IVrEyeDevice* camera = m_cameraList[cameraIndex].second;
if (!camera) {
LOG_ERROR("Camera device is null at index: %d\n", cameraIndex);
return false;
}
int result = camera->StopDetect();
if (result != 0) {
LOG_WARNING("Failed to stop camera detection, error: %d\n", result);
return false;
}
LOG_INFO("Camera scan stopped successfully for camera index: %d\n", cameraIndex);
return true;
}
void DialogCameraLevel::StaticDetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pUserData)
{
DialogCameraLevel* pThis = reinterpret_cast<DialogCameraLevel*>(pUserData);
if (pThis && pLaserLinePoint) {
pThis->DetectionCallback(eDataType, pLaserLinePoint);
}
}
void DialogCameraLevel::StaticStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam)
{
DialogCameraLevel* pThis = reinterpret_cast<DialogCameraLevel*>(pInfoParam);
if (pThis) {
pThis->StatusCallback(eStatus, pExtData, nDataLength, pInfoParam);
}
}
void DialogCameraLevel::StatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam)
{
LOG_DEBUG("[Leveling Status Callback] received: status=%d\n", (int)eStatus);
switch (eStatus) {
case EVzDeviceWorkStatus::keDeviceWorkStatus_Device_Swing_Finish:
{
LOG_INFO("[Leveling Status Callback] Camera swing finished, scan completed\n");
m_swingFinished = true;
break;
}
default:
LOG_DEBUG("[Leveling Status Callback] Other status: %d\n", (int)eStatus);
break;
}
}
void DialogCameraLevel::DetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint)
{
if (!pLaserLinePoint) {
LOG_WARNING("[Leveling Callback] pLaserLinePoint is null\n");
return;
}
if (pLaserLinePoint->nPointCount <= 0) {
LOG_WARNING("[Leveling Callback] Point count is zero or negative: %d\n", pLaserLinePoint->nPointCount);
return;
}
if (!pLaserLinePoint->p3DPoint) {
LOG_WARNING("[Leveling Callback] p3DPoint is null\n");
return;
}
// 将数据添加到缓存
std::vector<SVzNL3DPosition> lineData;
lineData.reserve(pLaserLinePoint->nPointCount);
// p3DPoint 是 void*,需要转换为 SVzNL3DPosition*
SVzNL3DPosition* p3DPoints = reinterpret_cast<SVzNL3DPosition*>(pLaserLinePoint->p3DPoint);
for (int i = 0; i < pLaserLinePoint->nPointCount; ++i) {
lineData.push_back(p3DPoints[i]);
}
std::lock_guard<std::mutex> lock(m_scanDataMutex);
m_scanDataCache.push_back(std::move(lineData));
}
bool DialogCameraLevel::calculatePlaneCalibration(double planeCalib[9], double& planeHeight, double invRMatrix[9])
{
std::lock_guard<std::mutex> lock(m_scanDataMutex);
if (m_scanDataCache.empty()) {
LOG_ERROR("No scan data available for plane calibration\n");
return false;
}
LOG_INFO("Calculating plane calibration from %zu scan lines\n", m_scanDataCache.size());
try {
// 调用 wheelArchHeigthMeasure SDK 的调平算法
SSG_planeCalibPara calibResult = wd_horizonCamera_getGroundCalibPara(m_scanDataCache);
// 复制调平矩阵
for (int i = 0; i < 9; i++) {
planeCalib[i] = calibResult.planeCalib[i];
invRMatrix[i] = calibResult.invRMatrix[i];
}
planeHeight = calibResult.planeHeight;
LOG_INFO("Plane calibration calculated successfully\n");
LOG_INFO(" planeHeight: %.3f\n", planeHeight);
LOG_INFO(" planeCalib: [%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f]\n",
planeCalib[0], planeCalib[1], planeCalib[2],
planeCalib[3], planeCalib[4], planeCalib[5],
planeCalib[6], planeCalib[7], planeCalib[8]);
LOG_INFO(" invRMatrix: [%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f]\n",
invRMatrix[0], invRMatrix[1], invRMatrix[2],
invRMatrix[3], invRMatrix[4], invRMatrix[5],
invRMatrix[6], invRMatrix[7], invRMatrix[8]);
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in calculatePlaneCalibration: %s\n", e.what());
return false;
}
}
void DialogCameraLevel::clearScanDataCache()
{
std::lock_guard<std::mutex> lock(m_scanDataMutex);
LOG_DEBUG("Clearing scan data cache, current size: %zu\n", m_scanDataCache.size());
m_scanDataCache.clear();
LOG_DEBUG("Scan data cache cleared successfully\n");
}
bool DialogCameraLevel::saveLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9],
int cameraIndex, const QString& cameraName)
{
try {
if (!m_pConfig || !m_pConfigResult) {
LOG_ERROR("Config is null, cannot save leveling results\n");
return false;
}
if (cameraIndex <= 0) {
LOG_ERROR("Invalid camera index: %d\n", cameraIndex);
return false;
}
if (cameraName.isEmpty()) {
LOG_ERROR("Camera name is empty\n");
return false;
}
// 获取UI中的误差补偿值
double errorCompensation = ui->edit_error_compensation->text().toDouble();
// 获取UI中的修正系数非法或为0时回退为1.0
bool cfOk = false;
double correctionFactor = ui->edit_correction_factor->text().toDouble(&cfOk);
if (!cfOk || correctionFactor == 0.0) {
correctionFactor = 1.0;
}
// 查找已有的调平参数,仅更新调平相关字段,保留 ROI 等其他字段
bool found = false;
for (auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == cameraIndex) {
param.cameraName = cameraName.toStdString();
param.planeHeight = planeHeight;
param.isCalibrated = true;
param.errorCompensation = errorCompensation;
param.correctionFactor = correctionFactor;
for (int i = 0; i < 9; i++) {
param.planeCalib[i] = planeCalib[i];
param.invRMatrix[i] = invRMatrix[i];
}
found = true;
break;
}
}
if (!found) {
WheelCameraPlaneCalibParam cameraParam;
cameraParam.cameraIndex = cameraIndex;
cameraParam.cameraName = cameraName.toStdString();
cameraParam.planeHeight = planeHeight;
cameraParam.isCalibrated = true;
cameraParam.errorCompensation = errorCompensation;
cameraParam.correctionFactor = correctionFactor;
for (int i = 0; i < 9; i++) {
cameraParam.planeCalib[i] = planeCalib[i];
cameraParam.invRMatrix[i] = invRMatrix[i];
}
m_pConfigResult->planeCalibParams.push_back(cameraParam);
}
// 保存配置
QString configPath = PathManager::GetInstance().GetConfigFilePath();
bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult);
if (!saveResult) {
LOG_ERROR("Failed to save config with leveling results\n");
return false;
}
LOG_INFO("Leveling results saved successfully for camera %d (%s)\n",
cameraIndex, cameraName.toUtf8().constData());
LOG_INFO("Plane height: %.3f, Error compensation: %.2f\n", planeHeight, errorCompensation);
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in saveLevelingResults: %s\n", e.what());
return false;
}
}
bool DialogCameraLevel::loadCameraCalibrationData(int cameraIndex, const QString& cameraName,
double planeCalib[9], double& planeHeight, double invRMatrix[9])
{
try {
if (!m_pConfigResult) {
LOG_ERROR("Config result is null, cannot load calibration data\n");
return false;
}
// 查找对应相机的调平参数
for (const auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == cameraIndex && param.isCalibrated) {
for (int i = 0; i < 9; i++) {
planeCalib[i] = param.planeCalib[i];
invRMatrix[i] = param.invRMatrix[i];
}
planeHeight = param.planeHeight;
// 加载该相机的地面高度和误差补偿值到UI
ui->edit_plane_height->setText(QString::number(param.planeHeight, 'f', 2));
ui->edit_error_compensation->setText(QString::number(param.errorCompensation, 'f', 1));
LOG_INFO("Calibration data loaded successfully for camera %d (%s)\n",
cameraIndex, cameraName.toUtf8().constData());
return true;
}
}
// 没有找到标定数据时,设置默认误差补偿值
ui->edit_error_compensation->setText(QString::number(-5.0, 'f', 1));
LOG_INFO("No calibration data found for camera %d (%s)\n",
cameraIndex, cameraName.toUtf8().constData());
return false;
} catch (const std::exception& e) {
LOG_ERROR("Exception in loadCameraCalibrationData: %s\n", e.what());
return false;
}
}
void DialogCameraLevel::checkAndDisplayCalibrationStatus(int cameraIndex)
{
if (cameraIndex < 0 || cameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_WARNING("Invalid camera index for status check: %d\n", cameraIndex);
ui->label_level_result->setText("无效的相机索引");
ui->label_level_result->setAlignment(Qt::AlignCenter);
return;
}
QString cameraName = QString::fromStdString(m_cameraList[cameraIndex].first);
int configCameraIndex = cameraIndex + 1;
double planeCalib[9];
double planeHeight;
double invRMatrix[9];
if (loadCameraCalibrationData(configCameraIndex, cameraName, planeCalib, planeHeight, invRMatrix)) {
// 有标定数据,显示
LOG_INFO("Displaying existing calibration data for camera %s\n", cameraName.toUtf8().constData());
updateLevelingResults(planeCalib, planeHeight, invRMatrix);
} else {
// 没有标定数据,显示未标定
LOG_INFO("No calibration data found for camera %s, showing uncalibrated\n", cameraName.toUtf8().constData());
ui->edit_plane_height->setText("-1.00");
ui->label_level_result->setText(QString("相机: %1\n\n未标定").arg(cameraName));
ui->label_level_result->setAlignment(Qt::AlignCenter);
}
}
void DialogCameraLevel::on_combo_camera_currentIndexChanged(int index)
{
m_currentCameraIndex = index;
if (index >= 0 && index < static_cast<int>(m_cameraList.size())) {
LOG_INFO("Camera selection changed to index: %d (%s)\n", index,
QString::fromStdString(m_cameraList[index].first).toUtf8().constData());
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
loadCameraRoiRange(m_currentCameraIndex);
} else {
LOG_WARNING("Invalid camera index selected: %d\n", index);
ui->label_level_result->setText("无效的相机选择");
ui->label_level_result->setAlignment(Qt::AlignCenter);
}
}
void DialogCameraLevel::setLevelingStatusCallback()
{
if (!m_presenter) {
LOG_ERROR("Presenter is null, cannot set leveling status callback\n");
return;
}
m_presenter->SetCameraStatusCallback(&DialogCameraLevel::StaticStatusCallback, this);
m_swingFinished = false;
m_callbackRestored = false;
LOG_INFO("Leveling status callback set for all cameras\n");
}
void DialogCameraLevel::restorePresenterStatusCallback()
{
if (m_callbackRestored.exchange(true)) {
LOG_DEBUG("Presenter status callback already restored, skipping\n");
return;
}
if (!m_presenter) {
LOG_ERROR("Presenter is null, cannot restore status callback\n");
return;
}
m_presenter->SetCameraStatusCallback(&WheelMeasurePresenter::_StaticCameraNotify, m_presenter);
LOG_INFO("Presenter status callback restored for all cameras\n");
}
void DialogCameraLevel::on_btn_save_compensation_clicked()
{
// 保存误差补偿值到当前相机的调平参数
if (!m_pConfig || !m_pConfigResult) {
LOG_ERROR("Config is null, cannot save error compensation\n");
return;
}
if (m_currentCameraIndex < 0 || m_currentCameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_WARNING("Invalid camera index: %d\n", m_currentCameraIndex);
return;
}
// 获取UI中的误差补偿值
double errorCompensation = ui->edit_error_compensation->text().toDouble();
int cameraIndex = m_currentCameraIndex + 1; // 转换为1-based索引
QString cameraName = QString::fromStdString(m_cameraList[m_currentCameraIndex].first);
LOG_INFO("Saving error compensation for camera %d (%s): %.2f\n",
cameraIndex, cameraName.toUtf8().constData(), errorCompensation);
// 查找或创建相机调平参数
bool found = false;
for (auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == cameraIndex) {
param.errorCompensation = errorCompensation;
found = true;
break;
}
}
// 如果没有找到现有记录,创建一个新的(仅包含误差补偿,其他值为默认)
if (!found) {
WheelCameraPlaneCalibParam newParam;
newParam.cameraIndex = cameraIndex;
newParam.cameraName = cameraName.toStdString();
newParam.errorCompensation = errorCompensation;
newParam.isCalibrated = false; // 尚未标定
m_pConfigResult->planeCalibParams.push_back(newParam);
}
// 保存配置到文件
QString configPath = PathManager::GetInstance().GetConfigFilePath();
bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult);
if (saveResult) {
LOG_INFO("Error compensation saved successfully for camera %d: %.2f\n", cameraIndex, errorCompensation);
} else {
LOG_ERROR("Failed to save error compensation\n");
}
}
void DialogCameraLevel::on_btn_save_plane_height_clicked()
{
if (!m_pConfig || !m_pConfigResult) {
LOG_ERROR("Config is null, cannot save plane height\n");
return;
}
if (m_currentCameraIndex < 0 || m_currentCameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_WARNING("Invalid camera index: %d\n", m_currentCameraIndex);
return;
}
double planeHeight = ui->edit_plane_height->text().toDouble();
int cameraIndex = m_currentCameraIndex + 1;
// 查找或创建相机调平参数
bool found = false;
for (auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == cameraIndex) {
param.planeHeight = planeHeight;
param.isCalibrated = true;
found = true;
break;
}
}
if (!found) {
WheelCameraPlaneCalibParam newParam;
newParam.cameraIndex = cameraIndex;
newParam.planeHeight = planeHeight;
newParam.isCalibrated = true;
m_pConfigResult->planeCalibParams.push_back(newParam);
}
// 保存配置到文件
QString configPath = PathManager::GetInstance().GetConfigFilePath();
bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult);
if (saveResult) {
LOG_INFO("Plane height saved successfully for camera %d: %.2f\n", cameraIndex, planeHeight);
// 刷新界面显示
checkAndDisplayCalibrationStatus(m_currentCameraIndex);
} else {
LOG_ERROR("Failed to save plane height\n");
}
}
void DialogCameraLevel::loadCameraRoiRange(int cameraIndex)
{
if (!m_pConfig || !m_pConfigResult) {
LOG_WARNING("Config is null, cannot load ROI range\n");
return;
}
if (cameraIndex < 0 || cameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_WARNING("Invalid camera index: %d\n", cameraIndex);
return;
}
int configCameraIndex = cameraIndex + 1; // 转换为1-based索引
// 查找相机的调平参数
for (const auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == configCameraIndex) {
// 加载ROI范围到UI
ui->edit_roi_x_min->setText(QString::number(param.wheelRoi3d_xMin, 'f', 1));
ui->edit_roi_x_max->setText(QString::number(param.wheelRoi3d_xMax, 'f', 1));
ui->edit_roi_y_min->setText(QString::number(param.wheelRoi3d_yMin, 'f', 1));
ui->edit_roi_y_max->setText(QString::number(param.wheelRoi3d_yMax, 'f', 1));
ui->edit_roi_z_min->setText(QString::number(param.wheelRoi3d_zMin, 'f', 1));
ui->edit_roi_z_max->setText(QString::number(param.wheelRoi3d_zMax, 'f', 1));
// 加载修正系数
ui->edit_correction_factor->setText(QString::number(param.correctionFactor, 'f', 8));
LOG_INFO("Loaded ROI range for camera %d: X[%.1f, %.1f], Y[%.1f, %.1f], Z[%.1f, %.1f], correctionFactor=%.8f\n",
configCameraIndex,
param.wheelRoi3d_xMin, param.wheelRoi3d_xMax,
param.wheelRoi3d_yMin, param.wheelRoi3d_yMax,
param.wheelRoi3d_zMin, param.wheelRoi3d_zMax,
param.correctionFactor);
return;
}
}
// 如果没有找到,使用默认值
ui->edit_roi_x_min->setText("-1000.0");
ui->edit_roi_x_max->setText("1000.0");
ui->edit_roi_y_min->setText("-1000.0");
ui->edit_roi_y_max->setText("1000.0");
ui->edit_roi_z_min->setText("-1000.0");
ui->edit_roi_z_max->setText("1000.0");
ui->edit_correction_factor->setText("1.00000000");
LOG_INFO("No ROI range found for camera %d, using default values\n", configCameraIndex);
}
void DialogCameraLevel::saveCameraRoiRange()
{
if (!m_pConfig || !m_pConfigResult) {
LOG_ERROR("Config is null, cannot save ROI range\n");
QMessageBox::warning(this, "错误", "配置对象为空无法保存ROI范围");
return;
}
if (m_currentCameraIndex < 0 || m_currentCameraIndex >= static_cast<int>(m_cameraList.size())) {
LOG_WARNING("Invalid camera index: %d\n", m_currentCameraIndex);
QMessageBox::warning(this, "错误", "请先选择相机");
return;
}
// 获取UI中的ROI范围值
double xMin = ui->edit_roi_x_min->text().toDouble();
double xMax = ui->edit_roi_x_max->text().toDouble();
double yMin = ui->edit_roi_y_min->text().toDouble();
double yMax = ui->edit_roi_y_max->text().toDouble();
double zMin = ui->edit_roi_z_min->text().toDouble();
double zMax = ui->edit_roi_z_max->text().toDouble();
// 验证范围有效性
if (xMin >= xMax || yMin >= yMax || zMin >= zMax) {
QMessageBox::warning(this, "错误", "ROI范围无效最小值必须小于最大值");
return;
}
// 获取UI中的修正系数非法或为0时回退为1.0
bool cfOk = false;
double correctionFactor = ui->edit_correction_factor->text().toDouble(&cfOk);
if (!cfOk || correctionFactor == 0.0) {
correctionFactor = 1.0;
}
int cameraIndex = m_currentCameraIndex + 1; // 转换为1-based索引
QString cameraName = QString::fromStdString(m_cameraList[m_currentCameraIndex].first);
LOG_INFO("Saving ROI range for camera %d (%s): X[%.1f, %.1f], Y[%.1f, %.1f], Z[%.1f, %.1f], correctionFactor=%.8f\n",
cameraIndex, cameraName.toUtf8().constData(),
xMin, xMax, yMin, yMax, zMin, zMax, correctionFactor);
// 查找或创建相机调平参数
bool found = false;
for (auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == cameraIndex) {
param.wheelRoi3d_xMin = xMin;
param.wheelRoi3d_xMax = xMax;
param.wheelRoi3d_yMin = yMin;
param.wheelRoi3d_yMax = yMax;
param.wheelRoi3d_zMin = zMin;
param.wheelRoi3d_zMax = zMax;
param.correctionFactor = correctionFactor;
found = true;
break;
}
}
// 如果没有找到现有记录,创建一个新的
if (!found) {
WheelCameraPlaneCalibParam newParam;
newParam.cameraIndex = cameraIndex;
newParam.cameraName = cameraName.toStdString();
newParam.wheelRoi3d_xMin = xMin;
newParam.wheelRoi3d_xMax = xMax;
newParam.wheelRoi3d_yMin = yMin;
newParam.wheelRoi3d_yMax = yMax;
newParam.wheelRoi3d_zMin = zMin;
newParam.wheelRoi3d_zMax = zMax;
newParam.correctionFactor = correctionFactor;
newParam.isCalibrated = false; // 尚未标定
m_pConfigResult->planeCalibParams.push_back(newParam);
}
// 保存配置到文件
QString configPath = PathManager::GetInstance().GetConfigFilePath();
bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult);
if (saveResult) {
LOG_INFO("ROI range saved successfully for camera %d\n", cameraIndex);
QMessageBox::information(this, "成功", "ROI范围和修正系数已保存");
} else {
LOG_ERROR("Failed to save ROI range\n");
QMessageBox::critical(this, "错误", "保存失败");
}
}
void DialogCameraLevel::on_btn_save_roi_clicked()
{
saveCameraRoiRange();
}
bool DialogCameraLevel::performCorrectionMeasurement(double& measuredLength)
{
measuredLength = 0.0;
int selectedIndex = ui->combo_camera->currentIndex();
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(m_cameraList.size())) {
LOG_ERROR("Invalid camera index for correction: %d\n", selectedIndex);
return false;
}
if (!m_pConfigResult) {
LOG_ERROR("Config result is null, cannot perform correction measurement\n");
return false;
}
// 解析 UI 中的标定块参数
bool ok = false;
double nominalLen = ui->edit_calib_nominal_length->text().toDouble(&ok);
if (!ok || nominalLen <= 0.0) {
QMessageBox::warning(this, "错误", "标定块标称值无效请输入大于0的数值");
return false;
}
double yMin = ui->edit_calib_y_min->text().toDouble();
double yMax = ui->edit_calib_y_max->text().toDouble();
if (yMin >= yMax) {
QMessageBox::warning(this, "错误", "标定块Y范围无效最小值必须小于最大值");
return false;
}
// 1. 设置状态回调(复用调平回调,监听摆动完成)
setLevelingStatusCallback();
// 2. 清空之前的扫描数据
clearScanDataCache();
// 3. 启动相机扫描
if (!startCameraScan(selectedIndex)) {
LOG_ERROR("Failed to start camera scan for correction\n");
restorePresenterStatusCallback();
return false;
}
// 4. 等待扫描完成
LOG_INFO("Collecting calibration block scan data, waiting for swing finish signal...\n");
int waitTime = 0;
const int maxWaitTime = 10000;
const int checkInterval = 100;
while (!m_swingFinished && waitTime < maxWaitTime) {
QThread::msleep(checkInterval);
QApplication::processEvents();
waitTime += checkInterval;
}
// 5. 停止扫描
stopCameraScan(selectedIndex);
restorePresenterStatusCallback();
if (!m_swingFinished) {
LOG_WARNING("Timeout waiting for camera swing finish signal in correction\n");
}
// 6. 准备算法参数(从 ConfigResult 取得)
SSG_outlierFilterParam filterParam;
filterParam.continuityTh = m_pConfigResult->algorithmParams.filterParam.continuityTh;
filterParam.outlierTh = m_pConfigResult->algorithmParams.filterParam.outlierTh;
SSG_treeGrowParam growParam;
growParam.yDeviation_max = m_pConfigResult->algorithmParams.growParam.yDeviation_max;
growParam.zDeviation_max = m_pConfigResult->algorithmParams.growParam.zDeviation_max;
growParam.maxLineSkipNum = m_pConfigResult->algorithmParams.growParam.maxLineSkipNum;
growParam.maxSkipDistance = m_pConfigResult->algorithmParams.growParam.maxSkipDistance;
growParam.minLTypeTreeLen = m_pConfigResult->algorithmParams.growParam.minLTypeTreeLen;
growParam.minVTypeTreeLen = m_pConfigResult->algorithmParams.growParam.minVTypeTreeLen;
SVzNLRangeD yRange;
yRange.min = yMin;
yRange.max = yMax;
// 7. 调用算法计算标定块测量长度
int errCode = 0;
double length = 0.0;
{
std::lock_guard<std::mutex> lock(m_scanDataMutex);
if (m_scanDataCache.empty()) {
LOG_ERROR("No scan data available for correction measurement\n");
return false;
}
LOG_INFO("Calling wd_getCalibrationBlockLength with %zu scan lines, nominalLen=%.3f, Y[%.1f, %.1f]\n",
m_scanDataCache.size(), nominalLen, yMin, yMax);
#if _OUTPUT_DEBUG_DATA
std::vector<std::vector<SVzNL3DPosition>> debugData;
length = wd_getCalibrationBlockLength(
m_scanDataCache, nominalLen, filterParam, growParam, yRange, debugData, &errCode);
#else
length = wd_getCalibrationBlockLength(
m_scanDataCache, nominalLen, filterParam, growParam, yRange, &errCode);
#endif
}
LOG_INFO("Calibration block length measurement: %.3f, errCode=%d\n", length, errCode);
clearScanDataCache();
if (errCode != 0 || length <= 0.0) {
LOG_ERROR("Calibration block measurement failed, errCode=%d, length=%.3f\n", errCode, length);
return false;
}
measuredLength = length;
return true;
}
void DialogCameraLevel::on_btn_start_correction_clicked()
{
if (m_cameraList.empty()) {
QMessageBox::warning(this, "错误", "无可用相机设备!");
return;
}
int selectedIndex = ui->combo_camera->currentIndex();
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(m_cameraList.size())) {
QMessageBox::warning(this, "错误", "请选择有效的相机!");
return;
}
// 扫描次数仅由配置文件控制WheelMeasureConfigResult::correctionScanCount
// 读不到配置或配置值不合理时统一回退到默认 10
constexpr int kDefaultScanCount = 10;
constexpr int kMaxScanCount = 50;
int scanCount = kDefaultScanCount;
if (m_pConfigResult && m_pConfigResult->correctionScanCount > 0
&& m_pConfigResult->correctionScanCount <= kMaxScanCount) {
scanCount = m_pConfigResult->correctionScanCount;
} else if (m_pConfigResult) {
LOG_WARNING("correctionScanCount=%d in config is invalid, fallback to %d\n",
m_pConfigResult->correctionScanCount, kDefaultScanCount);
}
LOG_INFO("Correction measurement will scan %d times (from config)\n", scanCount);
const QString originalBtnText = ui->btn_start_correction->text();
ui->btn_start_correction->setEnabled(false);
ui->btn_apply_correction->setEnabled(false);
ui->btn_start_correction->setText("正在测量...");
QApplication::processEvents();
try {
std::vector<double> validMeasurements;
validMeasurements.reserve(scanCount);
for (int i = 0; i < scanCount; ++i) {
ui->btn_start_correction->setText(QString("正在测量 %1/%2...").arg(i + 1).arg(scanCount));
QApplication::processEvents();
double singleLength = 0.0;
if (performCorrectionMeasurement(singleLength)) {
LOG_INFO("Correction measurement %d/%d: %.3f\n", i + 1, scanCount, singleLength);
validMeasurements.push_back(singleLength);
} else {
LOG_WARNING("Correction measurement %d/%d failed, skipped\n", i + 1, scanCount);
}
}
if (validMeasurements.empty()) {
QMessageBox::warning(this, "错误",
QString("标定块测量失败!%1次扫描均未获得有效结果。\n\n请检查:\n1. 相机连接是否正常\n2. 标定块是否放置在Y范围内\n3. 扫描数据是否充足").arg(scanCount));
} else {
// 求平均值
double sum = 0.0;
for (double v : validMeasurements) {
sum += v;
}
double averageLength = sum / validMeasurements.size();
// 计算修正系数 = 凹槽间距(标称) / 平均测量值(算法输出为凹槽间距)
double grooveSpacing = ui->edit_calib_groove_spacing->text().toDouble();
if (grooveSpacing <= 0.0) {
LOG_WARNING("Groove spacing is invalid (%.3f), skip correction factor calculation\n", grooveSpacing);
} else if (averageLength > 0.0) {
double correctionFactor = grooveSpacing / averageLength;
ui->edit_correction_factor->setText(QString::number(correctionFactor, 'f', 8));
LOG_INFO("Correction factor calculated: %.8f (grooveSpacing=%.3f / avg=%.3f, valid=%zu/%d)\n",
correctionFactor, grooveSpacing, averageLength, validMeasurements.size(), scanCount);
} else {
LOG_WARNING("Average length is zero or negative, skip correction factor calculation\n");
}
if (static_cast<int>(validMeasurements.size()) < scanCount) {
QMessageBox::information(this, "提示",
QString("共扫描 %1 次,有效结果 %2 次,已按有效结果求平均值")
.arg(scanCount).arg(validMeasurements.size()));
}
}
} catch (const std::exception& e) {
LOG_ERROR("Exception in on_btn_start_correction_clicked: %s\n", e.what());
QMessageBox::critical(this, "错误", QString("测量过程发生异常:%1").arg(e.what()));
restorePresenterStatusCallback();
}
ui->btn_start_correction->setText(originalBtnText);
ui->btn_start_correction->setEnabled(true);
ui->btn_apply_correction->setEnabled(true);
}
void DialogCameraLevel::on_btn_apply_correction_clicked()
{
if (!m_pConfig || !m_pConfigResult) {
LOG_ERROR("Config is null, cannot save correction factor\n");
return;
}
if (m_currentCameraIndex < 0 || m_currentCameraIndex >= static_cast<int>(m_cameraList.size())) {
QMessageBox::warning(this, "错误", "请先选择相机");
return;
}
// 从 UI 读取修正系数
bool cfOk = false;
double correctionFactor = ui->edit_correction_factor->text().toDouble(&cfOk);
if (!cfOk || correctionFactor == 0.0) {
QMessageBox::warning(this, "错误", "修正系数无效,请先执行测量或手动输入有效值");
return;
}
int cameraIndex = m_currentCameraIndex + 1;
QString cameraName = QString::fromStdString(m_cameraList[m_currentCameraIndex].first);
LOG_INFO("Applying correction factor for camera %d (%s): %.8f\n",
cameraIndex, cameraName.toUtf8().constData(), correctionFactor);
bool found = false;
for (auto& param : m_pConfigResult->planeCalibParams) {
if (param.cameraIndex == cameraIndex) {
param.correctionFactor = correctionFactor;
found = true;
break;
}
}
if (!found) {
WheelCameraPlaneCalibParam newParam;
newParam.cameraIndex = cameraIndex;
newParam.cameraName = cameraName.toStdString();
newParam.correctionFactor = correctionFactor;
newParam.isCalibrated = false;
m_pConfigResult->planeCalibParams.push_back(newParam);
}
QString configPath = PathManager::GetInstance().GetConfigFilePath();
bool saveResult = m_pConfig->SaveConfig(configPath.toStdString(), *m_pConfigResult);
if (saveResult) {
LOG_INFO("Correction factor saved successfully for camera %d: %.8f\n", cameraIndex, correctionFactor);
QMessageBox::information(this, "成功", QString("修正系数已保存:%1").arg(QString::number(correctionFactor, 'f', 8)));
} else {
LOG_ERROR("Failed to save correction factor\n");
QMessageBox::critical(this, "错误", "保存失败");
}
}