恢复到我自己的生长算法
This commit is contained in:
parent
9d304b19ac
commit
3aae9159ed
@ -335,136 +335,26 @@ void BarIntersectionVisualizer::VisualizeResult(
|
||||
}
|
||||
|
||||
void BarIntersectionVisualizer::VisualizeBestCluster(
|
||||
const SVzNLPointXYZ* points, int count,
|
||||
const SBarIntersectionResult& result,
|
||||
const std::string& title
|
||||
) {
|
||||
if (!points || count <= 0 || result.validClusterIndices.empty()) {
|
||||
std::cout << "No valid bar-like cluster to visualize." << std::endl;
|
||||
if (result.bestClusterIndex < 0 || result.bestClusterPoints.empty()) {
|
||||
std::cout << "No valid best cluster to visualize." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
auto renderer = vtkSmartPointer<vtkRenderer>::New();
|
||||
|
||||
vtkActor* allPointsActor = static_cast<vtkActor*>(
|
||||
// Best cluster filtered points (aligned coordinate system)
|
||||
vtkActor* bestActor = static_cast<vtkActor*>(
|
||||
CreatePointCloudActor(
|
||||
points,
|
||||
count,
|
||||
0.75, 0.75, 0.75, 2.0
|
||||
result.bestClusterPoints.data(),
|
||||
(int)result.bestClusterPoints.size(),
|
||||
0.0, 1.0, 0.3, 3.0
|
||||
)
|
||||
);
|
||||
renderer->AddActor(allPointsActor);
|
||||
allPointsActor->Delete();
|
||||
|
||||
double clusterColors[][3] = {
|
||||
{1.0, 0.2, 0.1}, {0.1, 0.8, 0.2}, {0.1, 0.6, 1.0},
|
||||
{1.0, 0.8, 0.1}, {1.0, 0.2, 0.8}, {0.0, 0.9, 0.9}
|
||||
};
|
||||
const int numColors = 6;
|
||||
|
||||
for (size_t i = 0; i < result.validClusterIndices.size(); ++i) {
|
||||
const int clusterIdx = result.validClusterIndices[i];
|
||||
if (clusterIdx < 0 || clusterIdx >= result.clusterCount) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const SGrowthCluster& cluster = result.clusters[clusterIdx];
|
||||
std::vector<SVzNLPointXYZ> clusterPts;
|
||||
clusterPts.reserve(cluster.pointCount);
|
||||
for (size_t j = 0; j < cluster.pointIndices.size(); ++j) {
|
||||
const int linearIdx = cluster.pointIndices[j];
|
||||
if (linearIdx >= 0 && linearIdx < count) {
|
||||
clusterPts.push_back(points[linearIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
if (clusterPts.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int ci = static_cast<int>(i % numColors);
|
||||
vtkActor* clusterActor = static_cast<vtkActor*>(
|
||||
CreatePointCloudActor(
|
||||
clusterPts.data(),
|
||||
(int)clusterPts.size(),
|
||||
clusterColors[ci][0], clusterColors[ci][1], clusterColors[ci][2],
|
||||
4.5
|
||||
)
|
||||
);
|
||||
renderer->AddActor(clusterActor);
|
||||
clusterActor->Delete();
|
||||
}
|
||||
renderer->AddActor(bestActor);
|
||||
bestActor->Delete();
|
||||
|
||||
ShowWindow(renderer, title, m_interactive, this, "06_best_cluster.png");
|
||||
}
|
||||
|
||||
void BarIntersectionVisualizer::VisualizeStep5Selection(
|
||||
const SBarIntersectionResult& result,
|
||||
const std::string& title
|
||||
) {
|
||||
if (result.step5BaseClusterIndex < 0 ||
|
||||
result.step5BaseClusterAlignedPoints.empty() ||
|
||||
result.step5FilteredAlignedPoints.empty()) {
|
||||
std::cout << "No valid Step 5 selection to visualize." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
auto renderer = vtkSmartPointer<vtkRenderer>::New();
|
||||
|
||||
vtkActor* allPointsActor = static_cast<vtkActor*>(
|
||||
CreatePointCloudActor(
|
||||
result.step5FilteredAlignedPoints.data(),
|
||||
static_cast<int>(result.step5FilteredAlignedPoints.size()),
|
||||
0.75, 0.75, 0.75, 2.0
|
||||
)
|
||||
);
|
||||
renderer->AddActor(allPointsActor);
|
||||
allPointsActor->Delete();
|
||||
|
||||
vtkActor* baseActor = static_cast<vtkActor*>(
|
||||
CreatePointCloudActor(
|
||||
result.step5BaseClusterAlignedPoints.data(),
|
||||
static_cast<int>(result.step5BaseClusterAlignedPoints.size()),
|
||||
1.0, 0.2, 0.1, 5.0
|
||||
)
|
||||
);
|
||||
renderer->AddActor(baseActor);
|
||||
baseActor->Delete();
|
||||
|
||||
const size_t perpendicularClusterCount = std::min(
|
||||
result.step5PerpendicularClusterAlignedPoints.size(),
|
||||
result.step5PerpendicularClusterAlignedCentroids.size()
|
||||
);
|
||||
|
||||
for (size_t i = 0; i < perpendicularClusterCount; ++i) {
|
||||
const std::vector<SVzNLPointXYZ>& clusterPoints = result.step5PerpendicularClusterAlignedPoints[i];
|
||||
if (!clusterPoints.empty()) {
|
||||
vtkActor* clusterActor = static_cast<vtkActor*>(
|
||||
CreatePointCloudActor(
|
||||
clusterPoints.data(),
|
||||
static_cast<int>(clusterPoints.size()),
|
||||
0.1, 0.6, 1.0, 4.5
|
||||
)
|
||||
);
|
||||
renderer->AddActor(clusterActor);
|
||||
clusterActor->Delete();
|
||||
}
|
||||
|
||||
const SVzNLPointXYZ& clusterCentroid = result.step5PerpendicularClusterAlignedCentroids[i];
|
||||
vtkActor* lineActor = static_cast<vtkActor*>(
|
||||
CreateLineActor(
|
||||
result.step5BaseClusterAlignedCentroid.x,
|
||||
result.step5BaseClusterAlignedCentroid.y,
|
||||
result.step5BaseClusterAlignedCentroid.z,
|
||||
clusterCentroid.x,
|
||||
clusterCentroid.y,
|
||||
clusterCentroid.z,
|
||||
1.0, 1.0, 0.1, 3.0
|
||||
)
|
||||
);
|
||||
renderer->AddActor(lineActor);
|
||||
lineActor->Delete();
|
||||
}
|
||||
|
||||
ShowWindow(renderer, title, m_interactive, this, "07_step5_selection.png");
|
||||
}
|
||||
|
||||
@ -54,18 +54,11 @@ public:
|
||||
const std::string& title = "Detection Result"
|
||||
);
|
||||
|
||||
/** @brief Visualize best cluster highlighted on the full point cloud */
|
||||
/** @brief Visualize best cluster highlighted (filtered aligned points) */
|
||||
void VisualizeBestCluster(
|
||||
const SVzNLPointXYZ* points, int count,
|
||||
const SBarIntersectionResult& result,
|
||||
const std::string& title = "Best Cluster"
|
||||
);
|
||||
|
||||
/** @brief Visualize Step 5 base cluster and perpendicular matches */
|
||||
void VisualizeStep5Selection(
|
||||
const SBarIntersectionResult& result,
|
||||
const std::string& title = "Step 5 Selection (Aligned)"
|
||||
);
|
||||
|
||||
void SetInteractive(bool interactive);
|
||||
void SetOutputDirectory(const std::string& dir);
|
||||
|
||||
@ -251,17 +251,8 @@ int ProcessSingleFile(
|
||||
// Final visualization
|
||||
visualizer.VisualizeResult(points, rows, cols, result, "Final Result");
|
||||
|
||||
// Visualize all valid bar-like clusters on the full point cloud
|
||||
visualizer.VisualizeBestCluster(
|
||||
points, rows * cols,
|
||||
result,
|
||||
"Valid Bar-Like Clusters on Full Point Cloud"
|
||||
);
|
||||
|
||||
visualizer.VisualizeStep5Selection(
|
||||
result,
|
||||
"Step 5: Min-Z Base Cluster and XY-Perpendicular Matches (Aligned)"
|
||||
);
|
||||
// Best cluster visualization (filtered aligned points)
|
||||
visualizer.VisualizeBestCluster(result, "Best Cluster (min Z, high pts)");
|
||||
|
||||
// Cleanup
|
||||
FreeBarIntersectionResult(&result);
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
#ifndef BAR_INTERSECTION_PARAMS_H
|
||||
#ifndef BAR_INTERSECTION_PARAMS_H
|
||||
#define BAR_INTERSECTION_PARAMS_H
|
||||
|
||||
#include "VZNL_Types.h"
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief RANSAC plane segmentation parameters
|
||||
* @brief RANSAC 平面分割参数
|
||||
*/
|
||||
struct SPlaneSegmentationParams {
|
||||
float distanceThreshold; // Point-to-plane distance threshold (mm)
|
||||
int maxIterations; // Maximum RANSAC iterations
|
||||
int minPlanePoints; // Minimum plane point count
|
||||
float minPlaneRatio; // Minimum plane point ratio
|
||||
float heightThreshold; // Minimum height above the plane to keep (mm)
|
||||
float distanceThreshold; // 点到平面距离阈值 (mm)
|
||||
int maxIterations; // RANSAC 最大迭代次数
|
||||
int minPlanePoints; // 最小平面点数
|
||||
float minPlaneRatio; // 平面最小点数占比
|
||||
float heightThreshold; // 高于平面的最小高度,用于过滤平面点 (mm)
|
||||
|
||||
SPlaneSegmentationParams()
|
||||
: distanceThreshold(1.0f)
|
||||
@ -24,13 +24,13 @@ struct SPlaneSegmentationParams {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Region growing parameters
|
||||
* @brief 区域生长参数
|
||||
*/
|
||||
struct SGrowthParams {
|
||||
float thresholdX; // Max centroid-x difference when merging adjacent row segments (mm)
|
||||
float thresholdY; // Max y difference for adjacent points in the same row (mm)
|
||||
float thresholdZ; // Max z difference for adjacent points in the same row (mm)
|
||||
int minClusterSize; // Minimum cluster size
|
||||
float thresholdX; // 跨行合并阈值:相邻行 segment 质心 x 的最大允许差 (mm)
|
||||
float thresholdY; // 行内分段阈值:同行相邻点 y 的最大允许差 (mm)
|
||||
float thresholdZ; // 行内分段阈值:同行相邻点 z 的最大允许差 (mm)
|
||||
int minClusterSize; // 最小簇点数
|
||||
float maxAxisDeviationFromXYDeg; // Max allowed axis deviation from the XY plane (deg)
|
||||
float maxPerpendicularDeviationDeg; // Max deviation from 90 deg for centroid-connection test
|
||||
int minContinuousValidPointCount; // Min consecutive valid points kept on each laser line after plane filtering; <=1 disables
|
||||
@ -49,13 +49,13 @@ struct SGrowthParams {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Region growing output: one connected cluster
|
||||
* @brief 生长结果:一个连通簇
|
||||
*/
|
||||
struct SGrowthCluster {
|
||||
std::vector<int> pointIndices; // Linear indices in the original rows*cols grid
|
||||
SVzNL3DPointF centroid;
|
||||
SVzNL3DPointF minBound;
|
||||
SVzNL3DPointF maxBound;
|
||||
std::vector<int> pointIndices; // 簇内点在原始 rows*cols 网格中的线性索引
|
||||
SVzNL3DPointF centroid; // 簇质心
|
||||
SVzNL3DPointF minBound; // 包围盒最小角
|
||||
SVzNL3DPointF maxBound; // 包围盒最大角
|
||||
int pointCount;
|
||||
|
||||
SGrowthCluster()
|
||||
@ -64,64 +64,37 @@ struct SGrowthCluster {
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Detection result
|
||||
* @brief 检测结果
|
||||
*/
|
||||
struct SBarIntersectionResult {
|
||||
// Plane information
|
||||
SVzNL3DPointF planeNormal;
|
||||
float planeD; // Plane equation ax + by + cz + d = 0
|
||||
// 平面信息
|
||||
SVzNL3DPointF planeNormal; // 平面法向量
|
||||
float planeD; // 平面方程 ax+by+cz+d=0 中的 d
|
||||
|
||||
// Clusters in the original coordinate system
|
||||
// 簇(原始坐标系)
|
||||
int clusterCount;
|
||||
SGrowthCluster* clusters;
|
||||
|
||||
// Best cluster index, -1 means no valid cluster
|
||||
// 最优簇索引(-1表示无有效簇)
|
||||
int bestClusterIndex;
|
||||
|
||||
// All cluster indices that satisfy the bar geometry constraint
|
||||
std::vector<int> validClusterIndices;
|
||||
|
||||
// Step 5: the valid cluster with minimum z centroid
|
||||
int step5BaseClusterIndex;
|
||||
|
||||
// Step 5: valid clusters whose centroid-connection line is perpendicular to the base cluster axis
|
||||
std::vector<int> step5PerpendicularClusterIndices;
|
||||
|
||||
// Step 5 visualization data kept in aligned coordinates
|
||||
std::vector<SVzNLPointXYZ> step5FilteredAlignedPoints;
|
||||
std::vector<SVzNLPointXYZ> step5BaseClusterAlignedPoints;
|
||||
SVzNLPointXYZ step5BaseClusterAlignedCentroid;
|
||||
std::vector<std::vector<SVzNLPointXYZ> > step5PerpendicularClusterAlignedPoints;
|
||||
std::vector<SVzNLPointXYZ> step5PerpendicularClusterAlignedCentroids;
|
||||
|
||||
// Best cluster filtered points in the original coordinate system
|
||||
// 最优簇的过滤后点云(对齐坐标系,用于可视化)
|
||||
std::vector<SVzNLPointXYZ> bestClusterPoints;
|
||||
|
||||
SBarIntersectionResult()
|
||||
: planeNormal(), planeD(0.0f)
|
||||
, clusterCount(0), clusters(nullptr)
|
||||
, bestClusterIndex(-1)
|
||||
, step5BaseClusterIndex(-1)
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Release memory in a detection result
|
||||
* @brief 释放检测结果内存
|
||||
*/
|
||||
inline void FreeBarIntersectionResult(SBarIntersectionResult* result) {
|
||||
if (result) {
|
||||
if (result->clusters) { delete[] result->clusters; result->clusters = nullptr; }
|
||||
result->clusterCount = 0;
|
||||
result->bestClusterIndex = -1;
|
||||
result->validClusterIndices.clear();
|
||||
result->step5BaseClusterIndex = -1;
|
||||
result->step5PerpendicularClusterIndices.clear();
|
||||
result->step5FilteredAlignedPoints.clear();
|
||||
result->step5BaseClusterAlignedPoints.clear();
|
||||
result->step5BaseClusterAlignedCentroid = SVzNLPointXYZ();
|
||||
result->step5PerpendicularClusterAlignedPoints.clear();
|
||||
result->step5PerpendicularClusterAlignedCentroids.clear();
|
||||
result->bestClusterPoints.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -610,8 +610,6 @@ BAR_INTERSECTION_API int DetectBarIntersections(
|
||||
// 在该范围内选点数最多的簇
|
||||
// ============================================
|
||||
int bestIdx = -1;
|
||||
std::vector<SClusterLineMetrics> clusterLineMetrics(clusters.size());
|
||||
std::vector<int> validClusterIndices;
|
||||
if (!clusters.empty()) {
|
||||
// 找所有簇中最小的 centroid.z(对齐空间)
|
||||
float minZ = std::numeric_limits<float>::max();
|
||||
@ -626,31 +624,28 @@ BAR_INTERSECTION_API int DetectBarIntersections(
|
||||
|
||||
// 在范围内找点数最多的簇
|
||||
int maxPoints = 0;
|
||||
float bestDistanceStdDev = std::numeric_limits<float>::max();
|
||||
float bestInlierRatio = 0.0f;
|
||||
for (int i = 0; i < (int)clusters.size(); i++) {
|
||||
if (clusters[i].centroid.z > zUpperBound) continue;
|
||||
|
||||
const SClusterLineMetrics lineMetrics = EvaluateClusterLinearity(
|
||||
clusters[i], alignedPoints, totalPoints, growthParams
|
||||
);
|
||||
clusterLineMetrics[i] = lineMetrics;
|
||||
if (!lineMetrics.isValid) continue;
|
||||
validClusterIndices.push_back(i);
|
||||
|
||||
if (clusters[i].pointCount > maxPoints ||
|
||||
(clusters[i].pointCount == maxPoints && lineMetrics.distanceStdDev < bestDistanceStdDev) ||
|
||||
(clusters[i].pointCount == maxPoints &&
|
||||
std::abs(lineMetrics.distanceStdDev - bestDistanceStdDev) < 1e-6f &&
|
||||
lineMetrics.inlierRatio > bestInlierRatio)) {
|
||||
if (clusters[i].pointCount > maxPoints) {
|
||||
maxPoints = clusters[i].pointCount;
|
||||
bestDistanceStdDev = lineMetrics.distanceStdDev;
|
||||
bestInlierRatio = lineMetrics.inlierRatio;
|
||||
bestIdx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提取最优簇的过滤后点云(对齐坐标系)
|
||||
std::vector<SVzNLPointXYZ> bestClusterFilteredPts;
|
||||
if (bestIdx >= 0) {
|
||||
const SGrowthCluster& bestCluster = clusters[bestIdx];
|
||||
bestClusterFilteredPts.reserve(bestCluster.pointCount);
|
||||
for (int j = 0; j < (int)bestCluster.pointIndices.size(); j++) {
|
||||
int linearIdx = bestCluster.pointIndices[j];
|
||||
if (linearIdx >= 0 && linearIdx < totalPoints) {
|
||||
bestClusterFilteredPts.push_back(alignedPoints[linearIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Transform cluster centroids back to original coordinate system
|
||||
// ============================================
|
||||
@ -661,113 +656,6 @@ BAR_INTERSECTION_API int DetectBarIntersections(
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Step 5: from valid clusters, find the one with minimum z and then
|
||||
// find clusters whose centroid-connection line is perpendicular to its axis
|
||||
// ============================================
|
||||
int step5BaseClusterIndex = -1;
|
||||
std::vector<int> step5PerpendicularClusterIndices;
|
||||
if (!validClusterIndices.empty()) {
|
||||
float minValidClusterZ = std::numeric_limits<float>::max();
|
||||
for (size_t i = 0; i < validClusterIndices.size(); ++i) {
|
||||
const int clusterIdx = validClusterIndices[i];
|
||||
if (clusters[clusterIdx].centroid.z < minValidClusterZ) {
|
||||
minValidClusterZ = clusters[clusterIdx].centroid.z;
|
||||
step5BaseClusterIndex = clusterIdx;
|
||||
}
|
||||
}
|
||||
|
||||
if (step5BaseClusterIndex >= 0) {
|
||||
const SClusterLineMetrics& baseMetrics = clusterLineMetrics[step5BaseClusterIndex];
|
||||
SVzNLPointXYZ baseAxisDirection =
|
||||
ComputeDirectionVector(baseMetrics.axisPointA, baseMetrics.axisPointB);
|
||||
baseAxisDirection.z = 0.0f;
|
||||
const float perpendicularTolerance =
|
||||
std::sin(std::max(0.0f, std::min(growthParams.maxPerpendicularDeviationDeg, 89.0f)) *
|
||||
3.1415926f / 180.0f);
|
||||
|
||||
for (size_t i = 0; i < validClusterIndices.size(); ++i) {
|
||||
const int clusterIdx = validClusterIndices[i];
|
||||
if (clusterIdx == step5BaseClusterIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SVzNLPointXYZ centroidConnection = {};
|
||||
centroidConnection.x = clusters[clusterIdx].centroid.x - clusters[step5BaseClusterIndex].centroid.x;
|
||||
centroidConnection.y = clusters[clusterIdx].centroid.y - clusters[step5BaseClusterIndex].centroid.y;
|
||||
centroidConnection.z = 0.0f;
|
||||
|
||||
const float absDot = ComputeNormalizedAbsDot(baseAxisDirection, centroidConnection);
|
||||
if (absDot <= perpendicularTolerance) {
|
||||
step5PerpendicularClusterIndices.push_back(clusterIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SVzNLPointXYZ> step5FilteredAlignedPoints;
|
||||
std::vector<SVzNLPointXYZ> step5BaseClusterAlignedPoints;
|
||||
SVzNLPointXYZ step5BaseClusterAlignedCentroid = {};
|
||||
std::vector<std::vector<SVzNLPointXYZ> > step5PerpendicularClusterAlignedPoints;
|
||||
std::vector<SVzNLPointXYZ> step5PerpendicularClusterAlignedCentroids;
|
||||
|
||||
step5FilteredAlignedPoints.reserve(validCount);
|
||||
for (int i = 0; i < totalPoints; ++i) {
|
||||
if (IsValidPoint(alignedPoints[i])) {
|
||||
step5FilteredAlignedPoints.push_back(alignedPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (step5BaseClusterIndex >= 0) {
|
||||
const SGrowthCluster& baseCluster = clusters[step5BaseClusterIndex];
|
||||
step5BaseClusterAlignedCentroid.x = baseCluster.centroid.x;
|
||||
step5BaseClusterAlignedCentroid.y = baseCluster.centroid.y;
|
||||
step5BaseClusterAlignedCentroid.z = baseCluster.centroid.z;
|
||||
step5BaseClusterAlignedPoints.reserve(baseCluster.pointCount);
|
||||
for (size_t j = 0; j < baseCluster.pointIndices.size(); ++j) {
|
||||
const int linearIdx = baseCluster.pointIndices[j];
|
||||
if (linearIdx >= 0 && linearIdx < totalPoints && IsValidPoint(alignedPoints[linearIdx])) {
|
||||
step5BaseClusterAlignedPoints.push_back(alignedPoints[linearIdx]);
|
||||
}
|
||||
}
|
||||
|
||||
step5PerpendicularClusterAlignedPoints.reserve(step5PerpendicularClusterIndices.size());
|
||||
step5PerpendicularClusterAlignedCentroids.reserve(step5PerpendicularClusterIndices.size());
|
||||
for (size_t i = 0; i < step5PerpendicularClusterIndices.size(); ++i) {
|
||||
const int clusterIdx = step5PerpendicularClusterIndices[i];
|
||||
const SGrowthCluster& cluster = clusters[clusterIdx];
|
||||
std::vector<SVzNLPointXYZ> clusterAlignedPoints;
|
||||
clusterAlignedPoints.reserve(cluster.pointCount);
|
||||
for (size_t j = 0; j < cluster.pointIndices.size(); ++j) {
|
||||
const int linearIdx = cluster.pointIndices[j];
|
||||
if (linearIdx >= 0 && linearIdx < totalPoints && IsValidPoint(alignedPoints[linearIdx])) {
|
||||
clusterAlignedPoints.push_back(alignedPoints[linearIdx]);
|
||||
}
|
||||
}
|
||||
step5PerpendicularClusterAlignedPoints.push_back(std::move(clusterAlignedPoints));
|
||||
SVzNLPointXYZ clusterAlignedCentroid;
|
||||
clusterAlignedCentroid.x = cluster.centroid.x;
|
||||
clusterAlignedCentroid.y = cluster.centroid.y;
|
||||
clusterAlignedCentroid.z = cluster.centroid.z;
|
||||
step5PerpendicularClusterAlignedCentroids.push_back(clusterAlignedCentroid);
|
||||
}
|
||||
}
|
||||
|
||||
// 提取最优簇的过滤后点云,并逆变换回原始坐标系,便于在整云上高亮显示
|
||||
std::vector<SVzNLPointXYZ> bestClusterFilteredPts;
|
||||
if (bestIdx >= 0) {
|
||||
const SGrowthCluster& bestCluster = clusters[bestIdx];
|
||||
bestClusterFilteredPts.reserve(bestCluster.pointCount);
|
||||
for (int j = 0; j < (int)bestCluster.pointIndices.size(); j++) {
|
||||
int linearIdx = bestCluster.pointIndices[j];
|
||||
if (linearIdx >= 0 && linearIdx < totalPoints && IsValidPoint(alignedPoints[linearIdx])) {
|
||||
bestClusterFilteredPts.push_back(
|
||||
InverseTransformPoint(alignedPoints[linearIdx], invRotation, planeCentroid)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& cluster : clusters) {
|
||||
// Transform centroid
|
||||
float x = cluster.centroid.x;
|
||||
@ -803,14 +691,6 @@ BAR_INTERSECTION_API int DetectBarIntersections(
|
||||
result->planeNormal = planeNormal;
|
||||
result->planeD = planeD;
|
||||
result->bestClusterIndex = bestIdx;
|
||||
result->validClusterIndices = std::move(validClusterIndices);
|
||||
result->step5BaseClusterIndex = step5BaseClusterIndex;
|
||||
result->step5PerpendicularClusterIndices = std::move(step5PerpendicularClusterIndices);
|
||||
result->step5FilteredAlignedPoints = std::move(step5FilteredAlignedPoints);
|
||||
result->step5BaseClusterAlignedPoints = std::move(step5BaseClusterAlignedPoints);
|
||||
result->step5BaseClusterAlignedCentroid = step5BaseClusterAlignedCentroid;
|
||||
result->step5PerpendicularClusterAlignedPoints = std::move(step5PerpendicularClusterAlignedPoints);
|
||||
result->step5PerpendicularClusterAlignedCentroids = std::move(step5PerpendicularClusterAlignedCentroids);
|
||||
result->bestClusterPoints = std::move(bestClusterFilteredPts);
|
||||
|
||||
result->clusterCount = (int)clusters.size();
|
||||
@ -841,7 +721,6 @@ BAR_INTERSECTION_API void PrintDetectionResults(
|
||||
std::cout << "Plane Normal: (" << result.planeNormal.x << ", "
|
||||
<< result.planeNormal.y << ", " << result.planeNormal.z << ")" << std::endl;
|
||||
std::cout << "Clusters detected: " << result.clusterCount << std::endl;
|
||||
std::cout << "Valid bar-like clusters: " << result.validClusterIndices.size() << std::endl;
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "\n--- Clusters ---" << std::endl;
|
||||
@ -865,33 +744,6 @@ BAR_INTERSECTION_API void PrintDetectionResults(
|
||||
} else {
|
||||
std::cout << " No valid best cluster found." << std::endl;
|
||||
}
|
||||
|
||||
if (!result.validClusterIndices.empty()) {
|
||||
std::cout << "\n--- Valid Cluster Indices ---" << std::endl;
|
||||
for (size_t i = 0; i < result.validClusterIndices.size(); ++i) {
|
||||
std::cout << " #" << result.validClusterIndices[i] << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\n--- Step 5 ---" << std::endl;
|
||||
if (result.step5BaseClusterIndex >= 0 && result.step5BaseClusterIndex < result.clusterCount) {
|
||||
const SGrowthCluster& base = result.clusters[result.step5BaseClusterIndex];
|
||||
std::cout << " Base: Cluster #" << result.step5BaseClusterIndex
|
||||
<< " pts=" << base.pointCount
|
||||
<< " centroid=(" << base.centroid.x << "," << base.centroid.y << "," << base.centroid.z << ")"
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cout << " No Step 5 base cluster found." << std::endl;
|
||||
}
|
||||
|
||||
if (!result.step5PerpendicularClusterIndices.empty()) {
|
||||
std::cout << " Perpendicular clusters:" << std::endl;
|
||||
for (size_t i = 0; i < result.step5PerpendicularClusterIndices.size(); ++i) {
|
||||
std::cout << " #" << result.step5PerpendicularClusterIndices[i] << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << " No perpendicular cluster found." << std::endl;
|
||||
}
|
||||
}
|
||||
std::cout << "===========================================" << std::endl;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,40 +1,37 @@
|
||||
#ifndef REGION_GROWING_H
|
||||
#define REGION_GROWING_H
|
||||
|
||||
#include "VZNL_Types.h"
|
||||
#include "BarIntersectionParams.h"
|
||||
#include <vector>
|
||||
|
||||
namespace bar_intersection {
|
||||
|
||||
/**
|
||||
* @brief Region growing based on scan-line feature trees.
|
||||
*
|
||||
* Stage 1: extract contiguous valid point runs from each row as line features.
|
||||
* Stage 2: grow features onto alive trees using sg_lineGapsGrowing-style
|
||||
* tail matching on centroid x/y/z differences. Unmatched features
|
||||
* start new trees.
|
||||
* Stage 3: finalize stale trees and flatten valid trees into SGrowthCluster.
|
||||
*
|
||||
* The input point cloud must be a regular grid. Invalid points are (0,0,0).
|
||||
*
|
||||
* @param points Filtered aligned point grid with rows * cols elements.
|
||||
* @param rows Grid row count.
|
||||
* @param cols Grid column count.
|
||||
* @param params thresholdY/Z are used for row feature extraction and matching;
|
||||
* thresholdX constrains cross-row displacement.
|
||||
* @param outClusters Output clusters. pointIndices are linear indices in the
|
||||
* original rows * cols grid.
|
||||
* @return Number of valid clusters with pointCount >= minClusterSize.
|
||||
*/
|
||||
int RegionGrowClusters(
|
||||
const SVzNLPointXYZ* points,
|
||||
int rows,
|
||||
int cols,
|
||||
const SGrowthParams& params,
|
||||
std::vector<SGrowthCluster>& outClusters
|
||||
);
|
||||
|
||||
} // namespace bar_intersection
|
||||
|
||||
#endif // REGION_GROWING_H
|
||||
#ifndef REGION_GROWING_H
|
||||
#define REGION_GROWING_H
|
||||
|
||||
#include "VZNL_Types.h"
|
||||
#include "BarIntersectionParams.h"
|
||||
#include <vector>
|
||||
|
||||
namespace bar_intersection {
|
||||
|
||||
/**
|
||||
* @brief 基于两阶段线扫描的点云区域生长
|
||||
*
|
||||
* 阶段1:逐行扫描,按 col 顺序比较相邻点的 y/z 差值,将连续满足阈值的点归为 Segment
|
||||
* 阶段2:跨行合并,当前行 Segment 与前一行 Segment 比较 x/z 距离和 col 区间重叠,
|
||||
* 使用并查集合并属于同一棵树的 Segment
|
||||
* 阶段3:按并查集根节点展平为 SGrowthCluster
|
||||
*
|
||||
* 输入点云已原地过滤:无效点为 (0,0,0),自动跳过。
|
||||
*
|
||||
* @param points 对齐并过滤后的点云数组 (rows * cols),无效点为 (0,0,0)
|
||||
* @param rows 网格行数
|
||||
* @param cols 网格列数
|
||||
* @param params 生长参数(thresholdY/Z=行内分段, thresholdX/Z=跨行合并)
|
||||
* @param outClusters 输出簇列表,pointIndices 为原始网格线性索引
|
||||
* @return 有效簇数量 (>= minClusterSize)
|
||||
*/
|
||||
int RegionGrowClusters(
|
||||
const SVzNLPointXYZ* points,
|
||||
int rows,
|
||||
int cols,
|
||||
const SGrowthParams& params,
|
||||
std::vector<SGrowthCluster>& outClusters
|
||||
);
|
||||
|
||||
} // namespace bar_intersection
|
||||
|
||||
#endif // REGION_GROWING_H
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user