修改算法,获取了单根钢筋

This commit is contained in:
cool609 2026-04-11 23:24:16 +08:00
parent 1d649f9c01
commit 7969a242ba
6 changed files with 179 additions and 146 deletions

View File

@ -334,27 +334,27 @@ void BarIntersectionVisualizer::VisualizeResult(
); );
} }
void BarIntersectionVisualizer::VisualizeBestCluster( void BarIntersectionVisualizer::VisualizeBestCluster(
const SBarIntersectionResult& result, const SBarIntersectionResult& result,
const std::string& title const std::string& title
) { ) {
if (result.bestClusterIndex < 0 || result.bestClusterPoints.empty()) { if (result.bestClusterIndex < 0 || result.bestClusterPoints.empty()) {
std::cout << "No valid best cluster to visualize." << std::endl; std::cout << "No valid best cluster to visualize." << std::endl;
return; return;
} }
auto renderer = vtkSmartPointer<vtkRenderer>::New(); auto renderer = vtkSmartPointer<vtkRenderer>::New();
// Best cluster filtered points (aligned coordinate system) // Best cluster filtered points (aligned coordinate system)
vtkActor* bestActor = static_cast<vtkActor*>( vtkActor* bestActor = static_cast<vtkActor*>(
CreatePointCloudActor( CreatePointCloudActor(
result.bestClusterPoints.data(), result.bestClusterPoints.data(),
(int)result.bestClusterPoints.size(), (int)result.bestClusterPoints.size(),
0.0, 1.0, 0.3, 3.0 0.0, 1.0, 0.3, 3.0
) )
); );
renderer->AddActor(bestActor); renderer->AddActor(bestActor);
bestActor->Delete(); bestActor->Delete();
ShowWindow(renderer, title, m_interactive, this, "06_best_cluster.png"); ShowWindow(renderer, title, m_interactive, this, "06_best_cluster.png");
} }

View File

@ -251,10 +251,10 @@ int ProcessSingleFile(
// Final visualization // Final visualization
visualizer.VisualizeResult(points, rows, cols, result, "Final Result"); visualizer.VisualizeResult(points, rows, cols, result, "Final Result");
// Best cluster visualization (filtered aligned points) // Best cluster visualization (filtered aligned points)
visualizer.VisualizeBestCluster(result, "Best Cluster (min Z, high pts)"); visualizer.VisualizeBestCluster(result, "Best Cluster (min Z, high pts)");
// Cleanup // Cleanup
FreeBarIntersectionResult(&result); FreeBarIntersectionResult(&result);
delete[] points; delete[] points;

View File

@ -37,6 +37,8 @@ struct SGrowthParams {
float maxPerpendicularDeviationDeg; // Max deviation from 90 deg for centroid-connection test 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 int minContinuousValidPointCount; // Min consecutive valid points kept on each laser line after plane filtering; <=1 disables
float maxContinuousPointZTolerance; // Max z difference between adjacent points inside one valid run (mm) float maxContinuousPointZTolerance; // Max z difference between adjacent points inside one valid run (mm)
float minBarDiameter;
float maxBarDiameter;
SGrowthParams() SGrowthParams()
: thresholdX(5.0f) : thresholdX(5.0f)
@ -49,6 +51,8 @@ struct SGrowthParams {
, maxPerpendicularDeviationDeg(50.0f) , maxPerpendicularDeviationDeg(50.0f)
, minContinuousValidPointCount(0) , minContinuousValidPointCount(0)
, maxContinuousPointZTolerance(5.0f) , maxContinuousPointZTolerance(5.0f)
, minBarDiameter(10.0f)
, maxBarDiameter(30.0f)
{} {}
}; };

View File

@ -249,7 +249,10 @@ 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
); );
// ============================================ // ============================================

View File

@ -114,86 +114,36 @@ static void FinalizeSegment(SLineSegment& seg) {
} }
} }
static bool HasColumnOverlap(const SLineSegment& a, const SLineSegment& b) {
return !(b.endCol < a.startCol || a.endCol < b.startCol);
}
static bool HasAnyColumnOverlapWithSegments(
const SLineSegment& seg,
const std::vector<SLineSegment>& otherSegments
) {
for (size_t i = 0; i < otherSegments.size(); ++i) {
if (HasColumnOverlap(seg, otherSegments[i])) {
return true;
}
}
return false;
}
struct SSegmentRelationResult { struct SSegmentRelationResult {
bool hasOverlap;
bool hasMatch; bool hasMatch;
float bestDistanceSq; float bestDistanceSq;
}; };
struct SCarrySegmentState { struct SCarrySegmentState {
bool hasOverlap;
bool hasMatch; bool hasMatch;
}; };
static SSegmentRelationResult EvaluateSegmentRelation( static SSegmentRelationResult EvaluateSegmentRelation(
const SLineSegment& prevSeg, const SLineSegment& prevSeg,
const SLineSegment& currSeg, const SLineSegment& currSeg,
const SVzNLPointXYZ* points,
const SGrowthParams& params const SGrowthParams& params
) { ) {
SSegmentRelationResult result; SSegmentRelationResult result;
result.hasOverlap = false;
result.hasMatch = false; result.hasMatch = false;
result.bestDistanceSq = std::numeric_limits<float>::max(); result.bestDistanceSq = std::numeric_limits<float>::max();
if (!HasColumnOverlap(prevSeg, currSeg)) { const float centroidDistanceThresholdSq =
return result;
}
const float pointDistanceThresholdSq =
params.thresholdX * params.thresholdX + params.thresholdX * params.thresholdX +
params.thresholdY * params.thresholdY + params.thresholdY * params.thresholdY +
params.thresholdZ * params.thresholdZ; params.thresholdZ * params.thresholdZ;
result.hasOverlap = true; const float dx = currSeg.centroid.x - prevSeg.centroid.x;
const float dy = currSeg.centroid.y - prevSeg.centroid.y;
size_t prevIdx = 0; const float dz = currSeg.centroid.z - prevSeg.centroid.z;
size_t currIdx = 0; const float distanceSq = dx * dx + dy * dy + dz * dz;
while (prevIdx < prevSeg.points.size() && currIdx < currSeg.points.size()) { if (distanceSq < centroidDistanceThresholdSq) {
const SGridPoint& prevGp = prevSeg.points[prevIdx]; result.hasMatch = true;
const SGridPoint& currGp = currSeg.points[currIdx]; result.bestDistanceSq = distanceSq;
if (prevGp.col < currGp.col) {
++prevIdx;
continue;
}
if (currGp.col < prevGp.col) {
++currIdx;
continue;
}
const SVzNLPointXYZ& prevPt = points[prevGp.linearIdx];
const SVzNLPointXYZ& currPt = points[currGp.linearIdx];
const float dx = currPt.x - prevPt.x;
const float dy = currPt.y - prevPt.y;
const float dz = currPt.z - prevPt.z;
const float distanceSq = dx * dx + dy * dy + dz * dz;
if (distanceSq < pointDistanceThresholdSq) {
result.hasMatch = true;
if (distanceSq < result.bestDistanceSq) {
result.bestDistanceSq = distanceSq;
}
}
++prevIdx;
++currIdx;
} }
return result; return result;
@ -222,7 +172,9 @@ static bool IsValidPoint(const SVzNLPointXYZ& pt) {
void EvaluateLine( void EvaluateLine(
const SVzNLPointXYZ* points, const SVzNLPointXYZ* points,
int count, int count,
int row, int step,
int startIdx,
int cols,
const SGrowthParams& params, const SGrowthParams& params,
std::vector<SLineSegment>& currRowSegments, std::vector<SLineSegment>& currRowSegments,
bool& bIsInvalidLine, bool& bIsInvalidLine,
@ -234,7 +186,7 @@ void EvaluateLine(
constexpr float kPi = 3.14159265358979323846f; constexpr float kPi = 3.14159265358979323846f;
const float searchDist = params.angleSearchDistance; const float searchDist = params.angleSearchDistance;
const float posThresh = params.maxAxisDeviationFromXYDeg; const float posThresh = params.maxAxisDeviationFromXYDeg;
const float negThresh = params.maxPerpendicularDeviationDeg; const float negThresh = -params.maxPerpendicularDeviationDeg;
// YZ_PANEL: coord=y, XZ_PANEL: coord=x // YZ_PANEL: coord=y, XZ_PANEL: coord=x
auto getCoord = [panelType](const SVzNLPointXYZ& pt) -> float { auto getCoord = [panelType](const SVzNLPointXYZ& pt) -> float {
@ -252,7 +204,8 @@ void EvaluateLine(
// ================================================================ // ================================================================
struct PointInfo { struct PointInfo {
SVzNLPointXYZ point; SVzNLPointXYZ point;
int linePos; int row;
int col;
int globalIdx; int globalIdx;
bool valid; bool valid;
float signedAngleDeg; float signedAngleDeg;
@ -261,13 +214,14 @@ void EvaluateLine(
}; };
std::vector<PointInfo> pts(count); std::vector<PointInfo> pts(count);
int startIdx = row * count; // global index of the first point in this line auto curPoint = points;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int gIdx = startIdx + i; int gIdx = startIdx + i * step;
pts[i].point = points[i]; pts[i].point = *curPoint;
pts[i].linePos = i; pts[i].row = gIdx / cols;
pts[i].col = gIdx % cols;
pts[i].globalIdx = gIdx; pts[i].globalIdx = gIdx;
pts[i].valid = IsValidPoint(points[i]); pts[i].valid = IsValidPoint(*curPoint);
pts[i].signedAngleDeg = 0.0f; pts[i].signedAngleDeg = 0.0f;
pts[i].hasAngle = false; pts[i].hasAngle = false;
pts[i].trend = keLineAngleTrend_Invalid; pts[i].trend = keLineAngleTrend_Invalid;
@ -275,6 +229,8 @@ void EvaluateLine(
if (pts[i].valid) { if (pts[i].valid) {
bIsInvalidLine = false; bIsInvalidLine = false;
} }
curPoint += step;
} }
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@ -324,10 +280,12 @@ void EvaluateLine(
pts[i].hasAngle = true; pts[i].hasAngle = true;
// 两线夹角line1(backRef→cur) 延长后 与 line2(cur→fwdRef) 的有符号夹角 // 局部弯折强度line1(backRef→cur) 延长后 与 line2(cur→fwdRef) 的夹角绝对值。
// 与旧方案backRef→fwdRef 整体坡度角)的本质区别: // 与旧方案backRef→fwdRef 整体坡度角)的本质区别:
// 均匀倾斜面(无弯折)→ 0°只有在 pts[i] 处发生方向变化时才得到非零角。 // 均匀倾斜面(无弯折)→ 0°只有在 pts[i] 处发生方向变化时才得到非零角。
// 正角度 = 向相机弯折(上升),负角度 = 远离相机弯折(下降进坑) // signedAngleDeg 的语义:
// 符号 = 整体跳变方向(按坐标正向统一;低→高为正,高→低为负)
// 绝对值 = pts[i] 处的局部弯折强度
float curCoord = getCoord(cur); float curCoord = getCoord(cur);
float v1_coord = curCoord - backCoord; // backRef → cur水平分量 float v1_coord = curCoord - backCoord; // backRef → cur水平分量
float v1_z = cur.z - backMeanZ; // backRef → curz 分量) float v1_z = cur.z - backMeanZ; // backRef → curz 分量)
@ -337,9 +295,17 @@ void EvaluateLine(
// 2D 叉积z 分量)和点积,用于计算有符号夹角 // 2D 叉积z 分量)和点积,用于计算有符号夹角
float cross2d = v1_coord * v2_z - v1_z * v2_coord; float cross2d = v1_coord * v2_z - v1_z * v2_coord;
float dot2d = v1_coord * v2_coord + v1_z * v2_z; float dot2d = v1_coord * v2_coord + v1_z * v2_z;
// 消除扫描方向(正向/反向)对叉积符号的影响 // 消除扫描方向(正向/反向)对整体跳变方向符号的影响
float scanDirSign = ((v1_coord + v2_coord) >= 0.0f) ? 1.0f : -1.0f; float scanDirSign = ((v1_coord + v2_coord) >= 0.0f) ? 1.0f : -1.0f;
float slopeAngleDeg = std::atan2(-cross2d * scanDirSign, dot2d) * (180.0f / kPi); float bendAngleDeg = std::atan2(std::fabs(cross2d), dot2d) * (180.0f / kPi);
float overallDeltaZ = (fwdMeanZ - backMeanZ) * scanDirSign;
float slopeAngleDeg = 0.0f;
if (overallDeltaZ > 1e-6f) {
slopeAngleDeg = bendAngleDeg;
}
else if (overallDeltaZ < -1e-6f) {
slopeAngleDeg = -bendAngleDeg;
}
pts[i].signedAngleDeg = slopeAngleDeg; pts[i].signedAngleDeg = slopeAngleDeg;
@ -517,6 +483,36 @@ void EvaluateLine(
segs = merged; segs = merged;
} }
const float minBarDiameter = params.minBarDiameter;
const float maxBarDiameter = params.maxBarDiameter;
for (size_t si = 0; si < segs.size(); ++si) {
const Segment& seg = segs[si];
if (seg.type != ESegType::Flat) continue;
if (seg.length < minBarDiameter || seg.length > maxBarDiameter) continue;
bool hasPoint = false;
SLineSegment lineSeg;
for (int pos = seg.startPos; pos <= seg.endPos; ++pos) {
if (!pts[pos].valid) continue;
SGridPoint gp;
gp.row = pts[pos].row;
gp.col = pts[pos].col;
gp.linearIdx = pts[pos].globalIdx;
if (!hasPoint) {
lineSeg = StartNewSegment(gp, pts[pos].point);
hasPoint = true;
}
else {
AppendPoint(lineSeg, gp, pts[pos].point);
}
}
if (!hasPoint) continue;
FinalizeSegment(lineSeg);
currRowSegments.push_back(lineSeg);
}
} }
static void ProcessCurrentRowSegmentPoint( static void ProcessCurrentRowSegmentPoint(
@ -586,13 +582,12 @@ static void MergeCurrentRowSegments(
const std::vector<SLineSegment>& carriedRowSegments, const std::vector<SLineSegment>& carriedRowSegments,
std::vector<SLineSegment>& currRowSegments, std::vector<SLineSegment>& currRowSegments,
UnionFind& uf, UnionFind& uf,
const SVzNLPointXYZ* points,
const SGrowthParams& params, const SGrowthParams& params,
std::vector<SLineSegment>& nextCarriedRowSegments std::vector<SLineSegment>& nextCarriedRowSegments
) { ) {
nextCarriedRowSegments.clear(); nextCarriedRowSegments.clear();
std::vector<SCarrySegmentState> prevStates(prevRowSegments.size(), SCarrySegmentState{ false, false }); std::vector<SCarrySegmentState> prevStates(prevRowSegments.size(), SCarrySegmentState{ false });
std::vector<SCarrySegmentState> carriedStates(carriedRowSegments.size(), SCarrySegmentState{ false, false }); std::vector<SCarrySegmentState> carriedStates(carriedRowSegments.size(), SCarrySegmentState{ false });
for (size_t ci = 0; ci < currRowSegments.size(); ++ci) { for (size_t ci = 0; ci < currRowSegments.size(); ++ci) {
SLineSegment& currSeg = currRowSegments[ci]; SLineSegment& currSeg = currRowSegments[ci];
@ -603,12 +598,7 @@ static void MergeCurrentRowSegments(
auto tryMatchSegments = [&](const std::vector<SLineSegment>& candidateSegments, std::vector<SCarrySegmentState>& candidateStates) { auto tryMatchSegments = [&](const std::vector<SLineSegment>& candidateSegments, std::vector<SCarrySegmentState>& candidateStates) {
for (size_t pi = 0; pi < candidateSegments.size(); ++pi) { for (size_t pi = 0; pi < candidateSegments.size(); ++pi) {
const SLineSegment& prevSeg = candidateSegments[pi]; const SLineSegment& prevSeg = candidateSegments[pi];
const SSegmentRelationResult relation = EvaluateSegmentRelation(prevSeg, currSeg, points, params); const SSegmentRelationResult relation = EvaluateSegmentRelation(prevSeg, currSeg, params);
if (!relation.hasOverlap) {
continue;
}
candidateStates[pi].hasOverlap = true;
if (!relation.hasMatch) { if (!relation.hasMatch) {
continue; continue;
} }
@ -650,14 +640,14 @@ static void MergeCurrentRowSegments(
for (size_t pi = 0; pi < prevRowSegments.size(); ++pi) { for (size_t pi = 0; pi < prevRowSegments.size(); ++pi) {
const SLineSegment& prevSeg = prevRowSegments[pi]; const SLineSegment& prevSeg = prevRowSegments[pi];
if (!prevStates[pi].hasOverlap && !prevStates[pi].hasMatch) { if (!prevStates[pi].hasMatch) {
nextCarriedRowSegments.push_back(prevSeg); nextCarriedRowSegments.push_back(prevSeg);
} }
} }
for (size_t pi = 0; pi < carriedRowSegments.size(); ++pi) { for (size_t pi = 0; pi < carriedRowSegments.size(); ++pi) {
const SLineSegment& carriedSeg = carriedRowSegments[pi]; const SLineSegment& carriedSeg = carriedRowSegments[pi];
if (!carriedStates[pi].hasOverlap && !carriedStates[pi].hasMatch) { if (!carriedStates[pi].hasMatch) {
nextCarriedRowSegments.push_back(carriedSeg); nextCarriedRowSegments.push_back(carriedSeg);
} }
} }
@ -743,11 +733,10 @@ 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
) { ) {
outClusters.clear();
if (!points || cols <= 0 || rows <= 0) { if (!points || cols <= 0 || rows <= 0) {
return 0; return 0;
} }
@ -759,45 +748,81 @@ int RegionGrowClusters(
std::vector<SLineSegment> nextCarriedRowSegments; std::vector<SLineSegment> nextCarriedRowSegments;
std::vector<SLineSegment> currRowSegments; std::vector<SLineSegment> currRowSegments;
for (int row = 0; row < rows; ++row) { if (bHorizontalScan)
const SVzNLPointXYZ* rowPoints = points + row * cols; {
int nPointIdx = 0;
const SVzNLPointXYZ* rowPoints = points;
for (int row = 0; row < rows; ++row) {
bool bIsValidLine = false;
EvaluateLine(
rowPoints,
static_cast<unsigned int>(cols),
1,
nPointIdx,
cols,
params,
currRowSegments,
bIsValidLine,
EPanelType::YZ_PANEL
);
bool bIsValidLine = false; MergeCurrentRowSegments(
EvaluateLine( prevRowSegments,
rowPoints, carriedRowSegments,
static_cast<unsigned int>(cols), currRowSegments,
row, uf,
params, params,
currRowSegments, nextCarriedRowSegments
bIsValidLine, );
EPanelType::YZ_PANEL allSegments.insert(allSegments.end(), currRowSegments.begin(), currRowSegments.end());
);
ProcessCurrentRowSegmentPoint( carriedRowSegments.swap(nextCarriedRowSegments);
rowPoints, nextCarriedRowSegments.clear();
static_cast<unsigned int>(cols), prevRowSegments.swap(currRowSegments);
row, currRowSegments.clear();
params,
currRowSegments
);
MergeCurrentRowSegments( nPointIdx += cols;
prevRowSegments, rowPoints += cols;
carriedRowSegments, }
currRowSegments,
uf,
points,
params,
nextCarriedRowSegments
);
allSegments.insert(allSegments.end(), currRowSegments.begin(), currRowSegments.end());
carriedRowSegments.swap(nextCarriedRowSegments);
nextCarriedRowSegments.clear();
prevRowSegments.swap(currRowSegments);
currRowSegments.clear();
} }
else
{
int nPointIdx = 0;
const SVzNLPointXYZ* colPoints = points;
for (int col = 0; col < cols; ++col) {
bool bIsValidLine = false;
EvaluateLine(
colPoints,
rows,
cols,
nPointIdx,
cols,
params,
currRowSegments,
bIsValidLine,
EPanelType::XZ_PANEL
);
MergeCurrentRowSegments(
prevRowSegments,
carriedRowSegments,
currRowSegments,
uf,
params,
nextCarriedRowSegments
);
allSegments.insert(allSegments.end(), currRowSegments.begin(), currRowSegments.end());
carriedRowSegments.swap(nextCarriedRowSegments);
nextCarriedRowSegments.clear();
prevRowSegments.swap(currRowSegments);
currRowSegments.clear();
colPoints++;
nPointIdx++;
}
}
FlattenTreesToClusters(allSegments, uf, points, params, outClusters); FlattenTreesToClusters(allSegments, uf, points, params, outClusters);
return static_cast<int>(outClusters.size()); return static_cast<int>(outClusters.size());
} }

View File

@ -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
); );