diff --git a/App/HoleDetection/HoleDetectionApp/Version.h b/App/HoleDetection/HoleDetectionApp/Version.h
index 8c758e2..a768be7 100644
--- a/App/HoleDetection/HoleDetectionApp/Version.h
+++ b/App/HoleDetection/HoleDetectionApp/Version.h
@@ -5,8 +5,8 @@
#define HOLEDETECTION_APP_NAME "孔洞检测"
// 版本字符串
-#define HOLEDETECTION_VERSION_STRING "1.0.1"
-#define HOLEDETECTION_BUILD_STRING "0"
+#define HOLEDETECTION_VERSION_STRING "1.1.0"
+#define HOLEDETECTION_BUILD_STRING "1"
#define HOLEDETECTION_FULL_VERSION_STRING "V" HOLEDETECTION_VERSION_STRING "_" HOLEDETECTION_BUILD_STRING
// 构建日期
diff --git a/App/HoleDetection/HoleDetectionApp/Version.md b/App/HoleDetection/HoleDetectionApp/Version.md
index 3175a18..96c7494 100644
--- a/App/HoleDetection/HoleDetectionApp/Version.md
+++ b/App/HoleDetection/HoleDetectionApp/Version.md
@@ -1,3 +1,9 @@
+# 1.1.0 2026-03-125
+## build_1
+1. 算法优化
+2. 配置结构修改
+3. 手眼标定,网络配置公用
+
# 1.0.0 2026-03-11
## build_4
1. 修复矩阵配置
diff --git a/App/HoleDetection/HoleDetectionApp/dialogalgoarg.ui b/App/HoleDetection/HoleDetectionApp/dialogalgoarg.ui
index 868b93a..8b7d916 100644
--- a/App/HoleDetection/HoleDetectionApp/dialogalgoarg.ui
+++ b/App/HoleDetection/HoleDetectionApp/dialogalgoarg.ui
@@ -7,7 +7,7 @@
0
0
780
- 656
+ 541
@@ -323,6 +323,12 @@ QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;
+
+
+ 手眼标定
+
+
+
网络配置
@@ -390,12 +396,6 @@ QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;
-
-
- 手眼标定
-
-
-
-
@@ -435,6 +435,19 @@ QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 3px 0 3px;
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
-
diff --git a/App/HoleDetection/HoleDetectionConfig/HoleDetectionConfig.pro b/App/HoleDetection/HoleDetectionConfig/HoleDetectionConfig.pro
index 12883fd..5e5daa5 100644
--- a/App/HoleDetection/HoleDetectionConfig/HoleDetectionConfig.pro
+++ b/App/HoleDetection/HoleDetectionConfig/HoleDetectionConfig.pro
@@ -4,6 +4,11 @@ TEMPLATE = lib
CONFIG += staticlib
CONFIG += c++11
+# 编码设置
+win32-msvc {
+ QMAKE_CXXFLAGS += /utf-8
+}
+
# Output directory configuration
TARGET = HoleDetectionConfig
diff --git a/App/HoleDetection/HoleDetectionConfig/Inc/IVrConfig.h b/App/HoleDetection/HoleDetectionConfig/Inc/IVrConfig.h
index 9b60a2a..c25ae4f 100644
--- a/App/HoleDetection/HoleDetectionConfig/Inc/IVrConfig.h
+++ b/App/HoleDetection/HoleDetectionConfig/Inc/IVrConfig.h
@@ -18,8 +18,8 @@
struct VrHoleDetectionParam
{
int neighborCount = 3; // 相邻点连接数
- double angleThresholdPos = 10.0; // 正角度阈值(度)
- double angleThresholdNeg = -10.0; // 负角度阈值(度)
+ double angleThresholdPos = 30.0; // 正角度阈值(度)
+ double angleThresholdNeg = -30.0; // 负角度阈值(度)
double minPitDepth = 1.0; // 最小凹坑深度(mm)
double minRadius = 2.0; // 最小孔半径(mm)
double maxRadius = 50.0; // 最大孔半径(mm)
diff --git a/App/HoleDetection/HoleDetectionConfig/Src/AlgoParamConverter.cpp b/App/HoleDetection/HoleDetectionConfig/Src/AlgoParamConverter.cpp
index 0302cd5..7eec5ef 100644
--- a/App/HoleDetection/HoleDetectionConfig/Src/AlgoParamConverter.cpp
+++ b/App/HoleDetection/HoleDetectionConfig/Src/AlgoParamConverter.cpp
@@ -7,7 +7,6 @@ namespace AlgoParamConverter
SHoleDetectionParams ToAlgoParam(const VrHoleDetectionParam& param)
{
SHoleDetectionParams algo;
- algo.neighborCount = param.neighborCount;
algo.angleThresholdPos = static_cast(param.angleThresholdPos);
algo.angleThresholdNeg = static_cast(param.angleThresholdNeg);
algo.minPitDepth = static_cast(param.minPitDepth);
@@ -44,8 +43,8 @@ void LogAlgoParams(const std::string& logTag,
clibMatrix[8], clibMatrix[9], clibMatrix[10], clibMatrix[11],
clibMatrix[12], clibMatrix[13], clibMatrix[14], clibMatrix[15]);
- LOG_INFO("%s DetectionParams: neighborCount=%d, angleThresholdPos=%.1f, angleThresholdNeg=%.1f, minPitDepth=%.1f\n",
- logTag.c_str(), detectionParams.neighborCount, detectionParams.angleThresholdPos,
+ LOG_INFO("%s DetectionParams: angleThresholdPos=%.1f, angleThresholdNeg=%.1f, minPitDepth=%.1f\n",
+ logTag.c_str(), detectionParams.angleThresholdPos,
detectionParams.angleThresholdNeg, detectionParams.minPitDepth);
LOG_INFO("%s DetectionParams: minRadius=%.1f, maxRadius=%.1f, expansionSize1=%d, expansionSize2=%d, minVTransitionPoints=%d\n",
logTag.c_str(), detectionParams.minRadius, detectionParams.maxRadius,
diff --git a/AppAlgo/holeDetection/arm/debug/libHoleDetectionLib.so.1.0.0 b/AppAlgo/holeDetection/arm/debug/libHoleDetectionLib.so.1.0.0
index 9c68401..f0de25c 100644
Binary files a/AppAlgo/holeDetection/arm/debug/libHoleDetectionLib.so.1.0.0 and b/AppAlgo/holeDetection/arm/debug/libHoleDetectionLib.so.1.0.0 differ
diff --git a/AppAlgo/holeDetection/arm/release/libHoleDetectionLib.so.1.0.0 b/AppAlgo/holeDetection/arm/release/libHoleDetectionLib.so.1.0.0
index c92f37b..3ea6675 100644
Binary files a/AppAlgo/holeDetection/arm/release/libHoleDetectionLib.so.1.0.0 and b/AppAlgo/holeDetection/arm/release/libHoleDetectionLib.so.1.0.0 differ
diff --git a/AppAlgo/holeDetection/include/HoleDetection.h b/AppAlgo/holeDetection/include/HoleDetection.h
index 5a5d75f..a354f44 100644
--- a/AppAlgo/holeDetection/include/HoleDetection.h
+++ b/AppAlgo/holeDetection/include/HoleDetection.h
@@ -12,6 +12,8 @@
#else
#define HOLE_DETECTION_API __declspec(dllimport)
#endif
+#elif defined(__GNUC__) || defined(__clang__)
+ #define HOLE_DETECTION_API __attribute__((visibility("default")))
#else
#define HOLE_DETECTION_API
#endif
@@ -31,6 +33,47 @@ struct SHoleBoundaryPoint {
: point(p), row(r), col(c) {}
};
+/**
+ * @brief Per-point signed angle state along one scanned line
+ */
+enum ELineAngleTrend {
+ keLineAngleTrend_Invalid = 0,
+ keLineAngleTrend_Flat = 1,
+ keLineAngleTrend_PositiveJump = 2,
+ keLineAngleTrend_NegativeJump = 3
+};
+
+/**
+ * @brief Angle profile sample for one valid point on a scanned line
+ */
+struct SLineAngleSample {
+ SVzNLPointXYZ point; // Current point
+ int row; // Grid row
+ int col; // Grid column
+ int linePos; // Position inside the scanned line
+ float signedAngleDeg; // Signed turning angle in degrees
+ float beforeMeanZ; // Mean Z of points found within A before current point
+ float afterMeanZ; // Mean Z of points found within A after current point
+ float beforeCoord; // Projected coord of the furthest point before current point
+ float afterCoord; // Projected coord of the furthest point after current point
+ ELineAngleTrend trend; // Flat / positive jump / negative jump
+ unsigned char hasAngle; // 1 if both sides have enough points to form an angle
+
+ SLineAngleSample()
+ : point()
+ , row(-1)
+ , col(-1)
+ , linePos(-1)
+ , signedAngleDeg(0.0f)
+ , beforeMeanZ(0.0f)
+ , afterMeanZ(0.0f)
+ , beforeCoord(0.0f)
+ , afterCoord(0.0f)
+ , trend(keLineAngleTrend_Invalid)
+ , hasAngle(0U)
+ {}
+};
+
/**
* @brief Cluster information for debug callbacks
*/
@@ -90,6 +133,22 @@ struct SHoleDetectionDebugCallbacks {
*/
void (*onSegmentPairsDetected)(const SSegmentPair* segmentPairs, int count, void* userData);
+ /**
+ * @brief Called when one scanned line's signed angle profile is available
+ * @param samples Array of valid-point angle samples in line order
+ * @param count Number of samples
+ * @param lineType "Row" or "Column"
+ * @param lineIndex Row/column index
+ * @param userData User-provided context pointer
+ */
+ void (*onLineAngleProfileDetected)(
+ const SLineAngleSample* samples,
+ int count,
+ const char* lineType,
+ int lineIndex,
+ void* userData
+ );
+
/**
* @brief User-provided context pointer, passed to all callbacks
*/
diff --git a/AppAlgo/holeDetection/include/HoleDetectionParams.h b/AppAlgo/holeDetection/include/HoleDetectionParams.h
index 9dfafe1..2591142 100644
--- a/AppAlgo/holeDetection/include/HoleDetectionParams.h
+++ b/AppAlgo/holeDetection/include/HoleDetectionParams.h
@@ -2,26 +2,16 @@
#define HOLE_DETECTION_PARAMS_H
#include
-#include "../include/VZNL_Types.h" // 使用 VZNL_Types.h 中的类型
-
-/**
- * @brief 检测到的孔洞排序模式
- */
-enum ESortMode {
- keSortMode_None = 0, // 不排序
- keSortMode_ByRadius = 1, // 按半径排序(最大的在前)
- keSortMode_ByDepth = 2, // 按深度排序(最深的在前)
- keSortMode_ByQuality = 3 // 按质量分数排序(最高的在前)
-};
+#include "VZNL_Types.h" // 使用 VZNL_Types.h 中定义的类型
/**
* @brief 孔洞检测算法的检测参数
*/
struct SHoleDetectionParams {
// 凹坑检测参数
- int neighborCount; // 线连接的相邻点数(默认值:3)
- float angleThresholdPos; // 正角度阈值,单位:度(默认值:10.0)
- float angleThresholdNeg; // 负角度阈值,单位:度(默认值:-10.0)
+ float angleThresholdPos; // 正角度阈值,单位:度(默认值:30.0)
+ float angleThresholdNeg; // 负角度阈值,单位:度(默认值:-30.0)
+ float angleSearchDistance; // Method1 前后搜索距离 A,单位:mm
float minPitDepth; // 最小凹坑深度,单位:mm(默认值:1.0)
// 椭圆拟合参数
@@ -29,23 +19,38 @@ struct SHoleDetectionParams {
float maxRadius; // 最大孔洞半径,单位:mm(默认值:50.0)
// 平面拟合参数
- int expansionSize1; // 第一环扩展大小,单位:mm(默认值:5)
- int expansionSize2; // 第二环扩展大小,单位:mm(默认值:10)
+ int expansionSize1; // 第一圈扩展大小(默认值:5)
+ int expansionSize2; // 第二圈扩展大小(默认值:10)
- // V型检测参数
- int minVTransitionPoints; // V型端点之间的最小有效过渡点数(默认值:1)
+ // V 型检测参数
+ int minVTransitionPoints; // V 型端点之间的最小有效过渡点数(默认值:1)
+ float jumpThresholdResidual; // 相邻有效点的残差跳变阈值,<=0 表示自适应
+ float gapJumpThresholdResidual; // 跨无效点间隙的残差跳变阈值,<=0 表示自适应
+ int maxGapPointsInLine; // 允许跨越的最大无效点数
+ float minFeatureSpan; // 特征点对最小弧长跨度(mm)
+ int residualSmoothWindow; // 残差平滑窗口(奇数,建议 3~7)
+ float slopeAngleThreshold; // 坡度补充判断阈值,单位:度(默认值:3.0)
+ // 当曲率角未超过 angleThreshold 时,若前后参考点间的
+ // 净 Z 坡度角超过此阈值,则将该点判定为下降或上升趋势。
+ // 设置为 0 可禁用坡度补充判断。
// 构造函数,设置默认值
SHoleDetectionParams()
- : neighborCount(3)
- , angleThresholdPos(10.0f)
- , angleThresholdNeg(-10.0f)
+ : angleThresholdPos(30.0f)
+ , angleThresholdNeg(-30.0f)
+ , angleSearchDistance(2.0f)
, minPitDepth(1.0f)
, minRadius(2.0f)
, maxRadius(50.0f)
, expansionSize1(5)
, expansionSize2(10)
, minVTransitionPoints(1)
+ , jumpThresholdResidual(0.0f)
+ , gapJumpThresholdResidual(0.0f)
+ , maxGapPointsInLine(12)
+ , minFeatureSpan(2.0f)
+ , residualSmoothWindow(5)
+ , slopeAngleThreshold(3.0f)
{}
};
@@ -60,18 +65,18 @@ struct SHoleFilterParams {
// 形状过滤(拟合前)
float minAngularCoverage; // 最小角度覆盖范围,单位:度(默认值:10.0)
// 用于过滤非闭合边界。设置为 0 可禁用。
- float maxRadiusFitRatio; // 最大半径拟合比率 radiusVariance/radius(默认值:1.0)
- // 衡量点与圆的拟合程度。设置为 1.0 可禁用。
+ float maxRadiusFitRatio; // 最大半径拟合比率 radiusVariance / radius(默认值:1.0)
+ // 衡量边界点与圆的拟合程度。设置为 1.0 可禁用。
float minQualityScore; // 最小整体质量分数(默认值:0.0)
// 形状指标的加权组合。设置为 0 可禁用。
- // 平面性过滤(投影前)
+ // 平面一致性过滤(投影前)
float maxPlaneResidual; // 最大点到平面残差,单位:mm(默认值:10.0)
- // 拒绝非平面簇(例如悬崖、台阶边缘)。
+ // 用于拒绝非平面簇,例如悬崖、台阶边缘。
float maxAngularGap; // 最大角度间隙,单位:度(默认值:90.0)
- // 拒绝 L 型或非闭合边界。
- float minInlierRatio; // 椭圆拟合的最小内点比率(默认值:0.0)
- // 在拟合椭圆容差范围内的点的比例。
+ // 用于拒绝 L 型或非闭合边界。
+ float minInlierRatio; // 圆拟合的最小内点比率(默认值:0.0)
+ // 表示落在拟合圆容差范围内的点所占比例。
// 构造函数,设置默认值
SHoleFilterParams()
@@ -88,17 +93,17 @@ struct SHoleFilterParams {
/**
* @brief 单个孔洞检测结果
*
- * 注意:SVzNL3DPointF 和 SVzNL2DPointF 在 VZNL_Types.h 中定义
+ * 注意:SVzNL3DPointF 和 SVzNL2DPointF 在 VZNL_Types.h 中定义。
*/
struct SHoleResult {
SVzNL3DPointF center; // 孔洞中心点 (x, y, z)
- SVzNL3DPointF normal; // 平面法向量(单位长度)
+ SVzNL3DPointF normal; // 拟合平面法向量(单位长度)
float radius; // 孔洞半径,单位:mm
float depth; // 凹坑深度,单位:mm
- float eccentricity; // 离心率(0 = 完美圆形)
- float radiusVariance; // 半径方差,单位:mm
+ float eccentricity; // 离心率(0 表示完美圆形)
+ float radiusVariance; // 半径离散度,单位:mm
float angularSpan; // 角度覆盖范围,单位:度
- float qualityScore; // 质量分数(0-1,越高越好)
+ float qualityScore; // 质量分数(0~1,越高越好)
SHoleResult()
: center()
@@ -146,11 +151,10 @@ inline void FreeMultiHoleResult(SMultiHoleResult* result) {
}
/**
- * @brief 线段端点对结构
+ * @brief 线段端点对结果
*
- * 表示在线扫描中检测到的线段,包含起点和终点。
- * 注意:尽管名称为"PeakValley",但此结构存储的是线段端点,
- * 不一定是峰值/谷值点。保留此命名是为了兼容性。
+ * 表示在线扫描中检测到的一段特征,包含起点和终点。
+ * 可用于描述凹坑边界、空洞间隙等一维特征。
*/
struct SSegmentPair {
SVzNLPointXYZ startPoint; // 线段起点
diff --git a/AppAlgo/holeDetection/include/PlaneSegmentation.h b/AppAlgo/holeDetection/include/PlaneSegmentation.h
new file mode 100644
index 0000000..56aa8e0
--- /dev/null
+++ b/AppAlgo/holeDetection/include/PlaneSegmentation.h
@@ -0,0 +1,285 @@
+#ifndef PLANE_SEGMENTATION_H
+#define PLANE_SEGMENTATION_H
+
+#include "../include/VZNL_Types.h"
+#include "ErrorCodes.h"
+#include
+
+/**
+ * @brief Z值直方图峰值信息
+ */
+struct ZHistogramPeak {
+ float zValue; // 峰值对应的Z坐标
+ int pointCount; // 该峰值区域的点数
+ float zMin; // 峰值区域的Z最小值
+ float zMax; // 峰值区域的Z最大值
+ std::vector pointIndices; // 属于该平面的点索引
+};
+
+/**
+ * @brief Z值直方图平面分割参数
+ */
+struct ZHistogramSegmentationParams {
+ float binSize; // 直方图bin大小 (mm), 建议 1.0-5.0
+ int minPeakPoints; // 最小峰值点数, 建议 50-100
+ float minPeakRatio; // 最小峰值点数占比, 建议 0.03 (3%)
+ float peakMergeThreshold; // 峰值合并阈值 (mm), 建议 5.0-10.0
+ int smoothingWindow; // 平滑窗口大小, 建议 3-5
+};
+
+/**
+ * @brief 使用Z值直方图分割平面
+ *
+ * 算法流程:
+ * 1. 计算Z值范围,创建直方图
+ * 2. 统计每个bin的点数
+ * 3. 对直方图进行平滑处理(可选)
+ * 4. 检测峰值(局部最大值)
+ * 5. 合并相邻的峰值
+ * 6. 为每个峰值收集对应的点
+ *
+ * 优点:
+ * - 计算速度快 O(n)
+ * - 适合水平或接近水平的平面
+ * - 对噪声有一定鲁棒性
+ *
+ * 局限:
+ * - 对倾斜平面效果较差(Z值会分散)
+ * - 只能检测Z方向分层明显的平面
+ *
+ * @param points 输入点云
+ * @param pointCount 点数
+ * @param params 分割参数
+ * @param outPeaks 输出峰值列表(按Z值排序)
+ * @param errCode 错误码
+ * @return 检测到的平面数量
+ */
+int SegmentPlanesByZHistogram(
+ const SVzNLPointXYZ* points,
+ int pointCount,
+ const ZHistogramSegmentationParams& params,
+ std::vector& outPeaks,
+ int* errCode
+);
+
+/**
+ * @brief 改进版:自适应倾斜平面分割
+ *
+ * 针对倾斜平面的改进算法:
+ * 1. 先用Z直方图粗分割
+ * 2. 对每个Z层,在XY平面上再做一次分层
+ * 3. 或者:先估计主平面法向量,然后投影到该法向量方向再做直方图
+ *
+ * @param points 输入点云
+ * @param pointCount 点数
+ * @param params 分割参数
+ * @param outPeaks 输出峰值列表
+ * @param errCode 错误码
+ * @return 检测到的平面数量
+ */
+int SegmentTiltedPlanesByHistogram(
+ const SVzNLPointXYZ* points,
+ int pointCount,
+ const ZHistogramSegmentationParams& params,
+ std::vector& outPeaks,
+ int* errCode
+);
+
+/**
+ * @brief Z值波动统计信息
+ */
+struct ZFluctuationStats {
+ float meanZ; // Z值均值
+ float stdDevZ; // Z值标准差(去除离群点后)
+ float minZ; // Z值最小值(去除离群点后)
+ float maxZ; // Z值最大值(去除离群点后)
+ float rangeZ; // Z值范围(maxZ - minZ)
+ int validPointCount; // 有效点数(去除离群点后)
+ int outlierCount; // 离群点数量
+ float outlierRatio; // 离群点占比
+ float medianZ; // Z值中位数
+ float iqrZ; // 四分位距(IQR)
+};
+
+/**
+ * @brief Z值波动分析参数
+ */
+struct ZFluctuationParams {
+ enum OutlierMethod {
+ SIGMA_3, // 3-sigma规则(适合正态分布)
+ IQR, // 四分位距方法(更鲁棒,推荐)
+ PERCENTILE // 百分位数方法
+ };
+
+ OutlierMethod method; // 离群点检测方法
+ float sigmaMultiplier; // sigma倍数,默认3.0
+ float iqrMultiplier; // IQR倍数,默认1.5
+ float percentileLow; // 下百分位数,默认5.0 (5%)
+ float percentileHigh; // 上百分位数,默认95.0 (95%)
+
+ // 默认构造函数
+ ZFluctuationParams()
+ : method(IQR)
+ , sigmaMultiplier(3.0f)
+ , iqrMultiplier(1.5f)
+ , percentileLow(5.0f)
+ , percentileHigh(95.0f)
+ {}
+};
+
+/**
+ * @brief 计算点云Z值波动统计(鲁棒版本,自动去除离群点)
+ *
+ * 算法流程:
+ * 1. 收集所有有效点的Z值
+ * 2. 根据选择的方法检测并去除离群点:
+ * - 3-sigma: 去除距离均值超过3倍标准差的点
+ * - IQR: 去除超出 [Q1-1.5*IQR, Q3+1.5*IQR] 范围的点(推荐)
+ * - Percentile: 只保留指定百分位数范围内的点
+ * 3. 用剩余点重新计算统计信息
+ *
+ * 使用场景:
+ * - 评估点云平整度
+ * - 检测是否存在多个平面
+ * - 质量控制(如木板平整度检测)
+ *
+ * 示例:
+ * ```cpp
+ * ZFluctuationParams params;
+ * params.method = ZFluctuationParams::IQR; // 使用IQR方法(推荐)
+ *
+ * ZFluctuationStats stats;
+ * int errCode = 0;
+ * ComputeZFluctuation(points, pointCount, params, &stats, &errCode);
+ *
+ * std::cout << "Z波动: " << stats.stdDevZ << " mm" << std::endl;
+ * std::cout << "Z范围: " << stats.rangeZ << " mm" << std::endl;
+ * std::cout << "离群点: " << stats.outlierCount << " ("
+ * << stats.outlierRatio * 100 << "%)" << std::endl;
+ * ```
+ *
+ * @param points 输入点云
+ * @param pointCount 点数
+ * @param params 波动分析参数
+ * @param outStats 输出统计信息
+ * @param errCode 错误码
+ * @return HD_SUCCESS 成功,其他值表示错误
+ */
+int ComputeZFluctuation(
+ const SVzNLPointXYZ* points,
+ int pointCount,
+ const ZFluctuationParams& params,
+ ZFluctuationStats* outStats,
+ int* errCode
+);
+
+/**
+ * @brief 简化版:使用默认参数计算Z值波动
+ *
+ * 使用IQR方法自动去除离群点,适合大多数场景。
+ *
+ * @param points 输入点云
+ * @param pointCount 点数
+ * @param outStats 输出统计信息
+ * @param errCode 错误码
+ * @return HD_SUCCESS 成功,其他值表示错误
+ */
+int ComputeZFluctuation(
+ const SVzNLPointXYZ* points,
+ int pointCount,
+ ZFluctuationStats* outStats,
+ int* errCode
+);
+
+/**
+ * @brief 点云采样间距统计信息
+ */
+struct PointSpacingStats {
+ // 主要结果:取两个方向的最大值
+ float typicalSpacing; // 典型间距(行内和列内中位数的最大值,推荐使用)
+
+ // 分方向统计
+ float rowSpacing; // 行内平均间距(同一激光线上相邻点,YZ平面距离)
+ float colSpacing; // 列内平均间距(相邻激光线间,XZ平面距离)
+ float rowSpacingMedian; // 行内间距中位数(更鲁棒)
+ float colSpacingMedian; // 列内间距中位数(更鲁棒)
+ float rowSpacingStdDev; // 行内间距标准差
+ float colSpacingStdDev; // 列内间距标准差
+ int rowSampleCount; // 行内采样数量
+ int colSampleCount; // 列内采样数量
+ float rowSpacingMin; // 行内最小间距
+ float rowSpacingMax; // 行内最大间距
+ float colSpacingMin; // 列内最小间距
+ float colSpacingMax; // 列内最大间距
+};
+
+/**
+ * @brief 计算点云相邻点间距(鲁棒版本,自动去除噪点)
+ *
+ * 算法流程:
+ * 1. 遍历所有相邻点对:
+ * - 行内相邻点(同一激光线):使用YZ平面距离 sqrt((y2-y1)² + (z2-z1)²)
+ * - 列内相邻点(相邻激光线):使用XZ平面距离 sqrt((x2-x1)² + (z2-z1)²)
+ * 2. 跳过无效点 (0,0,0)
+ * 3. 收集所有距离值
+ * 4. 使用中位数和IQR方法去除噪点
+ * 5. 计算统计信息(均值、中位数、标准差等)
+ *
+ * 使用场景:
+ * - 估计点云采样密度/分辨率
+ * - 验证扫描仪参数
+ * - 检测采样不均匀性
+ * - 为后续算法选择合适的邻域大小
+ *
+ * 注意事项:
+ * - 行内距离使用YZ平面:因为激光线沿Y方向扫描,Z是深度
+ * - 列内距离使用XZ平面:因为相邻激光线在X方向分布,Z是深度
+ * - 自动跳过无效点 (0,0,0)
+ * - 使用中位数避免噪点影响
+ *
+ * 示例:
+ * ```cpp
+ * PointSpacingStats stats;
+ * int errCode = 0;
+ * ComputePointSpacing(points, rows, cols, &stats, &errCode);
+ *
+ * std::cout << "行内间距: " << stats.rowSpacingMedian << " mm" << std::endl;
+ * std::cout << "列内间距: " << stats.colSpacingMedian << " mm" << std::endl;
+ * ```
+ *
+ * @param points 输入点云(栅格化数据,按行优先存储)
+ * @param rows 激光线数量(行数)
+ * @param cols 每条激光线上的点数(列数)
+ * @param outStats 输出统计信息
+ * @param errCode 错误码
+ * @return HD_SUCCESS 成功,其他值表示错误
+ */
+int ComputePointSpacing(
+ const SVzNLPointXYZ* points,
+ int rows,
+ int cols,
+ PointSpacingStats* outStats,
+ int* errCode
+);
+
+struct RowZDiffFluctuationStats {
+ float meanAbsDz; // |dz| 均值(去除异常值后)
+ float medianAbsDz; // |dz| 中位数(去除异常值后)
+ float stdDevAbsDz; // |dz| 标准差,作为Z波动值
+ float minAbsDz; // |dz| 最小值(去除异常值后)
+ float maxAbsDz; // |dz| 最大值(去除异常值后)
+ float rangeAbsDz; // |dz| 波动范围(maxAbsDz - minAbsDz)
+ int sampleCount; // 有效样本数(去除异常值后)
+ int outlierCount; // 被剔除的异常样本数
+ float outlierRatio; // 异常样本占比(outlierCount / 原始样本数)
+};
+
+int ComputeRowZDiffFluctuation(
+ const SVzNLPointXYZ* points,
+ int rows,
+ int cols,
+ RowZDiffFluctuationStats* outStats,
+ int* errCode
+);
+
+#endif // PLANE_SEGMENTATION_H
diff --git a/AppAlgo/holeDetection/windows/x64/Debug/HoleDetectionLib.dll b/AppAlgo/holeDetection/windows/x64/Debug/HoleDetectionLib.dll
index 87b1bef..217e114 100644
Binary files a/AppAlgo/holeDetection/windows/x64/Debug/HoleDetectionLib.dll and b/AppAlgo/holeDetection/windows/x64/Debug/HoleDetectionLib.dll differ
diff --git a/AppAlgo/holeDetection/windows/x64/Debug/HoleDetectionLib.pdb b/AppAlgo/holeDetection/windows/x64/Debug/HoleDetectionLib.pdb
index 5d03022..3aab682 100644
Binary files a/AppAlgo/holeDetection/windows/x64/Debug/HoleDetectionLib.pdb and b/AppAlgo/holeDetection/windows/x64/Debug/HoleDetectionLib.pdb differ
diff --git a/AppAlgo/holeDetection/windows/x64/Release/HoleDetectionLib.dll b/AppAlgo/holeDetection/windows/x64/Release/HoleDetectionLib.dll
index bd89b6b..3d512ab 100644
Binary files a/AppAlgo/holeDetection/windows/x64/Release/HoleDetectionLib.dll and b/AppAlgo/holeDetection/windows/x64/Release/HoleDetectionLib.dll differ