algoLib/sourceCode/rodAndBarDetection.cpp
jerryzeng 693471b311 rodAndBarDetection version 1.4.0 :
矩森棒材抓取算法改进:(1)修正了两个棒材被识别成一根的问题(2)增强了对噪点的容忍度,增强了鲁棒性
2026-06-24 21:13:49 +08:00

5214 lines
154 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <vector>
#include "SG_baseDataType.h"
#include "SG_baseAlgo_Export.h"
#include "rodAndBarDetection_Export.h"
#include <opencv2/opencv.hpp>
#include <limits>
//version 1.0.0 : base version release to customer
//version 1.1.0 : 添加了地面调平和棒材定位
//version 1.1.1 : 初始发布给客户的版本
//version 1.2.0 : 配天螺杆测量增加了定位盘中心测量功能
//version 1.2.1 : 增加了定位盘中心完整姿态输出
//version 1.2.2 : 调整了定位盘中心姿态定义法向Z向向右Y向下
//version 1.2.3 : 根据定向盘轮廓的直线段确定向右的向量
//version 1.2.4 : 根据定向盘左上点和右下点确定姿态向量
//version 1.2.5 : 添加筑裕钢筋焊缝提取API
//version 1.2.6 : 定位盘位姿计算由RANSAC调整为迭代拟合测试精度是否有提高
//version 1.2.7 : (1)根据实际点云修正了螺杆定位算法2定位盘位姿计算改回RANSAC
//version 1.2.8 : 修正螺杆定位算法中的一个Bug
//version 1.2.9 : 修正螺杆定位算法中轴向拟合计算时方向确定的问题保证投影后的点云与投影前的点云的Z变化趋势一致
//version 1.2.10 : 改进螺杆定位取点算法
//version 1.2.11 : 根据现场数据调整特征生长参数
//version 1.3.0 : 新的定位盘中心测量功能
//version 1.3.1 : 定位盘中心测量功能添加了噪声过滤
//version 1.3.2 : 矩森棒材抓取修正一个错误码返回错误测试了地面调平API
//version 1.3.3 : 筑裕钢筋焊缝提取算法改进中间版本
//version 1.3.4 : 新的定位盘中心测量功能算法优化
//version 1.3.5 : 新的定位盘中心测量功能占将float运算改成double ,测试PC和3588差异
//version 1.3.6 : 新的定位盘中心测量功能:优化聚类前的垂直点去除算法,保证聚类结果正确
//version 1.3.7 : 新的定位盘中心测量功能:进一步优化了内部参数,优化了垂直点去除效果
//version 1.3.8 : 新的螺杆定位算法使用PCA方法确定螺杆轴向
//version 1.4.0 : 矩森棒材抓取算法改进1修正了两个棒材被识别成一根的问题2增强了对噪点的容忍度增强了鲁棒性
std::string m_strVersion = "RodAndBarDetection_1.4.0";
const char* wd_rodAndBarDetectionVersion(void)
{
return m_strVersion.c_str();
}
//计算一个平面调平参数。
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara sx_rodPosition_getBaseCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
return sg_getPlaneCalibPara2(scanLines);
}
//相机姿态调平,并去除地面
void sx_rodPosition_lineDataR(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH)
{
lineDataRT_vector(a_line, camPoseR, groundH);
}
SVzNL3DPoint _translatePoint(SVzNL3DPoint point, const double rMatrix[9])
{
SVzNL3DPoint result;
double x = point.x * rMatrix[0] + point.y * rMatrix[1] + point.z * rMatrix[2];
double y = point.x * rMatrix[3] + point.y * rMatrix[4] + point.z * rMatrix[5];
double z = point.x * rMatrix[6] + point.y * rMatrix[7] + point.z * rMatrix[8];
result.x = x;
result.y = y;
result.z = z;
return result;
}
SVzNL3DPoint getArcPeak(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
SWD_segFeature & a_arcFeature,
SVzNL2DPoint& arcPos)
{
SVzNL3DPoint arcPeak = scanLines[a_arcFeature.lineIdx][a_arcFeature.startPtIdx].pt3D;
for (int i = a_arcFeature.startPtIdx+1; i <= a_arcFeature.endPtIdx; i++)
{
if (scanLines[a_arcFeature.lineIdx][i].pt3D.z > 1e-4) //跳开空点
{
if (arcPeak.z > scanLines[a_arcFeature.lineIdx][i].pt3D.z)
{
arcPeak = scanLines[a_arcFeature.lineIdx][i].pt3D;
arcPos = { a_arcFeature.lineIdx , i };
}
}
}
return arcPeak;
}
SVzNL3DPoint getArcPeak_parabolaFitting(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
SWD_rodArcFeature& a_arcFeature,
SVzNL2DPoint& arcPos)
{
std::vector<cv::Point2d> points;
for (int i = a_arcFeature.startPtIdx + 1; i <= a_arcFeature.endPtIdx; i++)
{
if (scanLines[a_arcFeature.lineIdx][i].pt3D.z > 1e-4) //跳开空点
{
cv::Point2d a_pt2D;
if (scanLines[a_arcFeature.lineIdx][i].pt3D.z > 1e-4)
{
a_pt2D.x = scanLines[a_arcFeature.lineIdx][i].pt3D.y;
a_pt2D.y = scanLines[a_arcFeature.lineIdx][i].pt3D.z;
points.push_back(a_pt2D);
}
}
}
double a, b, c, mse, max_err;
//抛物线最小二乘拟合 y = ax ^ 2 + bx + c
bool result = leastSquareParabolaFitEigen(
points,
a, b, c,
mse, max_err);
double yP = -b / (2 * a);
//寻找与yP最近的点作为Peak点
SVzNL3DPoint arcPeak = {0, 0, 0};
double minDist = -1;
for (int i = a_arcFeature.startPtIdx + 1; i <= a_arcFeature.endPtIdx; i++)
{
if (scanLines[a_arcFeature.lineIdx][i].pt3D.z > 1e-4) //跳开空点
{
double dist = abs(scanLines[a_arcFeature.lineIdx][i].pt3D.y - yP);
if (minDist < 0)
{
minDist = dist;
arcPeak = scanLines[a_arcFeature.lineIdx][i].pt3D;
arcPos = { a_arcFeature.lineIdx , i };
}
else
{
if(minDist > dist)
{
minDist = dist;
arcPeak = scanLines[a_arcFeature.lineIdx][i].pt3D;
arcPos = { a_arcFeature.lineIdx , i };
}
}
}
}
return arcPeak;
}
//投影提取ROI内的数据
void xoyROIProjection(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const double* rtMatrix,
SSG_ROIRectD& roi_xoy,
int objClusterId,
std::vector<std::vector<int>>& clusterIdMask,
std::vector<SWD3DPointPostion>& projectPoints
)
{
int lineNum = (int)scanLines.size();
for (int line = 0; line < lineNum; line++)
{
std::vector<SVzNL3DPosition>& a_line = scanLines[line];
int ptNum = (int)a_line.size();
for (int i = 0; i < (int)a_line.size(); i++)
{
SVzNL3DPoint a_pt = a_line[i].pt3D;
if (a_pt.z < 1e-4)
continue;
int cId = clusterIdMask[line][i];
if (cId != objClusterId)
continue;
double x = a_pt.x * rtMatrix[0] + a_pt.y * rtMatrix[1] + a_pt.z * rtMatrix[2];
double y = a_pt.x * rtMatrix[3] + a_pt.y * rtMatrix[4] + a_pt.z * rtMatrix[5];
double z = a_pt.x * rtMatrix[6] + a_pt.y * rtMatrix[7] + a_pt.z * rtMatrix[8];
if ((x >= roi_xoy.left) && (x <= roi_xoy.right) &&
(y >= roi_xoy.top) && (y <= roi_xoy.bottom))
{
SWD3DPointPostion projectPt;
projectPt.lineIdx = line;
projectPt.ptIdx = i;
projectPt.point.x = x;
projectPt.point.y = y;
projectPt.point.z = z;
projectPoints.push_back(projectPt);
}
}
}
}
SVzNLRangeD getZRange(std::vector<SWD3DPointPostion>& projectPoints)
{
int ptNum = (int)projectPoints.size();
SVzNLRangeD zRange;
zRange.min = DBL_MAX;
zRange.max = -DBL_MAX;
for (int i = 0; i < ptNum; i++)
{
zRange.min = zRange.min > projectPoints[i].point.z ? projectPoints[i].point.z : zRange.min;
zRange.max = zRange.max < projectPoints[i].point.z ? projectPoints[i].point.z : zRange.max;
}
return zRange;
}
void zCutPointClouds(
std::vector<SWD3DPointPostion>& projectPoints,
SVzNLRangeD& zRange,
std::vector<SWD3DPointPostion>& cutLayerPoints,
std::vector<std::vector<int>>& addrMapping)
{
int ptNum = (int)projectPoints.size();
for (int i = 0; i < ptNum; i++)
{
if ((projectPoints[i].point.z >= zRange.min) && (projectPoints[i].point.z <= zRange.max))
{
cutLayerPoints.push_back(projectPoints[i]);
int lineIdx = projectPoints[i].lineIdx;
int ptIdx = projectPoints[i].ptIdx;
addrMapping[lineIdx][ptIdx] = i;
}
}
}
SVzNL3DPoint getXoYCentroid(std::vector<SVzNL3DPoint>& points)
{
int ptNum = (int)points.size();
SVzNL3DPoint centroid = { 0.0, 0.0, 0.0 };
if (ptNum == 0)
return centroid;
for (int i = 0; i < ptNum; i++)
{
centroid.x += points[i].x;
centroid.y += points[i].y;
}
centroid.x = centroid.x / ptNum;
centroid.y = centroid.y / ptNum;
return centroid;
}
SVzNL3DPoint _ptRotate(SVzNL3DPoint pt3D, double matrix3d[9])
{
SVzNL3DPoint _r_pt;
_r_pt.x = pt3D.x * matrix3d[0] + pt3D.y * matrix3d[1] + pt3D.z * matrix3d[2];
_r_pt.y = pt3D.x * matrix3d[3] + pt3D.y * matrix3d[4] + pt3D.z * matrix3d[5];
_r_pt.z = pt3D.x * matrix3d[6] + pt3D.y * matrix3d[7] + pt3D.z * matrix3d[8];
return _r_pt;
}
void rodArcFeatueDetection(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
const SSG_outlierFilterParam filterParam,
const double segment_maxDistTh,
const double segment_minSegSze,
const double rodDiameter,
std::vector<std::vector<SWD_rodArcFeature>>& arcFeatures)
{
int lineNum = (int)scanLines.size();
int linePtNum = (int)scanLines[0].size();
for (int line = 0; line < lineNum; line++)
{
if (line == 780)
int kkk = 1;
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
// Filter outliers from line data
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], linePtNum, filterParam);
// Extract rod arc features
std::vector<SWD_rodArcFeature> line_rodArcs;
#if 0
double arcTotalCornerMinValue = 22.5; //整个Arc的转角最小值, 360/16
wd_getRodArcFeature_YZCurvatureMethod(
lineData,
line,
rodDiameter / 4,
rodDiameter / 4,
rodDiameter / 4,
rodDiameter,
arcTotalCornerMinValue,
line_rodArcs //环
);
#else
wd_getRodArcFeature_segmentPeakCornerMethod(
lineData,
line,
segment_maxDistTh,
segment_minSegSze,
rodDiameter / 2,
cornerPara,
line_rodArcs //环
);
#endif
//wd_getRodArcFeature_peakCornerMethod(lineData, line, rodDiameter / 2, cornerPara, line_rodArcs);
arcFeatures.push_back(line_rodArcs);
}
return;
}
#if 0
//提取扫描线上Arc和V形特征其中接近90度内凹和外凸的特征。用于钢筋焊接
void lineArcAndVFeatueDetection(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
const SSG_outlierFilterParam filterParam,
const double rodDiameter,
std::vector<std::vector<SWD_rodArcFeature>>& arcFeatures,
std::vector<std::vector< SWD_rodArcFeature>>& convexVFeature,
std::vector<std::vector< SWD_rodArcFeature>>& concaveVFeature
)
{
int lineNum = (int)scanLines.size();
int linePtNum = (int)scanLines[0].size();
for (int line = 0; line < lineNum; line++)
{
if (line == 1062)
int kkk = 1;
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
// Filter outliers from line data
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], linePtNum, filterParam);
// Extract rod arc features
std::vector<SWD_rodArcFeature> line_rodArcs;
#if 1
double arcTotalCornerMinValue = 22.5; //整个Arc的转角最小值, 360/16
wd_getRodArcFeature_YZCurvatureMethod(
lineData,
line,
rodDiameter / 4,
rodDiameter / 4,
rodDiameter / 4,
rodDiameter,
arcTotalCornerMinValue,
line_rodArcs //环
);
#else
wd_getRodArcFeature_segmentPeakCornerMethod(
lineData,
line,
rodDiameter / 4,
rodDiameter / 8,
rodDiameter / 2,
cornerPara,
line_rodArcs //环
);
#endif
//wd_getRodArcFeature_peakCornerMethod(lineData, line, rodDiameter / 2, cornerPara, line_rodArcs);
arcFeatures.push_back(line_rodArcs);
}
return;
}
#endif
//所有点计算点云ROI: vecotr格式, 不过滤z小于0的点
SVzNL3DRangeD _getPointCloudROI(std::vector<SWD3DPointPostion>& scanData)
{
SVzNL3DRangeD roi;
roi.xRange = { 0, -1 };
roi.yRange = { 0, -1 };
roi.zRange = { 0, -1 };
int nPositionCnt = (int)scanData.size();
for (int i = 0; i < nPositionCnt; i++)
{
SWD3DPointPostion& pt3D = scanData[i];
if (roi.xRange.max < roi.xRange.min)
{
roi.xRange.min = pt3D.point.x;
roi.xRange.max = pt3D.point.x;
}
else
{
if (roi.xRange.min > pt3D.point.x)
roi.xRange.min = pt3D.point.x;
if (roi.xRange.max < pt3D.point.x)
roi.xRange.max = pt3D.point.x;
}
//y
if (roi.yRange.max < roi.yRange.min)
{
roi.yRange.min = pt3D.point.y;
roi.yRange.max = pt3D.point.y;
}
else
{
if (roi.yRange.min > pt3D.point.y)
roi.yRange.min = pt3D.point.y;
if (roi.yRange.max < pt3D.point.y)
roi.yRange.max = pt3D.point.y;
}
//z
if (roi.zRange.max < roi.zRange.min)
{
roi.zRange.min = pt3D.point.z;
roi.zRange.max = pt3D.point.z;
}
else
{
if (roi.zRange.min > pt3D.point.z)
roi.zRange.min = pt3D.point.z;
if (roi.zRange.max < pt3D.point.z)
roi.zRange.max = pt3D.point.z;
}
}
return roi;
}
//计算螺杆端部中心点位姿
void sx_hexHeadScrewMeasure(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
bool isHorizonScan, //true:激光线平行槽道false:激光线垂直槽道
const SSG_cornerParam cornerPara,
const SSG_outlierFilterParam filterParam,
const SSG_treeGrowParam growParam,
double rodDiameter,
std::vector<SSX_rodPoseInfo>& screwInfo,
int* errCode)
{
*errCode = 0;
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return;
}
int linePtNum = (int)scanLines[0].size();
//判断数据格式是否为grid。算法只能处理grid数据格式
bool isGridData = true;
for (int line = 0; line < lineNum; line++)
{
if (linePtNum != (int)scanLines[line].size())
{
isGridData = false;
break;
}
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
//聚类
std::vector<SVzNL3DPosition> clusterDataSrc;
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
if (scanLines[line][j].pt3D.z > 1e-4)
{
SVzNL3DPosition a_pt;
a_pt.nPointIdx = (line << 16) | (j & 0xffff);
a_pt.pt3D = scanLines[line][j].pt3D;
clusterDataSrc.push_back(a_pt);
}
}
}
int clusterCheckWin = 5;
int clusterDist = sqrt(pow(growParam.zDeviation_max, 2) + pow(growParam.maxSkipDistance, 2) + pow(growParam.yDeviation_max, 2));
std::vector<std::vector< SVzNL3DPosition>> objClusters;
wd_pointClustering_speedUp(
clusterDataSrc,
lineNum, linePtNum, clusterCheckWin, //搜索窗口
clusterDist,
1, //0 - 2d distance; 1- 3d distance
objClusters //result
);
//生成ClusterMask
int clusterSize = (int)objClusters.size();
std::vector<std::vector<int>> clusterIdMask;
clusterIdMask.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
clusterIdMask[i].resize(linePtNum);
std::fill(clusterIdMask[i].begin(), clusterIdMask[i].end(), -1);
}
for (int i = 0; i < clusterSize; i++)
{
for (int j = 0; j < (int)objClusters[i].size(); j++)
{
int line = objClusters[i][j].nPointIdx >> 16;
int ptIdx = objClusters[i][j].nPointIdx & 0x0000FFFF;
clusterIdMask[line][ptIdx] = i;
}
}
//生成扫描数据副本
std::vector< std::vector<SVzNL3DPosition>> data_lines;
if (false == isHorizonScan)
{
data_lines.resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
data_lines[line].insert(data_lines[line].end(), scanLines[line].begin(), scanLines[line].end());
for (int j = 0, j_max = (int)data_lines[line].size(); j < j_max; j++)
{
data_lines[line][j].nPointIdx = j;
scanLines[line][j].nPointIdx = 0; //转义复用
}
}
}
else
{
data_lines.resize(linePtNum);
for (int i = 0; i < linePtNum; i++)
data_lines[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
data_lines[j][line] = scanLines[line][j];
data_lines[j][line].pt3D.x = scanLines[line][j].pt3D.y;
data_lines[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
lineNum = linePtNum;
linePtNum = (int)data_lines[0].size();
for (int line = 0; line < lineNum; line++)
{
for (int j = 0, j_max = (int)data_lines[line].size(); j < j_max; j++)
data_lines[line][j].nPointIdx = j;
}
}
std::vector<std::vector<SWD_rodArcFeature>> arcFeatures;
const double segment_maxDistTh = rodDiameter / 4;
const double segment_minSegSize = rodDiameter / 8;
rodArcFeatueDetection(
data_lines,
cornerPara,
filterParam,
segment_maxDistTh,
segment_minSegSize,
rodDiameter,
arcFeatures);
//特征生长
std::vector<SWD_rodArcFeatureTree> rodArcTrees;
wd_getRodArcFeatureGrowingTrees(arcFeatures, rodArcTrees, growParam);
if (rodArcTrees.size() == 0)
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
int objNum = (int)rodArcTrees.size();
//内部参数nodeSize小于此的认为无效
int validNodeSizeTH = 30;
//置标志用于debug
for (int i = 0; i < objNum; i++)
{
int nodeNum = (int)rodArcTrees[i].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int lineIdx, ptIdx;
if (false == isHorizonScan)
{
lineIdx = rodArcTrees[i].treeNodes[j].lineIdx;
for (int m = rodArcTrees[i].treeNodes[j].startPtIdx; m <= rodArcTrees[i].treeNodes[j].endPtIdx; m++)
{
ptIdx = m;
if( (m == rodArcTrees[i].treeNodes[j].startPtIdx) || (m == rodArcTrees[i].treeNodes[j].endPtIdx) ||
(m == rodArcTrees[i].treeNodes[j].peakPtIdx))
scanLines[lineIdx][ptIdx].nPointIdx = 2;
else
scanLines[lineIdx][ptIdx].nPointIdx = 1;
}
}
else
{
ptIdx = rodArcTrees[i].treeNodes[j].lineIdx;
for (int m = rodArcTrees[i].treeNodes[j].startPtIdx; m <= rodArcTrees[i].treeNodes[j].endPtIdx; m++)
{
lineIdx = m;
if ((m == rodArcTrees[i].treeNodes[j].startPtIdx) || (m == rodArcTrees[i].treeNodes[j].endPtIdx) ||
(m == rodArcTrees[i].treeNodes[j].peakPtIdx))
scanLines[lineIdx][ptIdx].nPointIdx = 2;
else
scanLines[lineIdx][ptIdx].nPointIdx = 1;
}
}
}
}
//确定目标,以及目标所有的类
//计算ROIz最小的生长树为目标
std::vector<SVzNL3DRangeD> objROIs;
std::vector<int> objCluster;
for (int i = 0; i < objNum; i++)
{
// Initialize min and max values
// Calculate X, Y and Z ranges
SVzNL3DRangeD a_roi3D;
a_roi3D.xRange.min = DBL_MAX; a_roi3D.xRange.max = -DBL_MAX;
a_roi3D.yRange.min = DBL_MAX; a_roi3D.yRange.max = -DBL_MAX;
a_roi3D.zRange.min = DBL_MAX; a_roi3D.zRange.max = -DBL_MAX;
std::vector<int> clusterHist;
clusterHist.resize(clusterSize);
std::fill(clusterHist.begin(), clusterHist.end(), 0);
int nodeNum = (int)rodArcTrees[i].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
SVzNL3DPoint peak;
int lineIdx, ptIdx;
if (false == isHorizonScan)
{
int lineIdx = rodArcTrees[i].treeNodes[j].lineIdx;
int ptIdx = rodArcTrees[i].treeNodes[j].peakPtIdx;
peak = scanLines[lineIdx][ptIdx].pt3D;
int clusterId = clusterIdMask[lineIdx][ptIdx];
if (clusterId >= 0)
clusterHist[clusterId]++;
}
else
{
int ptIdx = rodArcTrees[i].treeNodes[j].lineIdx;
int lineIdx = rodArcTrees[i].treeNodes[j].peakPtIdx;
peak = scanLines[lineIdx][ptIdx].pt3D;
int clusterId = clusterIdMask[lineIdx][ptIdx];
if (clusterId >= 0)
clusterHist[clusterId]++;
}
if (peak.z > 1e-4)
{
a_roi3D.xRange.min = std::min(a_roi3D.xRange.min, peak.x);
a_roi3D.xRange.max = std::max(a_roi3D.xRange.max, peak.x);
a_roi3D.yRange.min = std::min(a_roi3D.yRange.min, peak.y);
a_roi3D.yRange.max = std::max(a_roi3D.yRange.max, peak.y);
a_roi3D.zRange.min = std::min(a_roi3D.zRange.min, peak.z);
a_roi3D.zRange.max = std::max(a_roi3D.zRange.max, peak.z);
}
}
int objClusterId = 0;
for (int m = 1; m < clusterSize; m++)
{
if (clusterHist[objClusterId] < clusterHist[m])
objClusterId = m;
}
objROIs.push_back(a_roi3D);
objCluster.push_back(objClusterId);
}
//选择Z最小的作为目标
int objIdx = -1;
for (int i = 0; i < objNum; i++)
{
int nodeNum = (int)rodArcTrees[i].treeNodes.size();
if (nodeNum < validNodeSizeTH)
continue;
if (objIdx < 0)
objIdx = i;
else if (objROIs[objIdx].zRange.min > objROIs[i].zRange.min)
objIdx = i;
}
if(objIdx < 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
int resultObjClusterId = objCluster[objIdx];
//提取轴向
//空间直线拟合
std::vector<SVzNL3DPoint> fitPoints;
std::vector<SVzNL2DPoint> fit2DPos;
int nodeSize = (int)rodArcTrees[objIdx].treeNodes.size();
for (int j = 0; j < nodeSize; j++)
{
SVzNL2DPoint arcPos;
SVzNL3DPoint a_pt = getArcPeak_parabolaFitting(data_lines, rodArcTrees[objIdx].treeNodes[j], arcPos);
if (false == isHorizonScan)
{
fitPoints.push_back(a_pt);
fit2DPos.push_back(arcPos);
}
else
{
arcPos = { arcPos.y, arcPos.x };
a_pt = {a_pt.y, a_pt.x, a_pt.z};
fitPoints.push_back(a_pt);
fit2DPos.push_back(arcPos);
}
}
if (fitPoints.size() < 20)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
//去除头50mm 尾5个点防止在端部和根部扫描时的数据有干扰
SVzNL3DPoint pt_0 = fitPoints[0];
SVzNL3DPoint pt_last = fitPoints.back();
double headLenToRemove = 40;
if (pt_0.z < pt_last.z)
{
int eraseIdx = -1;
for (int i = 1; i < (int)fitPoints.size(); i++)
{
double dist = sqrt(pow(fitPoints[i].x - pt_0.x, 2) + pow(fitPoints[i].y - pt_0.y, 2) + pow(fitPoints[i].z - pt_0.z, 2));
if (dist > headLenToRemove)
{
eraseIdx = i;
break;
}
}
if(eraseIdx < 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
fitPoints.erase(fitPoints.begin(), fitPoints.begin() + eraseIdx);
fit2DPos.erase(fit2DPos.begin(), fit2DPos.begin() + eraseIdx);
fitPoints.erase(fitPoints.end() - 15, fitPoints.end());
fit2DPos.erase(fit2DPos.end() - 15, fit2DPos.end());
}
else
{
int eraseIdx = -1;
for (int i = (int)fitPoints.size()-1; i >= 0; i--)
{
double dist = sqrt(pow(fitPoints[i].x - pt_last.x, 2) + pow(fitPoints[i].y - pt_last.y, 2) + pow(fitPoints[i].z - pt_last.z, 2));
if (dist > headLenToRemove)
{
eraseIdx = i;
break;
}
}
if (eraseIdx < 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
int eraseNum = (int)fitPoints.size() - eraseIdx;
fitPoints.erase(fitPoints.end() - eraseNum, fitPoints.end());
fit2DPos.erase(fit2DPos.end() - eraseNum, fit2DPos.end());
fitPoints.erase(fitPoints.begin(), fitPoints.begin() + 15);
fit2DPos.erase(fit2DPos.begin(), fit2DPos.begin() + 15);
}
//置标志
for (int j = 0; j < (int)fit2DPos.size(); j++)
{
int lineIdx, ptIdx;
lineIdx = fit2DPos[j].x;
ptIdx = fit2DPos[j].y;
scanLines[lineIdx][ptIdx].nPointIdx = 4;
}
//拟合
SVzNL3DPoint P0_center, P1_dir;
bool result = fitLine3DLeastSquares(fitPoints, P0_center, P1_dir);
if (false == result)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
//投影
//计算旋转向量
SVzNL3DPoint vector1 = P1_dir;
SVzNL3DPoint vector2 = { 0, 0, 1.0 };
SSG_planeCalibPara rotatePara = wd_computeRTMatrix( vector1, vector2);
#if 1
std::vector< SVzNL3DPoint> verifyData;
for (int m = 0; m < (int)fitPoints.size(); m++)
{
SVzNL3DPoint rPt = _ptRotate(fitPoints[m], rotatePara.planeCalib);
verifyData.push_back(rPt);
}
#endif
bool dirInverting = false;
if (((fitPoints[0].z < fitPoints.back().z) && (verifyData[0].z > verifyData.back().z)) ||
((fitPoints[0].z > fitPoints.back().z) && (verifyData[0].z < verifyData.back().z)))
dirInverting = true;
if (true == dirInverting)
{
P1_dir = { -P1_dir.x, -P1_dir.y, -P1_dir.z };
vector1 = P1_dir;
rotatePara = wd_computeRTMatrix(vector1, vector2);
}
verifyData.clear();
for (int m = 0; m < (int)fitPoints.size(); m++)
{
SVzNL3DPoint rPt = _ptRotate(fitPoints[m], rotatePara.planeCalib);
verifyData.push_back(rPt);
}
if (((fitPoints[0].z < fitPoints.back().z) && (verifyData[0].z > verifyData.back().z)) ||
((fitPoints[0].z > fitPoints.back().z) && (verifyData[0].z < verifyData.back().z)))
dirInverting = true;
else
dirInverting = false;
//
SVzNL3DPoint test_center = { P0_center.x, P0_center.y + rodDiameter / 2, P0_center.z - 100 };
SVzNL3DPoint P0_rotate = _ptRotate(P0_center, rotatePara.planeCalib);
SVzNL3DPoint test_center_rotate = _ptRotate(test_center, rotatePara.planeCalib);
SSG_ROIRectD roi_xoy;
roi_xoy.left = P0_rotate.x - rodDiameter* 2.0; //2D范围
roi_xoy.right = P0_rotate.x + rodDiameter * 2.0; //2D范围
roi_xoy.top = P0_rotate.y - rodDiameter * 2.0; //2D范围
roi_xoy.bottom = P0_rotate.y + rodDiameter * 2.0; //2D范围
std::vector< SWD3DPointPostion> roiProjectionData;
xoyROIProjection(scanLines, rotatePara.planeCalib, roi_xoy, resultObjClusterId, clusterIdMask, roiProjectionData);
//取端面
SVzNLRangeD zRange = getZRange(roiProjectionData);
SVzNLRangeD cutZRange;
if (false == dirInverting)
{
cutZRange.min = zRange.min;
cutZRange.max = zRange.min + 10.0; //5mm的端面
}
else
{
cutZRange.max = zRange.max;
cutZRange.min = zRange.max - 10.0; //5mm的端面
}
std::vector<SWD3DPointPostion> surfacePoints;
std::vector<std::vector<int>>addrMapping;
addrMapping.resize(scanLines.size());
for (int i = 0; i < (int)scanLines.size(); i++)
{
addrMapping[i].resize(scanLines[i].size());
std::fill(addrMapping[i].begin(), addrMapping[i].end(), -1);
}
zCutPointClouds(roiProjectionData, cutZRange, surfacePoints, addrMapping);
//计算中心点
SWD3DPointPostion projectionCenter;// = getXoYCentroid(surfacePoints);
SVzNL3DRangeD roi3D = _getPointCloudROI(surfacePoints);
//计算XY平面上的质心
double sum_x = 0, sum_y = 0;
int sum_size = 0;
for (int i = 0; i < (int)surfacePoints.size(); i++)
{
if (surfacePoints[i].point.z > 1e-4)
{
sum_x += surfacePoints[i].point.x;
sum_y += surfacePoints[i].point.y;
sum_size++;
}
}
if(sum_size == 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
projectionCenter.lineIdx = -1;
projectionCenter.ptIdx = -1;
projectionCenter.point.x = sum_x / sum_size; // (roi3D.xRange.min + roi3D.xRange.max) / 2;
projectionCenter.point.y = sum_y / sum_size; // (roi3D.yRange.min + roi3D.yRange.max) / 2;
projectionCenter.point.z = zRange.min;
//迭代搜索搜索projectionCenter为中心5mm内z最大的的点为中心点
double searchR = 5.0;
int centerIdx = -1;
double maxZ = -1;
for (int i = 0; i < (int)surfacePoints.size(); i++)
{
double dist = sqrt(pow(surfacePoints[i].point.x - projectionCenter.point.x, 2) + pow(surfacePoints[i].point.y - projectionCenter.point.y, 2));
if (dist < searchR)
{
if (centerIdx < 0)
{
centerIdx = i;
maxZ = surfacePoints[i].point.z;
}
else
{
if (((false == dirInverting) && (surfacePoints[centerIdx].point.z < surfacePoints[i].point.z)) ||
((true == dirInverting) && (surfacePoints[centerIdx].point.z > surfacePoints[i].point.z)))
{
centerIdx = i;
maxZ = surfacePoints[i].point.z;
}
}
}
}
if(centerIdx < 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
int centerIdx_test = addrMapping[surfacePoints[centerIdx].lineIdx][surfacePoints[centerIdx].ptIdx];
//迭代一次
projectionCenter.lineIdx = surfacePoints[centerIdx].lineIdx;
projectionCenter.ptIdx = surfacePoints[centerIdx].ptIdx;
int sLine = projectionCenter.lineIdx - 5;
if (sLine < 0)
sLine = 0;
int eLine = projectionCenter.lineIdx + 5;
if (eLine >= (int)scanLines.size())
eLine = (int)scanLines.size() - 1;
int sPtIdx = projectionCenter.ptIdx - 5;
if (sPtIdx < 0)
sPtIdx = 0;
int ePtIdx = projectionCenter.ptIdx + 5;
if (ePtIdx >= (int)scanLines[0].size())
ePtIdx = (int)scanLines[0].size() - 1;
int objLine = -1;
int objPtIdx = -1;
maxZ = -1;
for (int line = sLine; line <= eLine; line++)
{
for (int ptIdx = sPtIdx; ptIdx <= ePtIdx; ptIdx++)
{
int idx_center = addrMapping[line][ptIdx];
if (idx_center < 0)
continue;
int sL = line - 1;
if (sL < 0)
sL = 0;
int eL = line + 1;
if (eL >= (int)scanLines.size())
eL = (int)scanLines.size() - 1;
int sPt = ptIdx - 1;
if (sPt < 0)
sPt = 0;
int ePt = ptIdx + 1;
if (ePt >= (int)scanLines[0].size())
ePt = (int)scanLines[0].size() - 1;
int size = 0;
double sumZ = 0;
for (int i = sL; i <= eL; i++)
{
for (int j = sPt; j <= ePt; j++)
{
int idx = addrMapping[i][j];
if (idx >= 0)
{
if (roiProjectionData[idx].point.z > 1e-4)
{
sumZ += roiProjectionData[idx].point.z;
size++;
}
}
}
}
if (size > 0)
{
sumZ = sumZ / size;
if (maxZ < 0)
{
maxZ = sumZ;
objLine = line;
objPtIdx = ptIdx;
}
else if (((false == dirInverting) && (maxZ < sumZ)) || ((true == dirInverting) && (maxZ > sumZ)))
{
maxZ = sumZ;
objLine = line;
objPtIdx = ptIdx;
}
}
}
}
if( (objLine >=0)&&(objPtIdx >=0))
centerIdx = addrMapping[objLine][objPtIdx];
//旋转回原坐标系
SVzNL3DPoint surfaceCenter = _ptRotate(roiProjectionData[centerIdx].point, rotatePara.invRMatrix);
//生成Rod信息
SSX_rodPoseInfo a_rod;
a_rod.center = surfaceCenter;
a_rod.axialDir = P1_dir;
screwInfo.push_back(a_rod);
return;
}
//PCA方法计算螺杆端部中心点位姿
//相对于sx_hexHeadScrewMeasure()算法上1去除了水平段2使用PCA方法计算轴向
void sx_hexHeadScrewMeasure_PCA(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
//bool isHorizonScan, //true:激光线平行槽道false:激光线垂直槽道
const SSG_cornerParam cornerPara,
const SSG_outlierFilterParam filterParam,
const SSG_treeGrowParam growParam,
double rodDiameter,
std::vector<SSX_rodPoseInfo>& screwInfo,
int* errCode)
{
*errCode = 0;
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return;
}
int linePtNum = (int)scanLines[0].size();
//判断数据格式是否为grid。算法只能处理grid数据格式
bool isGridData = true;
for (int line = 0; line < lineNum; line++)
{
if (linePtNum != (int)scanLines[line].size())
{
isGridData = false;
break;
}
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
//产生数据Copy和水平扫描数据
std::vector< std::vector<SVzNL3DPosition>> scanLines_copy;
scanLines_copy.resize(scanLines.size());
std::vector< std::vector<SVzNL3DPosition>> scanLines_h;
scanLines_h.resize(linePtNum);
for (int i = 0; i < linePtNum; i++)
scanLines_h[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
scanLines_copy[line].insert(scanLines_copy[line].end(), scanLines[line].begin(), scanLines[line].end());
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
scanLines_h[j][line] = scanLines[line][j];
scanLines_h[j][line].pt3D.x = scanLines[line][j].pt3D.y;
scanLines_h[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
for (int line = 0; line < linePtNum; line++)
{
for (int j = 0, j_max = (int)scanLines_h[line].size(); j < j_max; j++)
scanLines_h[line][j].nPointIdx = j;
}
//算法流程:
//1、检查水平方向数据并去除
//2、聚类
//3、保留最前面目标
//内部参数
SSG_cornerParam removeHorizonPara = cornerPara;
removeHorizonPara.scale = 5.0;
removeHorizonPara.cornerTh = 45;
double maxDistTh = 10.0;
double minSegSize = 3.0; //小于3mm的segment长度被过滤掉
std::vector<std::vector<int>> flags;
flags.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
flags[i].resize(linePtNum);
std::fill(flags[i].begin(), flags[i].end(), 0);
}
std::vector<std::vector<int>> zHorizonFlags;
for (int line = 0; line < lineNum; line++)
{
if (line == 248)
int kkk = 1;
std::vector<int> line_horizontalFlags;
wd_getXYHorizontalFeature_dirAngleMethod(
scanLines_copy[line],
line,
maxDistTh,
minSegSize,
removeHorizonPara,
line_horizontalFlags
);
zHorizonFlags.push_back(line_horizontalFlags);
for (int i = 0; i < (int)line_horizontalFlags.size(); i++)
{
if (line_horizontalFlags[i] > 0)
flags[line][i] = 1;
}
}
#if 0
std::vector<std::vector<int>> zHorizonFlags_h;
for (int line = 0; line < linePtNum; line++)
{
if (line == 1177)
int kkk = 1;
std::vector<int> line_horizontalFlags;
wd_getXYHorizontalFeature_dirAngleMethod(
scanLines_h[line],
line,
removeHorizonPara,
line_horizontalFlags
);
zHorizonFlags_h.push_back(line_horizontalFlags);
for (int i = 0; i < (int)line_horizontalFlags.size(); i++)
{
if (line_horizontalFlags[i] > 0)
flags[i][line] = 1;
}
}
#endif
//去除操作
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
if (flags[line][j] > 0)
{
scanLines_copy[line][j].pt3D.z = 0;
scanLines_h[j][line].pt3D.z = 0;
}
}
}
//迭代一次
SSG_lineSegParam lineSegPara;
lineSegPara.distScale = 10.0;
lineSegPara.segGapTh_y = 10.0;
lineSegPara.segGapTh_z = 10.0;
const int minSegLen = 5;
for (int line = 0; line < lineNum; line++)
{
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(
scanLines_copy[line],
lineSegPara,
segs);
for (int i = 0; i < (int)segs.size(); i++)
{
if (segs[i].len <= minSegLen)
{
int idx0 = segs[i].start;
for (int j = 0; j < segs[i].len; j++)
flags[line][idx0 + j] = 1;
}
}
}
for (int line = 0; line < linePtNum; line++)
{
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(
scanLines_h[line],
lineSegPara,
segs);
for (int i = 0; i < (int)segs.size(); i++)
{
if (segs[i].len <= minSegLen)
{
int idx0 = segs[i].start;
for (int j = 0; j < segs[i].len; j++)
flags[idx0 + j][line] = 1;
}
}
}
//标注
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
scanLines_copy[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
}
//将垂直线段去除
std::vector< SVzNL3DPosition> validPoints;
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
if (flags[line][j] > 0)
scanLines_copy[line][j].pt3D.z = 0;
if (scanLines_copy[line][j].pt3D.z > 1e-4)
{
SVzNL3DPosition a_vldPt;
a_vldPt.pt3D = scanLines_copy[line][j].pt3D;
a_vldPt.nPointIdx = (line << 16) | (j & 0xffff);
validPoints.push_back(a_vldPt);
}
}
}
//聚类
//内部参数
double minObjSize_w = 150;
double minScrewLen = 50;
int clusterCheckWin = 5;
double clusterDist = 2.5;
int distType = 1; //0 - 2d distance; 1- 3d distance
std::vector<std::vector< SVzNL3DPosition>> objClusters; //result
wd_pointClustering_speedUp(
validPoints,
lineNum, linePtNum, clusterCheckWin, //搜索窗口
clusterDist,
distType,
objClusters //result
);
//使用cluster的ROI信息过滤目标将Z最小的符合要求的目标判断为中间的螺杆
int clusterSize = (int)objClusters.size();
std::vector<SVzNL3DRangeD> objROIs;
for (int i = 0; i < clusterSize; i++)
{
// Initialize min and max values
// Calculate X, Y and Z ranges
SVzNL3DRangeD a_roi3D;
a_roi3D.xRange.min = DBL_MAX; a_roi3D.xRange.max = -DBL_MAX;
a_roi3D.yRange.min = DBL_MAX; a_roi3D.yRange.max = -DBL_MAX;
a_roi3D.zRange.min = DBL_MAX; a_roi3D.zRange.max = -DBL_MAX;
int nodeNum = (int)objClusters[i].size();
for (int j = 0; j < nodeNum; j++)
{
SVzNL3DPosition& a_pt = objClusters[i][j];
if (a_pt.pt3D.z > 1e-4)
{
a_roi3D.xRange.min = std::min(a_roi3D.xRange.min, a_pt.pt3D.x);
a_roi3D.xRange.max = std::max(a_roi3D.xRange.max, a_pt.pt3D.x);
a_roi3D.yRange.min = std::min(a_roi3D.yRange.min, a_pt.pt3D.y);
a_roi3D.yRange.max = std::max(a_roi3D.yRange.max, a_pt.pt3D.y);
a_roi3D.zRange.min = std::min(a_roi3D.zRange.min, a_pt.pt3D.z);
a_roi3D.zRange.max = std::max(a_roi3D.zRange.max, a_pt.pt3D.z);
}
}
objROIs.push_back(a_roi3D);
}
std::vector<int> objCluster;
for (int i = 0; i < clusterSize; i++)
{
double x_width = objROIs[i].xRange.max - objROIs[i].xRange.min;
double y_width = objROIs[i].yRange.max - objROIs[i].yRange.min;
double z_width = objROIs[i].zRange.max - objROIs[i].zRange.min;
if ((x_width < rodDiameter * 3.0) && (y_width < rodDiameter*3.0) && (z_width > minScrewLen) && (objClusters[i].size() > 100))
objCluster.push_back(i);
}
//取最前面的
int targetClusterID = -1;
for (int i = 0; i < objCluster.size(); i++)
{
int clusterIdx = objCluster[i];
if (targetClusterID < 0)
targetClusterID = clusterIdx;
else if(objROIs[targetClusterID].zRange.min > objROIs[clusterIdx].zRange.min)
targetClusterID = clusterIdx;
}
if(targetClusterID < 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
//进行PCA前去除螺杆根部防止影响PCA精度
double zMax = objROIs[targetClusterID].zRange.max - 30; //去除螺杆根部影响
double zMin = objROIs[targetClusterID].zRange.min + 30; //去除螺杆根部影响
std::vector< SVzNL3DPosition > PCA_points;
for (int i = 0; i < objClusters[targetClusterID].size(); i++)
{
if ((objClusters[targetClusterID][i].pt3D.z < zMax) && (objClusters[targetClusterID][i].pt3D.z > zMin))
PCA_points.push_back(objClusters[targetClusterID][i]);
}
SVzNL3DPoint vec_axis, vec_centroid;
//PCA计算轴向量
computeCylinderAxisFromIncompletePCA(
PCA_points,
vec_axis,
vec_centroid);
if (vec_axis.z < 0) //确定唯一方向
vec_axis = { -vec_axis.x, -vec_axis.y, -vec_axis.z };
//投影
//计算旋转向量
SVzNL3DPoint vector1 = vec_axis;
SVzNL3DPoint vector2 = { 0, 0, 1.0 };
SSG_planeCalibPara rotatePara = wd_computeRTMatrix(vector1, vector2);
///此处考虑到倾斜情况下最前面的螺杆和正投影下会有不同,需要迭代一下
//迭代一次,确定正确的螺杆
std::vector<std::vector< SVzNL3DPosition>> rotate_objClusters; //result
rotate_objClusters.resize(objClusters.size());
std::vector<SVzNL3DRangeD> rotate_objROIs;
for (int i = 0; i < clusterSize; i++)
{
rotate_objClusters[i].resize(objClusters[i].size());
// Initialize min and max values
// Calculate X, Y and Z ranges
SVzNL3DRangeD a_roi3D;
a_roi3D.xRange.min = DBL_MAX; a_roi3D.xRange.max = -DBL_MAX;
a_roi3D.yRange.min = DBL_MAX; a_roi3D.yRange.max = -DBL_MAX;
a_roi3D.zRange.min = DBL_MAX; a_roi3D.zRange.max = -DBL_MAX;
int nodeNum = (int)objClusters[i].size();
for (int j = 0; j < nodeNum; j++)
{
SVzNL3DPosition& a_pt = objClusters[i][j];
if (a_pt.pt3D.z > 1e-4)
{
SVzNL3DPosition rotate_pt;
rotate_pt.nPointIdx = a_pt.nPointIdx;
rotate_pt.pt3D = _translatePoint(a_pt.pt3D, rotatePara.planeCalib);
rotate_objClusters[i][j] = rotate_pt;
a_roi3D.xRange.min = std::min(a_roi3D.xRange.min, rotate_pt.pt3D.x);
a_roi3D.xRange.max = std::max(a_roi3D.xRange.max, rotate_pt.pt3D.x);
a_roi3D.yRange.min = std::min(a_roi3D.yRange.min, rotate_pt.pt3D.y);
a_roi3D.yRange.max = std::max(a_roi3D.yRange.max, rotate_pt.pt3D.y);
a_roi3D.zRange.min = std::min(a_roi3D.zRange.min, rotate_pt.pt3D.z);
a_roi3D.zRange.max = std::max(a_roi3D.zRange.max, rotate_pt.pt3D.z);
}
}
rotate_objROIs.push_back(a_roi3D);
}
//重新确定Z最小的目标
//取最前面的
targetClusterID = -1;
for (int i = 0; i < objCluster.size(); i++)
{
int clusterIdx = objCluster[i];
if (targetClusterID < 0)
targetClusterID = clusterIdx;
else if (rotate_objROIs[targetClusterID].zRange.min > rotate_objROIs[clusterIdx].zRange.min)
targetClusterID = clusterIdx;
}
if (targetClusterID < 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
//进行PCA前去除螺杆根部防止影响PCA精度
zMax = objROIs[targetClusterID].zRange.max - 30.0; //去除螺杆根部影响
zMin = objROIs[targetClusterID].zRange.min + 30.0; //去除螺杆根部影响
PCA_points.clear();
for (int i = 0; i < objClusters[targetClusterID].size(); i++)
{
if ((objClusters[targetClusterID][i].pt3D.z < zMax) && (objClusters[targetClusterID][i].pt3D.z > zMin))
PCA_points.push_back(objClusters[targetClusterID][i]);
}
//重新使用PCA方法计算轴向此处使用旋转前数据
computeCylinderAxisFromIncompletePCA(
PCA_points,
vec_axis,
vec_centroid);
if (vec_axis.z < 0) //确定唯一方向
vec_axis = { -vec_axis.x, -vec_axis.y, -vec_axis.z };
//生成原始数据的去零点的点云数据
std::vector< SVzNL3DPosition> raw_validPoints;
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
if (scanLines[line][j].pt3D.z > 1e-4)
{
SVzNL3DPosition a_vldPt;
a_vldPt.pt3D = scanLines[line][j].pt3D;
a_vldPt.nPointIdx = (line << 16) | (j & 0xffff);
raw_validPoints.push_back(a_vldPt);
}
}
}
// 2、补充完整端面数据
//在未旋转的点云中继续生长(端面可能在去除水平点中被去除)
SVzNL3DRangeD growingROI = objROIs[targetClusterID];
growingROI.xRange.min -= rodDiameter;
growingROI.xRange.max += rodDiameter;
growingROI.yRange.min -= rodDiameter;
growingROI.yRange.max += rodDiameter;
growingROI.zRange.max = (growingROI.zRange.max + growingROI.zRange.min) / 2; //从Z的中间向外生长
growingROI.zRange.min -= rodDiameter;
std::vector< SVzNL3DPosition>& screw_cluster = objClusters[targetClusterID];
std::vector< SVzNL3DPosition> added_points;
wd_clusterGrowing_speedUp(
raw_validPoints,
screw_cluster,
growingROI, //聚类范围,用于加速
lineNum, linePtNum, clusterCheckWin, //搜索窗口
clusterDist,
distType, //0 - 2d distance; 1- 3d distance
added_points
);
#if 1
for (int i = 0; i < (int)added_points.size(); i++)
{
int line = added_points[i].nPointIdx >> 16;
int ptIdx = added_points[i].nPointIdx & 0x0000FFFF;
scanLines_copy[line][ptIdx].pt3D = added_points[i].pt3D; //恢复
}
#endif
SVzNL3DPoint rotate_centroid = _ptRotate(vec_centroid, rotatePara.planeCalib);
std::vector< SWD3DPointPostion> roiProjectionData;
//投影提取ROI内的数据
for (int i = 0; i < (int)objClusters[targetClusterID].size(); i++)
{
SVzNL3DPoint a_pt = objClusters[targetClusterID][i].pt3D;
if (a_pt.z < 1e-4)
continue;
int line = objClusters[targetClusterID][i].nPointIdx >> 16;
int ptIdx = objClusters[targetClusterID][i].nPointIdx & 0x0000FFFF;
double x = a_pt.x * rotatePara.planeCalib[0] + a_pt.y * rotatePara.planeCalib[1] + a_pt.z * rotatePara.planeCalib[2];
double y = a_pt.x * rotatePara.planeCalib[3] + a_pt.y * rotatePara.planeCalib[4] + a_pt.z * rotatePara.planeCalib[5];
double z = a_pt.x * rotatePara.planeCalib[6] + a_pt.y * rotatePara.planeCalib[7] + a_pt.z * rotatePara.planeCalib[8];
if (z <= rotate_centroid.z)
{
SWD3DPointPostion projectPt;
projectPt.lineIdx = line;
projectPt.ptIdx = ptIdx;
projectPt.point.x = x;
projectPt.point.y = y;
projectPt.point.z = z;
roiProjectionData.push_back(projectPt);
}
}
bool dirInverting = false;
//取端面
SVzNLRangeD zRange = getZRange(roiProjectionData);
SVzNLRangeD cutZRange;
if (false == dirInverting)
{
cutZRange.min = zRange.min;
cutZRange.max = zRange.min + 10.0; //5mm的端面
}
else
{
cutZRange.max = zRange.max;
cutZRange.min = zRange.max - 10.0; //5mm的端面
}
std::vector<SWD3DPointPostion> surfacePoints;
std::vector<std::vector<int>>addrMapping;
addrMapping.resize(scanLines.size());
for (int i = 0; i < (int)scanLines.size(); i++)
{
addrMapping[i].resize(scanLines[i].size());
std::fill(addrMapping[i].begin(), addrMapping[i].end(), -1);
}
zCutPointClouds(roiProjectionData, cutZRange, surfacePoints, addrMapping);
//计算中心点
SWD3DPointPostion projectionCenter;// = getXoYCentroid(surfacePoints);
SVzNL3DRangeD roi3D = _getPointCloudROI(surfacePoints);
//计算XY平面上的质心
double sum_x = 0, sum_y = 0;
int sum_size = 0;
for (int i = 0; i < (int)surfacePoints.size(); i++)
{
if (surfacePoints[i].point.z > 1e-4)
{
sum_x += surfacePoints[i].point.x;
sum_y += surfacePoints[i].point.y;
sum_size++;
}
}
if (sum_size == 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
projectionCenter.lineIdx = -1;
projectionCenter.ptIdx = -1;
projectionCenter.point.x = sum_x / sum_size; // (roi3D.xRange.min + roi3D.xRange.max) / 2;
projectionCenter.point.y = sum_y / sum_size; // (roi3D.yRange.min + roi3D.yRange.max) / 2;
projectionCenter.point.z = zRange.min;
//迭代搜索搜索projectionCenter为中心5mm内z最大的的点为中心点
double searchR = 5.0;
int centerIdx = -1;
double maxZ = -1;
for (int i = 0; i < (int)surfacePoints.size(); i++)
{
double dist = sqrt(pow(surfacePoints[i].point.x - projectionCenter.point.x, 2) + pow(surfacePoints[i].point.y - projectionCenter.point.y, 2));
if (dist < searchR)
{
if (centerIdx < 0)
{
centerIdx = i;
maxZ = surfacePoints[i].point.z;
}
else
{
if (((false == dirInverting) && (surfacePoints[centerIdx].point.z < surfacePoints[i].point.z)) ||
((true == dirInverting) && (surfacePoints[centerIdx].point.z > surfacePoints[i].point.z)))
{
centerIdx = i;
maxZ = surfacePoints[i].point.z;
}
}
}
}
if (centerIdx < 0)
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
int centerIdx_test = addrMapping[surfacePoints[centerIdx].lineIdx][surfacePoints[centerIdx].ptIdx];
//迭代一次
projectionCenter.lineIdx = surfacePoints[centerIdx].lineIdx;
projectionCenter.ptIdx = surfacePoints[centerIdx].ptIdx;
int sLine = projectionCenter.lineIdx - 5;
if (sLine < 0)
sLine = 0;
int eLine = projectionCenter.lineIdx + 5;
if (eLine >= (int)scanLines.size())
eLine = (int)scanLines.size() - 1;
int sPtIdx = projectionCenter.ptIdx - 5;
if (sPtIdx < 0)
sPtIdx = 0;
int ePtIdx = projectionCenter.ptIdx + 5;
if (ePtIdx >= (int)scanLines[0].size())
ePtIdx = (int)scanLines[0].size() - 1;
int objLine = -1;
int objPtIdx = -1;
maxZ = -1;
for (int line = sLine; line <= eLine; line++)
{
for (int ptIdx = sPtIdx; ptIdx <= ePtIdx; ptIdx++)
{
int idx_center = addrMapping[line][ptIdx];
if (idx_center < 0)
continue;
int sL = line - 1;
if (sL < 0)
sL = 0;
int eL = line + 1;
if (eL >= (int)scanLines.size())
eL = (int)scanLines.size() - 1;
int sPt = ptIdx - 1;
if (sPt < 0)
sPt = 0;
int ePt = ptIdx + 1;
if (ePt >= (int)scanLines[0].size())
ePt = (int)scanLines[0].size() - 1;
int size = 0;
double sumZ = 0;
for (int i = sL; i <= eL; i++)
{
for (int j = sPt; j <= ePt; j++)
{
int idx = addrMapping[i][j];
if (idx >= 0)
{
if (roiProjectionData[idx].point.z > 1e-4)
{
sumZ += roiProjectionData[idx].point.z;
size++;
}
}
}
}
if (size > 0)
{
sumZ = sumZ / size;
if (maxZ < 0)
{
maxZ = sumZ;
objLine = line;
objPtIdx = ptIdx;
}
else if (((false == dirInverting) && (maxZ < sumZ)) || ((true == dirInverting) && (maxZ > sumZ)))
{
maxZ = sumZ;
objLine = line;
objPtIdx = ptIdx;
}
}
}
}
if ((objLine >= 0) && (objPtIdx >= 0))
centerIdx = addrMapping[objLine][objPtIdx];
//旋转回原坐标系
SVzNL3DPoint surfaceCenter = _ptRotate(roiProjectionData[centerIdx].point, rotatePara.invRMatrix);
//生成Rod信息
SSX_rodPoseInfo a_rod;
a_rod.center = surfaceCenter;
a_rod.axialDir = vec_axis;
screwInfo.push_back(a_rod);
#if 0
//自制scanlines_copy数据用于测试
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
scanLines[line][j].pt3D = scanLines_copy[line][j].pt3D;
}
#endif
return;
}
double _getListMeanZ(std::vector< SVzNL3DPosition>& listData, SVzNLRangeD& zRange)
{
if (listData.size() == 0)
return 0;
double meanZ = 0;
zRange.max = -1;
zRange.min = 0;
for (int i = 0; i < (int)listData.size(); i++)
{
meanZ += listData[i].pt3D.z;
if (zRange.max < 0)
{
zRange.max = listData[i].pt3D.z;
zRange.min = listData[i].pt3D.z;
}
else
{
zRange.max = zRange.max < listData[i].pt3D.z ? listData[i].pt3D.z : zRange.max;
zRange.min = zRange.min > listData[i].pt3D.z ? listData[i].pt3D.z : zRange.min;
}
}
meanZ = meanZ / (double)listData.size();
return meanZ;
}
SSG_ROIRectD _getListROI(std::vector< SVzNL3DPosition>& listData)
{
if (listData.size() == 0)
return { 0,0,0,0 };
SSG_ROIRectD roi = { listData[0].pt3D.x, listData[0].pt3D.x, listData[0].pt3D.y, listData[0].pt3D.y};
for (int i = 0; i < (int)listData.size(); i++)
{
roi.left = roi.left > listData[i].pt3D.x ? listData[i].pt3D.x : roi.left;
roi.right = roi.right < listData[i].pt3D.x ? listData[i].pt3D.x : roi.right;
roi.top = roi.top > listData[i].pt3D.y ? listData[i].pt3D.y : roi.top;
roi.bottom = roi.bottom < listData[i].pt3D.y ? listData[i].pt3D.y : roi.bottom;
}
return roi;
}
bool _compareByAngle(const SWD_polarPt& a, const SWD_polarPt& b) {
return a.angle < b.angle;
}
//计算封闭序列首尾相连在XOY平面内前向角和后向角以及拐角。 前向角:序号+ 后向角:序号-
void _computeClosedPntListDirCorners(std::vector<SWD_polarPt>& polarPoints, double scale, std::vector< SSG_dirCornerAngle>& dirCornerAngles)
{
int pntSize = (int)polarPoints.size();
//std::fill(dirCornerAngles.begin(), dirCornerAngles.end(), SSG_dirCornerAngle{ 0, 0, 0, 0, 0, 0 });
dirCornerAngles.resize(pntSize);
for (int i = 0; i < pntSize; i++)
{
if (i == 68)
int kkk = 1;
memset(&dirCornerAngles[i], 0, sizeof(SSG_dirCornerAngle));
SWD_polarPt& a_polarPt = polarPoints[i];
//前向寻找
int minus_i = -1;
for (int loop = i - 1; loop >= i - pntSize; loop--)
{
int j = loop >= 0 ? loop : (loop + pntSize);
double dist = sqrt(pow(polarPoints[i].x - polarPoints[j].x, 2) +
pow(polarPoints[i].y - polarPoints[j].y, 2));
if (dist >= scale)
{
minus_i = j;
break;
}
}
//后向寻找
int plus_i = -1;
for (int loop = i + 1; loop < i + pntSize; loop++)
{
int j = loop % pntSize;
double dist = sqrt(pow(polarPoints[i].x - polarPoints[j].x, 2) +
pow(polarPoints[i].y - polarPoints[j].y, 2));
if (dist >= scale)
{
plus_i = j;
break;
}
}
//计算拐角
if ((minus_i >= 0) && (plus_i >= 0))
{
double backwardAngle = atan2(polarPoints[i].y - polarPoints[minus_i].y, polarPoints[i].x - polarPoints[minus_i].x) * 180.0 / PI;
double forwardAngle = atan2(polarPoints[plus_i].y - polarPoints[i].y, polarPoints[plus_i].x - polarPoints[i].x) * 180.0 / PI;
dirCornerAngles[i].forwardAngle = forwardAngle;
dirCornerAngles[i].backwardAngle = backwardAngle;
double corner = forwardAngle - backwardAngle;
if (corner < -180)
corner += 360;
else if (corner > 180)
corner = corner - 360;
dirCornerAngles[i].flag = 0;
//过滤掉由于极角和方向角相近产生的轮廓点位序错误导致的corner错误
double polarAngle = dirCornerAngles[i].point.angle;
double angleDiff1 = abs(polarAngle - dirCornerAngles[i].forwardAngle);
if (angleDiff1 > 180)
angleDiff1 = 360 - angleDiff1;
double angleDiff2 = abs(polarAngle - dirCornerAngles[i].backwardAngle);
if (angleDiff2 > 180)
angleDiff2 = 360 - angleDiff2;
if ((angleDiff1 < 10) || (angleDiff2 < 10)) //过滤掉边与极线方向相近导致的波动
{
if (dirCornerAngles[i].corner > 160) //乱序产生角度错误
{
dirCornerAngles[i].flag = -1;
}
}
dirCornerAngles[i].corner = corner; //图像坐标系与正常坐标系y方向相反所以有“-”号
dirCornerAngles[i].pntIdx = i;
dirCornerAngles[i].forward_pntIdx = plus_i;
dirCornerAngles[i].backward_pntIdx = minus_i;
dirCornerAngles[i].point = polarPoints[i];
}
}
}
//提取corner极值较早实现函数可以使用此函数进行代码优化
void _searchPlusCornerPeaks(
std::vector< SSG_dirCornerAngle>& corners,
double cutAngleTh,
std::vector< SSG_dirCornerAngle>& cornerPlusPeaks
)
{
std::vector<SSG_dirCornerAngle> peakCorners;
int cornerSize = (int)corners.size();
//搜索拐角极值
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
SSG_dirCornerAngle* pre_data = NULL;
for (int i = 0, i_max = cornerSize; i < i_max; i++)
{
if (i == 451)
int kkk = 1;
SSG_dirCornerAngle* curr_data = &corners[i];
if (curr_data->flag < 0)
continue;
if (NULL == pre_data)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
continue;
}
eEdgePtIdx = i;
double cornerDiff = curr_data->corner - pre_data->corner;
switch (_state)
{
case 0: //初态
if (cornerDiff < 0) //下降
{
_state = 2;
}
else if (cornerDiff > 0) //上升
{
_state = 1;
}
break;
case 1: //上升
if (cornerDiff < 0) //下降
{
if (pre_data->corner > cutAngleTh) //截角门限,滤除点波动导致的角度变化
peakCorners.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (cornerDiff > 0) // 上升
_state = 1;
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//注意:最后一个不处理,为基座位置
//极小值点(峰顶)
//极值比较,在尺度窗口下寻找局部极值点
for (int i = 0, i_max = (int)peakCorners.size(); i < i_max; i++)
{
bool isPeak = true;
SSG_dirCornerAngle& a_dirAngle = peakCorners[i];
int curr_dist1 = a_dirAngle.pntIdx - a_dirAngle.backward_pntIdx;
if (curr_dist1 < 0)
curr_dist1 += cornerSize;
int curr_dist2 = a_dirAngle.forward_pntIdx - a_dirAngle.pntIdx;
if (curr_dist2 < 0)
curr_dist2 += cornerSize;
//minus方向寻找
int minus_i = i;
while (1)
{
minus_i--;
if (minus_i < 0)
minus_i += i_max;
SSG_dirCornerAngle& minus_dirAngle = peakCorners[minus_i];
int pntDist_0 = a_dirAngle.pntIdx - minus_dirAngle.pntIdx;
if (pntDist_0 < 0)
pntDist_0 += cornerSize;
if (pntDist_0 > curr_dist1)
break;
if (a_dirAngle.corner < minus_dirAngle.corner)
{
isPeak = false;
break;
}
}
//plus方向寻找
int plus_i = i;
while (1)
{
plus_i++;
if (plus_i >= i_max)
plus_i = 0;
SSG_dirCornerAngle& plus_dirAngle = peakCorners[plus_i];
int pntDist_0 = plus_dirAngle.pntIdx - a_dirAngle.pntIdx;
if (pntDist_0 < 0)
pntDist_0 += cornerSize;
if (pntDist_0 > curr_dist2)
break;
if (a_dirAngle.corner < plus_dirAngle.corner)
{
isPeak = false;
break;
}
}
if (true == isPeak)
{
double corner_curr = a_dirAngle.corner;
double corner_1 = corners[a_dirAngle.forward_pntIdx].corner;
double corner_2 = corners[a_dirAngle.backward_pntIdx].corner;
double diff_1 = corner_curr - corner_1;
double diff_2 = corner_curr - corner_2;
if ((diff_1 > corner_curr / 4) && (diff_2 > corner_curr / 4))
{
double polarAngle = a_dirAngle.point.angle;
double angleDiff1 = abs(polarAngle - a_dirAngle.forwardAngle);
if (angleDiff1 > 180)
angleDiff1 = 360 - angleDiff1;
double angleDiff2 = abs(polarAngle - a_dirAngle.backwardAngle);
if (angleDiff2 > 180)
angleDiff2 = 360 - angleDiff2;
if ((angleDiff1 < 10) || (angleDiff2 < 10)) //过滤掉边与极线方向相近导致的波动
{
if (a_dirAngle.corner < 160) //乱序产生角度错误
{
a_dirAngle.flag = 0;
cornerPlusPeaks.push_back(a_dirAngle);
}
}
else
{
a_dirAngle.flag = 0;
cornerPlusPeaks.push_back(a_dirAngle);
}
}
}
}
}
void _updateRoi3D(SVzNL3DRangeD& roi, SVzNL3DPoint& a_pt)
{
if (a_pt.z > 1E-4)
{
if (roi.zRange.max < 0)
{
roi.xRange.min = a_pt.x;
roi.xRange.max = a_pt.x;
roi.yRange.min = a_pt.y;
roi.yRange.max = a_pt.y;
roi.zRange.min = a_pt.z;
roi.zRange.max = a_pt.z;
}
else
{
if (roi.xRange.min > a_pt.x)
roi.xRange.min = a_pt.x;
if (roi.xRange.max < a_pt.x)
roi.xRange.max = a_pt.x;
if (roi.yRange.min > a_pt.y)
roi.yRange.min = a_pt.y;
if (roi.yRange.max < a_pt.y)
roi.yRange.max = a_pt.y;
if (roi.zRange.min > a_pt.z)
roi.zRange.min = a_pt.z;
if (roi.zRange.max < a_pt.z)
roi.zRange.max = a_pt.z;
}
}
return;
}
//计算定位盘中心点位姿
SSX_platePoseInfo sx_getLocationPlatePose(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
int* errCode)
{
*errCode = 0;
SSX_platePoseInfo resultPose;
resultPose.center = { 0, 0, 0 };
resultPose.normalDir = { 0, 0, 0 };
resultPose.holeLT = { 0, 0, 0 };
resultPose.holeRB = { 0, 0, 0 };
resultPose.xDir = { 0, 0, 0 };
resultPose.yDir = { 0, 0, 0 };
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return resultPose;
}
int linePtNum = (int)scanLines[0].size();
//判断数据格式是否为grid。算法只能处理grid数据格式
bool isGridData = true;
for (int line = 0; line < lineNum; line++)
{
if (linePtNum != (int)scanLines[line].size())
{
isGridData = false;
break;
}
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return resultPose;
}
//产生水平扫描数据
std::vector< std::vector<SVzNL3DPosition>> scanLines_h;
scanLines_h.resize(linePtNum);
for (int i = 0; i < linePtNum; i++)
scanLines_h[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
scanLines_h[j][line] = scanLines[line][j];
scanLines_h[j][line].pt3D.x = scanLines[line][j].pt3D.y;
scanLines_h[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
for (int line = 0; line < linePtNum; line++)
{
for (int j = 0, j_max = (int)scanLines_h[line].size(); j < j_max; j++)
scanLines_h[line][j].nPointIdx = j;
}
//获取定位盘外表面端点
//垂直方向
std::vector<std::vector<int>> flags;
flags.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
flags[i].resize(linePtNum);
std::fill(flags[i].begin(), flags[i].end(), 0);
}
std::vector< SVzNL3DPosition> endingPts;
for (int line = 0; line < lineNum; line++)
{
std::vector< SVzNL3DPosition> vldPts;
std::vector<SSG_RUN_EX> segs;
std::vector<int> backIndexing;
backIndexing.resize(scanLines[line].size());
wd_lineDataSegment_zDist(
scanLines[line],
vldPts,
segs,
backIndexing,
cornerPara
);
for (int i = 0; i < (int)segs.size(); i++)
{
int idx_0 = segs[i].start;
int idx_1 = segs[i].start + segs[i].len - 1;
SVzNL3DPosition pt_0 = vldPts[idx_0];
SVzNL3DPosition pt_1 = vldPts[idx_1];
flags[line][pt_0.nPointIdx] = 1;
flags[line][pt_1.nPointIdx] = 1;
pt_0.nPointIdx = pt_0.nPointIdx & 0xffff | (line << 16);
pt_1.nPointIdx = pt_1.nPointIdx & 0xffff | (line << 16);
endingPts.push_back(pt_0);
endingPts.push_back(pt_1);
}
}
for (int line = 0; line < linePtNum; line++)
{
std::vector< SVzNL3DPosition> vldPts;
std::vector<SSG_RUN_EX> segs;
std::vector<int> backIndexing;
backIndexing.resize(scanLines_h[line].size());
wd_lineDataSegment_zDist(
scanLines_h[line],
vldPts,
segs,
backIndexing,
cornerPara
);
for (int i = 0; i < (int)segs.size(); i++)
{
int idx_0 = segs[i].start;
int idx_1 = segs[i].start + segs[i].len - 1;
SVzNL3DPosition pt_0 = vldPts[idx_0];
SVzNL3DPosition pt_1 = vldPts[idx_1];
if (0 == flags[pt_0.nPointIdx][line])//不和垂直特征提取的点重复
{
flags[pt_0.nPointIdx][line] = 1;
pt_0.pt3D = scanLines[pt_0.nPointIdx][line].pt3D; //取原始点
pt_0.nPointIdx = (pt_0.nPointIdx <<16) | (line & 0xffff);
endingPts.push_back(pt_0);
}
if (0 == flags[pt_1.nPointIdx][line]) //不和垂直特征提取的点重复
{
flags[pt_1.nPointIdx][line] = 1;
pt_1.pt3D = scanLines[pt_1.nPointIdx][line].pt3D; //取原始点
pt_1.nPointIdx = (pt_1.nPointIdx << 16) | (line & 0xffff);
endingPts.push_back(pt_1);
}
}
}
//标注
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
}
for (int i = 0; i < (int)endingPts.size(); i++)
{
int line = endingPts[i].nPointIdx >> 16;
int ptIdx = endingPts[i].nPointIdx & 0x0000FFFF;
scanLines[line][ptIdx].nPointIdx = 1;
}
//聚类
//内部参数
int clusterCheckWin = 5;
double clusterDist = 5.0;
double topLayerThickness = 10.0;
double centerZ_R = 20.0;
int distType = 1; //0 - 2d distance; 1- 3d distance
std::vector<std::vector< SVzNL3DPosition>> objClusters; //result
wd_pointClustering_speedUp(
endingPts,
lineNum, linePtNum, clusterCheckWin, //搜索窗口
clusterDist,
distType,
objClusters //result
);
//判断上表面外轮廓
std::vector<double> objMeanZ;
objMeanZ.resize(objClusters.size());
std::vector< SSG_ROIRectD> objROIs;
objROIs.resize(objClusters.size());
//过滤出最小范围的Z
double minMeanZ = -1;
for (int i = 0; i < (int)objClusters.size(); i++)
{
SSG_ROIRectD a_roi = _getListROI(objClusters[i]);
objROIs[i] = a_roi;
double w = a_roi.right - a_roi.left;
double h = a_roi.bottom - a_roi.top;
SVzNLRangeD zRange;
double meanZ = _getListMeanZ(objClusters[i], zRange);
objMeanZ[i] = meanZ;
if ( (meanZ > 1e-4) && (w > 150) && (h > 100))
{
if (minMeanZ < 0)
minMeanZ = meanZ;
else
minMeanZ = minMeanZ > meanZ ? meanZ : minMeanZ;
}
}
//选出ROI最大的目标作为顶部轮廓
int objIdx = -1;
double maxSize = 0;
for (int i = 0; i < (int)objClusters.size(); i++)
{
if (objMeanZ[i] > 1e-4)
{
double zDist = objMeanZ[i] - minMeanZ;
if (zDist < topLayerThickness) //同在顶层
{
double size = (objROIs[i].right - objROIs[i].left) * (objROIs[i].bottom - objROIs[i].top);
if (maxSize < -1e-4)
{
maxSize = size;
objIdx = i;
}
else
{
if (maxSize < size)
{
maxSize = size;
objIdx = i;
}
}
}
}
}
if (objIdx < 0)
{
*errCode = SG_ERR_ZERO_OBJECTS;
return resultPose;
}
//标注
for (int i = 0; i < (int)objClusters[objIdx].size(); i++)
{
int line = objClusters[objIdx][i].nPointIdx >> 16;
int ptIdx = objClusters[objIdx][i].nPointIdx & 0x0000FFFF;
scanLines[line][ptIdx].nPointIdx = 2;
}
//拟合平面
std::vector<cv::Point3d> Points3ds;
std::vector<SWD_polarPt> polarPoints;
for (int i = 0; i < (int)objClusters[objIdx].size(); i++)
{
cv::Point3d a_pt = cv::Point3d(objClusters[objIdx][i].pt3D.x, objClusters[objIdx][i].pt3D.y, objClusters[objIdx][i].pt3D.z);
Points3ds.push_back(a_pt);
SWD_polarPt a_polarPt;
a_polarPt.lineIdx = objClusters[objIdx][i].nPointIdx >> 16;
a_polarPt.ptIdx = objClusters[objIdx][i].nPointIdx & 0x0000FFFF;
a_polarPt.R = 0;
a_polarPt.angle = 0;
a_polarPt.x = objClusters[objIdx][i].pt3D.x;
a_polarPt.y = objClusters[objIdx][i].pt3D.y;
a_polarPt.z = objClusters[objIdx][i].pt3D.z;
polarPoints.push_back(a_polarPt);
}
//计算面参数: z = Ax + By + C
//res: [0]=A, [1]= B, [2]=-1.0, [3]=C,
#if 1
std::vector<cv::Point3d> out_inliers;
Plane res = ransacFitPlane( Points3ds, out_inliers );
if (res.C < 0)
{
res.A = -res.A;
res.B = -res.B;
res.C = -res.C;
res.D = -res.D;
}
#else
Plane res = robustFitPlane(Points3ds);
#endif
//std::vector<double> res;
//vzCaculateLaserPlane(Points3ds, res);
//计算投影向量
SVzNL3DPoint vec_1 = {res.A, res.B, res.C};
SVzNL3DPoint vec_2 = { 0, 0, 1.0 };
SSG_planeCalibPara poseR = wd_computeRTMatrix(vec_1, vec_2);
//投影
double normDataPlane = sqrt(res.A * res.A + res.B * res.B + res.C * res.C);
std::vector<cv::Point3f> projectPoints3ds;
projectPoints3ds.resize(Points3ds.size());
double sum_x = 0, sum_y = 0, sum_z = 0;
double sumZConter = 0;
for (int i = 0; i < (int)Points3ds.size(); i++)
{
double distToPlane = abs(res.A * Points3ds[i].x + res.B * Points3ds[i].y + res.C * Points3ds[i].z + res.D) / normDataPlane;
double x = Points3ds[i].x * poseR.planeCalib[0] + Points3ds[i].y * poseR.planeCalib[1] + Points3ds[i].z * poseR.planeCalib[2];
double y = Points3ds[i].x * poseR.planeCalib[3] + Points3ds[i].y * poseR.planeCalib[4] + Points3ds[i].z * poseR.planeCalib[5];
double z = Points3ds[i].x * poseR.planeCalib[6] + Points3ds[i].y * poseR.planeCalib[7] + Points3ds[i].z * poseR.planeCalib[8];
projectPoints3ds[i] = cv::Point3f(x, y, z);
polarPoints[i].x = x;
polarPoints[i].y = y;
polarPoints[i].z = z;
sum_x += x;
sum_y += y;
if (distToPlane < 2.0)
{
sum_z += z;
sumZConter++;
}
}
for (int i = 0; i < lineNum; i++)
{
if (i == 14)
int kkk = 1;
//行处理
//调平,去除地面
lineDataRT_vector(scanLines[i], poseR.planeCalib, -1);
}
//计算质心
double center_x = sum_x / (double)Points3ds.size();
double center_y = sum_y / (double)Points3ds.size();
double center_z = sum_z / (double)sumZConter;
//计算极坐标的R和Theta
for (int pi = 0; pi < (int)polarPoints.size(); pi++)
{
double angle = atan2(polarPoints[pi].y - center_y, polarPoints[pi].x - center_x);
angle = (angle / PI) * 180 + 180.0;
double R = sqrt(pow(polarPoints[pi].y - center_y, 2) + pow(polarPoints[pi].x - center_x, 2));
polarPoints[pi].R = R;
polarPoints[pi].angle = angle;
}
//按角度大小排序
std::sort(polarPoints.begin(), polarPoints.end(), _compareByAngle);
for (int pi = 0; pi < (int)polarPoints.size(); pi++)
polarPoints[pi].cptIndex = pi; // index
//计算有序边缘点的角度变化 2026.03.06
std::vector< SSG_dirCornerAngle> dirCornerAngles;
double dirAngleScale = 20.0;
_computeClosedPntListDirCorners(polarPoints, dirAngleScale, dirCornerAngles);
//提取方向角拐点(正)极值
std::vector< SSG_dirCornerAngle> cornerPlusPeaks;
double minCutAngleTh = 30;
_searchPlusCornerPeaks(dirCornerAngles, minCutAngleTh, cornerPlusPeaks);
#if 1
int cornerIdx_LT = -1;
int cornerIdx_RB = -1;
for (int i = 0; i < (int)cornerPlusPeaks.size(); i++)
{
if ((cornerPlusPeaks[i].point.angle > 0) && (cornerPlusPeaks[i].point.angle < 90))
cornerIdx_LT = i;
if ((cornerPlusPeaks[i].point.angle > 180) && (cornerPlusPeaks[i].point.angle < 270))
cornerIdx_RB = i;
}
if ((cornerIdx_LT < 0) || (cornerIdx_RB < 0))
{
*errCode = SX_ERR_UNKNOWN_PLATE_DIR;
return resultPose;
}
SSG_dirCornerAngle& corner_LT = cornerPlusPeaks[cornerIdx_LT];
SSG_dirCornerAngle& corner_RB = cornerPlusPeaks[cornerIdx_RB];
//Z切割
//内部参数
double zTh_min = center_z - 1.0;
double zTh_max = center_z + 5.0;
std::vector< std::vector<SVzNL3DPosition>> zCutLines;
zCutLines.resize(scanLines.size());
for (int line = 0; line < lineNum; line++)
{
//行处理
zCutLines[line].resize(linePtNum);
for (int j = 0; j < linePtNum; j++)
{
zCutLines[line][j] = scanLines[line][j];
if ((scanLines[line][j].pt3D.z < zTh_min) || (scanLines[line][j].pt3D.z > zTh_max))
zCutLines[line][j].pt3D = { 0.0, 0.0, 0.0 };
}
}
//孔处理
std::vector<SSG_intPair> validObjects;
std::vector<SWD_segFeatureTree> holeSegTrees_v;
std::vector<SWD_segFeatureTree> holeSegTrees_h;
SSG_lineSegParam hole_lineSegPara;
hole_lineSegPara.distScale = 3.0;
hole_lineSegPara.segGapTh_y = 15.0; //
hole_lineSegPara.segGapTh_z = 0.0; //z方向间隔大于10mm认为是分段
SSG_outlierFilterParam hole_filterParam;
hole_filterParam.continuityTh = 20.0; //噪声滤除。当相邻点的z跳变大于此门限时检查是否为噪声。若长度小于outlierLen 视为噪声
hole_filterParam.outlierTh = 5;
SSG_treeGrowParam hole_growParam;
hole_growParam.maxLineSkipNum = 2;
hole_growParam.yDeviation_max = 4.0;
hole_growParam.maxSkipDistance = 2.0;
hole_growParam.zDeviation_max = 10.0;//
hole_growParam.minLTypeTreeLen = 2.0; //mm
hole_growParam.minVTypeTreeLen = 2.0; //mm
double valieCommonNumRatio = 0.25;
WD_getHoleInfo(
zCutLines,
hole_lineSegPara,
hole_filterParam,
hole_growParam,
valieCommonNumRatio,
holeSegTrees_v,
holeSegTrees_h,
validObjects
);
//寻找左上孔和右下孔
for (int i = 0; i < lineNum; i++)
{
for (int j = 0; j < (int)scanLines[i].size(); j++)
zCutLines[i][j].nPointIdx = 0; //清零
}
//生成聚类信息,
std::vector<std::vector< SVzNL2DPoint>> clusters; //只记录位置
std::vector<SVzNL3DRangeD> clustersRoi3D;
for (int i = 0; i < (int)validObjects.size(); i++)
{
std::vector< SVzNL2DPoint> a_cluster;
SVzNL3DRangeD a_roi3D = { {-1, -1}, {-1, -1}, {-1, -1 } };
int vTreeIdx = validObjects[i].data_0;
int hTreeIdx = validObjects[i].data_1;
for (int m = 0; m < (int)holeSegTrees_v[vTreeIdx].treeNodes.size(); m++)
{
SWD_segFeature& a_seg = holeSegTrees_v[vTreeIdx].treeNodes[m];
if (zCutLines[a_seg.lineIdx][a_seg.endPtIdx].nPointIdx == 0)
{
zCutLines[a_seg.lineIdx][a_seg.endPtIdx].nPointIdx = vTreeIdx + 1; // 0x01;
scanLines[a_seg.lineIdx][a_seg.endPtIdx].nPointIdx = vTreeIdx + 3; // 0x01;
SVzNL2DPoint a_pos = { a_seg.lineIdx , a_seg.endPtIdx };
a_cluster.push_back(a_pos);
_updateRoi3D(a_roi3D, zCutLines[a_seg.lineIdx][a_seg.endPtIdx].pt3D);
}
if (zCutLines[a_seg.lineIdx][a_seg.startPtIdx].nPointIdx == 0)
{
zCutLines[a_seg.lineIdx][a_seg.startPtIdx].nPointIdx = vTreeIdx + 1; // 0x01;
scanLines[a_seg.lineIdx][a_seg.startPtIdx].nPointIdx = vTreeIdx + 3; // 0x01;
SVzNL2DPoint a_pos = { a_seg.lineIdx , a_seg.startPtIdx };
a_cluster.push_back(a_pos);
_updateRoi3D(a_roi3D, zCutLines[a_seg.lineIdx][a_seg.startPtIdx].pt3D);
}
}
for (int m = 0; m < (int)holeSegTrees_h[hTreeIdx].treeNodes.size(); m++)
{
SWD_segFeature& a_seg = holeSegTrees_h[hTreeIdx].treeNodes[m];
if (zCutLines[a_seg.startPtIdx][a_seg.lineIdx].nPointIdx == 0)
{
zCutLines[a_seg.startPtIdx][a_seg.lineIdx].nPointIdx = hTreeIdx + 1; // 0x02;
scanLines[a_seg.startPtIdx][a_seg.lineIdx].nPointIdx = hTreeIdx + 4; // 0x02;
SVzNL2DPoint a_pos = { a_seg.startPtIdx , a_seg.lineIdx };
a_cluster.push_back(a_pos);
_updateRoi3D(a_roi3D, zCutLines[a_seg.startPtIdx][a_seg.lineIdx].pt3D);
}
if (zCutLines[a_seg.endPtIdx][a_seg.lineIdx].nPointIdx == 0)
{
zCutLines[a_seg.endPtIdx][a_seg.lineIdx].nPointIdx = hTreeIdx + 1; // 0x02;
scanLines[a_seg.endPtIdx][a_seg.lineIdx].nPointIdx = hTreeIdx + 4; // 0x02;
SVzNL2DPoint a_pos = { a_seg.endPtIdx , a_seg.lineIdx };
a_cluster.push_back(a_pos);
_updateRoi3D(a_roi3D, zCutLines[a_seg.endPtIdx][a_seg.lineIdx].pt3D);
}
}
clusters.push_back(a_cluster);
clustersRoi3D.push_back(a_roi3D);
}
//聚类结果分析:搜索距左上和右下点最后的目标
//内部参数
double minHoleSize = 10.0;
double maxHoleSize = 24.0;
double maxCornerHoleDistance = 40; //角点到孔的最大距离
int clusterSize = (int)clusters.size();
int clusterIdx_LT = -1;
double minDistLT = -1;
int clusterIdx_RB = -1;
double minDistRB = -1;
for (int i = 0; i < clusterSize; i++)
{
SVzNL3DRangeD& a_roi = clustersRoi3D[i];
double L = a_roi.xRange.max - a_roi.xRange.min;
double W = a_roi.yRange.max - a_roi.yRange.min;
double cx = (a_roi.xRange.max + a_roi.xRange.min) / 2;
double cy = (a_roi.yRange.max + a_roi.yRange.min) / 2;
if ((L > minHoleSize) && (L < maxHoleSize) && (W > minHoleSize) && (W < maxHoleSize))
{
double dist_1 = sqrt(pow(corner_LT.point.x - cx, 2) + pow(corner_LT.point.y - cy, 2));
if ((minDistLT < 0) || (minDistLT > dist_1))
{
minDistLT = dist_1;
clusterIdx_LT = i;
}
double dist_2 = sqrt(pow(corner_RB.point.x - cx, 2) + pow(corner_RB.point.y - cy, 2));
if ((minDistRB < 0) || (minDistRB > dist_2))
{
minDistRB = dist_2;
clusterIdx_RB = i;
}
}
}
if( (clusterIdx_LT < 0) || (clusterIdx_RB < 0) || (minDistLT > maxCornerHoleDistance) || (minDistRB > maxCornerHoleDistance))
{
*errCode = SX_ERR_UNKNOWN_PLATE_DIR;
return resultPose;
}
//目标圆拟合
std::vector<SVzNL3DPoint> pointArrayLT;
int clusterPtSizeLT = (int)clusters[clusterIdx_LT].size();
for (int i = 0; i < clusterPtSizeLT; i++)
{
SVzNL2DPoint a_pos = clusters[clusterIdx_LT][i];
int lineIdx = a_pos.x;
int ptIdx = a_pos.y;
SVzNL3DPoint a_pt3d = scanLines[lineIdx][ptIdx].pt3D;
pointArrayLT.push_back(a_pt3d);
}
//圆拟合
SVzNL3DPoint centerLT;
double radiusLT;
double err = fitCircleByLeastSquare(pointArrayLT, centerLT, radiusLT);
centerLT.z = center_z;
std::vector<SVzNL3DPoint> pointArrayRB;
int clusterPtSizeRB = (int)clusters[clusterIdx_RB].size();
for (int i = 0; i < clusterPtSizeRB; i++)
{
SVzNL2DPoint a_pos = clusters[clusterIdx_RB][i];
int lineIdx = a_pos.x;
int ptIdx = a_pos.y;
SVzNL3DPoint a_pt3d = scanLines[lineIdx][ptIdx].pt3D;
pointArrayRB.push_back(a_pt3d);
}
//圆拟合
SVzNL3DPoint centerRB;
double radiusRB;
err = fitCircleByLeastSquare(pointArrayRB, centerRB, radiusRB);
centerRB.z = center_z;
SVzNL3DPoint refVec = { (centerRB.x-centerLT.x)/2, (centerRB.y - centerLT.y)/2, center_z};
double angleToHorizon = -46; //两孔连线与水平线夹角46度
double angleToCenter = 3; //两孔连线中点与定位盘中心夹角3度
//逆时针旋转时 θ > 0 ;顺时针旋转时 θ < 0
SVzNL3DPoint rVec_1 = wd_rotate2D(refVec, angleToCenter);
center_x = rVec_1.x + centerLT.x;
center_y = rVec_1.y + centerLT.y;
//center_z = center_z + 34.0;
SVzNL3DPoint xDir = wd_rotate2D(refVec, angleToHorizon);
double normData = sqrt(pow(xDir.x, 2) + pow(xDir.y, 2));
xDir.x = xDir.x / normData;
xDir.y = xDir.y / normData;
xDir.z = 0;
resultPose.holeLT = centerLT;
resultPose.holeRB = centerRB;
center_z += 33.8; //定位盘中心点深33.8
#else
//生成直线段数据
int cornerIdx_0 = -1;
int cornerIdx_1 = -1;
for (int i = 0; i < (int)cornerPlusPeaks.size(); i++)
{
if ((cornerPlusPeaks[i].point.angle > 0) && (cornerPlusPeaks[i].point.angle < 90))
cornerIdx_0 = i;
if ((cornerPlusPeaks[i].point.angle > 90) && (cornerPlusPeaks[i].point.angle < 180))
cornerIdx_1 = i;
}
if ((cornerIdx_0 < 0) && (cornerIdx_1 < 0))
{
if (objIdx < 0)
{
*errCode = SX_ERR_UNKNOWN_PLATE_DIR;
return resultPose;
}
}
std::vector<SVzNL3DPoint> lineFittingPts;
double endingRemoveDist = 10.0;
double validFittingLen = 100;
if (cornerIdx_0 >= 0)
{
int startIdx = cornerPlusPeaks[cornerIdx_0].pntIdx;
double sx = polarPoints[startIdx].x;
double sy = polarPoints[startIdx].y;
for (int m = startIdx; m < (int)polarPoints.size(); m++)
{
double dist = sqrt(pow(polarPoints[m].x - sx, 2) + pow(polarPoints[m].y - sy, 2));
if ((dist >= endingRemoveDist) && (dist <= (validFittingLen + endingRemoveDist)))
{
SVzNL3DPoint a_fitPt = { polarPoints[m].x, polarPoints[m].y, polarPoints[m].z };
lineFittingPts.push_back(a_fitPt);
}
if (dist > (validFittingLen + endingRemoveDist))
break;
}
}
else
{
int startIdx = cornerPlusPeaks[cornerIdx_1].pntIdx;
double sx = polarPoints[startIdx].x;
double sy = polarPoints[startIdx].y;
for (int m = startIdx; m >= 0; m--)
{
double dist = sqrt(pow(polarPoints[m].x - sx, 2) + pow(polarPoints[m].y - sy, 2));
if ((dist >= endingRemoveDist) && (dist <= (validFittingLen + endingRemoveDist)))
{
SVzNL3DPoint a_fitPt = { polarPoints[m].x, polarPoints[m].y, polarPoints[m].z };
lineFittingPts.push_back(a_fitPt);
}
if (dist > (validFittingLen + endingRemoveDist))
break;
}
}
//直线拟合
double _k, _b;
lineFitting(lineFittingPts, &_k, &_b);
double normData = sqrt(pow(_k, 2) + 1);
SVzNL3DPoint xDir = { 1.0/normData, _k/normData, 0.0 };
//计算4个点
SVzNL2DPointD top_pt = { center_x, center_y - centerZ_R };
SVzNL2DPointD bottom_pt = { center_x, center_y + centerZ_R };
SVzNL2DPointD left_pt = { center_x - centerZ_R, center_y};
SVzNL2DPointD roght_pt = { center_x + centerZ_R, center_y};
//计算Z
SVzNL3DPoint ptTop = { 0, 0, 0 };
SVzNL3DPoint ptBtm = { 0, 0, 0 };
SVzNL3DPoint ptLeft = { 0, 0, 0 };
SVzNL3DPoint ptRight = { 0, 0, 0 };
double minDistTop = -1, minDistBtm = -1, minDistLeft = -1, minDistRight = -1;
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
if (scanLines[line][j].pt3D.z > 1e-4)
{
//top
if (minDistTop < 1e-4)
{
ptTop = scanLines[line][j].pt3D;
minDistTop = sqrt(pow(top_pt.x - scanLines[line][j].pt3D.x, 2) + pow(top_pt.y - scanLines[line][j].pt3D.y, 2));
}
else
{
double dist = sqrt(pow(top_pt.x - scanLines[line][j].pt3D.x, 2) + pow(top_pt.y - scanLines[line][j].pt3D.y, 2));
if (minDistTop > dist)
{
ptTop = scanLines[line][j].pt3D;
minDistTop = dist;
}
}
//bottom
if (minDistBtm < 1e-4)
{
ptBtm = scanLines[line][j].pt3D;
minDistBtm = sqrt(pow(bottom_pt.x - scanLines[line][j].pt3D.x, 2) + pow(bottom_pt.y - scanLines[line][j].pt3D.y, 2));
}
else
{
double dist = sqrt(pow(bottom_pt.x - scanLines[line][j].pt3D.x, 2) + pow(bottom_pt.y - scanLines[line][j].pt3D.y, 2));
if (minDistBtm > dist)
{
ptBtm = scanLines[line][j].pt3D;
minDistBtm = dist;
}
}
//left
if (minDistLeft < 1e-4)
{
ptLeft = scanLines[line][j].pt3D;
minDistLeft = sqrt(pow(left_pt.x - scanLines[line][j].pt3D.x, 2) + pow(left_pt.y - scanLines[line][j].pt3D.y, 2));
}
else
{
double dist = sqrt(pow(left_pt.x - scanLines[line][j].pt3D.x, 2) + pow(left_pt.y - scanLines[line][j].pt3D.y, 2));
if (minDistLeft > dist)
{
ptLeft = scanLines[line][j].pt3D;
minDistLeft = dist;
}
}
//right
if (minDistRight < 1e-4)
{
ptRight = scanLines[line][j].pt3D;
minDistRight = sqrt(pow(roght_pt.x - scanLines[line][j].pt3D.x, 2) + pow(roght_pt.y - scanLines[line][j].pt3D.y, 2));
}
else
{
double dist = sqrt(pow(roght_pt.x - scanLines[line][j].pt3D.x, 2) + pow(roght_pt.y - scanLines[line][j].pt3D.y, 2));
if (minDistRight > dist)
{
ptRight = scanLines[line][j].pt3D;
minDistRight = dist;
}
}
}
}
}
double center_z = (ptTop.z + ptBtm.z + ptLeft.z + ptRight.z) / 4;
#endif
resultPose.center = { center_x, center_y, center_z };
resultPose.xDir = { 0.0, 0.0, -1.0 }; // { 1.0, 0.0, 0 };
//resultPose.yDir = { 0.0, 1.0, 0 };
resultPose.normalDir = xDir; // { 1.0, 0.0, 0 };// { 0.0, 0.0, 1.0 };
//叉乘出y;
//向量叉乘
resultPose.yDir = vec3_cross(resultPose.normalDir, resultPose.xDir);
//旋转回去
for (int i = 0; i < lineNum; i++)
{
//行处理
//调平,去除地面
lineDataRT_vector(scanLines[i], poseR.invRMatrix, -1);
}
double x = resultPose.center.x * poseR.invRMatrix[0] + resultPose.center.y * poseR.invRMatrix[1] + resultPose.center.z * poseR.invRMatrix[2];
double y = resultPose.center.x * poseR.invRMatrix[3] + resultPose.center.y * poseR.invRMatrix[4] + resultPose.center.z * poseR.invRMatrix[5];
double z = resultPose.center.x * poseR.invRMatrix[6] + resultPose.center.y * poseR.invRMatrix[7] + resultPose.center.z * poseR.invRMatrix[8];
resultPose.center = { x, y, z };
x = resultPose.normalDir.x * poseR.invRMatrix[0] + resultPose.normalDir.y * poseR.invRMatrix[1] + resultPose.normalDir.z * poseR.invRMatrix[2];
y = resultPose.normalDir.x * poseR.invRMatrix[3] + resultPose.normalDir.y * poseR.invRMatrix[4] + resultPose.normalDir.z * poseR.invRMatrix[5];
z = resultPose.normalDir.x * poseR.invRMatrix[6] + resultPose.normalDir.y * poseR.invRMatrix[7] + resultPose.normalDir.z * poseR.invRMatrix[8];
resultPose.normalDir = { x, y, z };
x = resultPose.xDir.x * poseR.invRMatrix[0] + resultPose.xDir.y * poseR.invRMatrix[1] + resultPose.xDir.z * poseR.invRMatrix[2];
y = resultPose.xDir.x * poseR.invRMatrix[3] + resultPose.xDir.y * poseR.invRMatrix[4] + resultPose.xDir.z * poseR.invRMatrix[5];
z = resultPose.xDir.x * poseR.invRMatrix[6] + resultPose.xDir.y * poseR.invRMatrix[7] + resultPose.xDir.z * poseR.invRMatrix[8];
resultPose.xDir = { x, y, z };
x = resultPose.yDir.x * poseR.invRMatrix[0] + resultPose.yDir.y * poseR.invRMatrix[1] + resultPose.yDir.z * poseR.invRMatrix[2];
y = resultPose.yDir.x * poseR.invRMatrix[3] + resultPose.yDir.y * poseR.invRMatrix[4] + resultPose.yDir.z * poseR.invRMatrix[5];
z = resultPose.yDir.x * poseR.invRMatrix[6] + resultPose.yDir.y * poseR.invRMatrix[7] + resultPose.yDir.z * poseR.invRMatrix[8];
resultPose.yDir = { x, y, z };
x = resultPose.holeLT.x * poseR.invRMatrix[0] + resultPose.holeLT.y * poseR.invRMatrix[1] + resultPose.holeLT.z * poseR.invRMatrix[2];
y = resultPose.holeLT.x * poseR.invRMatrix[3] + resultPose.holeLT.y * poseR.invRMatrix[4] + resultPose.holeLT.z * poseR.invRMatrix[5];
z = resultPose.holeLT.x * poseR.invRMatrix[6] + resultPose.holeLT.y * poseR.invRMatrix[7] + resultPose.holeLT.z * poseR.invRMatrix[8];
resultPose.holeLT = { x, y, z };
x = resultPose.holeRB.x * poseR.invRMatrix[0] + resultPose.holeRB.y * poseR.invRMatrix[1] + resultPose.holeRB.z * poseR.invRMatrix[2];
y = resultPose.holeRB.x * poseR.invRMatrix[3] + resultPose.holeRB.y * poseR.invRMatrix[4] + resultPose.holeRB.z * poseR.invRMatrix[5];
z = resultPose.holeRB.x * poseR.invRMatrix[6] + resultPose.holeRB.y * poseR.invRMatrix[7] + resultPose.holeRB.z * poseR.invRMatrix[8];
resultPose.holeRB = { x, y, z };
return resultPose;
}
//新的计算定位盘中心点位姿(去除了定位盘,只有定位板)
SSX_platePoseInfo sx_getLocationPlatePose_new(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
int* errCode)
{
*errCode = 0;
SSX_platePoseInfo resultPose;
resultPose.center = { 0, 0, 0 };
resultPose.normalDir = { 0, 0, 0 };
resultPose.holeLT = { 0, 0, 0 };
resultPose.holeRB = { 0, 0, 0 };
resultPose.xDir = { 0, 0, 0 };
resultPose.yDir = { 0, 0, 0 };
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return resultPose;
}
int linePtNum = (int)scanLines[0].size();
//判断数据格式是否为grid。算法只能处理grid数据格式
bool isGridData = true;
for (int line = 0; line < lineNum; line++)
{
if (linePtNum != (int)scanLines[line].size())
{
isGridData = false;
break;
}
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return resultPose;
}
//产生水平扫描数据
std::vector< std::vector<SVzNL3DPosition>> scanLines_h;
scanLines_h.resize(linePtNum);
for (int i = 0; i < linePtNum; i++)
scanLines_h[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
scanLines_h[j][line] = scanLines[line][j];
scanLines_h[j][line].pt3D.x = scanLines[line][j].pt3D.y;
scanLines_h[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
for (int line = 0; line < linePtNum; line++)
{
for (int j = 0, j_max = (int)scanLines_h[line].size(); j < j_max; j++)
scanLines_h[line][j].nPointIdx = j;
}
//算法流程:
//1、检查垂直方向数据并去除
//2、聚类
//3、保留最前面目标
//4、提取孔
//5、拟合
//6、计算中间坐标
//内部参数
SSG_cornerParam removeVertialPara = cornerPara;
removeVertialPara.scale = 3.0;
removeVertialPara.cornerTh = 60;
std::vector<std::vector<int>> flags;
flags.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
flags[i].resize(linePtNum);
std::fill(flags[i].begin(), flags[i].end(), 0);
}
std::vector<std::vector<int>> zVertivalFlags;
for (int line = 0; line < lineNum; line++)
{
if (line == 700)
int kkk = 1;
std::vector<int> line_verticalFlags;
wd_getXYVertialFeature_dirAngleMethod(
scanLines[line],
line,
removeVertialPara,
line_verticalFlags
);
zVertivalFlags.push_back(line_verticalFlags);
for (int i = 0; i < (int)line_verticalFlags.size(); i++)
{
if(line_verticalFlags[i] > 0)
flags[line][i] = 1;
}
}
std::vector<std::vector<int>> zVertivalFlags_h;
for (int line = 0; line < linePtNum; line++)
{
if (line == 1177)
int kkk = 1;
std::vector<int> line_verticalFlags;
wd_getXYVertialFeature_dirAngleMethod(
scanLines_h[line],
line,
removeVertialPara,
line_verticalFlags
);
zVertivalFlags_h.push_back(line_verticalFlags);
for (int i = 0; i < (int)line_verticalFlags.size(); i++)
{
if(line_verticalFlags[i] > 0)
flags[i][line] = 1;
}
}
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
if (flags[line][j] > 0)
{
scanLines[line][j].pt3D.z = 0;
scanLines_h[j][line].pt3D.z = 0;
}
}
}
//迭代一次
SSG_lineSegParam lineSegPara;
lineSegPara.distScale = 5.0;
lineSegPara.segGapTh_y = 5.0;
lineSegPara.segGapTh_z = 5.0;
const int minSegLen = 5;
for (int line = 0; line < lineNum; line++)
{
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(
scanLines[line],
lineSegPara,
segs);
for (int i = 0; i < (int)segs.size(); i++)
{
if (segs[i].len <= minSegLen)
{
int idx0 = segs[i].start;
for (int j = 0; j < segs[i].len; j++)
flags[line][idx0 + j] = 1;
}
}
}
for (int line = 0; line < linePtNum; line++)
{
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(
scanLines_h[line],
lineSegPara,
segs);
for (int i = 0; i < (int)segs.size(); i++)
{
if (segs[i].len <= minSegLen)
{
int idx0 = segs[i].start;
for (int j = 0; j < segs[i].len; j++)
flags[idx0 + j][line] = 1;
}
}
}
//标注
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
}
//将垂直线段去除
std::vector< SVzNL3DPosition> validPoints;
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
if (flags[line][j] > 0)
scanLines[line][j].pt3D.z = 0;
if (scanLines[line][j].pt3D.z > 1e-4)
{
SVzNL3DPosition a_vldPt;
a_vldPt.pt3D = scanLines[line][j].pt3D;
a_vldPt.nPointIdx = (line << 16) | (j & 0xffff);
validPoints.push_back(a_vldPt);
}
}
}
//聚类
//内部参数
double minObjSize_w = 150;
double minObjSize_h = 150;
int clusterCheckWin = 5;
double clusterDist = 1.5;
int distType = 1; //0 - 2d distance; 1- 3d distance
std::vector<std::vector< SVzNL3DPosition>> objClusters; //result
wd_pointClustering_speedUp(
validPoints,
lineNum, linePtNum, clusterCheckWin, //搜索窗口
clusterDist,
distType,
objClusters //result
);
//保留最前面的目标
const double topPlateMinW = 150.0;
const double topPlateMinH = 150.0;
const double topPlateMaxZRange = 100.0;
std::vector<double> objMeanZ;
std::vector<SVzNLRangeD> objZRange;
objMeanZ.resize(objClusters.size());
objZRange.resize(objClusters.size());
for (int i = 0; i < (int)objClusters.size(); i++)
{
SSG_ROIRectD a_roi = _getListROI(objClusters[i]);
double w = a_roi.right - a_roi.left;
double h = a_roi.bottom - a_roi.top;
if ((w > topPlateMinW) && (h > topPlateMinH))
{
SVzNLRangeD zRange;
double meanZ = _getListMeanZ(objClusters[i], zRange);
objMeanZ[i] = meanZ;
objZRange[i]= zRange;
}
else
{
objMeanZ[i] = 0;
objZRange[i].max = -1.0;
objZRange[i].min = 0.0;
}
}
//选出z最小的目标作为顶部轮廓
int objIdx = -1;
double minMeanZ = DBL_MAX;
for (int i = 0; i < (int)objClusters.size(); i++)
{
if ( (objMeanZ[i] > 1e-4) && (objZRange[i].max > 0))
{
double range = objZRange[i].max - objZRange[i].min;
if (range < topPlateMaxZRange)
{
if (minMeanZ > objMeanZ[i])
{
minMeanZ = objMeanZ[i];
objIdx = i;
}
}
}
}
if (objIdx < 0)
{
*errCode = SG_ERR_ZERO_OBJECTS;
return resultPose;
}
std::vector< SVzNL3DPosition>& topCluster = objClusters[objIdx];
//标注
//重新将flags设置为目标的mask
for (int i = 0; i < lineNum; i++)
std::fill(flags[i].begin(), flags[i].end(), -1);
for (int i = 0; i < (int)topCluster.size(); i++)
{
int line = topCluster[i].nPointIdx >> 16;
int ptIdx = topCluster[i].nPointIdx & 0x0000FFFF;
scanLines[line][ptIdx].nPointIdx = 2;
flags[line][ptIdx] = i; //indexing
}
//拟合平面
std::vector<cv::Point3d> Points3ds;
for (int i = 0; i < (int)topCluster.size(); i++)
{
cv::Point3d a_pt = cv::Point3d(topCluster[i].pt3D.x, topCluster[i].pt3D.y, topCluster[i].pt3D.z);
Points3ds.push_back(a_pt);
}
//计算面参数: z = Ax + By + C
//res: [0]=A, [1]= B, [2]=-1.0, [3]=C,
#if 1
std::vector<cv::Point3d> out_inliers;
Plane res = ransacFitPlane(Points3ds, out_inliers);
if (res.C < 0)
{
res.A = -res.A;
res.B = -res.B;
res.C = -res.C;
res.D = -res.D;
}
#else
Plane res = robustFitPlane(Points3ds);
#endif
//计算投影向量
SVzNL3DPoint vec_1 = { res.A, res.B, res.C };
SVzNL3DPoint vec_2 = { 0, 0, 1.0 };
SSG_planeCalibPara poseR = wd_computeRTMatrix(vec_1, vec_2);
//投影
double normDataPlane = sqrt(res.A * res.A + res.B * res.B + res.C * res.C);
std::vector<SVzNL3DPosition> projectPoints3ds;
projectPoints3ds.resize(topCluster.size());
double sum_x = 0, sum_y = 0, sum_z = 0;
int sumZConter = 0;
for (int i = 0; i < (int)topCluster.size(); i++)
{
double distToPlane = abs(res.A * topCluster[i].pt3D.x + res.B * topCluster[i].pt3D.y + res.C * topCluster[i].pt3D.z + res.D) / normDataPlane;
double x = topCluster[i].pt3D.x * poseR.planeCalib[0] + topCluster[i].pt3D.y * poseR.planeCalib[1] + topCluster[i].pt3D.z * poseR.planeCalib[2];
double y = topCluster[i].pt3D.x * poseR.planeCalib[3] + topCluster[i].pt3D.y * poseR.planeCalib[4] + topCluster[i].pt3D.z * poseR.planeCalib[5];
double z = topCluster[i].pt3D.x * poseR.planeCalib[6] + topCluster[i].pt3D.y * poseR.planeCalib[7] + topCluster[i].pt3D.z * poseR.planeCalib[8];
projectPoints3ds[i].nPointIdx = topCluster[i].nPointIdx;
projectPoints3ds[i].pt3D = { x, y, z };
sum_x += x;
sum_y += y;
if (distToPlane < 2.0)
{
sum_z += z;
sumZConter++;
}
}
if(sumZConter == 0)
{
*errCode = SG_ERR_ZERO_OBJECTS;
return resultPose;
}
double meanZ = sum_z / sumZConter;
//生成孔的边界点
std::vector<std::vector<int>> endingMask;
endingMask.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
endingMask[i].resize(linePtNum);
std::fill(endingMask[i].begin(), endingMask[i].end(), 0);
}
std::vector< SVzNL3DPosition> endingPts;
//垂直
double topZTh = meanZ + 10.0;
for (int line = 0; line < lineNum; line++)
{
int preValid = -1;
for (int j = 0; j < linePtNum; j++)
{
if (flags[line][j] >= 0)
{
if (preValid == 0)
{
if (endingMask[line][j] == 0)
{
int idx = flags[line][j];
if (projectPoints3ds[idx].pt3D.z < topZTh)
{
endingPts.push_back(projectPoints3ds[idx]);
endingMask[line][j] = 1;
}
}
}
preValid = 1;
}
else
{
if (preValid == 1)
{
if (endingMask[line][j - 1] == 0)
{
int idx = flags[line][j - 1];
if (projectPoints3ds[idx].pt3D.z < topZTh)
{
endingPts.push_back(projectPoints3ds[idx]);
endingMask[line][j - 1] = 1;
}
}
}
preValid = 0;
}
}
}
//水平
for (int j = 0; j < linePtNum; j++)
{
int preValid = -1;
for (int line = 0; line < lineNum; line++)
{
if (flags[line][j] >= 0)
{
if (preValid == 0)
{
if (endingMask[line][j] == 0)
{
int idx = flags[line][j];
if (projectPoints3ds[idx].pt3D.z < topZTh)
{
endingPts.push_back(projectPoints3ds[idx]);
endingMask[line][j] = 1;
}
}
}
preValid = 1;
}
else
{
if (preValid == 1)
{
if (endingMask[line-1][j] == 0)
{
int idx = flags[line-1][j];
if (projectPoints3ds[idx].pt3D.z < topZTh)
{
endingPts.push_back(projectPoints3ds[idx]);
endingMask[line - 1][j] = 1;
}
}
}
preValid = 0;
}
}
}
//标注
for (int i = 0; i < (int)endingPts.size(); i++)
{
int line = endingPts[i].nPointIdx >> 16;
int ptIdx = endingPts[i].nPointIdx & 0x0000FFFF;
scanLines[line][ptIdx].nPointIdx = 3;
}
//孔聚类
clusterCheckWin = 10;
clusterDist = 5.0;
distType = 0; //0 - 2d distance; 1- 3d distance
std::vector<std::vector< SVzNL3DPosition>> holeClusters; //result
wd_pointClustering_speedUp(
endingPts,
lineNum, linePtNum, clusterCheckWin, //搜索窗口
clusterDist,
distType,
holeClusters //result
);
//拟合
std::vector< SVzNL3DPoint> holesCenter;
holesCenter.resize(holeClusters.size());
std::vector<double> holesR;
holesR.resize(holeClusters.size());
//目标圆拟合
//圆拟合
for (int i = 0; i < (int)holeClusters.size(); i++)
{
SVzNL3DPoint a_center;
double a_radius;
double err = fitCircleByLeastSquare_2(holeClusters[i], a_center, a_radius);
holesCenter[i] = a_center;
holesR[i] = a_radius;
}
//计算中心孔的姿态
//目标过滤
//首先挑出最大的孔
const double bigHoleD = 40.0;//大孔直径
const double smallHoleD = 5.0;//大孔直径
std::vector<int> bigHoleIdx;
std::vector<int> smallHoleIdx;
for (int i = 0; i < (int)holeClusters.size(); i++)
{
if ( (holesR[i] > bigHoleD/2) && (holesR[i] < bigHoleD * 1.5))
bigHoleIdx.push_back(i);
else if ( (holesR[i] > smallHoleD/2) && (holesR[i] < smallHoleD * 1.5))
smallHoleIdx.push_back(i);
}
if (bigHoleIdx.size() != 4)
{
*errCode = SG_ERR_ZERO_OBJECTS;
return resultPose;
}
int holeL_idx = -1, holeR_idx = -1, holeT_idx = -1, holeB_idx = -1;
for (int i = 0; i < (int)bigHoleIdx.size(); i++)
{
int idx = bigHoleIdx[i];
if (holeL_idx < 0)
holeL_idx = idx;
else if (holesCenter[holeL_idx].x > holesCenter[idx].x)
holeL_idx = idx;
if (holeR_idx < 0)
holeR_idx = idx;
else if (holesCenter[holeR_idx].x < holesCenter[idx].x)
holeR_idx = idx;
if (holeT_idx < 0)
holeT_idx = idx;
else if (holesCenter[holeT_idx].y > holesCenter[idx].y)
holeT_idx = idx;
if (holeB_idx < 0)
holeB_idx = idx;
else if (holesCenter[holeB_idx].y < holesCenter[idx].y)
holeB_idx = idx;
}
double ref_x = (holesCenter[holeL_idx].x + holesCenter[holeR_idx].x + holesCenter[holeT_idx].x + holesCenter[holeB_idx].x) / 4.0;
double ref_y = (holesCenter[holeL_idx].y + holesCenter[holeR_idx].y + holesCenter[holeT_idx].y + holesCenter[holeB_idx].y) / 4.0;
//寻找中间孔
int centerHoleIdx = -1;
double minDist = -1;
for (int i = 0; i < (int)smallHoleIdx.size(); i++)
{
int idx = smallHoleIdx[i];
double dist = sqrt(pow(holesCenter[idx].x - ref_x, 2) + pow(holesCenter[idx].y - ref_y, 2));
if (centerHoleIdx < 0)
{
centerHoleIdx = idx;
minDist = dist;
}
else if (minDist > dist)
{
centerHoleIdx = idx;
minDist = dist;
}
}
resultPose.center = { holesCenter[centerHoleIdx].x, holesCenter[centerHoleIdx].y, meanZ};
resultPose.xDir = {0.0, 0.0, -1.0}; // { 1.0, 0.0, 0 };// { 0.0, 0.0, 1.0 };
resultPose.normalDir = { holesCenter[holeR_idx].x - holesCenter[centerHoleIdx].x, holesCenter[holeR_idx].y - holesCenter[centerHoleIdx].y, 0.0 }; // { 1.0, 0.0, 0 };
resultPose.normalDir = vec3_normalize(resultPose.normalDir);
//resultPose.yDir = { 0.0, 1.0, 0 };
//叉乘出y;
//向量叉乘
resultPose.yDir = vec3_cross(resultPose.normalDir, resultPose.xDir);
resultPose.holeLT = resultPose.center;
resultPose.holeRB = resultPose.center;
#if 0
//旋转回去
for (int i = 0; i < lineNum; i++)
{
//行处理
//调平,去除地面
lineDataRT_vector(scanLines[i], poseR.invRMatrix, -1);
}
#endif
double x = resultPose.center.x * poseR.invRMatrix[0] + resultPose.center.y * poseR.invRMatrix[1] + resultPose.center.z * poseR.invRMatrix[2];
double y = resultPose.center.x * poseR.invRMatrix[3] + resultPose.center.y * poseR.invRMatrix[4] + resultPose.center.z * poseR.invRMatrix[5];
double z = resultPose.center.x * poseR.invRMatrix[6] + resultPose.center.y * poseR.invRMatrix[7] + resultPose.center.z * poseR.invRMatrix[8];
resultPose.center = { x, y, z };
x = resultPose.normalDir.x * poseR.invRMatrix[0] + resultPose.normalDir.y * poseR.invRMatrix[1] + resultPose.normalDir.z * poseR.invRMatrix[2];
y = resultPose.normalDir.x * poseR.invRMatrix[3] + resultPose.normalDir.y * poseR.invRMatrix[4] + resultPose.normalDir.z * poseR.invRMatrix[5];
z = resultPose.normalDir.x * poseR.invRMatrix[6] + resultPose.normalDir.y * poseR.invRMatrix[7] + resultPose.normalDir.z * poseR.invRMatrix[8];
resultPose.normalDir = { x, y, z };
x = resultPose.xDir.x * poseR.invRMatrix[0] + resultPose.xDir.y * poseR.invRMatrix[1] + resultPose.xDir.z * poseR.invRMatrix[2];
y = resultPose.xDir.x * poseR.invRMatrix[3] + resultPose.xDir.y * poseR.invRMatrix[4] + resultPose.xDir.z * poseR.invRMatrix[5];
z = resultPose.xDir.x * poseR.invRMatrix[6] + resultPose.xDir.y * poseR.invRMatrix[7] + resultPose.xDir.z * poseR.invRMatrix[8];
resultPose.xDir = { x, y, z };
x = resultPose.yDir.x * poseR.invRMatrix[0] + resultPose.yDir.y * poseR.invRMatrix[1] + resultPose.yDir.z * poseR.invRMatrix[2];
y = resultPose.yDir.x * poseR.invRMatrix[3] + resultPose.yDir.y * poseR.invRMatrix[4] + resultPose.yDir.z * poseR.invRMatrix[5];
z = resultPose.yDir.x * poseR.invRMatrix[6] + resultPose.yDir.y * poseR.invRMatrix[7] + resultPose.yDir.z * poseR.invRMatrix[8];
resultPose.yDir = { x, y, z };
x = resultPose.holeLT.x * poseR.invRMatrix[0] + resultPose.holeLT.y * poseR.invRMatrix[1] + resultPose.holeLT.z * poseR.invRMatrix[2];
y = resultPose.holeLT.x * poseR.invRMatrix[3] + resultPose.holeLT.y * poseR.invRMatrix[4] + resultPose.holeLT.z * poseR.invRMatrix[5];
z = resultPose.holeLT.x * poseR.invRMatrix[6] + resultPose.holeLT.y * poseR.invRMatrix[7] + resultPose.holeLT.z * poseR.invRMatrix[8];
resultPose.holeLT = { x, y, z };
x = resultPose.holeRB.x * poseR.invRMatrix[0] + resultPose.holeRB.y * poseR.invRMatrix[1] + resultPose.holeRB.z * poseR.invRMatrix[2];
y = resultPose.holeRB.x * poseR.invRMatrix[3] + resultPose.holeRB.y * poseR.invRMatrix[4] + resultPose.holeRB.z * poseR.invRMatrix[5];
z = resultPose.holeRB.x * poseR.invRMatrix[6] + resultPose.holeRB.y * poseR.invRMatrix[7] + resultPose.holeRB.z * poseR.invRMatrix[8];
resultPose.holeRB = { x, y, z };
return resultPose;
}
#if 0
void rodAarcFeatueDetection(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
const SSG_outlierFilterParam filterParam,
const double rodDiameter,
std::vector<std::vector<SWD_rodArcFeature>>& arcFeatures)
{
int lineNum = (int)scanLines.size();
int linePtNum = (int)scanLines[0].size();
for (int line = 0; line < lineNum; line++)
{
if (line == 697)
int kkk = 1;
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
//滤波,滤除异常点
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], linePtNum, filterParam);
//提取rodArc特征
std::vector<SWD_rodArcFeature> line_rodArcs;
wd_getRodArcFeature_peakCornerMethod(lineData, line, rodDiameter/2, cornerPara, line_rodArcs );
arcFeatures.push_back(line_rodArcs);
}
return;
}
#endif
SVzNL3DPoint _exchangeXY(SVzNL3DPoint pt)
{
SVzNL3DPoint result = {pt.y, pt.x, pt.z};
return result;
}
void _computeRodInfo(
SWD_rodArcFeatureTree& a_objTree,
int startIdx,
int endIdx,
bool treeIsHorizon,
std::vector< SVzNL3DPoint>& fittingPoints,
SSX_rodPositionInfo& a_objRod)
{
//拟合
double _a, _b, _c;
lineFitting_abc(fittingPoints, &_a, &_b, &_c);
int dataSize = (int)fittingPoints.size();
SVzNL2DPointD foot1 = sx_getFootPoint_abc(fittingPoints[0].x, fittingPoints[0].y, _a, _b, _c);
SVzNL2DPointD foot2 = sx_getFootPoint_abc(fittingPoints[dataSize - 1].x, fittingPoints[dataSize - 1].y, _a, _b, _c);
double deltaZ = fittingPoints[dataSize - 1].z - fittingPoints[0].z;
double len = sqrt(pow(foot1.x - foot2.x, 2) + pow(foot1.y - foot2.y, 2));
//直线的轴向向量
SVzNL3DPoint axialDir = { foot2.x - foot1.x, foot2.y - foot1.y, fittingPoints[dataSize - 1].z - fittingPoints[0].z };
//归一化
double normData = sqrt(pow(axialDir.x, 2) + pow(axialDir.y, 2) + pow(axialDir.z, 2));
axialDir.x = axialDir.x / normData;
axialDir.y = axialDir.y / normData;
axialDir.z = axialDir.z / normData;
//计算一个辅助平面Y=0平面旋转一个角度的法向
double theta = atan2(foot2.y - foot1.y, foot2.x - foot1.x);
double sinTheta = sin(theta);
double cosTheta = cos(theta);
SVzNL3DPoint tmpDir = { sinTheta, -cosTheta, 0 };
//叉乘出棒材的法向
SVzNL3DPoint normalDir = { axialDir.y * tmpDir.z - tmpDir.y * axialDir.z,
axialDir.z * tmpDir.x - tmpDir.z * axialDir.x,
axialDir.x * tmpDir.y - tmpDir.x * axialDir.y };
//确定真正起点和终点到直线距离小于5mm
double tmpData = sqrt(_a * _a + _b * _b);
_a = _a / tmpData;
_b = _b / tmpData;
_c = _c / tmpData;
SVzNL3DPoint realStart, realEnd;
bool foundStart = false;
for (int j = startIdx; j < endIdx; j++)
{
SVzNL3DPoint a_pt = a_objTree.treeNodes[j].peakPt;
if (true == treeIsHorizon)
a_pt = _exchangeXY(a_pt);
double dist = abs(a_pt.x * _a + a_pt.y * _b + _c);
if (dist < 5.0)
{
realStart = a_pt;
foundStart = true;
break;
}
}
if (false == foundStart)
{
realStart = a_objTree.treeNodes[startIdx].peakPt;
if (true == treeIsHorizon)
realStart = _exchangeXY(realStart);
}
bool foundEnd = false;
for (int j = endIdx; j >= startIdx; j--)
{
SVzNL3DPoint a_pt = a_objTree.treeNodes[j].peakPt;
if (true == treeIsHorizon)
a_pt = _exchangeXY(a_pt);
double dist = abs(a_pt.x * _a + a_pt.y * _b + _c);
if (dist < 5.0)
{
realEnd = a_pt;
foundEnd = true;
break;
}
}
if (false == foundEnd)
{
realEnd = a_objTree.treeNodes[endIdx].peakPt;
if (true == treeIsHorizon)
realEnd = _exchangeXY(realEnd);
}
SVzNL2DPointD foot_s = sx_getFootPoint_abc(realStart.x, realStart.y, _a, _b, _c);
SVzNL2DPointD foot_e = sx_getFootPoint_abc(realEnd.x, realEnd.y, _a, _b, _c);
double dist_s = sqrt(pow(foot_s.x - foot1.x, 2) + pow(foot_s.y - foot1.y, 2));
double dist_e = sqrt(pow(foot_e.x - foot1.x, 2) + pow(foot_e.y - foot1.y, 2));
//生成目标信息
a_objRod.startPt = { foot_s.x, foot_s.y, -(dist_s / len) * deltaZ + fittingPoints[0].z };
a_objRod.endPt = { foot_e.x, foot_e.y, (dist_e / len) * deltaZ + fittingPoints[0].z };
a_objRod.center = { (a_objRod.startPt.x + a_objRod.endPt.x) / 2,
(a_objRod.startPt.y + a_objRod.endPt.y) / 2,
(a_objRod.startPt.z + a_objRod.endPt.z) / 2 };
a_objRod.axialDir = axialDir;
a_objRod.normalDir = normalDir;
return;
}
bool checkObjEixst(SSX_rodPositionInfo& a_objRod, std::vector<SSX_rodPositionInfo>& existObjs, const SSX_rodParam rodParam)
{
double minDistance = -1;
for (int i = 0; i < (int)existObjs.size(); i++)
{
double dist = sqrt(pow(a_objRod.center.x - existObjs[i].center.x, 2) +
pow(a_objRod.center.y - existObjs[i].center.y, 2) +
pow(a_objRod.center.z - existObjs[i].center.z, 2));
if (minDistance < 0)
minDistance = dist;
else if (minDistance > dist)
minDistance = dist;
}
if (minDistance < 0)
return false;
else if (minDistance < rodParam.diameter / 4) // R/2
return true;
else
return false;
}
bool _commpareByCenterZ(SSX_rodPositionInfo& a, SSX_rodPositionInfo& b)
{
return (a.center.z < b.center.z);
}
void sx_rodPositioning(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_planeCalibPara poseCalibPara,
const SSG_cornerParam cornerPara,
const SSG_outlierFilterParam filterParam,
const SSG_treeGrowParam growParam,
const SSX_rodParam rodParam,
std::vector<SSX_rodPositionInfo>& rodInfo,
int* errCode)
{
*errCode = 0;
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return;
}
int linePtNum = (int)scanLines[0].size();
//判断数据格式是否为grid。算法只能处理grid数据格式
bool isGridData = true;
for (int line = 0; line < lineNum; line++)
{
if (linePtNum != (int)scanLines[line].size())
{
isGridData = false;
break;
}
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
for (int i = 0, i_max = (int)scanLines.size(); i < i_max; i++)
{
if (i == 14)
int kkk = 1;
//行处理
//调平,去除地面
double cuttingZ = -1;
sx_rodPosition_lineDataR(scanLines[i], poseCalibPara.planeCalib, cuttingZ);
}
//生成水平扫描数据
std::vector<std::vector<SVzNL3DPosition>> hLines_raw;
hLines_raw.resize(linePtNum);
for (int i = 0; i < linePtNum; i++)
hLines_raw[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
hLines_raw[j][line] = scanLines[line][j];
hLines_raw[j][line].pt3D.x = scanLines[line][j].pt3D.y;
hLines_raw[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
//内部参数
const double segment_maxDistTh = 5.0;
const double segment_minSegSize = rodParam.diameter / 8;
//在垂直方向上分别提取ARC特征并进行特征生长
std::vector<std::vector<SWD_rodArcFeature>> arcFeatures_v;
rodArcFeatueDetection( scanLines, cornerPara, filterParam, segment_maxDistTh, segment_minSegSize, rodParam.diameter, arcFeatures_v);
//特征生长
std::vector<SWD_rodArcFeatureTree> rodArcTrees_v;
wd_getRodArcFeatureGrowingTrees(arcFeatures_v, rodArcTrees_v, growParam);
//水平方向
std::vector<std::vector<SWD_rodArcFeature>> arcFeatures_h;
rodArcFeatueDetection(hLines_raw, cornerPara, filterParam, segment_maxDistTh, segment_minSegSize, rodParam.diameter, arcFeatures_h);
//特征生长
std::vector<SWD_rodArcFeatureTree> rodArcTrees_h;
wd_getRodArcFeatureGrowingTrees(arcFeatures_h, rodArcTrees_h, growParam);
if ((rodArcTrees_v.size() == 0) && (rodArcTrees_h.size() == 0))
{
*errCode = SX_ERR_ZERO_OBJECTS;
return;
}
int objNum_v = (int)rodArcTrees_v.size();
int objNum_h = (int)rodArcTrees_h.size();
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
}
}
//置标志用于debug
for (int i = 0; i < objNum_v; i++)
{
int nodeNum = (int)rodArcTrees_v[i].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int lineIdx = rodArcTrees_v[i].treeNodes[j].lineIdx;
int centerPtIdx = rodArcTrees_v[i].treeNodes[j].peakPtIdx;
for (int m = rodArcTrees_v[i].treeNodes[j].startPtIdx; m <= rodArcTrees_v[i].treeNodes[j].endPtIdx; m++)
scanLines[lineIdx][m].nPointIdx = 1;
scanLines[lineIdx][centerPtIdx].nPointIdx |= 0x10;
}
}
//置标志用于debug
for (int i = 0; i < objNum_h; i++)
{
int nodeNum = (int)rodArcTrees_h[i].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int ptIdx = rodArcTrees_h[i].treeNodes[j].lineIdx;
int centerLineIdx = rodArcTrees_h[i].treeNodes[j].peakPtIdx;
for (int m = rodArcTrees_h[i].treeNodes[j].startPtIdx; m <= rodArcTrees_h[i].treeNodes[j].endPtIdx; m++)
scanLines[m][ptIdx].nPointIdx |= 2;
scanLines[centerLineIdx][ptIdx].nPointIdx |= 0x20;
}
}
//目标判断
//(1)长度过滤
//垂直目标
for (int i = 0; i < (int)rodArcTrees_v.size(); i++)
{
int nodeSize = rodArcTrees_v[i].treeNodes.size();
SVzNL3DPoint startCenter = rodArcTrees_v[i].treeNodes[0].peakPt;
SVzNL3DPoint endCenter = rodArcTrees_v[i].treeNodes[nodeSize-1].peakPt;
double sumLen = sqrt(pow(startCenter.x - endCenter.x, 2) +
pow(startCenter.y - endCenter.y, 2) +
pow(startCenter.z - endCenter.z, 2));
double len_ratio = sumLen / rodParam.len;
int len_mod = (int)(len_ratio + 0.5);
double len_diff = (double)len_mod - len_ratio;
if (len_diff < 0.15)
{
double preSegLen = sumLen / (double)len_mod;
std::vector<SVzNLRange> segEndings;
segEndings.resize(len_mod);
int preIdx = -1;
for (int m = 0; m < len_mod; m++)
{
int distIdx = preIdx + 1;
startCenter = rodArcTrees_v[i].treeNodes[distIdx].peakPt;
SVzNLRange a_segEnding = { distIdx , distIdx };
double currSegLen = 0;
while (distIdx < nodeSize)
{
currSegLen = sqrt(pow(rodArcTrees_v[i].treeNodes[distIdx].peakPt.x - startCenter.x, 2) +
pow(rodArcTrees_v[i].treeNodes[distIdx].peakPt.y - startCenter.y, 2) +
pow(rodArcTrees_v[i].treeNodes[distIdx].peakPt.z - startCenter.z, 2));
if (currSegLen > preSegLen)
break;
else
a_segEnding.nMax = distIdx;
distIdx++;
}
segEndings[m] = a_segEnding;
preIdx = a_segEnding.nMax;
}
for (int m = 0; m < len_mod; m++)
{
//在XY平面内直线拟合
//为了防止端部影响,跳过端面数据
std::vector<SVzNL3DPoint> fittingPoints;
for (int j = segEndings[m].nMin; j <= segEndings[m].nMax; j++)
{
int sIdx = segEndings[m].nMin;
int eIdx = segEndings[m].nMax;
startCenter = rodArcTrees_v[i].treeNodes[sIdx].peakPt;
endCenter = rodArcTrees_v[i].treeNodes[eIdx].peakPt;
double dist1 = sqrt(pow(rodArcTrees_v[i].treeNodes[j].peakPt.x - startCenter.x, 2) +
pow(rodArcTrees_v[i].treeNodes[j].peakPt.y - startCenter.y, 2) +
pow(rodArcTrees_v[i].treeNodes[j].peakPt.z - startCenter.z, 2));
double dist2 = sqrt(pow(rodArcTrees_v[i].treeNodes[j].peakPt.x - endCenter.x, 2) +
pow(rodArcTrees_v[i].treeNodes[j].peakPt.y - endCenter.y, 2) +
pow(rodArcTrees_v[i].treeNodes[j].peakPt.z - endCenter.z, 2));
if ((dist1 > rodParam.diameter / 2) && (dist2 > rodParam.diameter / 2))
fittingPoints.push_back(rodArcTrees_v[i].treeNodes[j].peakPt);
}
if (fittingPoints.size() < 3)
continue;
SSX_rodPositionInfo a_objRod;
_computeRodInfo(rodArcTrees_v[i], segEndings[m].nMin, segEndings[m].nMax, false, fittingPoints, a_objRod);
rodInfo.push_back(a_objRod);
}
}
}
//水平目标
for (int i = 0; i < (int)rodArcTrees_h.size(); i++)
{
int nodeSize = rodArcTrees_h[i].treeNodes.size();
SVzNL3DPoint startCenter = rodArcTrees_h[i].treeNodes[0].peakPt;
startCenter = _exchangeXY(startCenter);
SVzNL3DPoint endCenter = rodArcTrees_h[i].treeNodes[nodeSize - 1].peakPt;
endCenter = _exchangeXY(endCenter);
double sumLen = sqrt(pow(startCenter.x - endCenter.x, 2) +
pow(startCenter.y - endCenter.y, 2) +
pow(startCenter.z - endCenter.z, 2));
double len_ratio = sumLen / rodParam.len;
int len_mod = (int)(len_ratio + 0.5);
double len_diff = (double)len_mod - len_ratio;
if (len_diff < 0.15)
{
double preSegLen = sumLen / (double)len_mod;
std::vector<SVzNLRange> segEndings;
segEndings.resize(len_mod);
int preIdx = -1;
for (int m = 0; m < len_mod; m++)
{
int distIdx = preIdx + 1;
startCenter = _exchangeXY(rodArcTrees_h[i].treeNodes[distIdx].peakPt);
SVzNLRange a_segEnding = { distIdx , distIdx };
double currSegLen = 0;
while (distIdx < nodeSize)
{
SVzNL3DPoint a_pt = _exchangeXY(rodArcTrees_h[i].treeNodes[distIdx].peakPt);
currSegLen = sqrt(pow(a_pt.x - startCenter.x, 2) +
pow(a_pt.y - startCenter.y, 2) +
pow(a_pt.z - startCenter.z, 2));
if (currSegLen > preSegLen)
break;
else
a_segEnding.nMax = distIdx;
distIdx++;
}
segEndings[m] = a_segEnding;
preIdx = a_segEnding.nMax;
}
for (int m = 0; m < len_mod; m++)
{
//在XY平面内直线拟合
//为了防止端部影响,跳过端面数据
int sIdx = segEndings[m].nMin;
int eIdx = segEndings[m].nMax;
startCenter = _exchangeXY(rodArcTrees_h[i].treeNodes[sIdx].peakPt);
endCenter = _exchangeXY(rodArcTrees_h[i].treeNodes[eIdx].peakPt);
std::vector<SVzNL3DPoint> fittingPoints;
for (int j = segEndings[m].nMin; j < segEndings[m].nMax; j++)
{
SVzNL3DPoint a_pt = _exchangeXY(rodArcTrees_h[i].treeNodes[j].peakPt);
double dist1 = sqrt(pow(a_pt.x - startCenter.x, 2) + pow(a_pt.y - startCenter.y, 2) + pow(a_pt.z - startCenter.z, 2));
double dist2 = sqrt(pow(a_pt.x - endCenter.x, 2) + pow(a_pt.y - endCenter.y, 2) + pow(a_pt.z - endCenter.z, 2));
if ((dist1 > rodParam.diameter / 2) && (dist2 > rodParam.diameter / 2))
fittingPoints.push_back(a_pt);
}
if (fittingPoints.size() < 3)
continue;
SSX_rodPositionInfo a_objRod;
_computeRodInfo(rodArcTrees_h[i], segEndings[m].nMin, segEndings[m].nMax, true, fittingPoints, a_objRod);
//检查是否与垂直检测目标重叠
bool isExist = checkObjEixst(a_objRod, rodInfo, rodParam);
if (false == isExist)
rodInfo.push_back(a_objRod);
}
}
}
//2遮挡判断
//按高度排序
std::sort(rodInfo.begin(), rodInfo.end(), _commpareByCenterZ);
//将数据重新投射回原来的坐标系,以保持手眼标定结果正确
for (int i = 0; i < lineNum; i++)
sx_rodPosition_lineDataR(scanLines[i], poseCalibPara.invRMatrix, -1);
//将检测结果重新投射回原来的坐标系
for (int i = 0; i < (int)rodInfo.size(); i++)
{
SSX_rodPositionInfo& a_rod = rodInfo[i];
SVzNL3DPoint rawObj = _translatePoint(a_rod.center, poseCalibPara.invRMatrix);
a_rod.center = rawObj;
rawObj = _translatePoint(a_rod.axialDir, poseCalibPara.invRMatrix);
a_rod.axialDir = rawObj;
rawObj = _translatePoint(a_rod.normalDir, poseCalibPara.invRMatrix);
a_rod.normalDir = rawObj;
rawObj = _translatePoint(a_rod.startPt, poseCalibPara.invRMatrix);
a_rod.startPt = rawObj;
rawObj = _translatePoint(a_rod.endPt, poseCalibPara.invRMatrix);
a_rod.endPt = rawObj;
}
return;
}
typedef struct
{
int lineType;
int treeIdx;
double xyLine_a, xyLine_b, xyLine_c;
SWD3DPointPostion line_start;
SWD3DPointPostion line_end;
std::vector<SWD3DPointPostion> linePts;
}_RodLineInfo;
double _compute2DDistance(SVzNL3DPoint& pt1, SVzNL3DPoint& pt2)
{
return sqrt(pow(pt1.x - pt2.x, 2) + pow(pt1.y - pt2.y, 2));
}
double _compute2DDistance_indexingPt(SWD3DPointPostion& pt1, SWD3DPointPostion& pt2)
{
return sqrt(pow(pt1.point.x - pt2.point.x, 2) + pow(pt1.point.y - pt2.point.y, 2));
}
SWD3DPointPostion _computeNearestPoint(_RodLineInfo& a_rod, SVzNL3DPoint& refPt, int* refPtIdx)
{
SWD3DPointPostion nearestLinePt= { 0, 0, {0, 0, -1} };
//线上
double minDist = -1;
int idx = -1;
for (int i = 0; i < (int)a_rod.linePts.size(); i++)
{
SWD3DPointPostion a_linePt = a_rod.linePts[i];
double dist = _compute2DDistance(a_linePt.point, refPt);
if ((minDist < 0) || (minDist > dist))
{
minDist = dist;
nearestLinePt = a_linePt;
idx = i;
}
}
*refPtIdx = idx;
return nearestLinePt;
}
SWD3DPointPostion _computeGivenDistancePoint(_RodLineInfo& a_rod, SVzNL3DPoint& refPt, int refPtIdx, double givenDistance, int* ptPos, bool searchDir_forward)
{
SWD3DPointPostion nearestLinePt = { 0, 0, {0, 0, -1} };
//线上
double minDist = -1;
int position = -1;
if (refPtIdx < 0)
{
for (int i = 0; i < (int)a_rod.linePts.size(); i++)
{
SWD3DPointPostion a_linePt = a_rod.linePts[i];
double dist = _compute2DDistance(a_linePt.point, refPt);
double distDiff = abs(dist - givenDistance);
if ((minDist < 0) || (minDist > distDiff))
{
minDist = distDiff;
nearestLinePt = a_linePt;
position = i;
}
}
}
else
{
if (true == searchDir_forward)
{
for (int i = refPtIdx; i < (int)a_rod.linePts.size(); i++)
{
SWD3DPointPostion a_linePt = a_rod.linePts[i];
double dist = _compute2DDistance(a_linePt.point, refPt);
double distDiff = abs(dist - givenDistance);
if ((minDist < 0) || (minDist > distDiff))
{
minDist = distDiff;
nearestLinePt = a_linePt;
position = i;
}
}
}
else
{
for (int i = refPtIdx; i >= 0; i--)
{
SWD3DPointPostion a_linePt = a_rod.linePts[i];
double dist = _compute2DDistance(a_linePt.point, refPt);
double distDiff = abs(dist - givenDistance);
if ((minDist < 0) || (minDist > distDiff))
{
minDist = distDiff;
nearestLinePt = a_linePt;
position = i;
}
}
}
}
*ptPos = position;
return nearestLinePt;
}
bool _compareLineHeighth(_RodLineInfo& rod1, _RodLineInfo& rod2)
{
//求交点
SVzNL3DPoint crossPt = computeLineCrossPt_abs(
rod1.xyLine_a, rod1.xyLine_b, rod1.xyLine_c,
rod2.xyLine_a, rod2.xyLine_b, rod2.xyLine_c);
//判断交点在线上还是线外
//寻找离交点最近的点
int posIdx = -1;
double len_rod1 = _compute2DDistance_indexingPt(rod1.line_start, rod1.line_end);
double cross_rod1_start = _compute2DDistance(rod1.line_start.point, crossPt);
double cross_rod1_end = _compute2DDistance(rod1.line_end.point, crossPt);
SWD3DPointPostion nearestLinePt_rod1;
if ( (cross_rod1_start < len_rod1) && (cross_rod1_end < len_rod1)) //线上
nearestLinePt_rod1 = _computeNearestPoint(rod1, crossPt, &posIdx);
else
{
if (cross_rod1_start < cross_rod1_end)
nearestLinePt_rod1 = rod1.line_start;
else
nearestLinePt_rod1 = rod1.line_end;
}
if (nearestLinePt_rod1.point.z < 1e-4)
return true;
double len_rod2 = _compute2DDistance_indexingPt(rod2.line_start, rod2.line_end);
double cross_rod2_start = _compute2DDistance(rod2.line_start.point, crossPt);
double cross_rod2_end = _compute2DDistance(rod2.line_end.point, crossPt);
SWD3DPointPostion nearestLinePt_rod2;
if ((cross_rod2_start < len_rod2) && (cross_rod2_end < len_rod2))//线上
nearestLinePt_rod2 = _computeNearestPoint(rod2, crossPt, &posIdx);
else
{
if (cross_rod2_start < cross_rod2_end)
nearestLinePt_rod2 = rod2.line_start;
else
nearestLinePt_rod2 = rod2.line_end;
}
if (nearestLinePt_rod2.point.z < 1e-4)
return true;
//比较高度
if (nearestLinePt_rod1.point.z < nearestLinePt_rod2.point.z)
return true;
else
return false;
}
//寻找扫描线上距离直线最近的点
SVzNL3DPosition _computeLineNearestPoint(std::vector<SVzNL3DPosition>& lineData, int ptIdx_start, int ptIdx_end, double line_a, double line_b, double line_c)
{
SVzNL3DPosition nearestLinePt = { 0, {0, 0, 0} };
double normData = sqrt(pow(line_a, 2) + pow(line_b, 2));
line_a = line_a / normData;
line_b = line_b / normData;
line_c = line_c / normData;
//线上
double minDist = -1;
for (int i = ptIdx_start; i <= ptIdx_end; i++)
{
SVzNL3DPosition a_linePt = lineData[i];
double dist = abs(a_linePt.pt3D.x * line_a + a_linePt.pt3D.y * line_b + line_c);
if ((minDist < 0) || (minDist > dist))
{
minDist = dist;
a_linePt.nPointIdx = i;
nearestLinePt = a_linePt;
}
}
if( minDist > 2.0)
nearestLinePt = { 0, {0, 0, 0} };
return nearestLinePt;
}
//寻找水平扫描线上距离直线最近的点
SVzNL3DPosition _computeLineNearestPoint_H(std::vector<std::vector<SVzNL3DPosition>>& scanLines, int ptIdx, int line_start, int line_end, double line_a, double line_b, double line_c)
{
SVzNL3DPosition nearestLinePt = { 0, {0, 0, 0} };
double normData = sqrt(pow(line_a, 2) + pow(line_b, 2));
line_a = line_a / normData;
line_b = line_b / normData;
line_c = line_c / normData;
//线上
double minDist = -1;
for (int line = line_start; line <= line_end; line++)
{
SVzNL3DPosition a_linePt = scanLines[line][ptIdx];
double dist = abs(a_linePt.pt3D.x * line_a + a_linePt.pt3D.y * line_b + line_c);
if ((minDist < 0) || (minDist > dist))
{
minDist = dist;
a_linePt.nPointIdx = line;
nearestLinePt = a_linePt;
}
}
if (minDist > 2.0)
nearestLinePt = { 0, {0, 0, 0} };
return nearestLinePt;
}
//计算两根垂直钢筋的焊点
SSX_weldSeamInfo _computeWeldPoint(
_RodLineInfo& highestRod, _RodLineInfo& vRod,
const SSG_cornerParam cornerPara,
const SSX_rodParam rodParam,
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
SSX_weldSeamInfo a_weldInfo;
a_weldInfo.weldType = KeWD_WELD_UNKNOWN;
int lineSize = (int)scanLines.size();
int linePtSize = (int)scanLines[0].size();
int pointChkWin = 20;
//计算交点
SVzNL3DPoint crossPt = computeLineCrossPt_abs(
highestRod.xyLine_a, highestRod.xyLine_b, highestRod.xyLine_c,
vRod.xyLine_a, vRod.xyLine_b, vRod.xyLine_c);
int posIdx = -1;
SWD3DPointPostion nearestLinePt_topRebar = _computeNearestPoint(highestRod, crossPt, &posIdx);
double chkLen = rodParam.diameter * 2;
int position = -1;
SWD3DPointPostion chkStart = _computeGivenDistancePoint(vRod, crossPt, -1, chkLen, &position, true);
//计算扫描线和点的范围
int ptIdx_start, ptIdx_end;
if (nearestLinePt_topRebar.ptIdx > chkStart.ptIdx)
{
ptIdx_start = chkStart.ptIdx;
ptIdx_end = nearestLinePt_topRebar.ptIdx;
}
else
{
ptIdx_start = nearestLinePt_topRebar.ptIdx;
ptIdx_end = chkStart.ptIdx;
}
int line_start, line_end;
if (nearestLinePt_topRebar.lineIdx > chkStart.lineIdx)
{
line_start = chkStart.lineIdx;
line_end = nearestLinePt_topRebar.lineIdx;
}
else
{
line_end = chkStart.lineIdx;
line_start = nearestLinePt_topRebar.lineIdx;
}
double rotateAngle = 0;
if (vRod.lineType == 1) //垂直扫描
{
if (nearestLinePt_topRebar.lineIdx > chkStart.lineIdx)
rotateAngle = 45;
else
rotateAngle = -45;
}
else
{
if (nearestLinePt_topRebar.ptIdx > chkStart.ptIdx)
rotateAngle = 45;
else
rotateAngle = -45;
}
//计算沿线上的点
std::vector<SVzNL3DPosition> peakPoints;
std::vector<SSG_intPair> peakPostions;
if (vRod.lineType == 1) //垂直扫描
{
ptIdx_start = ptIdx_start - pointChkWin;
if (ptIdx_start < 0)
ptIdx_start = 0;
ptIdx_end = ptIdx_end + pointChkWin;
if (ptIdx_end >= linePtSize)
ptIdx_end = linePtSize - 1;
for (int line = line_start; line <= line_end; line++)
{
SVzNL3DPosition a_pkPt = _computeLineNearestPoint(scanLines[line], ptIdx_start, ptIdx_end, vRod.xyLine_a, vRod.xyLine_b, vRod.xyLine_c);
if (a_pkPt.pt3D.z > 1e-4)
{
SSG_intPair a_pos; //用于可视化显示
a_pos.data_0 = line;
a_pos.data_1 = a_pkPt.nPointIdx;
a_pos.idx = (int)peakPoints.size();
peakPostions.push_back(a_pos);
a_pkPt.nPointIdx = (int)peakPoints.size();
double tmp = a_pkPt.pt3D.x; //钢筋为水平方向X和Y调换。因为计算方向角以Y方向计算
a_pkPt.pt3D.x = a_pkPt.pt3D.y;
a_pkPt.pt3D.y = tmp;
peakPoints.push_back(a_pkPt);
}
}
}
else //水平扫描
{
line_start = line_start - pointChkWin;
if (line_start < 0)
line_start = 0;
line_end = line_end + pointChkWin;
if (line_end >= lineSize)
line_end = lineSize - 1;
for (int ptIdx = ptIdx_start; ptIdx <= ptIdx_end; ptIdx++)
{
SVzNL3DPosition a_pkPt = _computeLineNearestPoint_H(scanLines, ptIdx, line_start, line_end, vRod.xyLine_a, vRod.xyLine_b, vRod.xyLine_c);
if (a_pkPt.pt3D.z > 1e-4)
{
SSG_intPair a_pos; //用于可视化显示
a_pos.data_0 = a_pkPt.nPointIdx;
a_pos.data_1 = ptIdx;
a_pos.idx = (int)peakPoints.size();
peakPostions.push_back(a_pos);
a_pkPt.nPointIdx = (int)peakPoints.size();
peakPoints.push_back(a_pkPt);
}
}
}
std::vector< SSG_pntDirAngle> ptDirAngles;
wd_computeDirAngle_wholeLine(
peakPoints,
cornerPara,
ptDirAngles
);
//寻找焊点:拐点最大的点
std::vector< SSG_pntDirAngle> cornerPeakP;
std::vector< SSG_pntDirAngle> cornerPeakM;
wd_searchCornerPeaks(
ptDirAngles,
peakPoints,
cornerPara.cornerTh,
rodParam.diameter/2,
cornerPeakP,
cornerPeakM
);
if (cornerPeakP.size() > 0)
{
int maxIdx = 0;
for (int i = 1; i < (int)cornerPeakP.size(); i++)
{
if (cornerPeakP[i].corner > cornerPeakP[maxIdx].corner)
maxIdx = i;
}
//生成结果
//double rotateAngle = (cornerPeakP[maxIdx].backwardAngle + cornerPeakP[maxIdx].forwardAngle) / 2 + 90;
SVzNL3DPoint vec_axial = { highestRod.line_end.point.x - highestRod.line_start.point.x,
highestRod.line_end.point.y - highestRod.line_start.point.y,
highestRod.line_end.point.z - highestRod.line_start.point.z };
SVzNL3DPoint vec_vRod = { vRod.line_end.point.x - vRod.line_start.point.x,
vRod.line_end.point.y - vRod.line_start.point.y,
vRod.line_end.point.z - vRod.line_start.point.z };
SVzNL3DPoint v = vec3_cross(vec_axial, vec_vRod);
SVzNL3DPoint v_dir = wd_rotateVectorInPlane(v, vec_axial, rotateAngle);
if (v_dir.z > 0)
v_dir = {-v_dir.x, -v_dir.y, -v_dir.z};
v_dir = vec3_normalize(v_dir);
int pkIdx = cornerPeakP[maxIdx].pntIdx;
int obj_lineIdx = peakPostions[pkIdx].data_0;
int obj_ptIdx = peakPostions[pkIdx].data_1;
a_weldInfo.weldType = KeWD_WELD_POINT;
a_weldInfo.center = scanLines[obj_lineIdx][obj_ptIdx].pt3D;
a_weldInfo.startPt = a_weldInfo.center;
a_weldInfo.endPt = a_weldInfo.center;
a_weldInfo.axialDir = vec3_normalize(vec_axial);
a_weldInfo.normalDir = v_dir; //法向量
//标注
for (int m = 0; m < (int)peakPostions.size(); m++)
{
int lineIdx = peakPostions[m].data_0;
int centerPtIdx = peakPostions[m].data_1;
scanLines[lineIdx][centerPtIdx].nPointIdx &= 0x0F;
scanLines[lineIdx][centerPtIdx].nPointIdx |= 0x40;
}
}
return a_weldInfo;
}
typedef struct
{
EWD_weldType type;
SWD3DPointPostion startPt;
SWD3DPointPostion endPt;
SVzNL3DPoint center;
}SSX_weldSlice;
//计算焊缝剖面特征
SSX_weldSlice _computeWeldSliceInfo(
std::vector< SWD3DPointPostion>& weldSliceData,
const SSX_rodParam rodParam,
const SSG_cornerParam cornerPara,
const double maxDistTh,
bool dirForward
)
{
double rodR = rodParam.diameter / 2;
//提取段
std::vector<SSG_RUN> segs;
int minSegSize = 4;
wd_lineDataSegment_dist(weldSliceData, segs, maxDistTh, minSegSize);
if (segs.size() > 0)
{
SSG_RUN& base_seg = segs[0];
int base_s = base_seg.start;
int base_e = base_seg.len + base_s - 1;
double base_w = abs(weldSliceData[base_s].point.y - weldSliceData[base_e].point.y);
if (base_w > (rodR * 1.1))
{
std::vector< SVzNL3DPosition> line_data;
line_data.resize(weldSliceData.size());
SVzNL3DPosition zeroData = { 0, {0, 0, 0} };
std::fill(line_data.begin(), line_data.end(), zeroData);
for (int i = base_s; i <= base_e; i++)
{
line_data[i].nPointIdx = i;
line_data[i].pt3D = weldSliceData[i].point;
}
//寻找拐点
std::vector< SSG_pntDirAngle> ptDirAngles;
wd_computeDirAngle_wholeLine(
line_data,
cornerPara,
ptDirAngles
);
for(int i = 0; i < (int)ptDirAngles.size(); i ++)
{
if (ptDirAngles[i].type < 0)
ptDirAngles[i].pntIdx = -1;
}
//寻找焊点:拐点最大的点
std::vector< SSG_pntDirAngle> cornerPeakP;
std::vector< SSG_pntDirAngle> cornerPeakM;
wd_searchCornerPeaks(
ptDirAngles,
line_data,
cornerPara.cornerTh,
rodParam.diameter / 2,
cornerPeakP,
cornerPeakM
);
//if (true == dirForward)
{
if (cornerPeakP.size() > 0)
{
int maxIdx = 0;
for (int i = 1; i < (int)cornerPeakP.size(); i++)
{
if (cornerPeakP[i].corner > cornerPeakP[maxIdx].corner)
maxIdx = i;
}
int pkIdx = cornerPeakP[maxIdx].pntIdx;
SSX_weldSlice a_slice;
a_slice.type = KeWD_WELD_SEAM;
a_slice.startPt = weldSliceData[pkIdx];
a_slice.endPt = weldSliceData[pkIdx];
a_slice.center = weldSliceData[pkIdx].point;
return a_slice;
}
}
#if 0
else
{
if (cornerPeakM.size() > 0)
{
int maxIdx = 0;
for (int i = 1; i < (int)cornerPeakM.size(); i++)
{
if (cornerPeakM[i].corner < cornerPeakM[maxIdx].corner)
maxIdx = i;
}
int pkIdx = cornerPeakM[maxIdx].pntIdx;
SSX_weldSlice a_slice;
a_slice.type = KeWD_WELD_SEAM;
a_slice.startPt = weldSliceData[pkIdx];
a_slice.endPt = weldSliceData[pkIdx];
a_slice.center = weldSliceData[pkIdx].point;
return a_slice;
}
}
#endif
}
else
{
if (segs.size() > 1)
{
//寻找除了钢筋外的最近点大于0.5RodR
SWD3DPointPostion& refPt = weldSliceData[0];
double minDist = -1;
int minIdx = -1;
for (int i = 1; i < (int)segs.size(); i++)
{
int seg_s = segs[i].start;
int seg_e = segs[i].len + seg_s - 1;
for (int j = seg_s; j <= seg_e; j++)
{
double dist = _compute2DDistance_indexingPt(refPt, weldSliceData[j]);
if ((minDist < 0) || (minDist > dist))
{
minDist = dist;
minIdx = j;
}
}
}
if (minIdx >= 0)
{
SWD3DPointPostion endPoint = weldSliceData[minIdx];
int seg_s = segs[0].start;
int seg_e = segs[0].len + seg_s - 1; // 寻找Z值最接近的
double minZDiff = -1;
int minZDiff_idx = -1;
for (int i = seg_s; i <= seg_e; i++)
{
double zDiff = abs(endPoint.point.z - weldSliceData[i].point.z);
if ((minZDiff < 0) || (minZDiff > zDiff))
{
minZDiff = zDiff;
minZDiff_idx = i;
}
}
if (minZDiff_idx >= 0)
{
SSX_weldSlice a_slice;
a_slice.type = KeWD_WELD_SEAM_GAP;
a_slice.startPt = weldSliceData[minZDiff_idx];
a_slice.endPt = endPoint;
a_slice.center = {(a_slice.startPt.point.x + a_slice.endPt.point.x)/2,
(a_slice.startPt.point.y + a_slice.endPt.point.y) / 2,
(a_slice.startPt.point.z + a_slice.endPt.point.z) / 2};
return a_slice;
}
}
}
}
}
SSX_weldSlice nullData;
memset(&nullData, 0, sizeof(SSX_weldSlice));
return nullData;
}
SSX_weldSeamInfo _computeWeldSeamInfo(
std::vector<SSX_weldSlice>& seamData,
int rodScanType,
const SSG_cornerParam& weldPointCornerParam,
SVzNL3DPoint& v_zDir,
double rotateAngle
)
{
//拟合
std::vector<SVzNL3DPoint> fitting_xy;
std::vector<SVzNL3DPoint> fitting_yz;
double mean_w = 0;
for (int i = 0; i < (int)seamData.size(); i++)
{
fitting_xy.push_back(seamData[i].center);
SVzNL3DPoint a_pt = { seamData[i].center.y, seamData[i].center.z, seamData[i].center.x };
fitting_yz.push_back(a_pt);
double w = abs(seamData[i].endPt.point.y - seamData[i].startPt.point.y);
mean_w += w;
}
mean_w = mean_w / (double)seamData.size();
double xyLine_a, xyLine_b, xyLine_c;
lineFitting_abc(fitting_xy, &xyLine_a, &xyLine_b, &xyLine_c);
//计算起点和终点
SVzNL2DPointD foot_start_1 = sx_getFootPoint_abc(fitting_xy[0].x, fitting_xy[0].y, xyLine_a, xyLine_b, xyLine_c);
SVzNL2DPointD foot_end_1 = sx_getFootPoint_abc(fitting_xy.back().x, fitting_xy.back().y, xyLine_a, xyLine_b, xyLine_c);
double yzLine_a, yzLine_b, yzLine_c;
lineFitting_abc(fitting_yz, &yzLine_a, &yzLine_b, &yzLine_c);
//计算起点和终点
SVzNL2DPointD foot_start_2 = sx_getFootPoint_abc(fitting_yz[0].x, fitting_yz[0].y, yzLine_a, yzLine_b, yzLine_c);
SVzNL2DPointD foot_end_2 = sx_getFootPoint_abc(fitting_yz.back().x, fitting_yz.back().y, yzLine_a, yzLine_b, yzLine_c);
SSX_weldSeamInfo a_weldSeam;
a_weldSeam.weldType = mean_w > weldPointCornerParam.scale ? KeWD_WELD_SEAM_GAP : KeWD_WELD_SEAM;
if (rodScanType == 1) //垂直
{
a_weldSeam.startPt = { foot_start_1.x, foot_start_1.y, foot_start_2.y };
a_weldSeam.endPt = { foot_end_1.x, foot_end_1.y, foot_end_2.y };
a_weldSeam.center = { (a_weldSeam.startPt.x + a_weldSeam.endPt.x) / 2,
(a_weldSeam.startPt.y + a_weldSeam.endPt.y) / 2,
(a_weldSeam.startPt.z + a_weldSeam.endPt.z) / 2 };
a_weldSeam.axialDir = { (a_weldSeam.endPt.x - a_weldSeam.startPt.x) / 2,
(a_weldSeam.endPt.y - a_weldSeam.startPt.y) / 2,
(a_weldSeam.endPt.z - a_weldSeam.startPt.z) / 2 };
a_weldSeam.axialDir = vec3_normalize(a_weldSeam.axialDir);
SVzNL3DPoint v_dir = wd_rotateVectorInPlane(v_zDir, a_weldSeam.axialDir, rotateAngle);
if (v_dir.z > 0)
v_dir = { -v_dir.x, -v_dir.y, -v_dir.z };
v_dir = vec3_normalize(v_dir);
a_weldSeam.normalDir = v_dir;//法向量
for (int i = 0; i < (int)seamData.size(); i++)
{
a_weldSeam.centerPts.push_back(seamData[i].center);
SVzNL3DPoint pt_0 = seamData[i].startPt.point;
SVzNL3DPoint pt_1 = seamData[i].endPt.point;
a_weldSeam.edgePts_0.push_back(pt_0);
a_weldSeam.edgePts_1.push_back(pt_1);
}
}
else
{
a_weldSeam.startPt = { foot_start_1.y, foot_start_1.x, foot_start_2.y };
a_weldSeam.endPt = { foot_end_1.y, foot_end_1.x, foot_end_2.y };
a_weldSeam.center = { (a_weldSeam.startPt.x + a_weldSeam.endPt.x) / 2,
(a_weldSeam.startPt.y + a_weldSeam.endPt.y) / 2,
(a_weldSeam.startPt.z + a_weldSeam.endPt.z) / 2 };
a_weldSeam.axialDir = { (a_weldSeam.endPt.x - a_weldSeam.startPt.x) / 2,
(a_weldSeam.endPt.y - a_weldSeam.startPt.y) / 2,
(a_weldSeam.endPt.z - a_weldSeam.startPt.z) / 2 };
a_weldSeam.axialDir = vec3_normalize(a_weldSeam.axialDir);
SVzNL3DPoint v_dir = wd_rotateVectorInPlane(v_zDir, a_weldSeam.axialDir, rotateAngle);
if (v_dir.z > 0)
v_dir = { -v_dir.x, -v_dir.y, -v_dir.z };
v_dir = vec3_normalize(v_dir);
a_weldSeam.normalDir = v_dir;//法向量
for (int i = 0; i < (int)seamData.size(); i++)
{
SVzNL3DPoint a_pt = { seamData[i].center.y, seamData[i].center.x, seamData[i].center.z };
a_weldSeam.centerPts.push_back(a_pt);
SVzNL3DPoint pt_0 = { seamData[i].startPt.point.y, seamData[i].startPt.point.x, seamData[i].startPt.point.z };
SVzNL3DPoint pt_1 = { seamData[i].endPt.point.y, seamData[i].endPt.point.x, seamData[i].endPt.point.z };
a_weldSeam.edgePts_0.push_back(pt_0);
a_weldSeam.edgePts_1.push_back(pt_1);
}
}
return a_weldSeam;
}
//焊缝计算
void _computeRebarWeldSeam(
_RodLineInfo& vRod,
SVzNL3DPoint& v_zDir,
bool searchDir_forward,
SVzNL3DPoint& crossPoint,
int crossPtIdx,
std::vector<SSX_weldSeamInfo>& rebarWeldSeam,
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSX_rodParam rodParam,
const SSG_cornerParam weldPointCornerParam,
const SVzNLRangeD weldSeamRange //焊缝距钢筋交叉点的范围(最大值)
)
{
int lineSize = (int)scanLines.size();
int linePtSize = (int)scanLines[0].size();
//提取范围内的值
int position_1 = -1;
int position_2 = -1;
SWD3DPointPostion chkPt_1 = _computeGivenDistancePoint(vRod, crossPoint, crossPtIdx, weldSeamRange.min, &position_1, searchDir_forward);
SWD3DPointPostion chkPt_2 = _computeGivenDistancePoint(vRod, crossPoint, crossPtIdx, weldSeamRange.max, &position_2, searchDir_forward);
if ((position_1 < 0) || (position_2 < 0))
return;
std::vector<std::vector< SWD3DPointPostion>> checkingData_1; //从Peak向左或向上
std::vector<std::vector< SWD3DPointPostion>> checkingData_2; //从peak向右或向下
int startIdx = position_1 < position_2 ? position_1 : position_2;
int endIdx = position_1 < position_2 ? position_2 : position_1;
for (int i = startIdx; i <= endIdx; i++)
{
std::vector< SWD3DPointPostion> a_line_1;
std::vector< SWD3DPointPostion> a_line_2;
SWD3DPointPostion& refPt = vRod.linePts[i];
if (vRod.lineType == 1) //垂直
{
int lineIdx = refPt.lineIdx;
for (int j = refPt.ptIdx; j >= 0; j--)
{
SVzNL3DPosition& a_pt = scanLines[lineIdx][j];
if (a_pt.pt3D.z > 1e-4)
{
double xyDist = sqrt(pow(a_pt.pt3D.x - refPt.point.x, 2) + pow(a_pt.pt3D.y - refPt.point.y, 2));
double zDiff = abs(a_pt.pt3D.z - refPt.point.z);
if (xyDist > rodParam.diameter * 1.5)
break;
if (zDiff < rodParam.diameter)
{
SWD3DPointPostion a_chkPt;
a_chkPt.lineIdx = lineIdx;
a_chkPt.ptIdx = j;
a_chkPt.point = a_pt.pt3D;
a_line_1.push_back( a_chkPt);
}
}
}
//向后
for (int j = refPt.ptIdx; j < linePtSize; j++)
{
SVzNL3DPosition& a_pt = scanLines[lineIdx][j];
if (a_pt.pt3D.z > 1e-4)
{
double xyDist = sqrt(pow(a_pt.pt3D.x - refPt.point.x, 2) + pow(a_pt.pt3D.y - refPt.point.y, 2));
double zDiff = abs(a_pt.pt3D.z - refPt.point.z);
if (xyDist > rodParam.diameter * 1.5)
break;
if (zDiff < rodParam.diameter)
{
SWD3DPointPostion a_chkPt;
a_chkPt.lineIdx = lineIdx;
a_chkPt.ptIdx = j;
a_chkPt.point = a_pt.pt3D;
a_line_2.push_back(a_chkPt);
}
}
}
}
else
{
int ptIdx = refPt.ptIdx;
for (int j = refPt.lineIdx; j >= 0; j--)
{
SVzNL3DPosition& a_pt = scanLines[j][ptIdx];
if (a_pt.pt3D.z > 1e-4)
{
double xyDist = sqrt(pow(a_pt.pt3D.x - refPt.point.x, 2) + pow(a_pt.pt3D.y - refPt.point.y, 2));
double zDiff = abs(a_pt.pt3D.z - refPt.point.z);
if (xyDist > rodParam.diameter * 1.5)
break;
if (zDiff < rodParam.diameter)
{
SWD3DPointPostion a_chkPt;
a_chkPt.lineIdx = j;
a_chkPt.ptIdx = ptIdx;
a_chkPt.point.x = a_pt.pt3D.y;
a_chkPt.point.y = a_pt.pt3D.x;
a_chkPt.point.z = a_pt.pt3D.z;
a_line_1.push_back(a_chkPt);
}
}
}
//向后
for (int j = refPt.lineIdx; j < lineSize; j++)
{
SVzNL3DPosition& a_pt = scanLines[j][ptIdx];
if (a_pt.pt3D.z > 1e-4)
{
double xyDist = sqrt(pow(a_pt.pt3D.x - refPt.point.x, 2) + pow(a_pt.pt3D.y - refPt.point.y, 2));
double zDiff = abs(a_pt.pt3D.z - refPt.point.z);
if (xyDist > rodParam.diameter * 1.5)
break;
if (zDiff < rodParam.diameter)
{
SWD3DPointPostion a_chkPt;
a_chkPt.lineIdx = j;
a_chkPt.ptIdx = ptIdx;
a_chkPt.point.x = a_pt.pt3D.y;
a_chkPt.point.y = a_pt.pt3D.x;
a_chkPt.point.z = a_pt.pt3D.z;
a_line_2.push_back(a_chkPt);
}
}
}
}
checkingData_1.push_back(a_line_1);
checkingData_2.push_back(a_line_2);
}
//宽度分析
int dataLineSize = (int)checkingData_1.size();
std::vector<double> lineW;
int seam_start = -1;
int seam_end = -1;
for (int i = 0; i < dataLineSize; i++)
{
double w;
if ((checkingData_1[i].size() == 0) || (checkingData_2[i].size() == 0))
w = -1;
else
w = checkingData_2[i].back().point.y - checkingData_1[i].back().point.y;
if (w > rodParam.diameter * 1.2)
{
if (seam_start < 0)
seam_start = i;
seam_end = i;
}
lineW.push_back(w);
}
//焊缝分析
double segment_maxDistTh = 2.0;
std::vector<SSX_weldSlice> seamData_1;
std::vector<SSX_weldSlice> seamData_2;
for (int i = seam_start; i <= seam_end; i++)
{
SSX_weldSlice a_slice_1 = _computeWeldSliceInfo(
checkingData_1[i],
rodParam,
weldPointCornerParam,
segment_maxDistTh,
false
);
if (KeWD_WELD_UNKNOWN != a_slice_1.type)
seamData_1.push_back(a_slice_1);
SSX_weldSlice a_slice_2 = _computeWeldSliceInfo(
checkingData_2[i],
rodParam,
weldPointCornerParam,
segment_maxDistTh,
true
);
if (KeWD_WELD_UNKNOWN != a_slice_2.type)
seamData_2.push_back(a_slice_2);
}
//生成焊缝信息
if (seamData_1.size() > 10)
{
double rotateAngle = vRod.lineType == 1 ? -45.0 : 45.0;
SSX_weldSeamInfo a_weldSeam = _computeWeldSeamInfo( seamData_1, vRod.lineType, weldPointCornerParam, v_zDir, rotateAngle);
rebarWeldSeam.push_back(a_weldSeam);
//置标志用于debug
for (int i = 0; i < (int)seamData_1.size(); i++)
{
int lineIdx = seamData_1[i].startPt.lineIdx;
int ptIdx = seamData_1[i].startPt.ptIdx;
scanLines[lineIdx][ptIdx].nPointIdx |= 0x40;
lineIdx = seamData_1[i].endPt.lineIdx;
ptIdx = seamData_1[i].endPt.ptIdx;
scanLines[lineIdx][ptIdx].nPointIdx |= 0x40;
}
}
if (seamData_2.size() > 10)
{
double rotateAngle = vRod.lineType == 1 ? 45.0 : -45.0;
SSX_weldSeamInfo a_weldSeam = _computeWeldSeamInfo(seamData_2, vRod.lineType, weldPointCornerParam, v_zDir, rotateAngle);
rebarWeldSeam.push_back(a_weldSeam);
//置标志用于debug
for (int i = 0; i < (int)seamData_2.size(); i++)
{
int lineIdx = seamData_2[i].startPt.lineIdx;
int ptIdx = seamData_2[i].startPt.ptIdx;
scanLines[lineIdx][ptIdx].nPointIdx |= 0x40;
lineIdx = seamData_2[i].endPt.lineIdx;
ptIdx = seamData_2[i].endPt.ptIdx;
scanLines[lineIdx][ptIdx].nPointIdx |= 0x40;
}
}
}
#if 0
SVzNLRangeD _computeZRange()
{
}
void _zHist()
{
}
void zSumHist()
{
}
//焊缝计算
void _computeRebarWeldSeam_2(
_RodLineInfo& vRod,
SVzNL3DPoint& v_zDir,
bool searchDir_forward,
SVzNL3DPoint& crossPoint,
int crossPtIdx,
std::vector<SSX_weldSeamInfo>& rebarWeldSeam,
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSX_rodParam rodParam,
const SSG_cornerParam weldPointCornerParam,
const SVzNLRangeD weldSeamRange //焊缝距钢筋交叉点的范围(最大值)
)
{
int lineSize = (int)scanLines.size();
int linePtSize = (int)scanLines[0].size();
//提取范围内的值
int position_1 = -1;
int position_2 = -1;
SWD3DPointPostion chkPt_1 = _computeGivenDistancePoint(vRod, crossPoint, crossPtIdx, weldSeamRange.min, &position_1, searchDir_forward);
SWD3DPointPostion chkPt_2 = _computeGivenDistancePoint(vRod, crossPoint, crossPtIdx, weldSeamRange.max, &position_2, searchDir_forward);
if ((position_1 < 0) || (position_2 < 0))
return;
std::vector<std::vector< SWD3DPointPostion>> checkingData_1; //从Peak向左或向上
std::vector<std::vector< SWD3DPointPostion>> checkingData_2; //从peak向右或向下
int startIdx = position_1 < position_2 ? position_1 : position_2;
int endIdx = position_1 < position_2 ? position_2 : position_1;
for (int i = startIdx; i <= endIdx; i++)
{
std::vector< SWD3DPointPostion> a_line_1;
std::vector< SWD3DPointPostion> a_line_2;
SWD3DPointPostion& refPt = vRod.linePts[i];
if (vRod.lineType == 1) //垂直
{
int lineIdx = refPt.lineIdx;
for (int j = refPt.ptIdx; j >= 0; j--)
{
SVzNL3DPosition& a_pt = scanLines[lineIdx][j];
if (a_pt.pt3D.z > 1e-4)
{
double xyDist = sqrt(pow(a_pt.pt3D.x - refPt.point.x, 2) + pow(a_pt.pt3D.y - refPt.point.y, 2));
double zDiff = abs(a_pt.pt3D.z - refPt.point.z);
if (xyDist > rodParam.diameter * 1.5)
break;
if (zDiff < rodParam.diameter)
{
SWD3DPointPostion a_chkPt;
a_chkPt.lineIdx = lineIdx;
a_chkPt.ptIdx = j;
a_chkPt.point = a_pt.pt3D;
a_line_1.push_back(a_chkPt);
}
}
}
//向后
for (int j = refPt.ptIdx; j < linePtSize; j++)
{
SVzNL3DPosition& a_pt = scanLines[lineIdx][j];
if (a_pt.pt3D.z > 1e-4)
{
double xyDist = sqrt(pow(a_pt.pt3D.x - refPt.point.x, 2) + pow(a_pt.pt3D.y - refPt.point.y, 2));
double zDiff = abs(a_pt.pt3D.z - refPt.point.z);
if (xyDist > rodParam.diameter * 1.5)
break;
if (zDiff < rodParam.diameter)
{
SWD3DPointPostion a_chkPt;
a_chkPt.lineIdx = lineIdx;
a_chkPt.ptIdx = j;
a_chkPt.point = a_pt.pt3D;
a_line_2.push_back(a_chkPt);
}
}
}
}
else
{
int ptIdx = refPt.ptIdx;
for (int j = refPt.lineIdx; j >= 0; j--)
{
SVzNL3DPosition& a_pt = scanLines[j][ptIdx];
if (a_pt.pt3D.z > 1e-4)
{
double xyDist = sqrt(pow(a_pt.pt3D.x - refPt.point.x, 2) + pow(a_pt.pt3D.y - refPt.point.y, 2));
double zDiff = abs(a_pt.pt3D.z - refPt.point.z);
if (xyDist > rodParam.diameter * 1.5)
break;
if (zDiff < rodParam.diameter)
{
SWD3DPointPostion a_chkPt;
a_chkPt.lineIdx = j;
a_chkPt.ptIdx = ptIdx;
a_chkPt.point.x = a_pt.pt3D.y;
a_chkPt.point.y = a_pt.pt3D.x;
a_chkPt.point.z = a_pt.pt3D.z;
a_line_1.push_back(a_chkPt);
}
}
}
//向后
for (int j = refPt.lineIdx; j < lineSize; j++)
{
SVzNL3DPosition& a_pt = scanLines[j][ptIdx];
if (a_pt.pt3D.z > 1e-4)
{
double xyDist = sqrt(pow(a_pt.pt3D.x - refPt.point.x, 2) + pow(a_pt.pt3D.y - refPt.point.y, 2));
double zDiff = abs(a_pt.pt3D.z - refPt.point.z);
if (xyDist > rodParam.diameter * 1.5)
break;
if (zDiff < rodParam.diameter)
{
SWD3DPointPostion a_chkPt;
a_chkPt.lineIdx = j;
a_chkPt.ptIdx = ptIdx;
a_chkPt.point.x = a_pt.pt3D.y;
a_chkPt.point.y = a_pt.pt3D.x;
a_chkPt.point.z = a_pt.pt3D.z;
a_line_2.push_back(a_chkPt);
}
}
}
}
checkingData_1.push_back(a_line_1);
checkingData_2.push_back(a_line_2);
}
//zSlicea切割
//内部参数
const double maxDistTh = 2.0; //
const int minSegSize = 5; //
{
std::vector<SSG_RUN> segs;
wd_lineDataSegment_dist(data, segs, maxDistTh, minSegSize);
if (segs.size() == 1) //检查拐点
{
}
//宽度分析(需要替换成V型分析)
int dataLineSize = (int)checkingData_1.size();
std::vector<double> lineW;
int seam_start = -1;
int seam_end = -1;
for (int i = 0; i < dataLineSize; i++)
{
double w;
if ((checkingData_1[i].size() == 0) || (checkingData_2[i].size() == 0))
w = -1;
else
w = checkingData_2[i].back().point.y - checkingData_1[i].back().point.y;
if (w > rodParam.diameter * 1.2)
{
if (seam_start < 0)
seam_start = i;
seam_end = i;
}
lineW.push_back(w);
}
//焊缝分析
double segment_maxDistTh = 2.0;
std::vector<SSX_weldSlice> seamData_1;
std::vector<SSX_weldSlice> seamData_2;
for (int i = seam_start; i <= seam_end; i++)
{
SSX_weldSlice a_slice_1 = _computeWeldSliceInfo(
checkingData_1[i],
rodParam,
weldPointCornerParam,
segment_maxDistTh,
false
);
if (KeWD_WELD_UNKNOWN != a_slice_1.type)
seamData_1.push_back(a_slice_1);
SSX_weldSlice a_slice_2 = _computeWeldSliceInfo(
checkingData_2[i],
rodParam,
weldPointCornerParam,
segment_maxDistTh,
true
);
if (KeWD_WELD_UNKNOWN != a_slice_2.type)
seamData_2.push_back(a_slice_2);
}
//生成焊缝信息
if (seamData_1.size() > 10)
{
double rotateAngle = vRod.lineType == 1 ? -45.0 : 45.0;
SSX_weldSeamInfo a_weldSeam = _computeWeldSeamInfo(seamData_1, vRod.lineType, weldPointCornerParam, v_zDir, rotateAngle);
rebarWeldSeam.push_back(a_weldSeam);
//置标志用于debug
for (int i = 0; i < (int)seamData_1.size(); i++)
{
int lineIdx = seamData_1[i].startPt.lineIdx;
int ptIdx = seamData_1[i].startPt.ptIdx;
scanLines[lineIdx][ptIdx].nPointIdx |= 0x40;
lineIdx = seamData_1[i].endPt.lineIdx;
ptIdx = seamData_1[i].endPt.ptIdx;
scanLines[lineIdx][ptIdx].nPointIdx |= 0x40;
}
}
if (seamData_2.size() > 10)
{
double rotateAngle = vRod.lineType == 1 ? 45.0 : -45.0;
SSX_weldSeamInfo a_weldSeam = _computeWeldSeamInfo(seamData_2, vRod.lineType, weldPointCornerParam, v_zDir, rotateAngle);
rebarWeldSeam.push_back(a_weldSeam);
//置标志用于debug
for (int i = 0; i < (int)seamData_2.size(); i++)
{
int lineIdx = seamData_2[i].startPt.lineIdx;
int ptIdx = seamData_2[i].startPt.ptIdx;
scanLines[lineIdx][ptIdx].nPointIdx |= 0x40;
lineIdx = seamData_2[i].endPt.lineIdx;
ptIdx = seamData_2[i].endPt.ptIdx;
scanLines[lineIdx][ptIdx].nPointIdx |= 0x40;
}
}
}
#endif
//筑裕钢结构钢筋焊缝定位
void sx_rebarWeldSeamPositioning(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_planeCalibPara poseCalibPara,
const SSG_cornerParam cornerPara,
const SSG_outlierFilterParam filterParam,
const SSG_treeGrowParam growParam,
const SSX_rodParam rodParam,
const SVzNLRangeD weldSeamRange, //焊缝距钢筋交叉点的范围(最小值和最大值)
EWD_weldingCategory weldCategory,
std::vector<SSX_weldSeamInfo>& weldSeamInfo,
int* errCode)
{
*errCode = 0;
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return;
}
int linePtNum = (int)scanLines[0].size();
//判断数据格式是否为grid。算法只能处理grid数据格式
bool isGridData = true;
for (int line = 0; line < lineNum; line++)
{
if (linePtNum != (int)scanLines[line].size())
{
isGridData = false;
break;
}
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
for (int i = 0, i_max = (int)scanLines.size(); i < i_max; i++)
{
if (i == 14)
int kkk = 1;
//行处理
//调平,去除地面
double cuttingZ;
if (poseCalibPara.planeHeight < 0)
cuttingZ = -1;
else
cuttingZ = poseCalibPara.planeHeight - 100;// -1;
sx_rodPosition_lineDataR(scanLines[i], poseCalibPara.planeCalib, cuttingZ);
}
//
wd_noiseFilter(scanLines, filterParam, errCode);
if (*errCode != 0)//数据不是网格格式
return;
//寻找水平和垂直方向的钢筋
//生成水平扫描数据
std::vector<std::vector<SVzNL3DPosition>> hLines_raw;
hLines_raw.resize(linePtNum);
for (int i = 0; i < linePtNum; i++)
hLines_raw[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
hLines_raw[j][line] = scanLines[line][j];
hLines_raw[j][line].pt3D.x = scanLines[line][j].pt3D.y;
hLines_raw[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
#if 0
for (int line = 0; line < linePtNum; line++)
{
//平滑
sg_lineSegSmoothing(
hLines_raw[line],
double seg_y_deltaTh, //分段的Y间隔。大于此间隔为新的分段
double seg_z_deltaTh,//分段的Z间隔。大于此间隔为新的分段
int smoothWin,
std::vector<SVzNL3DPosition>&output);
}
#endif
//在垂直方向上分别提取ARC特征并进行特征生长 const double segment_maxDistTh = rodDiameter / 4;
const double segment_maxDistTh = rodParam.diameter / 4;
const double segment_minSegSize = rodParam.diameter / 8;
std::vector<std::vector<SWD_rodArcFeature>> arcFeatures_v;
#if 1
rodArcFeatueDetection(scanLines, cornerPara, filterParam, segment_maxDistTh, segment_minSegSize, rodParam.diameter, arcFeatures_v);
#else
lineArcAndWeldFeatueDetection(scanLines, cornerPara, filterParam, rodParam.diameter, arcFeatures_v);
#endif
//特征生长
std::vector<SWD_rodArcFeatureTree> allRodArcTrees_v;
wd_getRodArcFeatureGrowingTrees(arcFeatures_v, allRodArcTrees_v, growParam);
//提取内V和外V特征(按段进行)
//根据水平度过滤目标(已经调平)
double maxTanValue = tan(10.0 * PI / 180);
std::vector<SWD_rodArcFeatureTree> rodArcTrees_v;
for (int i = 0; i < (int)allRodArcTrees_v.size(); i++)
{
SWD_rodArcFeature node_first = allRodArcTrees_v[i].treeNodes[0];
SWD_rodArcFeature node_last = allRodArcTrees_v[i].treeNodes.back();
double dist_xy = sqrt(pow(node_first.peakPt.x - node_last.peakPt.x, 2) + pow(node_first.peakPt.y - node_last.peakPt.y, 2));
double dist_z = abs(node_first.peakPt.z - node_last.peakPt.z);
double tanValue = dist_z / dist_xy;
if (tanValue < maxTanValue)
rodArcTrees_v.push_back(allRodArcTrees_v[i]);
}
//水平方向
std::vector<std::vector<SWD_rodArcFeature>> arcFeatures_h;
rodArcFeatueDetection(hLines_raw, cornerPara, filterParam, segment_maxDistTh, segment_minSegSize, rodParam.diameter, arcFeatures_h);
//特征生长
std::vector<SWD_rodArcFeatureTree> allRodArcTrees_h;
wd_getRodArcFeatureGrowingTrees(arcFeatures_h, allRodArcTrees_h, growParam);
//根据水平度过滤目标(已经调平)
std::vector<SWD_rodArcFeatureTree> rodArcTrees_h;
for (int i = 0; i < (int)allRodArcTrees_h.size(); i++)
{
SWD_rodArcFeature node_first = allRodArcTrees_h[i].treeNodes[0];
SWD_rodArcFeature node_last = allRodArcTrees_h[i].treeNodes.back();
double dist_xy = sqrt(pow(node_first.peakPt.x - node_last.peakPt.x, 2) + pow(node_first.peakPt.y - node_last.peakPt.y, 2));
double dist_z = abs(node_first.peakPt.z - node_last.peakPt.z);
double tanValue = dist_z / dist_xy;
if (tanValue < maxTanValue)
rodArcTrees_h.push_back(allRodArcTrees_h[i]);
}
if ((rodArcTrees_v.size() == 0) && (rodArcTrees_h.size() == 0))
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
int objNum_v = (int)rodArcTrees_v.size();
int objNum_h = (int)rodArcTrees_h.size();
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
}
}
//选择最高的目标
std::vector<_RodLineInfo> rodLineInfo;
for (int i = 0; i < objNum_v; i++)
{
_RodLineInfo a_rodInfo;
int nodeNum = (int)rodArcTrees_v[i].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int lineIdx = rodArcTrees_v[i].treeNodes[j].lineIdx;
int centerPtIdx = rodArcTrees_v[i].treeNodes[j].peakPtIdx;
SWD3DPointPostion a_pt;
a_pt.lineIdx = lineIdx;
a_pt.ptIdx = centerPtIdx;
a_pt.point = scanLines[lineIdx][centerPtIdx].pt3D;
if(a_pt.point.z > 1e-4)
a_rodInfo.linePts.push_back(a_pt);
}
a_rodInfo.line_start = a_rodInfo.linePts[0];
a_rodInfo.line_end = a_rodInfo.linePts.back();
a_rodInfo.lineType = 1;
a_rodInfo.treeIdx = i;
indexingPtLineFitting_abc(a_rodInfo.linePts, &a_rodInfo.xyLine_a, &a_rodInfo.xyLine_b, &a_rodInfo.xyLine_c);
rodLineInfo.push_back(a_rodInfo);
}
for (int i = 0; i < objNum_h; i++)
{
_RodLineInfo a_rodInfo;
int nodeNum = (int)rodArcTrees_h[i].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int ptIdx = rodArcTrees_h[i].treeNodes[j].lineIdx;
int centerLineIdx = rodArcTrees_h[i].treeNodes[j].peakPtIdx;
SWD3DPointPostion a_pt;
a_pt.lineIdx = centerLineIdx;
a_pt.ptIdx = ptIdx;
a_pt.point = scanLines[centerLineIdx][ptIdx].pt3D;
if(a_pt.point.z > 1e-4)
a_rodInfo.linePts.push_back(a_pt);
}
a_rodInfo.line_start = a_rodInfo.linePts[0];
a_rodInfo.line_end = a_rodInfo.linePts.back();
a_rodInfo.lineType = 2;
a_rodInfo.treeIdx = i;
indexingPtLineFitting_abc(a_rodInfo.linePts, &a_rodInfo.xyLine_a, &a_rodInfo.xyLine_b, &a_rodInfo.xyLine_c);
rodLineInfo.push_back(a_rodInfo);
}
int totalRodLineSize = (int)rodLineInfo.size();
//计算最高的线
int highestLineIdx = -1;
for (int i = 0; i < totalRodLineSize; i++)
{
bool isHighest = true;
for (int j = i + 1; j < totalRodLineSize; j++)
{
bool isHigher = _compareLineHeighth(rodLineInfo[i], rodLineInfo[j]);
if (false == isHigher)
{
isHighest = false;
break;
}
}
if (true == isHighest)
{
highestLineIdx = i;
break;
}
}
if(highestLineIdx < 0)
{
*errCode = SX_ERR_NO_HIGHEST_ROD;
return;
}
_RodLineInfo& highestRod = rodLineInfo[highestLineIdx];
//确定垂直的钢筋
int vRodType;
if (highestRod.lineType == 1)
vRodType = 2;
else
vRodType = 1;
//沿交点向两侧寻找
//内部参数
double validCrossRebar_distTh = rodParam.diameter * 1.5;
double len_highestRod = _compute2DDistance_indexingPt(highestRod.line_start, highestRod.line_end);
std::vector< _RodLineInfo> validVRod;
std::vector<SWD3DPointPostion> crossPoints_highestRod;
std::vector<SWD3DPointPostion> crossPoints_vRod;
for (int i = 0; i < totalRodLineSize; i++)
{
if (rodLineInfo[i].lineType == vRodType)
{
_RodLineInfo& vRod = rodLineInfo[i];
//求交点
SVzNL3DPoint crossPt = computeLineCrossPt_abs(
highestRod.xyLine_a, highestRod.xyLine_b, highestRod.xyLine_c,
vRod.xyLine_a, vRod.xyLine_b, vRod.xyLine_c);
//判断交点在线上还是线外
//交点需要在最高的钢筋上,同时需要在另一个钢筋的线外
double cross_highest_start = _compute2DDistance(highestRod.line_start.point, crossPt);
double cross_highest_end = _compute2DDistance(highestRod.line_end.point, crossPt);
if ((cross_highest_start < len_highestRod ) && ( cross_highest_end < len_highestRod ))
{
int posIdx = -1;
SWD3DPointPostion nearestLinePt_highestRod = _computeNearestPoint(highestRod, crossPt, &posIdx);
//寻找离交点最近的点
double len_vRod = _compute2DDistance_indexingPt(vRod.line_start, vRod.line_end);
double cross_vRod_start = _compute2DDistance(vRod.line_start.point, crossPt);
double cross_vRod_end = _compute2DDistance(vRod.line_end.point, crossPt);
if ( (cross_vRod_start > (len_vRod-rodParam.diameter/2)) || (cross_vRod_end > (len_vRod-rodParam.diameter/2))) //线外
{
SWD3DPointPostion nearestLinePt_vRod;
if (cross_vRod_start < cross_vRod_end)
nearestLinePt_vRod = vRod.line_start;
else
nearestLinePt_vRod = vRod.line_end;
double distance = _compute2DDistance_indexingPt(nearestLinePt_vRod, nearestLinePt_highestRod);
double zDiff = nearestLinePt_vRod.point.z - nearestLinePt_highestRod.point.z;
if ((distance < validCrossRebar_distTh) && (zDiff < validCrossRebar_distTh))
{
crossPoints_highestRod.push_back(nearestLinePt_highestRod);
crossPoints_vRod.push_back(nearestLinePt_vRod);
validVRod.push_back(vRod);
}
}
}
}
}
//置标志用于debug
if(highestRod.lineType == 1) //vTree
{
int nodeNum = (int)rodArcTrees_v[highestRod.treeIdx].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int lineIdx = rodArcTrees_v[highestRod.treeIdx].treeNodes[j].lineIdx;
int centerPtIdx = rodArcTrees_v[highestRod.treeIdx].treeNodes[j].peakPtIdx;
for (int m = rodArcTrees_v[highestRod.treeIdx].treeNodes[j].startPtIdx; m <= rodArcTrees_v[highestRod.treeIdx].treeNodes[j].endPtIdx; m++)
scanLines[lineIdx][m].nPointIdx = 1;
scanLines[lineIdx][centerPtIdx].nPointIdx |= 0x10;
}
for (int i = 0; i < (int)validVRod.size(); i++)
{
int treeIdx = validVRod[i].treeIdx;
int nodeNum = (int)rodArcTrees_h[treeIdx].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int ptIdx = rodArcTrees_h[treeIdx].treeNodes[j].lineIdx;
int centerLineIdx = rodArcTrees_h[treeIdx].treeNodes[j].peakPtIdx;
for (int m = rodArcTrees_h[treeIdx].treeNodes[j].startPtIdx; m <= rodArcTrees_h[treeIdx].treeNodes[j].endPtIdx; m++)
scanLines[m][ptIdx].nPointIdx |= 2;
scanLines[centerLineIdx][ptIdx].nPointIdx |= 0x20;
}
}
}
else
{
int nodeNum = (int)rodArcTrees_h[highestRod.treeIdx].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int ptIdx = rodArcTrees_h[highestRod.treeIdx].treeNodes[j].lineIdx;
int centerLineIdx = rodArcTrees_h[highestRod.treeIdx].treeNodes[j].peakPtIdx;
for (int m = rodArcTrees_h[highestRod.treeIdx].treeNodes[j].startPtIdx; m <= rodArcTrees_h[highestRod.treeIdx].treeNodes[j].endPtIdx; m++)
scanLines[m][ptIdx].nPointIdx |= 1;
scanLines[centerLineIdx][ptIdx].nPointIdx |= 0x10;
}
//置标志用于debug
for (int i = 0; i < (int)validVRod.size(); i++)
{
int treeIdx = validVRod[i].treeIdx;
int nodeNum = (int)rodArcTrees_v[treeIdx].treeNodes.size();
for (int j = 0; j < nodeNum; j++)
{
int lineIdx = rodArcTrees_v[treeIdx].treeNodes[j].lineIdx;
int centerPtIdx = rodArcTrees_v[treeIdx].treeNodes[j].peakPtIdx;
for (int m = rodArcTrees_v[treeIdx].treeNodes[j].startPtIdx; m <= rodArcTrees_v[treeIdx].treeNodes[j].endPtIdx; m++)
scanLines[lineIdx][m].nPointIdx = 2;
scanLines[lineIdx][centerPtIdx].nPointIdx |= 0x20;
}
}
}
SSG_cornerParam weldPointCornerParam;
memset(&weldPointCornerParam, 0, sizeof(SSG_cornerParam));
weldPointCornerParam.cornerTh = 45; //45度角
weldPointCornerParam.scale = 2.0; // algoParam.bagParam.bagH / 8; // 15; // algoParam.bagParam.bagH / 8;
//计算焊点位置
for (int i = 0; i < (int)validVRod.size(); i++)
{
//每一个与最高钢筋相交的钢筋都有一个焊点
SSX_weldSeamInfo a_weldInfo = _computeWeldPoint(
highestRod, validVRod[i],
weldPointCornerParam,
rodParam,
scanLines);
if (a_weldInfo.weldType != KeWD_WELD_UNKNOWN)
weldSeamInfo.push_back(a_weldInfo);
}
std::vector<SWD3DPointPostion> crossPoints;
std::vector<SVzNL3DPosition> vectorPositions;
for (int i = 0; i < (int)validVRod.size(); i++)
{
//计算交点
SVzNL3DPoint crossPt = computeLineCrossPt_abs(
highestRod.xyLine_a, highestRod.xyLine_b, highestRod.xyLine_c,
validVRod[i].xyLine_a, validVRod[i].xyLine_b, validVRod[i].xyLine_c);
int posIdx = -1;
SWD3DPointPostion nearestLinePt_topRebar = _computeNearestPoint(highestRod, crossPt, &posIdx);
crossPoints.push_back(nearestLinePt_topRebar);
//计算垂直向上的向量,用于计算焊缝方向
SVzNL3DPoint vec_axial = { highestRod.line_end.point.x - highestRod.line_start.point.x,
highestRod.line_end.point.y - highestRod.line_start.point.y,
highestRod.line_end.point.z - highestRod.line_start.point.z };
SVzNL3DPoint vec_vRod = { validVRod[i].line_end.point.x - validVRod[i].line_start.point.x,
validVRod[i].line_end.point.y - validVRod[i].line_start.point.y,
validVRod[i].line_end.point.z - validVRod[i].line_start.point.z };
SVzNL3DPoint v = vec3_cross(vec_axial, vec_vRod);
SVzNL3DPosition a_vectorPosition;
a_vectorPosition.nPointIdx = posIdx;
a_vectorPosition.pt3D = v;
vectorPositions.push_back(a_vectorPosition);
if (KeWD_WELD_CATEGPRY_I == weldCategory)
{
//计算下面钢筋的焊缝
//SWD3DPointPostion nearestLinePt_btmRebar = _computeNearestPoint(validVRod[i], crossPt);
std::vector<SSX_weldSeamInfo> a_rebarWeldSeam;
_computeRebarWeldSeam(
validVRod[i],
v,
true,
crossPt,
-1,
a_rebarWeldSeam,
scanLines,
rodParam,
weldPointCornerParam,
weldSeamRange //焊缝距钢筋交叉点的范围(最大值)
);
weldSeamInfo.insert(weldSeamInfo.end(), a_rebarWeldSeam.begin(), a_rebarWeldSeam.end());
}
}
for (int i = 0; i < (int)vectorPositions.size(); i++)
{
if (vectorPositions[i].nPointIdx < 0)
continue;
for (int j = i + 1; j < (int)vectorPositions.size(); j++)
{
if (vectorPositions[j].nPointIdx < 0)
continue;
double cross_dist = _compute2DDistance(crossPoints[i].point, crossPoints[j].point);
if (cross_dist < rodParam.diameter)
vectorPositions[j].nPointIdx = -1;
}
}
for (int i = 0; i < (int)vectorPositions.size(); i++)
{
if (vectorPositions[i].nPointIdx < 0)
continue;
//计算上面钢筋的焊缝
std::vector<SSX_weldSeamInfo> a_rebarWeldSeam_0;
_computeRebarWeldSeam(
highestRod,
vectorPositions[i].pt3D,
true,
crossPoints[i].point,
vectorPositions[i].nPointIdx,
a_rebarWeldSeam_0,
scanLines,
rodParam,
weldPointCornerParam,
weldSeamRange //焊缝距钢筋交叉点的范围(最大值)
);
weldSeamInfo.insert(weldSeamInfo.end(), a_rebarWeldSeam_0.begin(), a_rebarWeldSeam_0.end());
//另一个方向
std::vector<SSX_weldSeamInfo> a_rebarWeldSeam_1;
_computeRebarWeldSeam(
highestRod,
vectorPositions[i].pt3D,
false,
crossPoints[i].point,
vectorPositions[i].nPointIdx,
a_rebarWeldSeam_1,
scanLines,
rodParam,
weldPointCornerParam,
weldSeamRange //焊缝距钢筋交叉点的范围(最大值)
);
weldSeamInfo.insert(weldSeamInfo.end(), a_rebarWeldSeam_1.begin(), a_rebarWeldSeam_1.end());
}
}