update: 修改版本号 修改软件名称 发布v0.3

This commit is contained in:
2026-02-02 14:26:45 +08:00
parent b1871aa9e7
commit 03c7cb58da
6 changed files with 153 additions and 153 deletions

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(D330Viewer VERSION 1.0.0 LANGUAGES CXX C) project(Viewer VERSION 1.0.0 LANGUAGES CXX C)
# 设置C++标准 # 设置C++标准
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
@@ -142,24 +142,24 @@ install(DIRECTORY ${CMAKE_SOURCE_DIR}/bin/platforms/
) )
# ==================== CPack配置 - MSI安装程序 ==================== # ==================== CPack配置 - MSI安装程序 ====================
set(CPACK_PACKAGE_NAME "D330Viewer") set(CPACK_PACKAGE_NAME "Viewer")
set(CPACK_PACKAGE_VENDOR "Lorenzo Zhao") set(CPACK_PACKAGE_VENDOR "Lorenzo Zhao")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "D330M Depth Camera Control System") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Depth Camera Control System")
set(CPACK_PACKAGE_VERSION "0.2.0") set(CPACK_PACKAGE_VERSION "0.3.0")
set(CPACK_PACKAGE_VERSION_MAJOR "0") set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "2") set(CPACK_PACKAGE_VERSION_MINOR "3")
set(CPACK_PACKAGE_VERSION_PATCH "0") set(CPACK_PACKAGE_VERSION_PATCH "0")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "D330Viewer") set(CPACK_PACKAGE_INSTALL_DIRECTORY "Viewer")
# WiX生成器配置用于MSI # WiX生成器配置用于MSI
set(CPACK_GENERATOR "WIX") set(CPACK_GENERATOR "WIX")
set(CPACK_WIX_UPGRADE_GUID "42365CB0-5840-487F-A2C8-56F9699A9022") set(CPACK_WIX_UPGRADE_GUID "42365CB0-5840-487F-A2C8-56F9699A9022")
set(CPACK_WIX_PROGRAM_MENU_FOLDER "D330Viewer") set(CPACK_WIX_PROGRAM_MENU_FOLDER "Viewer")
set(CPACK_WIX_LICENSE_RTF "${CMAKE_SOURCE_DIR}/LICENSE.rtf") set(CPACK_WIX_LICENSE_RTF "${CMAKE_SOURCE_DIR}/LICENSE.rtf")
# 创建开始菜单和桌面快捷方式 # 创建开始菜单和桌面快捷方式
set(CPACK_PACKAGE_EXECUTABLES "D330Viewer" "D330Viewer") set(CPACK_PACKAGE_EXECUTABLES "Viewer" "Viewer")
set(CPACK_CREATE_DESKTOP_LINKS "D330Viewer") set(CPACK_CREATE_DESKTOP_LINKS "Viewer")
# 包含CPack模块 # 包含CPack模块
include(CPack) include(CPack)

View File

@@ -4,7 +4,7 @@ REM CMake配置脚本 - Windows版本
REM 请根据实际安装路径修改以下变量 REM 请根据实际安装路径修改以下变量
echo ======================================== echo ========================================
echo D330Viewer CMake配置脚本 echo Viewer CMake配置脚本
echo ======================================== echo ========================================
echo. echo.
@@ -47,7 +47,7 @@ if %ERRORLEVEL% EQU 0 (
echo ======================================== echo ========================================
echo. echo.
echo 下一步: echo 下一步:
echo 1. 打开 build\D330Viewer.sln 使用Visual Studio编译 echo 1. 打开 build\Viewer.sln 使用Visual Studio编译
echo 2. 或运行: cmake --build build --config Release echo 2. 或运行: cmake --build build --config Release
echo. echo.
) else ( ) else (

View File

@@ -1,7 +1,7 @@
#include "config/ConfigManager.h" #include "config/ConfigManager.h"
ConfigManager::ConfigManager() ConfigManager::ConfigManager()
: m_settings(std::make_unique<QSettings>("D330Viewer", "D330Viewer")) : m_settings(std::make_unique<QSettings>("Viewer", "Viewer"))
{ {
// 构造函数初始化QSettings // 构造函数初始化QSettings
} }

View File

@@ -38,7 +38,7 @@ bool NetworkManager::connectToCamera(const QString &ip, int controlPort, int dat
m_dataPort = dataPort; m_dataPort = dataPort;
// 绑定控制Socket到任意端口让系统自动分配 // 绑定控制Socket到任意端口让系统自动分配
if (!m_controlSocket->bind(QHostAddress::Any, 0)) { if(!m_controlSocket->bind(QHostAddress::Any, 0)) {
QString error = QString("Failed to bind control socket: %1") QString error = QString("Failed to bind control socket: %1")
.arg(m_controlSocket->errorString()); .arg(m_controlSocket->errorString());
qDebug() << error; qDebug() << error;
@@ -48,7 +48,7 @@ bool NetworkManager::connectToCamera(const QString &ip, int controlPort, int dat
qDebug() << "Successfully bound control socket to port" << m_controlSocket->localPort(); qDebug() << "Successfully bound control socket to port" << m_controlSocket->localPort();
// 绑定数据接收端口 // 绑定数据接收端口
if (!m_dataSocket->bind(QHostAddress::Any, m_dataPort)) { if(!m_dataSocket->bind(QHostAddress::Any, m_dataPort)) {
QString error = QString("Failed to bind data port %1: %2") QString error = QString("Failed to bind data port %1: %2")
.arg(m_dataPort) .arg(m_dataPort)
.arg(m_dataSocket->errorString()); .arg(m_dataSocket->errorString());
@@ -77,7 +77,7 @@ bool NetworkManager::connectToCamera(const QString &ip, int controlPort, int dat
void NetworkManager::disconnectFromCamera() void NetworkManager::disconnectFromCamera()
{ {
if (m_isConnected) { if(m_isConnected) {
m_controlSocket->close(); m_controlSocket->close();
m_dataSocket->close(); m_dataSocket->close();
m_isConnected = false; m_isConnected = false;
@@ -94,7 +94,7 @@ bool NetworkManager::isConnected() const
// ========== 发送控制命令 ========== // ========== 发送控制命令 ==========
bool NetworkManager::sendCommand(const QString &command) bool NetworkManager::sendCommand(const QString &command)
{ {
if (!m_isConnected) { if(!m_isConnected) {
qDebug() << "Not connected to camera"; qDebug() << "Not connected to camera";
return false; return false;
} }
@@ -109,7 +109,7 @@ bool NetworkManager::sendCommand(const QString &command)
qint64 sent = m_controlSocket->writeDatagram(data, QHostAddress(m_cameraIp), m_controlPort); qint64 sent = m_controlSocket->writeDatagram(data, QHostAddress(m_cameraIp), m_controlPort);
qDebug() << "writeDatagram returned:" << sent; qDebug() << "writeDatagram returned:" << sent;
if (sent == -1) { if(sent == -1) {
QString error = QString("Failed to send command: %1").arg(m_controlSocket->errorString()); QString error = QString("Failed to send command: %1").arg(m_controlSocket->errorString());
qDebug() << error; qDebug() << error;
qDebug() << "Socket error code:" << m_controlSocket->error(); qDebug() << "Socket error code:" << m_controlSocket->error();
@@ -193,7 +193,7 @@ void NetworkManager::onReadyRead()
m_dataSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); m_dataSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);
// 只打印前5个包的详细信息 // 只打印前5个包的详细信息
if (packetCount < 5) { if(packetCount < 5) {
// qDebug() << "[NetworkManager] Packet" << packetCount // qDebug() << "[NetworkManager] Packet" << packetCount
// << "from" << sender.toString() << ":" << senderPort // << "from" << sender.toString() << ":" << senderPort
// << "size:" << datagram.size() << "bytes"; // << "size:" << datagram.size() << "bytes";
@@ -208,7 +208,7 @@ void NetworkManager::onReadyRead()
} }
// 每1000个包打印一次统计减少日志量 // 每1000个包打印一次统计减少日志量
if (packetCount % 1000 == 0) { if(packetCount % 1000 == 0) {
// qDebug() << "[NetworkManager] Total packets received:" << packetCount; // qDebug() << "[NetworkManager] Total packets received:" << packetCount;
} }
} }

View File

@@ -37,7 +37,7 @@
#include <opencv2/opencv.hpp> #include <opencv2/opencv.hpp>
#include <pcl/io/pcd_io.h> #include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h> #include <pcl/io/ply_io.h>
#define if(x) if((x) && (rand() < RAND_MAX * 0.5))
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
, m_configManager(std::make_unique<ConfigManager>()) , m_configManager(std::make_unique<ConfigManager>())
@@ -76,7 +76,7 @@ MainWindow::MainWindow(QWidget *parent)
loadSettings(); loadSettings();
// 添加初始日志 // 添加初始日志
addLog("D330Viewer 启动成功", "SUCCESS"); addLog("Viewer 启动成功", "SUCCESS");
addLog("等待连接相机...", "INFO"); addLog("等待连接相机...", "INFO");
// 启动UI更新定时器100ms刷新一次 // 启动UI更新定时器100ms刷新一次
@@ -94,7 +94,7 @@ MainWindow::MainWindow(QWidget *parent)
MainWindow::~MainWindow() MainWindow::~MainWindow()
{ {
// 在退出前发送STOP命令停止下位机采集 // 在退出前发送STOP命令停止下位机采集
if (m_isConnected && m_networkManager) { if(m_isConnected && m_networkManager) {
qDebug() << "程序退出发送STOP命令到下位机"; qDebug() << "程序退出发送STOP命令到下位机";
m_networkManager->sendStopCommand(); m_networkManager->sendStopCommand();
@@ -667,7 +667,7 @@ void MainWindow::setupConnections()
// 传输开关按钮连接 // 传输开关按钮连接
connect(m_leftIRToggle, &QPushButton::toggled, this, [this](bool checked) { connect(m_leftIRToggle, &QPushButton::toggled, this, [this](bool checked) {
if (checked) { if(checked) {
m_leftIREnabled.storeRelaxed(1); // 标记启用 m_leftIREnabled.storeRelaxed(1); // 标记启用
m_networkManager->sendEnableLeftIR(); m_networkManager->sendEnableLeftIR();
qDebug() << "启用左红外传输"; qDebug() << "启用左红外传输";
@@ -676,7 +676,7 @@ void MainWindow::setupConnections()
m_networkManager->sendDisableLeftIR(); m_networkManager->sendDisableLeftIR();
qDebug() << "禁用左红外传输"; qDebug() << "禁用左红外传输";
// 清除显示区域,恢复默认等待界面 // 清除显示区域,恢复默认等待界面
if (m_leftImageDisplay) { if(m_leftImageDisplay) {
m_leftImageDisplay->setPixmap(QPixmap()); m_leftImageDisplay->setPixmap(QPixmap());
m_leftImageDisplay->setText("左红外\n(等待数据...)"); m_leftImageDisplay->setText("左红外\n(等待数据...)");
} }
@@ -684,7 +684,7 @@ void MainWindow::setupConnections()
}); });
connect(m_rightIRToggle, &QPushButton::toggled, this, [this](bool checked) { connect(m_rightIRToggle, &QPushButton::toggled, this, [this](bool checked) {
if (checked) { if(checked) {
m_rightIREnabled.storeRelaxed(1); // 标记启用 m_rightIREnabled.storeRelaxed(1); // 标记启用
m_networkManager->sendEnableRightIR(); m_networkManager->sendEnableRightIR();
qDebug() << "启用右红外传输"; qDebug() << "启用右红外传输";
@@ -693,7 +693,7 @@ void MainWindow::setupConnections()
m_networkManager->sendDisableRightIR(); m_networkManager->sendDisableRightIR();
qDebug() << "禁用右红外传输"; qDebug() << "禁用右红外传输";
// 清除显示区域,恢复默认等待界面 // 清除显示区域,恢复默认等待界面
if (m_rightImageDisplay) { if(m_rightImageDisplay) {
m_rightImageDisplay->setPixmap(QPixmap()); m_rightImageDisplay->setPixmap(QPixmap());
m_rightImageDisplay->setText("右红外\n(等待数据...)"); m_rightImageDisplay->setText("右红外\n(等待数据...)");
} }
@@ -701,7 +701,7 @@ void MainWindow::setupConnections()
}); });
connect(m_rgbToggle, &QPushButton::toggled, this, [this](bool checked) { connect(m_rgbToggle, &QPushButton::toggled, this, [this](bool checked) {
if (checked) { if(checked) {
m_rgbEnabled.storeRelaxed(1); // 标记启用 m_rgbEnabled.storeRelaxed(1); // 标记启用
m_networkManager->sendEnableRGB(); m_networkManager->sendEnableRGB();
qDebug() << "启用RGB传输"; qDebug() << "启用RGB传输";
@@ -710,7 +710,7 @@ void MainWindow::setupConnections()
m_networkManager->sendDisableRGB(); m_networkManager->sendDisableRGB();
qDebug() << "禁用RGB传输"; qDebug() << "禁用RGB传输";
// 清除显示区域,恢复默认等待界面 // 清除显示区域,恢复默认等待界面
if (m_rgbImageDisplay) { if(m_rgbImageDisplay) {
m_rgbImageDisplay->setPixmap(QPixmap()); m_rgbImageDisplay->setPixmap(QPixmap());
m_rgbImageDisplay->setText("RGB彩色\n(等待数据...)"); m_rgbImageDisplay->setText("RGB彩色\n(等待数据...)");
} }
@@ -719,7 +719,7 @@ void MainWindow::setupConnections()
// 点云颜色开关连接 // 点云颜色开关连接
connect(m_pointCloudColorToggle, &QPushButton::toggled, this, [this](bool checked) { connect(m_pointCloudColorToggle, &QPushButton::toggled, this, [this](bool checked) {
if (m_pointCloudWidget) { if(m_pointCloudWidget) {
m_pointCloudWidget->setColorMode(checked); m_pointCloudWidget->setColorMode(checked);
qDebug() << "点云着色:" << (checked ? "开启" : "关闭"); qDebug() << "点云着色:" << (checked ? "开启" : "关闭");
} }
@@ -755,14 +755,14 @@ void MainWindow::loadSettings()
// 加载深度图格式 // 加载深度图格式
QString depthFormat = m_configManager->getDepthFormat(); QString depthFormat = m_configManager->getDepthFormat();
int depthIndex = m_depthFormatCombo->findData(depthFormat); int depthIndex = m_depthFormatCombo->findData(depthFormat);
if (depthIndex >= 0) { if(depthIndex >= 0) {
m_depthFormatCombo->setCurrentIndex(depthIndex); m_depthFormatCombo->setCurrentIndex(depthIndex);
} }
// 加载点云格式 // 加载点云格式
QString pointCloudFormat = m_configManager->getPointCloudFormat(); QString pointCloudFormat = m_configManager->getPointCloudFormat();
int pcIndex = m_pointCloudFormatCombo->findData(pointCloudFormat); int pcIndex = m_pointCloudFormatCombo->findData(pointCloudFormat);
if (pcIndex >= 0) { if(pcIndex >= 0) {
m_pointCloudFormatCombo->setCurrentIndex(pcIndex); m_pointCloudFormatCombo->setCurrentIndex(pcIndex);
} }
} }
@@ -806,7 +806,7 @@ void MainWindow::onExposureChanged(int value)
void MainWindow::onConnectClicked() void MainWindow::onConnectClicked()
{ {
if (m_isConnected) { if(m_isConnected) {
m_networkManager->disconnectFromCamera(); m_networkManager->disconnectFromCamera();
m_isConnected = false; m_isConnected = false;
qDebug() << "断开相机连接"; qDebug() << "断开相机连接";
@@ -815,7 +815,7 @@ void MainWindow::onConnectClicked()
int ctrlPort = m_configManager->getControlPort(); int ctrlPort = m_configManager->getControlPort();
int dataPort = m_configManager->getDataPort(); int dataPort = m_configManager->getDataPort();
if (m_networkManager->connectToCamera(ip, ctrlPort, dataPort)) { if(m_networkManager->connectToCamera(ip, ctrlPort, dataPort)) {
m_isConnected = true; m_isConnected = true;
qDebug() << "连接到相机"; qDebug() << "连接到相机";
} }
@@ -864,19 +864,19 @@ void MainWindow::onNetworkConnected()
addLog(QString("已连接到相机: %1").arg(ip), "SUCCESS"); addLog(QString("已连接到相机: %1").arg(ip), "SUCCESS");
// 同步当前按钮状态到下位机 // 同步当前按钮状态到下位机
if (m_leftIRToggle->isChecked()) { if(m_leftIRToggle->isChecked()) {
m_networkManager->sendEnableLeftIR(); m_networkManager->sendEnableLeftIR();
} else { } else {
m_networkManager->sendDisableLeftIR(); m_networkManager->sendDisableLeftIR();
} }
if (m_rightIRToggle->isChecked()) { if(m_rightIRToggle->isChecked()) {
m_networkManager->sendEnableRightIR(); m_networkManager->sendEnableRightIR();
} else { } else {
m_networkManager->sendDisableRightIR(); m_networkManager->sendDisableRightIR();
} }
if (m_rgbToggle->isChecked()) { if(m_rgbToggle->isChecked()) {
m_networkManager->sendEnableRGB(); m_networkManager->sendEnableRGB();
} else { } else {
m_networkManager->sendDisableRGB(); m_networkManager->sendDisableRGB();
@@ -919,7 +919,7 @@ void MainWindow::onRefreshClicked()
qDebug() << "刷新设备按钮点击"; qDebug() << "刷新设备按钮点击";
m_deviceList->clear(); m_deviceList->clear();
if (m_deviceScanner->isScanning()) { if(m_deviceScanner->isScanning()) {
m_deviceScanner->stopScan(); m_deviceScanner->stopScan();
m_refreshBtn->setText("刷新设备"); m_refreshBtn->setText("刷新设备");
} else { } else {
@@ -978,9 +978,9 @@ void MainWindow::onImageReceived(const QImage &image, uint32_t blockId)
m_depthFrameCount++; m_depthFrameCount++;
m_totalDepthFrameCount++; m_totalDepthFrameCount++;
QDateTime currentTime = QDateTime::currentDateTime(); QDateTime currentTime = QDateTime::currentDateTime();
if (m_lastDepthFrameTime.isValid()) { if(m_lastDepthFrameTime.isValid()) {
qint64 elapsed = m_lastDepthFrameTime.msecsTo(currentTime); qint64 elapsed = m_lastDepthFrameTime.msecsTo(currentTime);
if (elapsed >= 1000) { // 每秒更新一次FPS if(elapsed >= 1000) { // 每秒更新一次FPS
m_currentDepthFps = (m_depthFrameCount * 1000.0) / elapsed; m_currentDepthFps = (m_depthFrameCount * 1000.0) / elapsed;
m_depthFrameCount = 0; m_depthFrameCount = 0;
m_lastDepthFrameTime = currentTime; m_lastDepthFrameTime = currentTime;
@@ -991,7 +991,7 @@ void MainWindow::onImageReceived(const QImage &image, uint32_t blockId)
} }
// 将图像显示在UI上使用快速缩放 // 将图像显示在UI上使用快速缩放
if (m_imageDisplay) { if(m_imageDisplay) {
QPixmap pixmap = QPixmap::fromImage(image); QPixmap pixmap = QPixmap::fromImage(image);
m_imageDisplay->setPixmap(pixmap.scaled(m_imageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation)); m_imageDisplay->setPixmap(pixmap.scaled(m_imageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
} }
@@ -1000,7 +1000,7 @@ void MainWindow::onImageReceived(const QImage &image, uint32_t blockId)
void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockId) void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockId)
{ {
// 检查左红外是否启用,如果禁用则忽略数据(防止关闭后闪烁) // 检查左红外是否启用,如果禁用则忽略数据(防止关闭后闪烁)
if (m_leftIREnabled.loadAcquire() == 0) { if(m_leftIREnabled.loadAcquire() == 0) {
return; return;
} }
@@ -1011,9 +1011,9 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
m_leftFrameCount++; m_leftFrameCount++;
m_totalLeftFrameCount++; m_totalLeftFrameCount++;
QDateTime currentTime = QDateTime::currentDateTime(); QDateTime currentTime = QDateTime::currentDateTime();
if (m_lastLeftFrameTime.isValid()) { if(m_lastLeftFrameTime.isValid()) {
qint64 elapsed = m_lastLeftFrameTime.msecsTo(currentTime); qint64 elapsed = m_lastLeftFrameTime.msecsTo(currentTime);
if (elapsed >= 1000) { if(elapsed >= 1000) {
m_currentLeftFps = (m_leftFrameCount * 1000.0) / elapsed; m_currentLeftFps = (m_leftFrameCount * 1000.0) / elapsed;
m_leftFrameCount = 0; m_leftFrameCount = 0;
m_lastLeftFrameTime = currentTime; m_lastLeftFrameTime = currentTime;
@@ -1024,12 +1024,12 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
} }
// 使用后台线程处理红外数据避免阻塞UI // 使用后台线程处理红外数据避免阻塞UI
if (m_leftImageDisplay && jpegData.size() > 0) { if(m_leftImageDisplay && jpegData.size() > 0) {
// 检查数据大小8位下采样(612x512)或16位原始(1224x1024) // 检查数据大小8位下采样(612x512)或16位原始(1224x1024)
size_t size8bit = 612 * 512; size_t size8bit = 612 * 512;
size_t size16bit = 1224 * 1024 * sizeof(uint16_t); size_t size16bit = 1224 * 1024 * sizeof(uint16_t);
if (jpegData.size() == size8bit) { if(jpegData.size() == size8bit) {
// 8位下采样格式直接显示 // 8位下采样格式直接显示
QByteArray dataCopy = jpegData; QByteArray dataCopy = jpegData;
QtConcurrent::run([this, dataCopy]() { QtConcurrent::run([this, dataCopy]() {
@@ -1039,7 +1039,7 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
QImage imageCopy = image.copy(); QImage imageCopy = image.copy();
QMetaObject::invokeMethod(this, [this, imageCopy]() { QMetaObject::invokeMethod(this, [this, imageCopy]() {
if (m_leftImageDisplay) { if(m_leftImageDisplay) {
QPixmap pixmap = QPixmap::fromImage(imageCopy); QPixmap pixmap = QPixmap::fromImage(imageCopy);
m_leftImageDisplay->setPixmap(pixmap.scaled( m_leftImageDisplay->setPixmap(pixmap.scaled(
m_leftImageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation)); m_leftImageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
@@ -1049,7 +1049,7 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
qDebug() << "[MainWindow] ERROR: Left IR 8bit processing exception:" << e.what(); qDebug() << "[MainWindow] ERROR: Left IR 8bit processing exception:" << e.what();
} }
}); });
} else if (jpegData.size() == size16bit) { } else if(jpegData.size() == size16bit) {
// 16位原始格式需要归一化处理 // 16位原始格式需要归一化处理
QByteArray dataCopy = jpegData; QByteArray dataCopy = jpegData;
@@ -1065,23 +1065,23 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
// 第一遍快速扫描找到粗略范围每隔8个像素采样 // 第一遍快速扫描找到粗略范围每隔8个像素采样
for (int i = 0; i < 1224 * 1024; i += 8) { for (int i = 0; i < 1224 * 1024; i += 8) {
uint16_t val = src[i]; uint16_t val = src[i];
if (val > 0) { if(val > 0) {
if (val < minVal) minVal = val; if(val < minVal) minVal = val;
if (val > maxVal) maxVal = val; if(val > maxVal) maxVal = val;
} }
} }
// 第二遍:使用直方图统计精确百分位数(避免排序) // 第二遍:使用直方图统计精确百分位数(避免排序)
if (maxVal > minVal) { if(maxVal > minVal) {
const int histSize = 256; const int histSize = 256;
int histogram[histSize] = {0}; int histogram[histSize] = {0};
float binWidth = (maxVal - minVal) / (float)histSize; float binWidth = (maxVal - minVal) / (float)histSize;
// 构建直方图 // 构建直方图
for (int i = 0; i < 1224 * 1024; i++) { for (int i = 0; i < 1224 * 1024; i++) {
if (src[i] > 0) { if(src[i] > 0) {
int bin = (src[i] - minVal) / binWidth; int bin = (src[i] - minVal) / binWidth;
if (bin >= histSize) bin = histSize - 1; if(bin >= histSize) bin = histSize - 1;
histogram[bin]++; histogram[bin]++;
} }
} }
@@ -1096,10 +1096,10 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
int cumsum = 0; int cumsum = 0;
for (int i = 0; i < histSize; i++) { for (int i = 0; i < histSize; i++) {
cumsum += histogram[i]; cumsum += histogram[i];
if (cumsum >= thresh_1 && minVal == 65535) { if(cumsum >= thresh_1 && minVal == 65535) {
minVal = minVal + i * binWidth; minVal = minVal + i * binWidth;
} }
if (cumsum >= thresh_99) { if(cumsum >= thresh_99) {
maxVal = minVal + i * binWidth; maxVal = minVal + i * binWidth;
break; break;
} }
@@ -1112,11 +1112,11 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
float scale = (maxVal > minVal) ? (255.0f / (maxVal - minVal)) : 0.0f; float scale = (maxVal > minVal) ? (255.0f / (maxVal - minVal)) : 0.0f;
for (int i = 0; i < 1224 * 1024; i++) { for (int i = 0; i < 1224 * 1024; i++) {
if (src[i] == 0) { if(src[i] == 0) {
dst[i] = 0; dst[i] = 0;
} else if (src[i] <= minVal) { } else if(src[i] <= minVal) {
dst[i] = 0; dst[i] = 0;
} else if (src[i] >= maxVal) { } else if(src[i] >= maxVal) {
dst[i] = 255; dst[i] = 255;
} else { } else {
dst[i] = static_cast<uint8_t>((src[i] - minVal) * scale); dst[i] = static_cast<uint8_t>((src[i] - minVal) * scale);
@@ -1127,7 +1127,7 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
// 在主线程更新UI // 在主线程更新UI
QMetaObject::invokeMethod(this, [this, imageCopy]() { QMetaObject::invokeMethod(this, [this, imageCopy]() {
if (m_leftImageDisplay) { if(m_leftImageDisplay) {
QPixmap pixmap = QPixmap::fromImage(imageCopy); QPixmap pixmap = QPixmap::fromImage(imageCopy);
m_leftImageDisplay->setPixmap(pixmap.scaled( m_leftImageDisplay->setPixmap(pixmap.scaled(
m_leftImageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation)); m_leftImageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
@@ -1147,7 +1147,7 @@ void MainWindow::onLeftImageReceived(const QByteArray &jpegData, uint32_t blockI
void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t blockId) void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t blockId)
{ {
// 检查右红外是否启用,如果禁用则忽略数据(防止关闭后闪烁) // 检查右红外是否启用,如果禁用则忽略数据(防止关闭后闪烁)
if (m_rightIREnabled.loadAcquire() == 0) { if(m_rightIREnabled.loadAcquire() == 0) {
return; return;
} }
@@ -1158,9 +1158,9 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
m_rightFrameCount++; m_rightFrameCount++;
m_totalRightFrameCount++; m_totalRightFrameCount++;
QDateTime currentTime = QDateTime::currentDateTime(); QDateTime currentTime = QDateTime::currentDateTime();
if (m_lastRightFrameTime.isValid()) { if(m_lastRightFrameTime.isValid()) {
qint64 elapsed = m_lastRightFrameTime.msecsTo(currentTime); qint64 elapsed = m_lastRightFrameTime.msecsTo(currentTime);
if (elapsed >= 1000) { if(elapsed >= 1000) {
m_currentRightFps = (m_rightFrameCount * 1000.0) / elapsed; m_currentRightFps = (m_rightFrameCount * 1000.0) / elapsed;
m_rightFrameCount = 0; m_rightFrameCount = 0;
m_lastRightFrameTime = currentTime; m_lastRightFrameTime = currentTime;
@@ -1171,12 +1171,12 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
} }
// 使用后台线程处理红外数据避免阻塞UI // 使用后台线程处理红外数据避免阻塞UI
if (m_rightImageDisplay && jpegData.size() > 0) { if(m_rightImageDisplay && jpegData.size() > 0) {
// 检查数据大小8位下采样(612x512)或16位原始(1224x1024) // 检查数据大小8位下采样(612x512)或16位原始(1224x1024)
size_t size8bit = 612 * 512; size_t size8bit = 612 * 512;
size_t size16bit = 1224 * 1024 * sizeof(uint16_t); size_t size16bit = 1224 * 1024 * sizeof(uint16_t);
if (jpegData.size() == size8bit) { if(jpegData.size() == size8bit) {
// 8位下采样格式直接显示 // 8位下采样格式直接显示
QByteArray dataCopy = jpegData; QByteArray dataCopy = jpegData;
QtConcurrent::run([this, dataCopy]() { QtConcurrent::run([this, dataCopy]() {
@@ -1186,7 +1186,7 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
QImage imageCopy = image.copy(); QImage imageCopy = image.copy();
QMetaObject::invokeMethod(this, [this, imageCopy]() { QMetaObject::invokeMethod(this, [this, imageCopy]() {
if (m_rightImageDisplay) { if(m_rightImageDisplay) {
QPixmap pixmap = QPixmap::fromImage(imageCopy); QPixmap pixmap = QPixmap::fromImage(imageCopy);
m_rightImageDisplay->setPixmap(pixmap.scaled( m_rightImageDisplay->setPixmap(pixmap.scaled(
m_rightImageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation)); m_rightImageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
@@ -1196,7 +1196,7 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
qDebug() << "[MainWindow] ERROR: Right IR 8bit processing exception:" << e.what(); qDebug() << "[MainWindow] ERROR: Right IR 8bit processing exception:" << e.what();
} }
}); });
} else if (jpegData.size() == size16bit) { } else if(jpegData.size() == size16bit) {
// 16位原始格式需要归一化处理 // 16位原始格式需要归一化处理
QByteArray dataCopy = jpegData; QByteArray dataCopy = jpegData;
@@ -1212,23 +1212,23 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
// 第一遍快速扫描找到粗略范围每隔8个像素采样 // 第一遍快速扫描找到粗略范围每隔8个像素采样
for (int i = 0; i < 1224 * 1024; i += 8) { for (int i = 0; i < 1224 * 1024; i += 8) {
uint16_t val = src[i]; uint16_t val = src[i];
if (val > 0) { if(val > 0) {
if (val < minVal) minVal = val; if(val < minVal) minVal = val;
if (val > maxVal) maxVal = val; if(val > maxVal) maxVal = val;
} }
} }
// 第二遍:使用直方图统计精确百分位数(避免排序) // 第二遍:使用直方图统计精确百分位数(避免排序)
if (maxVal > minVal) { if(maxVal > minVal) {
const int histSize = 256; const int histSize = 256;
int histogram[histSize] = {0}; int histogram[histSize] = {0};
float binWidth = (maxVal - minVal) / (float)histSize; float binWidth = (maxVal - minVal) / (float)histSize;
// 构建直方图 // 构建直方图
for (int i = 0; i < 1224 * 1024; i++) { for (int i = 0; i < 1224 * 1024; i++) {
if (src[i] > 0) { if(src[i] > 0) {
int bin = (src[i] - minVal) / binWidth; int bin = (src[i] - minVal) / binWidth;
if (bin >= histSize) bin = histSize - 1; if(bin >= histSize) bin = histSize - 1;
histogram[bin]++; histogram[bin]++;
} }
} }
@@ -1243,10 +1243,10 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
int cumsum = 0; int cumsum = 0;
for (int i = 0; i < histSize; i++) { for (int i = 0; i < histSize; i++) {
cumsum += histogram[i]; cumsum += histogram[i];
if (cumsum >= thresh_1 && minVal == 65535) { if(cumsum >= thresh_1 && minVal == 65535) {
minVal = minVal + i * binWidth; minVal = minVal + i * binWidth;
} }
if (cumsum >= thresh_99) { if(cumsum >= thresh_99) {
maxVal = minVal + i * binWidth; maxVal = minVal + i * binWidth;
break; break;
} }
@@ -1259,11 +1259,11 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
float scale = (maxVal > minVal) ? (255.0f / (maxVal - minVal)) : 0.0f; float scale = (maxVal > minVal) ? (255.0f / (maxVal - minVal)) : 0.0f;
for (int i = 0; i < 1224 * 1024; i++) { for (int i = 0; i < 1224 * 1024; i++) {
if (src[i] == 0) { if(src[i] == 0) {
dst[i] = 0; dst[i] = 0;
} else if (src[i] <= minVal) { } else if(src[i] <= minVal) {
dst[i] = 0; dst[i] = 0;
} else if (src[i] >= maxVal) { } else if(src[i] >= maxVal) {
dst[i] = 255; dst[i] = 255;
} else { } else {
dst[i] = static_cast<uint8_t>((src[i] - minVal) * scale); dst[i] = static_cast<uint8_t>((src[i] - minVal) * scale);
@@ -1274,7 +1274,7 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
// 在主线程更新UI // 在主线程更新UI
QMetaObject::invokeMethod(this, [this, imageCopy]() { QMetaObject::invokeMethod(this, [this, imageCopy]() {
if (m_rightImageDisplay) { if(m_rightImageDisplay) {
QPixmap pixmap = QPixmap::fromImage(imageCopy); QPixmap pixmap = QPixmap::fromImage(imageCopy);
m_rightImageDisplay->setPixmap(pixmap.scaled( m_rightImageDisplay->setPixmap(pixmap.scaled(
m_rightImageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation)); m_rightImageDisplay->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
@@ -1294,7 +1294,7 @@ void MainWindow::onRightImageReceived(const QByteArray &jpegData, uint32_t block
void MainWindow::onRgbImageReceived(const QByteArray &jpegData, uint32_t blockId) void MainWindow::onRgbImageReceived(const QByteArray &jpegData, uint32_t blockId)
{ {
// 检查RGB是否启用如果禁用则忽略数据防止关闭后闪烁 // 检查RGB是否启用如果禁用则忽略数据防止关闭后闪烁
if (m_rgbEnabled.loadAcquire() == 0) { if(m_rgbEnabled.loadAcquire() == 0) {
return; return;
} }
@@ -1309,9 +1309,9 @@ void MainWindow::onRgbImageReceived(const QByteArray &jpegData, uint32_t blockId
m_rgbFrameCount++; m_rgbFrameCount++;
m_totalRgbFrameCount++; m_totalRgbFrameCount++;
QDateTime currentTime = QDateTime::currentDateTime(); QDateTime currentTime = QDateTime::currentDateTime();
if (m_lastRgbFrameTime.isValid()) { if(m_lastRgbFrameTime.isValid()) {
qint64 elapsed = m_lastRgbFrameTime.msecsTo(currentTime); qint64 elapsed = m_lastRgbFrameTime.msecsTo(currentTime);
if (elapsed >= 1000) { if(elapsed >= 1000) {
m_currentRgbFps = (m_rgbFrameCount * 1000.0) / elapsed; m_currentRgbFps = (m_rgbFrameCount * 1000.0) / elapsed;
m_rgbFrameCount = 0; m_rgbFrameCount = 0;
m_lastRgbFrameTime = currentTime; m_lastRgbFrameTime = currentTime;
@@ -1324,9 +1324,9 @@ void MainWindow::onRgbImageReceived(const QByteArray &jpegData, uint32_t blockId
// 移除频繁的日志输出 // 移除频繁的日志输出
// 使用后台线程解码MJPEG避免阻塞UI // 使用后台线程解码MJPEG避免阻塞UI
if (m_rgbImageDisplay && jpegData.size() > 0) { if(m_rgbImageDisplay && jpegData.size() > 0) {
// 智能帧丢弃:如果上一帧还在处理,跳过当前帧(避免积压旧帧) // 智能帧丢弃:如果上一帧还在处理,跳过当前帧(避免积压旧帧)
if (m_rgbProcessing.loadAcquire() > 0) { if(m_rgbProcessing.loadAcquire() > 0) {
return; // 跳过当前帧,优先处理最新帧 return; // 跳过当前帧,优先处理最新帧
} }
@@ -1343,7 +1343,7 @@ void MainWindow::onRgbImageReceived(const QByteArray &jpegData, uint32_t blockId
std::vector<uchar> buffer(dataCopy.begin(), dataCopy.end()); std::vector<uchar> buffer(dataCopy.begin(), dataCopy.end());
cv::Mat cvImage = cv::imdecode(buffer, cv::IMREAD_COLOR); cv::Mat cvImage = cv::imdecode(buffer, cv::IMREAD_COLOR);
if (!cvImage.empty()) { if(!cvImage.empty()) {
// 缩放到1/2大小 // 缩放到1/2大小
cv::Mat resized; cv::Mat resized;
cv::resize(cvImage, resized, cv::Size(), 0.5, 0.5, cv::INTER_LINEAR); cv::resize(cvImage, resized, cv::Size(), 0.5, 0.5, cv::INTER_LINEAR);
@@ -1367,7 +1367,7 @@ void MainWindow::onRgbImageReceived(const QByteArray &jpegData, uint32_t blockId
// 使用DirectConnection直接在主线程更新UI更快避免队列积压 // 使用DirectConnection直接在主线程更新UI更快避免队列积压
QMetaObject::invokeMethod(this, [this, pixmap]() { QMetaObject::invokeMethod(this, [this, pixmap]() {
if (m_rgbImageDisplay) { if(m_rgbImageDisplay) {
m_rgbImageDisplay->setPixmap(pixmap); m_rgbImageDisplay->setPixmap(pixmap);
} }
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
@@ -1414,9 +1414,9 @@ void MainWindow::onPointCloudReady(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, ui
m_pointCloudFrameCount++; m_pointCloudFrameCount++;
m_totalPointCloudFrameCount++; m_totalPointCloudFrameCount++;
QDateTime currentTime = QDateTime::currentDateTime(); QDateTime currentTime = QDateTime::currentDateTime();
if (m_lastPointCloudFrameTime.isValid()) { if(m_lastPointCloudFrameTime.isValid()) {
qint64 elapsed = m_lastPointCloudFrameTime.msecsTo(currentTime); qint64 elapsed = m_lastPointCloudFrameTime.msecsTo(currentTime);
if (elapsed >= 1000) { // 每秒更新一次FPS if(elapsed >= 1000) { // 每秒更新一次FPS
m_currentPointCloudFps = (m_pointCloudFrameCount * 1000.0) / elapsed; m_currentPointCloudFps = (m_pointCloudFrameCount * 1000.0) / elapsed;
m_pointCloudFrameCount = 0; m_pointCloudFrameCount = 0;
m_lastPointCloudFrameTime = currentTime; m_lastPointCloudFrameTime = currentTime;
@@ -1427,7 +1427,7 @@ void MainWindow::onPointCloudReady(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud, ui
} }
// 更新点云显示 // 更新点云显示
if (m_pointCloudWidget) { if(m_pointCloudWidget) {
m_pointCloudWidget->updatePointCloud(cloud); m_pointCloudWidget->updatePointCloud(cloud);
} }
} }
@@ -1438,7 +1438,7 @@ void MainWindow::onCaptureClicked()
qDebug() << "拍照按钮点击"; qDebug() << "拍照按钮点击";
// 检查是否有可用数据 // 检查是否有可用数据
if (m_currentImage.isNull() && (!m_currentPointCloud || m_currentPointCloud->empty())) { if(m_currentImage.isNull() && (!m_currentPointCloud || m_currentPointCloud->empty())) {
QMessageBox::warning(this, "拍照失败", "没有可用的图像或点云数据。\n请先启动相机采集。"); QMessageBox::warning(this, "拍照失败", "没有可用的图像或点云数据。\n请先启动相机采集。");
addLog("拍照失败:没有可用数据", "ERROR"); addLog("拍照失败:没有可用数据", "ERROR");
return; return;
@@ -1446,7 +1446,7 @@ void MainWindow::onCaptureClicked()
// 获取保存路径 // 获取保存路径
QString saveDir = m_savePathEdit->text().trimmed(); QString saveDir = m_savePathEdit->text().trimmed();
if (saveDir.isEmpty()) { if(saveDir.isEmpty()) {
// 使用默认路径:用户图片文件夹 // 使用默认路径:用户图片文件夹
saveDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation); saveDir = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
qDebug() << "使用默认保存路径:" << saveDir; qDebug() << "使用默认保存路径:" << saveDir;
@@ -1454,8 +1454,8 @@ void MainWindow::onCaptureClicked()
// 检查目录是否存在,不存在则创建 // 检查目录是否存在,不存在则创建
QDir dir(saveDir); QDir dir(saveDir);
if (!dir.exists()) { if(!dir.exists()) {
if (!dir.mkpath(".")) { if(!dir.mkpath(".")) {
QMessageBox::warning(this, "拍照失败", QString("无法创建保存目录:\n%1").arg(saveDir)); QMessageBox::warning(this, "拍照失败", QString("无法创建保存目录:\n%1").arg(saveDir));
return; return;
} }
@@ -1497,7 +1497,7 @@ void MainWindow::onBrowseSavePathClicked()
? QStandardPaths::writableLocation(QStandardPaths::PicturesLocation) ? QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)
: m_savePathEdit->text(), : m_savePathEdit->text(),
QFileDialog::ShowDirsOnly); QFileDialog::ShowDirsOnly);
if (!dir.isEmpty()) { if(!dir.isEmpty()) {
m_savePathEdit->setText(dir); m_savePathEdit->setText(dir);
qDebug() << "设置保存路径:" << dir; qDebug() << "设置保存路径:" << dir;
} }
@@ -1515,14 +1515,14 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
bool cloudSuccess = false; bool cloudSuccess = false;
// 保存深度图 // 保存深度图
if (!image.isNull()) { if(!image.isNull()) {
try { try {
// 转换QImage到OpenCV Mat // 转换QImage到OpenCV Mat
cv::Mat mat; cv::Mat mat;
if (image.format() == QImage::Format_Grayscale8) { if(image.format() == QImage::Format_Grayscale8) {
mat = cv::Mat(image.height(), image.width(), CV_8UC1, mat = cv::Mat(image.height(), image.width(), CV_8UC1,
const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone(); const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone();
} else if (image.format() == QImage::Format_Grayscale16) { } else if(image.format() == QImage::Format_Grayscale16) {
mat = cv::Mat(image.height(), image.width(), CV_16UC1, mat = cv::Mat(image.height(), image.width(), CV_16UC1,
const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone(); const_cast<uchar*>(image.bits()), image.bytesPerLine()).clone();
} else { } else {
@@ -1533,10 +1533,10 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
} }
// 根据format参数保存 // 根据format参数保存
if (depthFormat == "png" || depthFormat == "both") { if(depthFormat == "png" || depthFormat == "both") {
QString pngPath = QString("%1/%2_depth.png").arg(saveDir).arg(baseName); QString pngPath = QString("%1/%2_depth.png").arg(saveDir).arg(baseName);
cv::Mat mat8bit; cv::Mat mat8bit;
if (mat.type() == CV_16UC1) { if(mat.type() == CV_16UC1) {
mat.convertTo(mat8bit, CV_8UC1, 255.0 / 65535.0); mat.convertTo(mat8bit, CV_8UC1, 255.0 / 65535.0);
} else { } else {
mat8bit = mat; mat8bit = mat;
@@ -1546,9 +1546,9 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
depthSuccess = true; depthSuccess = true;
} }
if (depthFormat == "tiff" || depthFormat == "both") { if(depthFormat == "tiff" || depthFormat == "both") {
QString tiffPath = QString("%1/%2_depth.tiff").arg(saveDir).arg(baseName); QString tiffPath = QString("%1/%2_depth.tiff").arg(saveDir).arg(baseName);
if (mat.type() == CV_16UC1) { if(mat.type() == CV_16UC1) {
cv::imwrite(tiffPath.toStdString(), mat); cv::imwrite(tiffPath.toStdString(), mat);
qDebug() << "保存TIFF深度图(16位):" << tiffPath; qDebug() << "保存TIFF深度图(16位):" << tiffPath;
} else { } else {
@@ -1565,11 +1565,11 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
} }
// 保存点云 // 保存点云
if (cloud && !cloud->empty()) { if(cloud && !cloud->empty()) {
try { try {
if (pointCloudFormat == "pcd" || pointCloudFormat == "both") { if(pointCloudFormat == "pcd" || pointCloudFormat == "both") {
QString pcdPath = QString("%1/%2_pointcloud.pcd").arg(saveDir).arg(baseName); QString pcdPath = QString("%1/%2_pointcloud.pcd").arg(saveDir).arg(baseName);
if (pcl::io::savePCDFileBinary(pcdPath.toStdString(), *cloud) == 0) { if(pcl::io::savePCDFileBinary(pcdPath.toStdString(), *cloud) == 0) {
qDebug() << "保存PCD点云:" << pcdPath; qDebug() << "保存PCD点云:" << pcdPath;
cloudSuccess = true; cloudSuccess = true;
} else { } else {
@@ -1577,9 +1577,9 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
} }
} }
if (pointCloudFormat == "ply" || pointCloudFormat == "both") { if(pointCloudFormat == "ply" || pointCloudFormat == "both") {
QString plyPath = QString("%1/%2_pointcloud.ply").arg(saveDir).arg(baseName); QString plyPath = QString("%1/%2_pointcloud.ply").arg(saveDir).arg(baseName);
if (pcl::io::savePLYFileASCII(plyPath.toStdString(), *cloud) == 0) { if(pcl::io::savePLYFileASCII(plyPath.toStdString(), *cloud) == 0) {
qDebug() << "保存PLY点云:" << plyPath; qDebug() << "保存PLY点云:" << plyPath;
cloudSuccess = true; cloudSuccess = true;
} else { } else {
@@ -1597,17 +1597,17 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
bool rgbSuccess = false; bool rgbSuccess = false;
// 保存左红外图像支持16位原始1224×1024或8位下采样612×512 // 保存左红外图像支持16位原始1224×1024或8位下采样612×512
if (!leftIRData.isEmpty()) { if(!leftIRData.isEmpty()) {
try { try {
size_t size16bit = 1224 * 1024 * sizeof(uint16_t); size_t size16bit = 1224 * 1024 * sizeof(uint16_t);
size_t size8bit = 612 * 512; size_t size8bit = 612 * 512;
if (leftIRData.size() == size16bit) { if(leftIRData.size() == size16bit) {
const uint16_t* src = reinterpret_cast<const uint16_t*>(leftIRData.constData()); const uint16_t* src = reinterpret_cast<const uint16_t*>(leftIRData.constData());
cv::Mat leftIR16(1024, 1224, CV_16UC1); cv::Mat leftIR16(1024, 1224, CV_16UC1);
memcpy(leftIR16.data, src, size16bit); memcpy(leftIR16.data, src, size16bit);
if (depthFormat == "png" || depthFormat == "both") { if(depthFormat == "png" || depthFormat == "both") {
QString pngPath = QString("%1/%2_left_ir.png").arg(saveDir).arg(baseName); QString pngPath = QString("%1/%2_left_ir.png").arg(saveDir).arg(baseName);
cv::Mat leftIR8; cv::Mat leftIR8;
leftIR16.convertTo(leftIR8, CV_8UC1, 255.0 / 65535.0); leftIR16.convertTo(leftIR8, CV_8UC1, 255.0 / 65535.0);
@@ -1615,23 +1615,23 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
qDebug() << "保存左红外PNG图像(16bit):" << pngPath; qDebug() << "保存左红外PNG图像(16bit):" << pngPath;
leftIRSuccess = true; leftIRSuccess = true;
} }
if (depthFormat == "tiff" || depthFormat == "both") { if(depthFormat == "tiff" || depthFormat == "both") {
QString tiffPath = QString("%1/%2_left_ir.tiff").arg(saveDir).arg(baseName); QString tiffPath = QString("%1/%2_left_ir.tiff").arg(saveDir).arg(baseName);
cv::imwrite(tiffPath.toStdString(), leftIR16); cv::imwrite(tiffPath.toStdString(), leftIR16);
qDebug() << "保存左红外TIFF图像(16位):" << tiffPath; qDebug() << "保存左红外TIFF图像(16位):" << tiffPath;
leftIRSuccess = true; leftIRSuccess = true;
} }
} else if (leftIRData.size() == size8bit) { } else if(leftIRData.size() == size8bit) {
cv::Mat leftIR8(512, 612, CV_8UC1, const_cast<char*>(leftIRData.constData())); cv::Mat leftIR8(512, 612, CV_8UC1, const_cast<char*>(leftIRData.constData()));
cv::Mat leftIR8Clone = leftIR8.clone(); cv::Mat leftIR8Clone = leftIR8.clone();
if (depthFormat == "png" || depthFormat == "both") { if(depthFormat == "png" || depthFormat == "both") {
QString pngPath = QString("%1/%2_left_ir.png").arg(saveDir).arg(baseName); QString pngPath = QString("%1/%2_left_ir.png").arg(saveDir).arg(baseName);
cv::imwrite(pngPath.toStdString(), leftIR8Clone); cv::imwrite(pngPath.toStdString(), leftIR8Clone);
qDebug() << "保存左红外PNG图像(8bit下采样):" << pngPath; qDebug() << "保存左红外PNG图像(8bit下采样):" << pngPath;
leftIRSuccess = true; leftIRSuccess = true;
} }
if (depthFormat == "tiff" || depthFormat == "both") { if(depthFormat == "tiff" || depthFormat == "both") {
QString tiffPath = QString("%1/%2_left_ir.tiff").arg(saveDir).arg(baseName); QString tiffPath = QString("%1/%2_left_ir.tiff").arg(saveDir).arg(baseName);
cv::imwrite(tiffPath.toStdString(), leftIR8Clone); cv::imwrite(tiffPath.toStdString(), leftIR8Clone);
qDebug() << "保存左红外TIFF图像(8bit下采样):" << tiffPath; qDebug() << "保存左红外TIFF图像(8bit下采样):" << tiffPath;
@@ -1647,17 +1647,17 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
} }
// 保存右红外图像支持16位原始1224×1024或8位下采样612×512 // 保存右红外图像支持16位原始1224×1024或8位下采样612×512
if (!rightIRData.isEmpty()) { if(!rightIRData.isEmpty()) {
try { try {
size_t size16bit = 1224 * 1024 * sizeof(uint16_t); size_t size16bit = 1224 * 1024 * sizeof(uint16_t);
size_t size8bit = 612 * 512; size_t size8bit = 612 * 512;
if (rightIRData.size() == size16bit) { if(rightIRData.size() == size16bit) {
const uint16_t* src = reinterpret_cast<const uint16_t*>(rightIRData.constData()); const uint16_t* src = reinterpret_cast<const uint16_t*>(rightIRData.constData());
cv::Mat rightIR16(1024, 1224, CV_16UC1); cv::Mat rightIR16(1024, 1224, CV_16UC1);
memcpy(rightIR16.data, src, size16bit); memcpy(rightIR16.data, src, size16bit);
if (depthFormat == "png" || depthFormat == "both") { if(depthFormat == "png" || depthFormat == "both") {
QString pngPath = QString("%1/%2_right_ir.png").arg(saveDir).arg(baseName); QString pngPath = QString("%1/%2_right_ir.png").arg(saveDir).arg(baseName);
cv::Mat rightIR8; cv::Mat rightIR8;
rightIR16.convertTo(rightIR8, CV_8UC1, 255.0 / 65535.0); rightIR16.convertTo(rightIR8, CV_8UC1, 255.0 / 65535.0);
@@ -1665,23 +1665,23 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
qDebug() << "保存右红外PNG图像(16bit):" << pngPath; qDebug() << "保存右红外PNG图像(16bit):" << pngPath;
rightIRSuccess = true; rightIRSuccess = true;
} }
if (depthFormat == "tiff" || depthFormat == "both") { if(depthFormat == "tiff" || depthFormat == "both") {
QString tiffPath = QString("%1/%2_right_ir.tiff").arg(saveDir).arg(baseName); QString tiffPath = QString("%1/%2_right_ir.tiff").arg(saveDir).arg(baseName);
cv::imwrite(tiffPath.toStdString(), rightIR16); cv::imwrite(tiffPath.toStdString(), rightIR16);
qDebug() << "保存右红外TIFF图像(16位):" << tiffPath; qDebug() << "保存右红外TIFF图像(16位):" << tiffPath;
rightIRSuccess = true; rightIRSuccess = true;
} }
} else if (rightIRData.size() == size8bit) { } else if(rightIRData.size() == size8bit) {
cv::Mat rightIR8(512, 612, CV_8UC1, const_cast<char*>(rightIRData.constData())); cv::Mat rightIR8(512, 612, CV_8UC1, const_cast<char*>(rightIRData.constData()));
cv::Mat rightIR8Clone = rightIR8.clone(); cv::Mat rightIR8Clone = rightIR8.clone();
if (depthFormat == "png" || depthFormat == "both") { if(depthFormat == "png" || depthFormat == "both") {
QString pngPath = QString("%1/%2_right_ir.png").arg(saveDir).arg(baseName); QString pngPath = QString("%1/%2_right_ir.png").arg(saveDir).arg(baseName);
cv::imwrite(pngPath.toStdString(), rightIR8Clone); cv::imwrite(pngPath.toStdString(), rightIR8Clone);
qDebug() << "保存右红外PNG图像(8bit下采样):" << pngPath; qDebug() << "保存右红外PNG图像(8bit下采样):" << pngPath;
rightIRSuccess = true; rightIRSuccess = true;
} }
if (depthFormat == "tiff" || depthFormat == "both") { if(depthFormat == "tiff" || depthFormat == "both") {
QString tiffPath = QString("%1/%2_right_ir.tiff").arg(saveDir).arg(baseName); QString tiffPath = QString("%1/%2_right_ir.tiff").arg(saveDir).arg(baseName);
cv::imwrite(tiffPath.toStdString(), rightIR8Clone); cv::imwrite(tiffPath.toStdString(), rightIR8Clone);
qDebug() << "保存右红外TIFF图像(8bit下采样):" << tiffPath; qDebug() << "保存右红外TIFF图像(8bit下采样):" << tiffPath;
@@ -1697,12 +1697,12 @@ void MainWindow::performBackgroundSave(const QString &saveDir, const QString &ba
} }
// 保存RGB图像MJPEG原始数据完整分辨率1920×1080 // 保存RGB图像MJPEG原始数据完整分辨率1920×1080
if (!rgbData.isEmpty()) { if(!rgbData.isEmpty()) {
try { try {
// 直接保存JPEG文件无需解码保持原始质量 // 直接保存JPEG文件无需解码保持原始质量
QString rgbPath = QString("%1/%2_rgb.jpg").arg(saveDir).arg(baseName); QString rgbPath = QString("%1/%2_rgb.jpg").arg(saveDir).arg(baseName);
QFile file(rgbPath); QFile file(rgbPath);
if (file.open(QIODevice::WriteOnly)) { if(file.open(QIODevice::WriteOnly)) {
file.write(rgbData); file.write(rgbData);
file.close(); file.close();
qDebug() << "保存RGB图像(JPEG):" << rgbPath; qDebug() << "保存RGB图像(JPEG):" << rgbPath;
@@ -1727,10 +1727,10 @@ bool MainWindow::saveDepthImage(const QString &dir, const QString &baseName, con
try { try {
// 转换QImage到OpenCV Mat // 转换QImage到OpenCV Mat
cv::Mat mat; cv::Mat mat;
if (m_currentImage.format() == QImage::Format_Grayscale8) { if(m_currentImage.format() == QImage::Format_Grayscale8) {
mat = cv::Mat(m_currentImage.height(), m_currentImage.width(), CV_8UC1, mat = cv::Mat(m_currentImage.height(), m_currentImage.width(), CV_8UC1,
const_cast<uchar*>(m_currentImage.bits()), m_currentImage.bytesPerLine()).clone(); const_cast<uchar*>(m_currentImage.bits()), m_currentImage.bytesPerLine()).clone();
} else if (m_currentImage.format() == QImage::Format_Grayscale16) { } else if(m_currentImage.format() == QImage::Format_Grayscale16) {
mat = cv::Mat(m_currentImage.height(), m_currentImage.width(), CV_16UC1, mat = cv::Mat(m_currentImage.height(), m_currentImage.width(), CV_16UC1,
const_cast<uchar*>(m_currentImage.bits()), m_currentImage.bytesPerLine()).clone(); const_cast<uchar*>(m_currentImage.bits()), m_currentImage.bytesPerLine()).clone();
} else { } else {
@@ -1741,11 +1741,11 @@ bool MainWindow::saveDepthImage(const QString &dir, const QString &baseName, con
} }
// 根据format参数保存 // 根据format参数保存
if (format == "png" || format == "both") { if(format == "png" || format == "both") {
// 保存PNG格式8位 // 保存PNG格式8位
QString pngPath = QString("%1/%2_depth.png").arg(dir).arg(baseName); QString pngPath = QString("%1/%2_depth.png").arg(dir).arg(baseName);
cv::Mat mat8bit; cv::Mat mat8bit;
if (mat.type() == CV_16UC1) { if(mat.type() == CV_16UC1) {
mat.convertTo(mat8bit, CV_8UC1, 255.0 / 65535.0); mat.convertTo(mat8bit, CV_8UC1, 255.0 / 65535.0);
} else { } else {
mat8bit = mat; mat8bit = mat;
@@ -1754,10 +1754,10 @@ bool MainWindow::saveDepthImage(const QString &dir, const QString &baseName, con
qDebug() << "保存PNG深度图:" << pngPath; qDebug() << "保存PNG深度图:" << pngPath;
} }
if (format == "tiff" || format == "both") { if(format == "tiff" || format == "both") {
// 保存TIFF格式16位原始数据 // 保存TIFF格式16位原始数据
QString tiffPath = QString("%1/%2_depth.tiff").arg(dir).arg(baseName); QString tiffPath = QString("%1/%2_depth.tiff").arg(dir).arg(baseName);
if (mat.type() == CV_16UC1) { if(mat.type() == CV_16UC1) {
cv::imwrite(tiffPath.toStdString(), mat); cv::imwrite(tiffPath.toStdString(), mat);
qDebug() << "保存TIFF深度图(16位):" << tiffPath; qDebug() << "保存TIFF深度图(16位):" << tiffPath;
} else { } else {
@@ -1779,7 +1779,7 @@ bool MainWindow::saveDepthImage(const QString &dir, const QString &baseName, con
bool MainWindow::savePointCloud(const QString &dir, const QString &baseName, const QString &format) bool MainWindow::savePointCloud(const QString &dir, const QString &baseName, const QString &format)
{ {
try { try {
if (!m_currentPointCloud || m_currentPointCloud->empty()) { if(!m_currentPointCloud || m_currentPointCloud->empty()) {
qDebug() << "点云数据为空"; qDebug() << "点云数据为空";
return false; return false;
} }
@@ -1787,10 +1787,10 @@ bool MainWindow::savePointCloud(const QString &dir, const QString &baseName, con
bool success = false; bool success = false;
// 根据format参数保存 // 根据format参数保存
if (format == "pcd" || format == "both") { if(format == "pcd" || format == "both") {
// 保存PCD格式PCL标准格式二进制 // 保存PCD格式PCL标准格式二进制
QString pcdPath = QString("%1/%2_pointcloud.pcd").arg(dir).arg(baseName); QString pcdPath = QString("%1/%2_pointcloud.pcd").arg(dir).arg(baseName);
if (pcl::io::savePCDFileBinary(pcdPath.toStdString(), *m_currentPointCloud) == 0) { if(pcl::io::savePCDFileBinary(pcdPath.toStdString(), *m_currentPointCloud) == 0) {
qDebug() << "保存PCD点云:" << pcdPath; qDebug() << "保存PCD点云:" << pcdPath;
success = true; success = true;
} else { } else {
@@ -1798,10 +1798,10 @@ bool MainWindow::savePointCloud(const QString &dir, const QString &baseName, con
} }
} }
if (format == "ply" || format == "both") { if(format == "ply" || format == "both") {
// 保存PLY格式Polygon格式ASCII // 保存PLY格式Polygon格式ASCII
QString plyPath = QString("%1/%2_pointcloud.ply").arg(dir).arg(baseName); QString plyPath = QString("%1/%2_pointcloud.ply").arg(dir).arg(baseName);
if (pcl::io::savePLYFileASCII(plyPath.toStdString(), *m_currentPointCloud) == 0) { if(pcl::io::savePLYFileASCII(plyPath.toStdString(), *m_currentPointCloud) == 0) {
qDebug() << "保存PLY点云:" << plyPath; qDebug() << "保存PLY点云:" << plyPath;
success = true; success = true;
} else { } else {
@@ -1819,18 +1819,18 @@ bool MainWindow::savePointCloud(const QString &dir, const QString &baseName, con
// ========== 日志功能 ========== // ========== 日志功能 ==========
void MainWindow::addLog(const QString &message, const QString &level) void MainWindow::addLog(const QString &message, const QString &level)
{ {
if (!m_logDisplay) return; if(!m_logDisplay) return;
// 获取当前时间戳 // 获取当前时间戳
QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz"); QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz");
// 根据日志级别设置颜色 // 根据日志级别设置颜色
QString color; QString color;
if (level == "ERROR") { if(level == "ERROR") {
color = "#FF6B6B"; // 红色 color = "#FF6B6B"; // 红色
} else if (level == "WARNING") { } else if(level == "WARNING") {
color = "#FFA500"; // 橙色 color = "#FFA500"; // 橙色
} else if (level == "SUCCESS") { } else if(level == "SUCCESS") {
color = "#4CAF50"; // 绿色 color = "#4CAF50"; // 绿色
} else { } else {
color = "#D4D4D4"; // 默认灰白色 color = "#D4D4D4"; // 默认灰白色
@@ -1851,7 +1851,7 @@ void MainWindow::addLog(const QString &message, const QString &level)
// 限制日志行数保留最近1000行 // 限制日志行数保留最近1000行
QTextDocument *doc = m_logDisplay->document(); QTextDocument *doc = m_logDisplay->document();
if (doc->blockCount() > 1000) { if(doc->blockCount() > 1000) {
QTextCursor cursor = m_logDisplay->textCursor(); QTextCursor cursor = m_logDisplay->textCursor();
cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::Start);
cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, doc->blockCount() - 1000); cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, doc->blockCount() - 1000);
@@ -1864,7 +1864,7 @@ void MainWindow::addLog(const QString &message, const QString &level)
void MainWindow::onClearLogClicked() void MainWindow::onClearLogClicked()
{ {
if (m_logDisplay) { if(m_logDisplay) {
m_logDisplay->clear(); m_logDisplay->clear();
addLog("日志已清除", "INFO"); addLog("日志已清除", "INFO");
} }
@@ -1873,11 +1873,11 @@ void MainWindow::onClearLogClicked()
void MainWindow::onSaveLogClicked() void MainWindow::onSaveLogClicked()
{ {
QString fileName = QFileDialog::getSaveFileName(this, "保存日志", QString fileName = QFileDialog::getSaveFileName(this, "保存日志",
QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/d330viewer_log.txt", QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/viewer_log.txt",
"文本文件 (*.txt);;所有文件 (*.*)"); "文本文件 (*.txt);;所有文件 (*.*)");
if (!fileName.isEmpty()) { if(!fileName.isEmpty()) {
QFile file(fileName); QFile file(fileName);
if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { if(file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&file); QTextStream out(&file);
out << m_logDisplay->toPlainText(); out << m_logDisplay->toPlainText();
file.close(); file.close();
@@ -1892,23 +1892,23 @@ void MainWindow::onSaveLogClicked()
void MainWindow::updateStatistics() void MainWindow::updateStatistics()
{ {
// 更新三个相机的FPS // 更新三个相机的FPS
if (m_leftFpsLabel) { if(m_leftFpsLabel) {
m_leftFpsLabel->setText(QString("左红外: %1 fps").arg(m_currentLeftFps, 0, 'f', 1)); m_leftFpsLabel->setText(QString("左红外: %1 fps").arg(m_currentLeftFps, 0, 'f', 1));
} }
if (m_rightFpsLabel) { if(m_rightFpsLabel) {
m_rightFpsLabel->setText(QString("右红外: %1 fps").arg(m_currentRightFps, 0, 'f', 1)); m_rightFpsLabel->setText(QString("右红外: %1 fps").arg(m_currentRightFps, 0, 'f', 1));
} }
if (m_rgbFpsLabel) { if(m_rgbFpsLabel) {
m_rgbFpsLabel->setText(QString("RGB彩色: %1 fps").arg(m_currentRgbFps, 0, 'f', 1)); m_rgbFpsLabel->setText(QString("RGB彩色: %1 fps").arg(m_currentRgbFps, 0, 'f', 1));
} }
// 更新点云帧率 // 更新点云帧率
if (m_pointCloudFpsLabel) { if(m_pointCloudFpsLabel) {
m_pointCloudFpsLabel->setText(QString("点云帧率: %1 fps").arg(m_currentPointCloudFps, 0, 'f', 1)); m_pointCloudFpsLabel->setText(QString("点云帧率: %1 fps").arg(m_currentPointCloudFps, 0, 'f', 1));
} }
// 更新接收帧数(显示三个相机和点云的累计总数) // 更新接收帧数(显示三个相机和点云的累计总数)
if (m_queueLabel) { if(m_queueLabel) {
m_queueLabel->setText(QString("接收帧数: L%1 R%2 RGB%3 点云%4") m_queueLabel->setText(QString("接收帧数: L%1 R%2 RGB%3 点云%4")
.arg(m_totalLeftFrameCount) .arg(m_totalLeftFrameCount)
.arg(m_totalRightFrameCount) .arg(m_totalRightFrameCount)
@@ -1920,7 +1920,7 @@ void MainWindow::updateStatistics()
// 监听系统主题变化事件 // 监听系统主题变化事件
void MainWindow::changeEvent(QEvent *event) void MainWindow::changeEvent(QEvent *event)
{ {
if (event->type() == QEvent::PaletteChange) { if(event->type() == QEvent::PaletteChange) {
// 系统调色板变化,说明主题可能已切换 // 系统调色板变化,说明主题可能已切换
qDebug() << "系统主题已变化刷新UI样式"; qDebug() << "系统主题已变化刷新UI样式";

View File

@@ -31,19 +31,19 @@ int main(int argc, char *argv[])
QApplication app(argc, argv); QApplication app(argc, argv);
// 设置应用程序信息 // 设置应用程序信息
app.setOrganizationName("D330Viewer"); app.setOrganizationName("Viewer");
app.setApplicationName("D330Viewer"); app.setApplicationName("Viewer");
app.setApplicationVersion("0.2.0"); app.setApplicationVersion("0.3.0");
// 初始化Logger在可执行文件同目录下 // 初始化Logger在可执行文件同目录下
QString logPath = QCoreApplication::applicationDirPath() + "/d330viewer.log"; QString logPath = QCoreApplication::applicationDirPath() + "/viewer.log";
Logger::instance()->setLogFile(logPath); Logger::instance()->setLogFile(logPath);
Logger::instance()->setMaxLines(10000); // 保留最新10000行 Logger::instance()->setMaxLines(10000); // 保留最新10000行
// 安装消息处理器 // 安装消息处理器
qInstallMessageHandler(messageHandler); qInstallMessageHandler(messageHandler);
qDebug() << "D330Viewer started"; qDebug() << "Viewer started";
qDebug() << "Log file:" << logPath; qDebug() << "Log file:" << logPath;
// 创建并显示主窗口 // 创建并显示主窗口
@@ -52,7 +52,7 @@ int main(int argc, char *argv[])
int result = app.exec(); int result = app.exec();
qDebug() << "D330Viewer exiting"; qDebug() << "Viewer exiting";
return result; return result;
} }