feat(点云处理): 添加基于Z阈值的点云旋转和保存功能
This commit is contained in:
parent
598cd176f5
commit
c088ad39c4
@ -6,6 +6,17 @@
|
|||||||
#include "VZNL_Types.h"
|
#include "VZNL_Types.h"
|
||||||
#include "VrError.h"
|
#include "VrError.h"
|
||||||
|
|
||||||
|
// 保存进度回调接口
|
||||||
|
class ISaveProgressCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ISaveProgressCallback() = default;
|
||||||
|
// 进度回调,progress 范围 0.0~1.0
|
||||||
|
virtual void onSaveProgress(float progress) = 0;
|
||||||
|
// 返回 true 取消保存
|
||||||
|
virtual bool isSaveCancelled() const { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
// 激光数据加载器类
|
// 激光数据加载器类
|
||||||
class LaserDataLoader
|
class LaserDataLoader
|
||||||
{
|
{
|
||||||
@ -30,6 +41,8 @@ public:
|
|||||||
int clockPerSecond);
|
int clockPerSecond);
|
||||||
|
|
||||||
int DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData);
|
int DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData);
|
||||||
|
int DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData,
|
||||||
|
ISaveProgressCallback* callback);
|
||||||
|
|
||||||
// 释放统一格式数据内存
|
// 释放统一格式数据内存
|
||||||
void FreeLaserScanData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines);
|
void FreeLaserScanData(std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>& laserLines);
|
||||||
|
|||||||
@ -246,6 +246,12 @@ int LaserDataLoader::SaveLaserScanData(const std::string& fileName,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition> > xyzData)
|
int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition> > xyzData)
|
||||||
|
{
|
||||||
|
return DebugSaveLaser(std::move(fileName), std::move(xyzData), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vector<SVzNL3DPosition>> xyzData,
|
||||||
|
ISaveProgressCallback* callback)
|
||||||
{
|
{
|
||||||
LOG_INFO("Saving unified laser scan data to file: %s\n", fileName.c_str());
|
LOG_INFO("Saving unified laser scan data to file: %s\n", fileName.c_str());
|
||||||
|
|
||||||
@ -270,10 +276,18 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
|
|||||||
sw << "PointAdjust: 1" << std::endl;
|
sw << "PointAdjust: 1" << std::endl;
|
||||||
sw << "MaxTimeStamp:" << 0 << "_" << 0 << std::endl;
|
sw << "MaxTimeStamp:" << 0 << "_" << 0 << std::endl;
|
||||||
|
|
||||||
|
const size_t totalLines = xyzData.size();
|
||||||
int index = 0;
|
int index = 0;
|
||||||
// 写入每条扫描线数据
|
// 写入每条扫描线数据
|
||||||
for (const auto& linePair : xyzData) {
|
for (const auto& linePair : xyzData) {
|
||||||
sw << "Line_" << index++ << "_" << 0 << "_" << linePair.size() << std::endl;
|
if (callback && callback->isSaveCancelled()) {
|
||||||
|
sw.close();
|
||||||
|
m_lastError = "Save cancelled by user";
|
||||||
|
LOG_INFO("Save cancelled by user at line %d/%zu\n", index, totalLines);
|
||||||
|
return ERR_CODE(DEV_ARG_INVAILD);
|
||||||
|
}
|
||||||
|
|
||||||
|
sw << "Line_" << index << "_" << 0 << "_" << linePair.size() << std::endl;
|
||||||
// 根据数据类型写入点云数据
|
// 根据数据类型写入点云数据
|
||||||
for(const auto& point : linePair){
|
for(const auto& point : linePair){
|
||||||
|
|
||||||
@ -287,6 +301,11 @@ int LaserDataLoader::DebugSaveLaser(std::string fileName, std::vector<std::vecto
|
|||||||
<< std::fixed << std::setprecision(6) << z << " } - ";
|
<< std::fixed << std::setprecision(6) << z << " } - ";
|
||||||
sw << "{ 0, 0 } - { 0, 0 }" << std::endl;
|
sw << "{ 0, 0 } - { 0, 0 }" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++index;
|
||||||
|
if (callback) {
|
||||||
|
callback->onSaveProgress(static_cast<float>(index) / static_cast<float>(totalLines));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sw.close();
|
sw.close();
|
||||||
|
|||||||
@ -162,11 +162,16 @@ private slots:
|
|||||||
* @brief 应用矩阵变换到所有点云
|
* @brief 应用矩阵变换到所有点云
|
||||||
*/
|
*/
|
||||||
void onApplyMatrix();
|
void onApplyMatrix();
|
||||||
|
void onApplyEyeInHandTransform();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 重置矩阵为单位矩阵
|
* @brief 重置矩阵为单位矩阵
|
||||||
*/
|
*/
|
||||||
void onResetMatrix();
|
void onResetMatrix();
|
||||||
|
void onGeneratePointCloud();
|
||||||
|
void onRotateCloudByZ();
|
||||||
|
void onSavePointCloud();
|
||||||
|
void onConvertEulerMatrix();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
@ -256,6 +261,8 @@ private:
|
|||||||
* @return 是否成功加载到包围盒数据
|
* @return 是否成功加载到包围盒数据
|
||||||
*/
|
*/
|
||||||
bool loadBoundingBoxFile(const QString& fileName);
|
bool loadBoundingBoxFile(const QString& fileName);
|
||||||
|
void applyTransformToAllClouds(const QMatrix4x4& matrix);
|
||||||
|
void addGeneratedCloud(const PointCloudXYZ& cloud, const QString& name);
|
||||||
|
|
||||||
// 点云显示控件
|
// 点云显示控件
|
||||||
PointCloudGLWidget* m_glWidget;
|
PointCloudGLWidget* m_glWidget;
|
||||||
@ -351,6 +358,13 @@ private:
|
|||||||
QTextEdit* m_matrixEdit;
|
QTextEdit* m_matrixEdit;
|
||||||
QPushButton* m_btnLoadMatrix;
|
QPushButton* m_btnLoadMatrix;
|
||||||
QPushButton* m_btnApplyMatrix;
|
QPushButton* m_btnApplyMatrix;
|
||||||
|
QLineEdit* m_editRobotX;
|
||||||
|
QLineEdit* m_editRobotY;
|
||||||
|
QLineEdit* m_editRobotZ;
|
||||||
|
QLineEdit* m_editRobotRx;
|
||||||
|
QLineEdit* m_editRobotRy;
|
||||||
|
QLineEdit* m_editRobotRz;
|
||||||
|
QPushButton* m_btnApplyEyeInHand;
|
||||||
QPushButton* m_btnResetMatrix;
|
QPushButton* m_btnResetMatrix;
|
||||||
|
|
||||||
// 悬浮缩放按钮
|
// 悬浮缩放按钮
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "PointCloudConverter.h"
|
#include "PointCloudConverter.h"
|
||||||
|
#include "VZNL_Types.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 点云显示颜色枚举
|
* @brief 点云显示颜色枚举
|
||||||
@ -243,6 +244,21 @@ public:
|
|||||||
*/
|
*/
|
||||||
void transformAllClouds(const QMatrix4x4& matrix);
|
void transformAllClouds(const QMatrix4x4& matrix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 对所有点云中 Z 值小于阈值的点应用旋转矩阵
|
||||||
|
* @param rotMatrix 旋转矩阵
|
||||||
|
* @param zThreshold Z 轴阈值
|
||||||
|
* @return 被旋转的点数
|
||||||
|
*/
|
||||||
|
size_t rotateCloudsByZThreshold(const QMatrix4x4& rotMatrix, float zThreshold);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取所有点云数据(按线分组),用于保存
|
||||||
|
* @param scanLines 输出:每条扫描线的点列表
|
||||||
|
* @return 总点数
|
||||||
|
*/
|
||||||
|
size_t getAllCloudsByLines(std::vector<std::vector<SVzNL3DPosition>>& scanLines) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 添加线段
|
* @brief 添加线段
|
||||||
*/
|
*/
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -750,6 +750,98 @@ void PointCloudGLWidget::transformAllClouds(const QMatrix4x4& matrix)
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t PointCloudGLWidget::rotateCloudsByZThreshold(const QMatrix4x4& rotMatrix, float zThreshold)
|
||||||
|
{
|
||||||
|
size_t rotatedCount = 0;
|
||||||
|
|
||||||
|
for (auto& cloudData : m_pointClouds) {
|
||||||
|
for (size_t i = 0; i + 2 < cloudData.vertices.size(); i += 3) {
|
||||||
|
if (cloudData.vertices[i + 2] < zThreshold) {
|
||||||
|
QVector3D pt(cloudData.vertices[i], cloudData.vertices[i + 1], cloudData.vertices[i + 2]);
|
||||||
|
QVector3D transformed = rotMatrix.map(pt);
|
||||||
|
cloudData.vertices[i] = transformed.x();
|
||||||
|
cloudData.vertices[i + 1] = transformed.y();
|
||||||
|
cloudData.vertices[i + 2] = transformed.z();
|
||||||
|
++rotatedCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotatedCount > 0) {
|
||||||
|
makeCurrent();
|
||||||
|
for (auto& cloudData : m_pointClouds) {
|
||||||
|
uploadToVBO(cloudData);
|
||||||
|
}
|
||||||
|
doneCurrent();
|
||||||
|
computeBoundingBox();
|
||||||
|
resetView();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
return rotatedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PointCloudGLWidget::getAllCloudsByLines(std::vector<std::vector<SVzNL3DPosition>>& scanLines) const
|
||||||
|
{
|
||||||
|
scanLines.clear();
|
||||||
|
size_t totalPoints = 0;
|
||||||
|
|
||||||
|
for (const auto& cloudData : m_pointClouds) {
|
||||||
|
const size_t displayCount = cloudData.vertices.size() / 3;
|
||||||
|
if (displayCount == 0) continue;
|
||||||
|
|
||||||
|
if (cloudData.hasLineInfo && cloudData.totalLines > 0 && cloudData.pointsPerLine > 0) {
|
||||||
|
// 有线信息:用 pointsPerLine 还原每条线的完整点数,过滤掉的 (0,0,0) 点补零
|
||||||
|
const int lines = cloudData.totalLines;
|
||||||
|
const int ptsPerLine = cloudData.pointsPerLine;
|
||||||
|
|
||||||
|
// 初始化所有线,每条线 pointsPerLine 个零点
|
||||||
|
const size_t baseIdx = scanLines.size();
|
||||||
|
for (int i = 0; i < lines; ++i) {
|
||||||
|
std::vector<SVzNL3DPosition> line(ptsPerLine);
|
||||||
|
memset(line.data(), 0, sizeof(SVzNL3DPosition) * ptsPerLine);
|
||||||
|
for (int j = 0; j < ptsPerLine; ++j) {
|
||||||
|
line[j].nPointIdx = j;
|
||||||
|
}
|
||||||
|
scanLines.push_back(std::move(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用实际显示的点填充对应位置
|
||||||
|
for (size_t i = 0; i < displayCount; ++i) {
|
||||||
|
const int lineIdx = (i < cloudData.lineIndices.size()) ? cloudData.lineIndices[i] : 0;
|
||||||
|
const int ptInLine = (i < cloudData.pointInLineIndices.size()) ? cloudData.pointInLineIndices[i] : static_cast<int>(i);
|
||||||
|
|
||||||
|
if (lineIdx >= 0 && lineIdx < lines && ptInLine >= 0 && ptInLine < ptsPerLine) {
|
||||||
|
auto& pos = scanLines[baseIdx + lineIdx][ptInLine];
|
||||||
|
pos.pt3D.x = cloudData.vertices[i * 3];
|
||||||
|
pos.pt3D.y = cloudData.vertices[i * 3 + 1];
|
||||||
|
pos.pt3D.z = cloudData.vertices[i * 3 + 2];
|
||||||
|
pos.nPointIdx = ptInLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalPoints += static_cast<size_t>(lines) * ptsPerLine;
|
||||||
|
} else {
|
||||||
|
// 无线信息,每个点云作为一条线
|
||||||
|
std::vector<SVzNL3DPosition> line;
|
||||||
|
line.reserve(displayCount);
|
||||||
|
for (size_t i = 0; i < displayCount; ++i) {
|
||||||
|
SVzNL3DPosition pos;
|
||||||
|
memset(&pos, 0, sizeof(pos));
|
||||||
|
pos.nPointIdx = static_cast<int>(i);
|
||||||
|
pos.pt3D.x = cloudData.vertices[i * 3];
|
||||||
|
pos.pt3D.y = cloudData.vertices[i * 3 + 1];
|
||||||
|
pos.pt3D.z = cloudData.vertices[i * 3 + 2];
|
||||||
|
line.push_back(pos);
|
||||||
|
}
|
||||||
|
totalPoints += displayCount;
|
||||||
|
scanLines.push_back(std::move(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalPoints;
|
||||||
|
}
|
||||||
|
|
||||||
void PointCloudGLWidget::computeBoundingBox()
|
void PointCloudGLWidget::computeBoundingBox()
|
||||||
{
|
{
|
||||||
if (m_pointClouds.empty()) {
|
if (m_pointClouds.empty()) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user