WoodAlgo/Algo/DetectHole/include/HoleDetection.h

265 lines
9.0 KiB
C

#ifndef HOLE_DETECTION_H
#define HOLE_DETECTION_H
#include "HoleDetectionParams.h"
#include "ErrorCodes.h"
#include "VZNL_Types.h"
// Export macro for shared library
#ifdef _WIN32
#ifdef HOLE_DETECTION_EXPORTS
#define HOLE_DETECTION_API __declspec(dllexport)
#else
#define HOLE_DETECTION_API __declspec(dllimport)
#endif
#else
#ifdef HOLE_DETECTION_EXPORTS
#define HOLE_DETECTION_API __attribute__((visibility("default")))
#else
#define HOLE_DETECTION_API
#endif
#endif
/**
* @brief Boundary point with grid location (for debug callbacks)
*/
struct SHoleBoundaryPoint {
SVzNLPointXYZ point; // 点坐标
int row; // 行号
int col; // 列号
SHoleBoundaryPoint()
: point(), row(-1), col(-1) {}
SHoleBoundaryPoint(const SVzNLPointXYZ& p, int r, int c)
: 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
*/
struct SClusterInfo {
const SHoleBoundaryPoint* points; // Points in this cluster
int count; // Number of points in this cluster
};
/**
* @brief Plane segmentation information for debug callbacks
*/
struct SPlaneSegmentInfo {
float normalX, normalY, normalZ; // 平面法向量 (归一化)
int pointCount; // 该平面的点数
const int* pointIndices; // 点索引数组 (在原始grid中的索引)
float normalAngleDeg; // 法向量与参考方向的夹角 (度)
};
/**
* @brief Debug callbacks for visualization and debugging
*
* All callbacks are optional (can be nullptr).
* userData is passed to all callbacks for user context.
*/
struct SHoleDetectionDebugCallbacks {
/**
* @brief Called after boundary points are detected
* @param points Array of boundary points
* @param count Number of boundary points
* @param userData User-provided context pointer
*/
void (*onBoundaryDetected)(const SHoleBoundaryPoint* points, int count, void* userData);
/**
* @brief Called after clustering is complete
* @param clusters Array of cluster information
* @param clusterCount Number of clusters found
* @param userData User-provided context pointer
*/
void (*onClustersFound)(const SClusterInfo* clusters, int clusterCount, void* userData);
/**
* @brief Called when expanded region points are collected for plane fitting
* @param clusterPoints Array of cluster boundary points
* @param clusterCount Number of cluster points
* @param expandedPoints Array of expanded region points (surrounding flat surface)
* @param expandedCount Number of expanded region points
* @param userData User-provided context pointer
*/
void (*onExpandedRegion)(const SHoleBoundaryPoint* clusterPoints, int clusterCount,
const SVzNLPointXYZ* expandedPoints, int expandedCount,
void* userData);
/**
* @brief Called after each hole is fitted
* @param hole The fitted hole result
* @param holeIndex Index of this hole (0-based)
* @param userData User-provided context pointer
*/
void (*onHoleFitted)(const SHoleResult* hole, int holeIndex, void* userData);
/**
* @brief Called when segment endpoint pairs are detected
* @param segmentPairs Array of segment pairs (each pair has start and end points)
* @param count Number of segment pairs
* @param userData User-provided context pointer
*/
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 Called after RANSAC plane segmentation and normal filtering
* @param planes Array of plane segment information
* @param planeCount Number of planes
* @param totalPointCount Total number of points in the grid (rows * cols)
* @param userData User-provided context pointer
*/
void (*onPlanesSegmented)(
const SPlaneSegmentInfo* planes,
int planeCount,
int totalPointCount,
void* userData
);
/**
* @brief Called after masked points grid is built for a plane
* @param points Masked point array (grid format, rows * cols)
* @param rows Row count
* @param cols Column count
* @param planeIndex Index of the current plane (0-based)
* @param userData User-provided context pointer
*/
void (*onMaskedPointsReady)(
const SVzNLPointXYZ* points,
int rows,
int cols,
int planeIndex,
void* userData
);
/**
* @brief User-provided context pointer, passed to all callbacks
*/
void* userData;
/**
* @brief If non-null, each cluster's boundary points are saved as a CSV file
* in this directory before fitting. Files are named cluster_0.csv, cluster_1.csv, ...
* Fields: row,col,x,y,z
*/
const char* clusterOutputDir;
};
/**
* @brief Detect multiple holes in point cloud
*
* This is the main entry point for hole detection. It performs the following steps:
* 1. Validate grid format
* 2. Detect pit boundaries
* 3. Cluster boundary points
* 4. Fit ellipse and plane for each cluster
* 5. Filter and sort results
*
* @param [in] points Input point cloud (grid format)
* @param [in] rows Number of rows
* @param [in] cols Number of columns
* @param [in] detectionParams Detection parameters
* @param [in] filterParams Filter parameters
* @param [out] result Output result structure
* @param [in] debugCallbacks Optional debug callbacks for visualization (can be nullptr)
* @return 0 on success, non-zero on error
*
* @pre points != nullptr
* @pre rows > 0
* @pre cols > 0
* @pre result != nullptr
*
* @post If return value is 0, result contains detected holes
* @post Caller must free result->holes using delete[]
*/
HOLE_DETECTION_API int DetectMultipleHoles(
const SVzNLPointXYZ* points,
int rows,
int cols,
const SHoleDetectionParams& detectionParams,
const SHoleFilterParams& filterParams,
SMultiHoleResult* result,
const SHoleDetectionDebugCallbacks* debugCallbacks = nullptr
);
/**
* @brief Get algorithm name and version information
*
* Returned pointers refer to internal static strings and must not be freed
* or modified by the caller. Either output parameter can be nullptr.
*
* @param [out] algorithmName Output algorithm name string
* @param [out] algorithmVersion Output algorithm version string
*/
HOLE_DETECTION_API const char* GetAlgorithmVersion();
HOLE_DETECTION_API const char* GetAlgorithmName();
/**
* @brief Print detection results to console in a formatted way
*
* @param [in] result Detection result structure
* @param [in] verbose If true, print detailed information for each hole
*/
HOLE_DETECTION_API void PrintDetectionResults(const SMultiHoleResult& result, bool verbose = true);
#endif // HOLE_DETECTION_H