GrabBag/AppUtils/UICommon/Src/CommonDialogCameraLevel.cpp

517 lines
18 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 "CommonDialogCameraLevel.h"
#include "ui_CommonDialogCameraLevel.h"
#include "IVrUtils.h"
#include "PathManager.h"
#include <QThread>
#include <QApplication>
#include <QAbstractItemView>
#include <cmath>
#include <mutex>
#include <vector>
#include "LaserDataLoader.h"
#include <QCoreApplication>
#include <QFileInfo>
#include <QTimer>
#include "StyledMessageBox.h"
CommonDialogCameraLevel::CommonDialogCameraLevel(QWidget *parent)
: QDialog(parent)
, ui(new Ui::CommonDialogCameraLevel)
{
ui->setupUi(this);
// 隐藏标题栏
// setWindowFlags(Qt::FramelessWindowHint);
// 初始化界面
initializeCameraCombo();
// 修复下拉列表文字颜色Windows 原生样式下 .ui 中的子选择器不生效)
ui->combo_camera->view()->setStyleSheet(
"QAbstractItemView { color: rgb(221, 225, 233); background-color: rgb(47, 48, 52); selection-background-color: rgb(60, 120, 180); }");
// 初始化结果显示区域
ui->label_level_result->setText("请选择相机并点击调平按钮\n开始相机调平操作");
ui->label_level_result->setAlignment(Qt::AlignCenter);
}
CommonDialogCameraLevel::~CommonDialogCameraLevel()
{
// 清理扫描数据缓存
clearScanDataCache();
// 确保恢复Presenter的状态回调
restorePresenterStatusCallback();
delete ui;
}
void CommonDialogCameraLevel::SetCameraList(const std::vector<std::pair<std::string, IVrEyeDevice*>>& cameraList,
BasePresenter* presenter,
ICameraLevelCalculator* calculator,
ICameraLevelResultSaver* resultSaver)
{
m_cameraList = cameraList;
m_presenter = presenter;
m_calculator = calculator;
m_resultSaver = resultSaver;
// 重新初始化相机选择框
initializeCameraCombo();
}
void CommonDialogCameraLevel::initializeCameraCombo()
{
ui->combo_camera->clear();
if (m_cameraList.empty()) {
ui->combo_camera->setEnabled(false);
ui->label_level_result->setText("无可用相机设备");
ui->label_level_result->setAlignment(Qt::AlignCenter);
} else {
for (const auto& camera : m_cameraList) {
ui->combo_camera->addItem(QString::fromStdString(camera.first));
}
ui->combo_camera->setEnabled(true);
// 检查并显示当前选中相机的标定状态
checkAndDisplayCalibrationStatus(0); // 默认选中第一个相机
}
}
void CommonDialogCameraLevel::on_btn_apply_clicked()
{
ui->label_level_result->setAlignment(Qt::AlignLeft);
// 检查是否有可用的相机
if (m_cameraList.empty()) {
StyledMessageBox::warning(this, "错误", "无可用相机设备!");
return;
}
// 获取选中的相机
int selectedIndex = ui->combo_camera->currentIndex();
if (selectedIndex < 0 || selectedIndex >= static_cast<int>(m_cameraList.size())) {
StyledMessageBox::warning(this, "错误", "请选择有效的相机!");
return;
}
// 清空之前的结果显示
ui->label_level_result->setText("调平计算中,请稍候...");
// 显示进度提示
ui->btn_apply->setEnabled(false);
QApplication::processEvents();
try {
// 执行相机调平
if (!performCameraLeveling()) {
// 显示失败信息到界面
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());
StyledMessageBox::critical(this, "错误", QString("调平过程发生异常:%1").arg(e.what()));
}
// 恢复按钮状态
ui->btn_apply->setEnabled(true);
}
void CommonDialogCameraLevel::on_btn_cancel_clicked()
{
// 直接关闭窗口,不保存任何更改
reject();
}
bool CommonDialogCameraLevel::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");
return false;
}
// 4. 等待扫描完成(使用状态回调判断)
LOG_INFO("Collecting ground scan data, waiting for swing finish signal...\n");
int waitTime = 0;
const int maxWaitTime = 30000; // 最大等待30秒
const int checkInterval = 100; // 每100ms检查一次
while (!m_swingFinished && waitTime < maxWaitTime) {
QThread::msleep(checkInterval);
QApplication::processEvents(); // 处理UI事件
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");
return false;
}
LOG_INFO("Camera leveling calculation completed\n");
// 7. 更新界面显示
updateLevelingResults(planeCalib, planeHeight, invRMatrix);
// 8. 保存结果到配置
if (m_resultSaver) {
int cameraIndex = selectedIndex + 1; // 转换为1-based索引
QString cameraName = QString::fromStdString(m_cameraList[selectedIndex].first);
if (!m_resultSaver->SaveLevelingResults(planeCalib, planeHeight, invRMatrix, cameraIndex, cameraName)) {
LOG_ERROR("Failed to save leveling results\n");
return false;
}
} else {
LOG_WARNING("No result saver provided, leveling results not saved\n");
}
clearScanDataCache();
LOG_INFO("Camera leveling completed successfully\n");
return true;
} catch (const std::exception& e) {
LOG_ERROR("Exception in performCameraLeveling: %s\n", e.what());
return false;
}
}
void CommonDialogCameraLevel::updateLevelingResults(double planeCalib[9], double planeHeight, double invRMatrix[9])
{
// 构建显示文本,将矩阵直接显示到页面上
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 CommonDialogCameraLevel::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(&CommonDialogCameraLevel::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 CommonDialogCameraLevel::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 CommonDialogCameraLevel::StaticDetectionCallback(EVzResultDataType eDataType, SVzLaserLineData* pLaserLinePoint, void* pUserData)
{
CommonDialogCameraLevel* pThis = reinterpret_cast<CommonDialogCameraLevel*>(pUserData);
if (pThis && pLaserLinePoint) {
pThis->DetectionCallback(eDataType, pLaserLinePoint);
}
}
// 静态状态回调函数
void CommonDialogCameraLevel::StaticStatusCallback(EVzDeviceWorkStatus eStatus, void* pExtData, unsigned int nDataLength, void* pInfoParam)
{
CommonDialogCameraLevel* pThis = reinterpret_cast<CommonDialogCameraLevel*>(pInfoParam);
if (pThis) {
pThis->StatusCallback(eStatus, pExtData, nDataLength, pInfoParam);
}
}
// 状态回调函数实例版本
void CommonDialogCameraLevel::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 CommonDialogCameraLevel::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;
}
// 使用与Presenter相同的方式存储数据
SVzLaserLineData lineData;
memset(&lineData, 0, sizeof(SVzLaserLineData));
// 根据数据类型分配和复制点云数据
if (eDataType == keResultDataType_Position) {
// 复制SVzNL3DPosition数据
if (pLaserLinePoint->p3DPoint && pLaserLinePoint->nPointCount > 0) {
lineData.p3DPoint = new SVzNL3DPosition[pLaserLinePoint->nPointCount];
if (lineData.p3DPoint) {
memcpy(lineData.p3DPoint, pLaserLinePoint->p3DPoint, sizeof(SVzNL3DPosition) * pLaserLinePoint->nPointCount);
}
lineData.p2DPoint = new SVzNL2DPosition[pLaserLinePoint->nPointCount];
if (lineData.p2DPoint) {
memcpy(lineData.p2DPoint, pLaserLinePoint->p2DPoint, sizeof(SVzNL2DPosition) * pLaserLinePoint->nPointCount);
}
}
} else if (eDataType == keResultDataType_PointXYZRGBA) {
// 复制SVzNLPointXYZRGBA数据
if (pLaserLinePoint->p3DPoint && pLaserLinePoint->nPointCount > 0) {
lineData.p3DPoint = new SVzNLPointXYZRGBA[pLaserLinePoint->nPointCount];
if (lineData.p3DPoint) {
memcpy(lineData.p3DPoint, pLaserLinePoint->p3DPoint, sizeof(SVzNLPointXYZRGBA) * pLaserLinePoint->nPointCount);
}
lineData.p2DPoint = new SVzNL2DLRPoint[pLaserLinePoint->nPointCount];
if (lineData.p2DPoint) {
memcpy(lineData.p2DPoint, pLaserLinePoint->p2DPoint, sizeof(SVzNL2DLRPoint) * pLaserLinePoint->nPointCount);
}
}
}
lineData.nPointCount = pLaserLinePoint->nPointCount;
lineData.llTimeStamp = pLaserLinePoint->llTimeStamp;
lineData.llFrameIdx = pLaserLinePoint->llFrameIdx;
lineData.nEncodeNo = pLaserLinePoint->nEncodeNo;
lineData.fSwingAngle = pLaserLinePoint->fSwingAngle;
lineData.bEndOnceScan = pLaserLinePoint->bEndOnceScan;
// 将转换后的数据保存到缓存中
std::lock_guard<std::mutex> lock(m_scanDataMutex);
m_scanDataCache.push_back(std::make_pair(eDataType, lineData));
}
// 调平算法计算
bool CommonDialogCameraLevel::calculatePlaneCalibration(double planeCalib[9], double& planeHeight, double invRMatrix[9])
{
std::lock_guard<std::mutex> lock(m_scanDataMutex);
// 检查计算接口是否已设置
if (!m_calculator) {
LOG_ERROR("Calculator interface is not set\n");
return false;
}
// 检查是否有足够的扫描数据
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());
// 调用应用实现的标定计算接口
bool result = m_calculator->CalculatePlaneCalibration(m_scanDataCache, planeCalib, planeHeight, invRMatrix);
if (result) {
// 计算旋转角度用于日志显示
double rotAngleX = atan2(planeCalib[5], planeCalib[8]) * 180.0 / M_PI;
double rotAngleY = atan2(-planeCalib[2], sqrt(planeCalib[5]*planeCalib[5] + planeCalib[8]*planeCalib[8])) * 180.0 / M_PI;
LOG_INFO("Plane calibration calculated: height=%.3f, rotX=%.2f°, rotY=%.2f°\n", planeHeight, rotAngleX, rotAngleY);
} else {
LOG_ERROR("Failed to calculate plane calibration\n");
}
return result;
}
// 清空扫描数据缓存
void CommonDialogCameraLevel::clearScanDataCache()
{
std::lock_guard<std::mutex> lock(m_scanDataMutex);
LOG_DEBUG("Clearing scan data cache, current size: %zu\n", m_scanDataCache.size());
// 使用LaserDataLoader释放缓存的内存
m_dataLoader.FreeLaserScanData(m_scanDataCache);
LOG_DEBUG("Scan data cache cleared successfully\n");
}
// 相机选择改变的槽函数
void CommonDialogCameraLevel::on_combo_camera_currentIndexChanged(int 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(index);
} else {
LOG_WARNING("Invalid camera index selected: %d\n", index);
ui->label_level_result->setText("无效的相机选择");
ui->label_level_result->setAlignment(Qt::AlignCenter);
}
}
// 检查并显示相机标定状态
void CommonDialogCameraLevel::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; // 转换为1-based索引
// 尝试加载该相机的标定数据
double planeCalib[9];
double planeHeight;
double invRMatrix[9];
if (m_resultSaver && m_resultSaver->LoadLevelingResults(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 instruction\n", cameraName.toUtf8().constData());
ui->label_level_result->setText(QString("请选择相机 \"%1\" 并点击调平按钮\n开始相机调平操作").arg(cameraName));
ui->label_level_result->setAlignment(Qt::AlignCenter);
}
}
// 设置调平时的状态回调
void CommonDialogCameraLevel::setLevelingStatusCallback()
{
if (!m_presenter) {
LOG_ERROR("Presenter is null, cannot set leveling status callback\n");
return;
}
// 为所有相机设置调平状态回调
m_presenter->SetCameraStatusCallback(&CommonDialogCameraLevel::StaticStatusCallback, this);
// 重置状态标志
m_swingFinished = false;
m_callbackRestored = false; // 重置恢复标志,允许稍后恢复
LOG_INFO("Leveling status callback set for all cameras\n");
}
// 恢复Presenter的状态回调
void CommonDialogCameraLevel::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;
}
// 恢复Presenter的状态回调 - 使用BasePresenter的通用静态回调
m_presenter->SetCameraStatusCallback(&BasePresenter::_StaticCameraStatusCallback, m_presenter);
LOG_INFO("Presenter status callback restored for all cameras\n");
}