algoLib/sourceCode/rodAndBarDetection.cpp

301 lines
8.2 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
std::string m_strVersion = "1.0.0";
const char* wd_rodAndBarDetectionVersion(void)
{
return m_strVersion.c_str();
}
SVzNL3DPoint getArcPeak(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
SWD_segFeature & a_arcFeature)
{
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;
}
}
return arcPeak;
}
//投影提取ROI内的数据
void xoyROIProjection(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const double* rtMatrix,
SSG_ROIRectD& roi_xoy,
std::vector<SVzNL3DPoint>& 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;
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))
{
a_pt.x = x;
a_pt.y = y;
a_pt.z = z;
projectPoints.push_back(a_pt);
}
}
}
}
SVzNLRangeD getZRange(std::vector<SVzNL3DPoint>& projectPoints)
{
int ptNum = (int)projectPoints.size();
SVzNLRangeD zRange;
zRange.min = DBL_MAX;
zRange.max = DBL_MIN;
for (int i = 0; i < ptNum; i++)
{
zRange.min = zRange.min > projectPoints[i].z ? projectPoints[i].z : zRange.min;
zRange.max = zRange.max < projectPoints[i].z ? projectPoints[i].z : zRange.max;
}
return zRange;
}
void zCutPointClouds(
std::vector<SVzNL3DPoint>& projectPoints,
SVzNLRangeD& zRange,
std::vector<SVzNL3DPoint>& cutLayerPoints)
{
int ptNum = (int)projectPoints.size();
for (int i = 0; i < ptNum; i++)
{
if ((projectPoints[i].z >= zRange.min) && (projectPoints[i].z <= zRange.max))
cutLayerPoints.push_back(projectPoints[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 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 rodRidius,
std::vector<SSX_hexHeadScrewInfo>& screwInfo,
int* errCode)
{
*errCode = 0;
SSX_hexHeadScrewInfo screwInfo;
memset(&screwInfo, 0, sizeof(SSX_hexHeadScrewInfo));
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< 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_segFeature>> arcFeatures;
for (int line = 0; line < lineNum; line++)
{
if (line == 44)
int kkk = 1;
std::vector<SVzNL3DPosition>& lineData = data_lines[line];
//滤波,滤除异常点
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], linePtNum, filterParam);
std::vector<SWD_segFeature> line_ringArcs;
int dataSize = (int)lineData.size();
//提取Arc特征
wd_getRingArcFeature(
lineData,
line, //当前扫描线序号
cornerPara,
rodRidius / 2, //环宽度
line_ringArcs //环
);
arcFeatures.push_back(line_ringArcs);
}
//特征生长
std::vector<SWD_segFeatureTree> growTrees;
wd_getSegFeatureGrowingTrees(
arcFeatures,
growTrees,
growParam);
if (growTrees.size() == 0)
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
int objNum = (int)growTrees.size();
for (int i = 0; i < objNum; i++)
{
//空间直线拟合
std::vector<SVzNL3DPoint> fitPoints;
int nodeSize = (int)growTrees[i].treeNodes.size();
for (int j = 0; j < nodeSize; j++)
{
SVzNL3DPoint a_pt = getArcPeak(data_lines, growTrees[i].treeNodes[j]);
fitPoints.push_back(a_pt);
}
if (fitPoints.size() < 12)
continue;
//去除头尾各5个点防止在端部和根部扫描时的数据有干扰
fitPoints.erase(fitPoints.begin(), fitPoints.begin() + 5);
fitPoints.erase(fitPoints.end() - 5, fitPoints.end());
//拟合
SVzNL3DPoint P0_center, P1_dir;
bool result = fitLine3DLeastSquares(fitPoints, P0_center, P1_dir);
if (false == result)
continue;
//投影
//计算旋转向量
SVzNL3DPoint vector1 = P1_dir;
SVzNL3DPoint vector2 = { 0, 0, -1.0 };
SSG_planeCalibPara rotatePara = wd_conputeRTMatrix( vector1, vector2);
//
SSG_ROIRectD roi_xoy;
roi_xoy.left = P0_center.x - rodRidius * 4; //2D范围
roi_xoy.right = P0_center.x + rodRidius * 4; //2D范围
roi_xoy.top = P0_center.y - rodRidius * 4; //2D范围
roi_xoy.bottom = P0_center.y + rodRidius * 4; //2D范围
std::vector< SVzNL3DPoint> roiProjectionData;
xoyROIProjection(scanLines, rotatePara.planeCalib, roi_xoy, roiProjectionData);
//取端面
SVzNLRangeD zRange = getZRange(roiProjectionData);
SVzNLRangeD cutZRange;
cutZRange.min = zRange.min;
cutZRange.max = zRange.min + 10.0; //取10mm的端面
std::vector<SVzNL3DPoint> surfacePoints;
zCutPointClouds(roiProjectionData, cutZRange, surfacePoints);
//计算中心点
SVzNL3DPoint projectionCenter = getXoYCentroid(surfacePoints);
projectionCenter.z = zRange.min;
//旋转回原坐标系
SVzNL3DPoint surfaceCenter = _ptRotate(projectionCenter, rotatePara.invRMatrix);
//生成Rod信息
SSX_hexHeadScrewInfo a_rod;
a_rod.center = surfaceCenter;
a_rod.axialDir = P1_dir;
a_rod.rotateAngle = 0;
screwInfo.push_back(a_rod);
}
if (true == isHorizonScan)
{
int objNum = (int)screwInfo.size();
for (int i = 0; i < objNum; i++)
{
double tmp = screwInfo[i].center.x;
screwInfo[i].center.x = screwInfo[i].center.y;
screwInfo[i].center.y = tmp;
tmp = screwInfo[i].axialDir.x;
screwInfo[i].axialDir.x = screwInfo[i].axialDir.y;
screwInfo[i].axialDir.y = tmp;
//screwInfo[i].rotateAngle += 90;
}
}
return;
}