Compare commits
10 Commits
6c55510809
...
7969a242ba
| Author | SHA1 | Date | |
|---|---|---|---|
| 7969a242ba | |||
| 1d649f9c01 | |||
| 916f2c7662 | |||
| 3aae9159ed | |||
| 9d304b19ac | |||
| 937e9c0d8a | |||
|
|
976f87b6ba | ||
| 2e92ddfab0 | |||
| 0d88858738 | |||
|
|
cced2a9646 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,4 +1,7 @@
|
|||||||
.claude
|
.claude
|
||||||
nul
|
nul
|
||||||
build
|
build
|
||||||
Algo/DetectHole/Export/
|
Algo/DetectHole/Export
|
||||||
|
Algo/DetectBarIntersection/build
|
||||||
|
Algo/DetectBarIntersection/data
|
||||||
|
Algo/DetectBarIntersection/Export
|
||||||
@ -1,23 +1,32 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(DetectBarIntersection VERSION 1.0.0 LANGUAGES CXX)
|
project(DetectBarIntersection VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
# Set C++ standard
|
# Set C++ standard
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if(WIN32)
|
if(CMAKE_CONFIGURATION_TYPES)
|
||||||
if(MSVC)
|
string(REPLACE ";" ", " DETECTBARINTERSECTION_CONFIGURATION_LIST "${CMAKE_CONFIGURATION_TYPES}")
|
||||||
add_compile_options(
|
set(DETECTBARINTERSECTION_BUILD_DESCRIPTION "Multi-config (${DETECTBARINTERSECTION_CONFIGURATION_LIST})")
|
||||||
/wd4267
|
else()
|
||||||
/wd4996
|
set(DETECTBARINTERSECTION_BUILD_DESCRIPTION "${CMAKE_BUILD_TYPE}")
|
||||||
)
|
endif()
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
|
||||||
add_compile_options(/O2)
|
if(NOT DETECTBARINTERSECTION_BUILD_DESCRIPTION)
|
||||||
endif()
|
set(DETECTBARINTERSECTION_BUILD_DESCRIPTION "Not specified")
|
||||||
endif()
|
endif()
|
||||||
else()
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if(WIN32)
|
||||||
message(STATUS "Debug mode: Adding debug flags")
|
if(MSVC)
|
||||||
|
add_compile_options(
|
||||||
|
/wd4267
|
||||||
|
/wd4996
|
||||||
|
$<$<CONFIG:Release>:/O2>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
message(STATUS "Debug mode: Adding debug flags")
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 -Wall -Wextra")
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 -Wall -Wextra")
|
||||||
add_definitions(-DDEBUG)
|
add_definitions(-DDEBUG)
|
||||||
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
|
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
@ -232,11 +241,11 @@ if(BUILD_EXAMPLES)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Print configuration summary
|
# Print configuration summary
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
message(STATUS "DetectBarIntersection Configuration Summary:")
|
message(STATUS "DetectBarIntersection Configuration Summary:")
|
||||||
message(STATUS " C++ Standard: ${CMAKE_CXX_STANDARD}")
|
message(STATUS " C++ Standard: ${CMAKE_CXX_STANDARD}")
|
||||||
message(STATUS " Build Type: ${CMAKE_BUILD_TYPE}")
|
message(STATUS " Build Type: ${DETECTBARINTERSECTION_BUILD_DESCRIPTION}")
|
||||||
message(STATUS " Eigen3 Include: ${EIGEN3_INCLUDE_DIR}")
|
message(STATUS " Eigen3 Include: ${EIGEN3_INCLUDE_DIR}")
|
||||||
message(STATUS " OpenMP Enabled: ${ENABLE_OPENMP}")
|
message(STATUS " OpenMP Enabled: ${ENABLE_OPENMP}")
|
||||||
message(STATUS " Build Examples: ${BUILD_EXAMPLES}")
|
message(STATUS " Build Examples: ${BUILD_EXAMPLES}")
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
|
|||||||
@ -1,26 +1,33 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal EnableExtensions
|
||||||
|
|
||||||
echo === Building DetectBarIntersection ===
|
set "BUILD_CONFIG=Debug"
|
||||||
|
|
||||||
if not exist build (
|
if not "%~1"=="" (
|
||||||
mkdir build
|
if /I "%~1"=="Debug" (
|
||||||
)
|
set "BUILD_CONFIG=Debug"
|
||||||
|
) else if /I "%~1"=="Release" (
|
||||||
cd build
|
set "BUILD_CONFIG=Release"
|
||||||
|
) else (
|
||||||
cmake .. -G "Visual Studio 17 2022" -A x64
|
echo Usage: %~nx0 [Debug^|Release]
|
||||||
|
exit /b 1
|
||||||
if %ERRORLEVEL% NEQ 0 (
|
)
|
||||||
echo CMake configuration failed!
|
)
|
||||||
exit /b %ERRORLEVEL%
|
|
||||||
)
|
echo === Building DetectBarIntersection [%BUILD_CONFIG%] ===
|
||||||
|
|
||||||
cmake --build . --config Debug
|
cmake -S . -B build -G "Visual Studio 17 2022" -A x64
|
||||||
|
|
||||||
if %ERRORLEVEL% NEQ 0 (
|
if errorlevel 1 (
|
||||||
echo Build failed!
|
echo CMake configuration failed!
|
||||||
exit /b %ERRORLEVEL%
|
exit /b %ERRORLEVEL%
|
||||||
)
|
)
|
||||||
|
|
||||||
echo === Build completed successfully ===
|
cmake --build build --config %BUILD_CONFIG%
|
||||||
|
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo Build failed!
|
||||||
|
exit /b %ERRORLEVEL%
|
||||||
|
)
|
||||||
|
|
||||||
|
echo === Build completed successfully [%BUILD_CONFIG%] ===
|
||||||
|
|||||||
@ -8,8 +8,8 @@ if(WIN32)
|
|||||||
add_subdirectory(visualization_demo)
|
add_subdirectory(visualization_demo)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
message(STATUS "Examples Configuration:")
|
message(STATUS "Examples Configuration:")
|
||||||
message(STATUS " Build Type: ${CMAKE_BUILD_TYPE}")
|
message(STATUS " Build Type: ${DETECTBARINTERSECTION_BUILD_DESCRIPTION}")
|
||||||
message(STATUS " DetectBarIntersection Root: ${DETECTBARINTERSECTION_ROOT}")
|
message(STATUS " DetectBarIntersection Root: ${DETECTBARINTERSECTION_ROOT}")
|
||||||
message(STATUS "")
|
message(STATUS "")
|
||||||
|
|||||||
@ -54,11 +54,11 @@ public:
|
|||||||
const std::string& title = "Detection Result"
|
const std::string& title = "Detection Result"
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @brief Visualize best cluster highlighted (filtered aligned points) */
|
/** @brief Visualize best cluster highlighted (filtered aligned points) */
|
||||||
void VisualizeBestCluster(
|
void VisualizeBestCluster(
|
||||||
const SBarIntersectionResult& result,
|
const SBarIntersectionResult& result,
|
||||||
const std::string& title = "Best Cluster"
|
const std::string& title = "Best Cluster"
|
||||||
);
|
);
|
||||||
|
|
||||||
void SetInteractive(bool interactive);
|
void SetInteractive(bool interactive);
|
||||||
void SetOutputDirectory(const std::string& dir);
|
void SetOutputDirectory(const std::string& dir);
|
||||||
|
|||||||
@ -48,27 +48,38 @@ if(VTK_FOUND)
|
|||||||
${VTK_LIBRARIES}
|
${VTK_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
add_custom_command(TARGET visualization_demo POST_BUILD
|
add_custom_command(TARGET visualization_demo POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
"$<TARGET_FILE:BarIntersectionLib>"
|
"$<TARGET_FILE:BarIntersectionLib>"
|
||||||
"$<TARGET_FILE_DIR:visualization_demo>"
|
"$<TARGET_FILE_DIR:visualization_demo>"
|
||||||
COMMENT "Copying BarIntersectionLib DLL to visualization_demo output directory"
|
COMMENT "Copying BarIntersectionLib DLL to visualization_demo output directory"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(VTK_BIN_DIR "${DETECTBARINTERSECTION_ROOT}/../../3rdparty/VTK-8.2.0/bin")
|
set(VTK_BIN_DIR "${DETECTBARINTERSECTION_ROOT}/../../3rdparty/VTK-8.2.0/bin")
|
||||||
|
|
||||||
# Copy VTK DLLs
|
# Copy VTK DLLs
|
||||||
file(GLOB VTK_DLLS "${VTK_BIN_DIR}/*.dll")
|
file(GLOB VTK_DLLS "${VTK_BIN_DIR}/*.dll")
|
||||||
foreach(vtk_dll ${VTK_DLLS})
|
set(VTK_DEBUG_DLLS)
|
||||||
add_custom_command(TARGET visualization_demo POST_BUILD
|
set(VTK_RELEASE_DLLS)
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
foreach(vtk_dll ${VTK_DLLS})
|
||||||
"${vtk_dll}"
|
get_filename_component(vtk_dll_name "${vtk_dll}" NAME)
|
||||||
"$<TARGET_FILE_DIR:visualization_demo>"
|
if(vtk_dll_name MATCHES "d\\.dll$")
|
||||||
COMMENT "Copying VTK DLL: ${vtk_dll}"
|
list(APPEND VTK_DEBUG_DLLS "${vtk_dll}")
|
||||||
)
|
else()
|
||||||
endforeach()
|
list(APPEND VTK_RELEASE_DLLS "${vtk_dll}")
|
||||||
endif()
|
endif()
|
||||||
else()
|
endforeach()
|
||||||
message(WARNING "VTK not found. visualization_demo will not be built.")
|
|
||||||
endif()
|
add_custom_command(TARGET visualization_demo POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"$<$<CONFIG:Debug>:${VTK_DEBUG_DLLS}>"
|
||||||
|
"$<$<NOT:$<CONFIG:Debug>>:${VTK_RELEASE_DLLS}>"
|
||||||
|
"$<TARGET_FILE_DIR:visualization_demo>"
|
||||||
|
COMMAND_EXPAND_LISTS
|
||||||
|
COMMENT "Copying configuration-matched VTK DLLs to visualization_demo output directory"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(WARNING "VTK not found. visualization_demo will not be built.")
|
||||||
|
endif()
|
||||||
|
|||||||
@ -224,7 +224,7 @@ int ProcessSingleFile(
|
|||||||
debugCallbacks.onPlaneSegmented = nullptr;// OnPlaneSegmented;
|
debugCallbacks.onPlaneSegmented = nullptr;// OnPlaneSegmented;
|
||||||
debugCallbacks.onPointsAligned = nullptr;// OnPointsAligned;
|
debugCallbacks.onPointsAligned = nullptr;// OnPointsAligned;
|
||||||
debugCallbacks.onPlaneFiltered = nullptr;// OnPlaneFiltered;
|
debugCallbacks.onPlaneFiltered = nullptr;// OnPlaneFiltered;
|
||||||
debugCallbacks.onClustersDetected = OnClustersDetected;
|
debugCallbacks.onClustersDetected = nullptr;// OnClustersDetected;
|
||||||
debugCallbacks.userData = &callbackData;
|
debugCallbacks.userData = &callbackData;
|
||||||
|
|
||||||
// Set parameters
|
// Set parameters
|
||||||
|
|||||||
@ -1,93 +1,109 @@
|
|||||||
#ifndef BAR_INTERSECTION_PARAMS_H
|
#ifndef BAR_INTERSECTION_PARAMS_H
|
||||||
#define BAR_INTERSECTION_PARAMS_H
|
#define BAR_INTERSECTION_PARAMS_H
|
||||||
|
|
||||||
#include "VZNL_Types.h"
|
#include "VZNL_Types.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief RANSAC 平面分割参数
|
* @brief RANSAC 平面分割参数
|
||||||
*/
|
*/
|
||||||
struct SPlaneSegmentationParams {
|
struct SPlaneSegmentationParams {
|
||||||
float distanceThreshold; // 点到平面距离阈值 (mm)
|
float distanceThreshold; // 点到平面距离阈值 (mm)
|
||||||
int maxIterations; // RANSAC 最大迭代次数
|
int maxIterations; // RANSAC 最大迭代次数
|
||||||
int minPlanePoints; // 最小平面点数
|
int minPlanePoints; // 最小平面点数
|
||||||
float minPlaneRatio; // 平面最小点数占比
|
float minPlaneRatio; // 平面最小点数占比
|
||||||
float heightThreshold; // 高于平面的最小高度,用于过滤平面点 (mm)
|
float heightThreshold; // 高于平面的最小高度,用于过滤平面点 (mm)
|
||||||
|
|
||||||
SPlaneSegmentationParams()
|
SPlaneSegmentationParams()
|
||||||
: distanceThreshold(1.0f)
|
: distanceThreshold(1.0f)
|
||||||
, maxIterations(500)
|
, maxIterations(500)
|
||||||
, minPlanePoints(1000)
|
, minPlanePoints(1000)
|
||||||
, minPlaneRatio(0.05f)
|
, minPlaneRatio(0.05f)
|
||||||
, heightThreshold(10.0f)
|
, heightThreshold(10.0f)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 区域生长参数
|
* @brief 区域生长参数
|
||||||
*/
|
*/
|
||||||
struct SGrowthParams {
|
struct SGrowthParams {
|
||||||
float thresholdX; // 跨行合并阈值:相邻行 segment 质心 x 的最大允许差 (mm)
|
float thresholdX; // 跨行合并阈值:相邻行 segment 质心 x 的最大允许差 (mm)
|
||||||
float thresholdY; // 行内分段阈值:同行相邻点 y 的最大允许差 (mm)
|
float thresholdY; // 行内分段阈值:同行相邻点 y 的最大允许差 (mm)
|
||||||
float thresholdZ; // 行内分段阈值:同行相邻点 z 的最大允许差 (mm)
|
float thresholdZ; // 行内分段阈值:同行相邻点 z 的最大允许差 (mm)
|
||||||
int minClusterSize; // 最小簇点数
|
int minClusterSize; // 最小簇点数
|
||||||
|
float angleSearchDistance;
|
||||||
SGrowthParams()
|
int residualSmoothWindow;
|
||||||
: thresholdX(5.0f)
|
float maxAxisDeviationFromXYDeg; // Max allowed axis deviation from the XY plane (deg)
|
||||||
, thresholdY(5.0f)
|
float maxPerpendicularDeviationDeg; // Max deviation from 90 deg for centroid-connection test
|
||||||
, thresholdZ(5.0f)
|
int minContinuousValidPointCount; // Min consecutive valid points kept on each laser line after plane filtering; <=1 disables
|
||||||
, minClusterSize(20)
|
float maxContinuousPointZTolerance; // Max z difference between adjacent points inside one valid run (mm)
|
||||||
{}
|
float minBarDiameter;
|
||||||
};
|
float maxBarDiameter;
|
||||||
|
|
||||||
/**
|
SGrowthParams()
|
||||||
* @brief 生长结果:一个连通簇
|
: thresholdX(5.0f)
|
||||||
*/
|
, thresholdY(5.0f)
|
||||||
struct SGrowthCluster {
|
, thresholdZ(5.0f)
|
||||||
std::vector<int> pointIndices; // 簇内点在原始 rows*cols 网格中的线性索引
|
, minClusterSize(20)
|
||||||
SVzNL3DPointF centroid; // 簇质心
|
, angleSearchDistance(2.f)
|
||||||
SVzNL3DPointF minBound; // 包围盒最小角
|
, residualSmoothWindow(5)
|
||||||
SVzNL3DPointF maxBound; // 包围盒最大角
|
, maxAxisDeviationFromXYDeg(50.0f)
|
||||||
int pointCount;
|
, maxPerpendicularDeviationDeg(50.0f)
|
||||||
|
, minContinuousValidPointCount(0)
|
||||||
SGrowthCluster()
|
, maxContinuousPointZTolerance(5.0f)
|
||||||
: centroid(), minBound(), maxBound(), pointCount(0)
|
, minBarDiameter(10.0f)
|
||||||
{}
|
, maxBarDiameter(30.0f)
|
||||||
};
|
{}
|
||||||
|
};
|
||||||
/**
|
|
||||||
* @brief 检测结果
|
/**
|
||||||
*/
|
* @brief 生长结果:一个连通簇
|
||||||
struct SBarIntersectionResult {
|
*/
|
||||||
// 平面信息
|
struct SGrowthCluster {
|
||||||
SVzNL3DPointF planeNormal; // 平面法向量
|
std::vector<int> pointIndices; // 簇内点在原始 rows*cols 网格中的线性索引
|
||||||
float planeD; // 平面方程 ax+by+cz+d=0 中的 d
|
SVzNL3DPointF centroid; // 簇质心
|
||||||
|
SVzNL3DPointF minBound; // 包围盒最小角
|
||||||
// 簇(原始坐标系)
|
SVzNL3DPointF maxBound; // 包围盒最大角
|
||||||
int clusterCount;
|
int pointCount;
|
||||||
SGrowthCluster* clusters;
|
|
||||||
|
SGrowthCluster()
|
||||||
// 最优簇索引(-1表示无有效簇)
|
: centroid(), minBound(), maxBound(), pointCount(0)
|
||||||
int bestClusterIndex;
|
{}
|
||||||
|
};
|
||||||
// 最优簇的过滤后点云(对齐坐标系,用于可视化)
|
|
||||||
std::vector<SVzNLPointXYZ> bestClusterPoints;
|
/**
|
||||||
|
* @brief 检测结果
|
||||||
SBarIntersectionResult()
|
*/
|
||||||
: planeNormal(), planeD(0.0f)
|
struct SBarIntersectionResult {
|
||||||
, clusterCount(0), clusters(nullptr)
|
// 平面信息
|
||||||
, bestClusterIndex(-1)
|
SVzNL3DPointF planeNormal; // 平面法向量
|
||||||
{}
|
float planeD; // 平面方程 ax+by+cz+d=0 中的 d
|
||||||
};
|
|
||||||
|
// 簇(原始坐标系)
|
||||||
/**
|
int clusterCount;
|
||||||
* @brief 释放检测结果内存
|
SGrowthCluster* clusters;
|
||||||
*/
|
|
||||||
inline void FreeBarIntersectionResult(SBarIntersectionResult* result) {
|
// 最优簇索引(-1表示无有效簇)
|
||||||
if (result) {
|
int bestClusterIndex;
|
||||||
if (result->clusters) { delete[] result->clusters; result->clusters = nullptr; }
|
|
||||||
result->clusterCount = 0;
|
// 最优簇的过滤后点云(对齐坐标系,用于可视化)
|
||||||
}
|
std::vector<SVzNLPointXYZ> bestClusterPoints;
|
||||||
}
|
|
||||||
|
SBarIntersectionResult()
|
||||||
#endif // BAR_INTERSECTION_PARAMS_H
|
: planeNormal(), planeD(0.0f)
|
||||||
|
, clusterCount(0), clusters(nullptr)
|
||||||
|
, bestClusterIndex(-1)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 释放检测结果内存
|
||||||
|
*/
|
||||||
|
inline void FreeBarIntersectionResult(SBarIntersectionResult* result) {
|
||||||
|
if (result) {
|
||||||
|
if (result->clusters) { delete[] result->clusters; result->clusters = nullptr; }
|
||||||
|
result->clusterCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // BAR_INTERSECTION_PARAMS_H
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "BarIntersection.h"
|
#include "BarIntersection.h"
|
||||||
#include "PlaneAlignment.h"
|
#include "PlaneAlignment.h"
|
||||||
#include "RegionGrowing.h"
|
#include "RegionGrowing.h"
|
||||||
|
#include <Eigen/Dense>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -9,7 +10,7 @@
|
|||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
static const char* s_algorithmVersion = "2.0.0";
|
static const char* s_algorithmVersion = "1.0.0";
|
||||||
static const char* s_algorithmName = "BarIntersectionDetection";
|
static const char* s_algorithmName = "BarIntersectionDetection";
|
||||||
|
|
||||||
static bool IsValidPoint(const SVzNLPointXYZ& pt) {
|
static bool IsValidPoint(const SVzNLPointXYZ& pt) {
|
||||||
@ -17,7 +18,107 @@ static bool IsValidPoint(const SVzNLPointXYZ& pt) {
|
|||||||
return !(std::abs(pt.x) < eps && std::abs(pt.y) < eps && std::abs(pt.z) < eps);
|
return !(std::abs(pt.x) < eps && std::abs(pt.y) < eps && std::abs(pt.z) < eps);
|
||||||
}
|
}
|
||||||
|
|
||||||
BAR_INTERSECTION_API int DetectBarIntersections(
|
static void ClearPoint(SVzNLPointXYZ* pt) {
|
||||||
|
if (!pt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pt->x = 0.0f;
|
||||||
|
pt->y = 0.0f;
|
||||||
|
pt->z = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ClearShortContinuousSegment(
|
||||||
|
SVzNLPointXYZ* rowPoints,
|
||||||
|
int startCol,
|
||||||
|
int segmentLength,
|
||||||
|
int minContinuousValidPointCount
|
||||||
|
) {
|
||||||
|
if (!rowPoints || startCol < 0 || segmentLength <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segmentLength >= minContinuousValidPointCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int offset = 0; offset < segmentLength; ++offset) {
|
||||||
|
ClearPoint(&rowPoints[startCol + offset]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FilterLaserLineNoiseInPlace(
|
||||||
|
SVzNLPointXYZ* alignedPoints,
|
||||||
|
int rows,
|
||||||
|
int cols,
|
||||||
|
int minContinuousValidPointCount,
|
||||||
|
float maxContinuousPointZTolerance
|
||||||
|
) {
|
||||||
|
if (!alignedPoints || rows <= 0 || cols <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minContinuousValidPointCount <= 1 || maxContinuousPointZTolerance < 0.0f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int row = 0; row < rows; ++row) {
|
||||||
|
SVzNLPointXYZ* rowPoints = alignedPoints + row * cols;
|
||||||
|
int segmentStartCol = -1;
|
||||||
|
int segmentLength = 0;
|
||||||
|
float prevZ = 0.0f;
|
||||||
|
|
||||||
|
for (int col = 0; col < cols; ++col) {
|
||||||
|
const SVzNLPointXYZ& pt = rowPoints[col];
|
||||||
|
if (!IsValidPoint(pt)) {
|
||||||
|
ClearShortContinuousSegment(
|
||||||
|
rowPoints,
|
||||||
|
segmentStartCol,
|
||||||
|
segmentLength,
|
||||||
|
minContinuousValidPointCount
|
||||||
|
);
|
||||||
|
segmentStartCol = -1;
|
||||||
|
segmentLength = 0;
|
||||||
|
prevZ = 0.0f;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segmentLength <= 0) {
|
||||||
|
segmentStartCol = col;
|
||||||
|
segmentLength = 1;
|
||||||
|
prevZ = pt.z;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float zDiff = std::abs(pt.z - prevZ);
|
||||||
|
if (zDiff <= maxContinuousPointZTolerance) {
|
||||||
|
++segmentLength;
|
||||||
|
prevZ = pt.z;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearShortContinuousSegment(
|
||||||
|
rowPoints,
|
||||||
|
segmentStartCol,
|
||||||
|
segmentLength,
|
||||||
|
minContinuousValidPointCount
|
||||||
|
);
|
||||||
|
|
||||||
|
segmentStartCol = col;
|
||||||
|
segmentLength = 1;
|
||||||
|
prevZ = pt.z;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearShortContinuousSegment(
|
||||||
|
rowPoints,
|
||||||
|
segmentStartCol,
|
||||||
|
segmentLength,
|
||||||
|
minContinuousValidPointCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int DetectBarIntersections(
|
||||||
const SVzNLPointXYZ* points,
|
const SVzNLPointXYZ* points,
|
||||||
int rows,
|
int rows,
|
||||||
int cols,
|
int cols,
|
||||||
@ -107,6 +208,17 @@ BAR_INTERSECTION_API int DetectBarIntersections(
|
|||||||
alignedPoints, totalPoints, planeZ, planeParams.heightThreshold
|
alignedPoints, totalPoints, planeZ, planeParams.heightThreshold
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Step 2b: 按激光线过滤短连续段噪点
|
||||||
|
// ============================================
|
||||||
|
FilterLaserLineNoiseInPlace(
|
||||||
|
alignedPoints,
|
||||||
|
rows,
|
||||||
|
cols,
|
||||||
|
growthParams.minContinuousValidPointCount,
|
||||||
|
growthParams.maxContinuousPointZTolerance
|
||||||
|
);
|
||||||
|
|
||||||
// Debug callback: plane filtered
|
// Debug callback: plane filtered
|
||||||
if (debugCallbacks && debugCallbacks->onPlaneFiltered) {
|
if (debugCallbacks && debugCallbacks->onPlaneFiltered) {
|
||||||
// 收集非零点用于回调显示
|
// 收集非零点用于回调显示
|
||||||
@ -137,7 +249,10 @@ BAR_INTERSECTION_API int DetectBarIntersections(
|
|||||||
// ============================================
|
// ============================================
|
||||||
std::vector<SGrowthCluster> clusters;
|
std::vector<SGrowthCluster> clusters;
|
||||||
bar_intersection::RegionGrowClusters(
|
bar_intersection::RegionGrowClusters(
|
||||||
alignedPoints, rows, cols, growthParams, clusters
|
alignedPoints, rows, cols, true, growthParams, clusters
|
||||||
|
);
|
||||||
|
bar_intersection::RegionGrowClusters(
|
||||||
|
alignedPoints, rows, cols, false, growthParams, clusters
|
||||||
);
|
);
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
#ifndef REGION_GROWING_H
|
#ifndef REGION_GROWING_H
|
||||||
#define REGION_GROWING_H
|
#define REGION_GROWING_H
|
||||||
|
|
||||||
#include "VZNL_Types.h"
|
#include "VZNL_Types.h"
|
||||||
@ -28,6 +28,7 @@ int RegionGrowClusters(
|
|||||||
const SVzNLPointXYZ* points,
|
const SVzNLPointXYZ* points,
|
||||||
int rows,
|
int rows,
|
||||||
int cols,
|
int cols,
|
||||||
|
bool bHorizontalScan,
|
||||||
const SGrowthParams& params,
|
const SGrowthParams& params,
|
||||||
std::vector<SGrowthCluster>& outClusters
|
std::vector<SGrowthCluster>& outClusters
|
||||||
);
|
);
|
||||||
|
|||||||
@ -16,6 +16,8 @@ struct RansacPlaneSegmentationParams {
|
|||||||
float minPlaneRatio; // 平面最小点数占比 (相对最大平面), 建议 0.05-0.2
|
float minPlaneRatio; // 平面最小点数占比 (相对最大平面), 建议 0.05-0.2
|
||||||
float maxNormalAngleDeg; // 平面法向量与Z轴最大夹角 (度), 超过则直接丢弃; <=0 表示不过滤
|
float maxNormalAngleDeg; // 平面法向量与Z轴最大夹角 (度), 超过则直接丢弃; <=0 表示不过滤
|
||||||
|
|
||||||
|
float maxDistFromPlane; // 点到平面的最大允许距离;超出则从 pointIndices 中移除;<=0 表示不过滤
|
||||||
|
|
||||||
RansacPlaneSegmentationParams()
|
RansacPlaneSegmentationParams()
|
||||||
: distanceThreshold(0.5f)
|
: distanceThreshold(0.5f)
|
||||||
, maxIterations(500)
|
, maxIterations(500)
|
||||||
@ -24,6 +26,7 @@ struct RansacPlaneSegmentationParams {
|
|||||||
, growthZThreshold(1.0f)
|
, growthZThreshold(1.0f)
|
||||||
, minPlaneRatio(0.1f)
|
, minPlaneRatio(0.1f)
|
||||||
, maxNormalAngleDeg(30.0f)
|
, maxNormalAngleDeg(30.0f)
|
||||||
|
, maxDistFromPlane(1.f)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const static char* g_algo_name = "DetectHole";
|
const static char* g_algo_name = "DetectHole";
|
||||||
const static char* g_algo_ver = "1.0.2";
|
const static char* g_algo_ver = "1.0.3";
|
||||||
|
|
||||||
|
|
||||||
void PrintHoleResult(const SHoleResult& hole, int index) {
|
void PrintHoleResult(const SHoleResult& hole, int index) {
|
||||||
@ -196,6 +196,7 @@ int DetectMultipleHoles(
|
|||||||
|
|
||||||
// ============ Step 2: 法向量过滤 ============
|
// ============ Step 2: 法向量过滤 ============
|
||||||
NormalFilterParams normalParams;
|
NormalFilterParams normalParams;
|
||||||
|
normalParams.maxDistFromPlane = ransacParams.maxDistFromPlane;
|
||||||
FilterPlanesByNormal(planes, normalParams, points, totalPointCount);
|
FilterPlanesByNormal(planes, normalParams, points, totalPointCount);
|
||||||
|
|
||||||
// 如果没有找到有效平面,回退到处理整个点云
|
// 如果没有找到有效平面,回退到处理整个点云
|
||||||
|
|||||||
@ -298,7 +298,7 @@ struct NormalFilterParams {
|
|||||||
, refNx(0.0f)
|
, refNx(0.0f)
|
||||||
, refNy(0.0f)
|
, refNy(0.0f)
|
||||||
, refNz(1.0f)
|
, refNz(1.0f)
|
||||||
, maxDistFromPlane(2.0f)
|
, maxDistFromPlane(0.0f)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user