wheelArchHeigthMeasure ver 1.0.0

-汽车轮眉高度测量初始版本
This commit is contained in:
jerryzeng 2025-12-24 00:18:30 +08:00
parent 007ce93dbf
commit 7e51432bd2
11 changed files with 4374 additions and 42 deletions

View File

@ -157,6 +157,17 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BQ_assemblyPosition", "BQ_a
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BQ_assemblyPosition_test", "BQ_assemblyPosition_test\BQ_assemblyPosition_test.vcxproj", "{BC38D1E5-10CB-438B-AC72-6012303CE139}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wheelArchHeigthMeaure", "wheelArchHeigthMeaure\wheelArchHeigthMeaure.vcxproj", "{CFE11556-106A-4216-BF62-FDA980528F7A}"
ProjectSection(ProjectDependencies) = postProject
{95DC3F1A-902A-490E-BD3B-B10463CF0EBD} = {95DC3F1A-902A-490E-BD3B-B10463CF0EBD}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wheelArchHeigthMeasure_test", "wheelArchHeigthMeasure_test\wheelArchHeigthMeasure_test.vcxproj", "{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}"
ProjectSection(ProjectDependencies) = postProject
{95DC3F1A-902A-490E-BD3B-B10463CF0EBD} = {95DC3F1A-902A-490E-BD3B-B10463CF0EBD}
{CFE11556-106A-4216-BF62-FDA980528F7A} = {CFE11556-106A-4216-BF62-FDA980528F7A}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -413,6 +424,22 @@ Global
{BC38D1E5-10CB-438B-AC72-6012303CE139}.Release|x64.Build.0 = Release|x64
{BC38D1E5-10CB-438B-AC72-6012303CE139}.Release|x86.ActiveCfg = Release|Win32
{BC38D1E5-10CB-438B-AC72-6012303CE139}.Release|x86.Build.0 = Release|Win32
{CFE11556-106A-4216-BF62-FDA980528F7A}.Debug|x64.ActiveCfg = Debug|x64
{CFE11556-106A-4216-BF62-FDA980528F7A}.Debug|x64.Build.0 = Debug|x64
{CFE11556-106A-4216-BF62-FDA980528F7A}.Debug|x86.ActiveCfg = Debug|Win32
{CFE11556-106A-4216-BF62-FDA980528F7A}.Debug|x86.Build.0 = Debug|Win32
{CFE11556-106A-4216-BF62-FDA980528F7A}.Release|x64.ActiveCfg = Release|x64
{CFE11556-106A-4216-BF62-FDA980528F7A}.Release|x64.Build.0 = Release|x64
{CFE11556-106A-4216-BF62-FDA980528F7A}.Release|x86.ActiveCfg = Release|Win32
{CFE11556-106A-4216-BF62-FDA980528F7A}.Release|x86.Build.0 = Release|Win32
{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}.Debug|x64.ActiveCfg = Debug|x64
{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}.Debug|x64.Build.0 = Debug|x64
{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}.Debug|x86.ActiveCfg = Debug|Win32
{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}.Debug|x86.Build.0 = Debug|Win32
{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}.Release|x64.ActiveCfg = Release|x64
{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}.Release|x64.Build.0 = Release|x64
{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}.Release|x86.ActiveCfg = Release|Win32
{BBF5341E-8447-45E9-ADA3-3E8A9B52F5EF}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -59,6 +59,14 @@ SG_APISHARED_EXPORT void sg_getLineCornerFeature(
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
SSG_lineFeature* line_features);
// 对激光线上由Mask(nPointIdx转义定义指定的点提取激光线上的拐点特征
SG_APISHARED_EXPORT void sg_maskData_getLineCornerFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
int maskID,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
std::vector<SSG_basicFeature1D>& features);
SG_APISHARED_EXPORT void sg_getLineCornerFeature_BQ(
SVzNL3DPosition* lineData,
int dataSize,
@ -67,6 +75,20 @@ SG_APISHARED_EXPORT void sg_getLineCornerFeature_BQ(
const SSG_cornerParam cornerPara,
std::vector<SSG_basicFeature1D>& line_features);
SG_APISHARED_EXPORT void wd_getLineDataIntervals(
std::vector<SVzNL3DPosition>& lineData,
const SSG_lineSegParam lineSegPara,
std::vector<SSG_RUN>& segs);
// 最小点集数量(小于此数无法拟合直线)
const int MIN_POINT_COUNT = 3;
//使用端点直线,检查点到直线的距离,大于门限的分割
SG_APISHARED_EXPORT void split(
SSG_RUN a_run,
std::vector< SVzNL3DPosition>& lineData,
const double maxError,
std::vector< SSG_RUN>& lineSegs);
/// <summary>
/// 提取激光线上的特征跳变、低于z阈值、V及L型用于粒径检测PSM)
/// seg端点z距离大于门限
@ -360,6 +382,12 @@ SG_APISHARED_EXPORT double fitCircleByLeastSquare(
SVzNL3DPoint& center,
double& radius);
//抛物线最小二乘拟合 y=ax^2 + bx + c
SG_APISHARED_EXPORT bool leastSquareParabolaFitEigen(
const std::vector<cv::Point2d>& points,
double& a, double& b, double& c,
double& mse, double& max_err);
//计算Z均值
SG_APISHARED_EXPORT double computeMeanZ(std::vector< SVzNL3DPoint>& pts);
@ -446,6 +474,9 @@ SG_APISHARED_EXPORT SSG_planeCalibPara sg_getPlaneCalibPara(
SVzNL3DLaserLine* laser3DPoints,
int lineNum);
SG_APISHARED_EXPORT SSG_planeCalibPara sg_HCameraVScan_getGroundCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
//将一个平面调整为水平平面(z为固定值
SG_APISHARED_EXPORT SSG_planeCalibPara adjustPlaneToXYPlane(
double plane_A, double plane_B, double plane_C //平面法向量
@ -487,6 +518,10 @@ SG_APISHARED_EXPORT void lineDataRT_vector(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH);
SG_APISHARED_EXPORT void HCamera_lineDataRT_vector(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH);
SG_APISHARED_EXPORT void lineDataRT_RGBD(
SVzNLXYZRGBDLaserLine* a_line,
const double* camPoseR,

View File

@ -525,3 +525,10 @@ typedef struct
double cornerAngle;
int cornerDir;
}SWD_polarPeakInfo;
typedef struct
{
int clusterIdx;
int ptSize;
SVzNL3DRangeD roi3D;
}SWD_clustersInfo;

View File

@ -346,6 +346,148 @@ double fitCircleByLeastSquare(
return err;
}
#if 0
bool leastSquareParabolaFit(const std::vector<cv::Point2d>& points,
double& a, double& b, double& c,
double& mse, double& max_err)
{
// 校验点集数量至少3个点才能拟合抛物线
int n = points.size();
if (n < 3) {
return false;
}
// 初始化各项求和参数
double sum_x = 0.0, sum_x2 = 0.0, sum_x3 = 0.0, sum_x4 = 0.0;
double sum_y = 0.0, sum_xy = 0.0, sum_x2y = 0.0;
// 计算各项求和
for (const auto& p : points) {
double x = p.x;
double y = p.y;
double x2 = x * x;
double x3 = x2 * x;
double x4 = x3 * x;
sum_x += x;
sum_x2 += x2;
sum_x3 += x3;
sum_x4 += x4;
sum_y += y;
sum_xy += x * y;
sum_x2y += x2 * y;
}
// 构建线性方程组 M * [a,b,c]^T = N
// M矩阵3x3
double M[3][3] = {
{sum_x4, sum_x3, sum_x2},
{sum_x3, sum_x2, sum_x},
{sum_x2, sum_x, (double)n}
};
// N向量3x1
double N[3] = { sum_x2y, sum_xy, sum_y };
// 高斯消元法求解线性方程组3元一次方程组
// 步骤1将M转化为上三角矩阵
for (int i = 0; i < 3; i++) {
// 选主元避免除数为0
int pivot = i;
for (int j = i; j < 3; j++) {
if (fabs(M[j][i]) > fabs(M[pivot][i])) {
pivot = j;
}
}
// 交换行
std::swap(M[i], M[pivot]);
std::swap(N[i], N[pivot]);
// 归一化主元行
double div = M[i][i];
if (fabs(div) < 1e-10) {
return false;
}
for (int j = i; j < 3; j++) {
M[i][j] /= div;
}
N[i] /= div;
// 消去下方行
for (int j = i + 1; j < 3; j++) {
double factor = M[j][i];
for (int k = i; k < 3; k++) {
M[j][k] -= factor * M[i][k];
}
N[j] -= factor * N[i];
}
}
// 步骤2回代求解
c = N[2];
b = N[1] - M[1][2] * c;
a = N[0] - M[0][1] * b - M[0][2] * c;
// 计算拟合误差
mse = 0.0;
max_err = 0.0;
for (const auto& p : points) {
double y_fit = a * p.x * p.x + b * p.x + c;
double err = y_fit - p.y;
double err_abs = fabs(err);
mse += err * err;
if (err_abs > max_err) {
max_err = err_abs;
}
}
mse /= n; // 均方误差
return true;
}
#endif
bool leastSquareParabolaFitEigen(
const std::vector<cv::Point2d>& points,
double& a, double& b, double& c,
double& mse, double& max_err)
{
int n = points.size();
if (n < 3) {
return false;
}
// 构建系数矩阵A和目标向量B
Eigen::MatrixXd A(n, 3);
Eigen::VectorXd B(n);
for (int i = 0; i < n; i++) {
double x = points[i].x;
A(i, 0) = x * x;
A(i, 1) = x;
A(i, 2) = 1.0;
B(i) = points[i].y;
}
// 最小二乘求解Ax = B直接调用Eigen的求解器
Eigen::Vector3d coeffs = A.bdcSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(B);
a = coeffs(0);
b = coeffs(1);
c = coeffs(2);
// 计算误差
mse = 0.0;
max_err = 0.0;
for (const auto& p : points) {
double y_fit = a * p.x * p.x + b * p.x + c;
double err = y_fit - p.y;
double err_abs = fabs(err);
mse += err * err;
if (err_abs > max_err) {
max_err = err_abs;
}
}
mse /= n;
return true;
}
//¼ÆËãZ¾ùÖµ
double computeMeanZ(std::vector< SVzNL3DPoint>& pts)
{
@ -1915,6 +2057,162 @@ SSG_planeCalibPara sg_getPlaneCalibPara2(
return planePara;
}
//水平安装相机垂直扫描模式地面调平
SSG_planeCalibPara sg_HCameraVScan_getGroundCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
//设置初始结果
double initCalib[9] = {
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 };
SSG_planeCalibPara planePara;
for (int i = 0; i < 9; i++)
planePara.planeCalib[i] = initCalib[i];
planePara.planeHeight = -1.0;
//提取地面直线段
SSG_lineSegParam lineSegPara;
lineSegPara.maxDist = 2.0;
lineSegPara.segGapTh_y = 5.0; //y方向间隔大于5mm认为是分段
lineSegPara.segGapTh_z = 10.0; //z方向间隔大于10mm认为是分段
std::vector<cv::Point3f> groundPts;
int lineNum = (int)scanLines.size();
for (int line = 0; line < lineNum; line++)
{
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
int dataSize = (int)lineData.size();
//去除零点
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(lineData, lineSegPara, segs);
if (segs.size() == 0)
continue;
//对最后一段进行处理
SSG_RUN lastSeg = segs.back();
//直线分割
std::vector< SSG_RUN> segmentationLines;
split(lastSeg, lineData, lineSegPara.maxDist, segmentationLines);
//检查最后一段的直线段的斜率
SSG_RUN lastLine = segmentationLines.back();
//计算斜率
int startIdx = lastLine.start;
int endIdx = lastLine.start + lastLine.len - 1;
double dy = abs(lineData[endIdx].pt3D.y - lineData[startIdx].pt3D.y) + 1e-8; //加扰防止dy为0
double dz = lineData[startIdx].pt3D.z - lineData[endIdx].pt3D.z;
if (dz > 0)
{
double tan_k = dz / dy;
if (tan_k > tan(PI / 3)) //大于60度合格
{
for (int i = startIdx; i <= endIdx; i++)
{
if (lineData[i].pt3D.z > 1e-4)
{
lineData[i].nPointIdx = 1;
cv::Point3f a_pt = cv::Point3f(lineData[i].pt3D.x, lineData[i].pt3D.y, lineData[i].pt3D.z);
groundPts.push_back(a_pt);
}
}
}
}
}
//平面拟合
std::vector<double> planceFunc;
vzCaculateLaserPlane(groundPts, planceFunc);
#if 1 //两个向量的旋转旋转,使用四元数法,
Vector3 a = Vector3(planceFunc[0], planceFunc[1], planceFunc[2]);
Vector3 b = Vector3(0, 1.0, 0);
Quaternion quanPara = rotationBetweenVectors(a, b);
RotationMatrix rMatrix;
quaternionToMatrix(quanPara, rMatrix.data);
//计算反向旋转矩阵
Quaternion invQuanPara = rotationBetweenVectors(b, a);
RotationMatrix invMatrix;
quaternionToMatrix(invQuanPara, invMatrix.data);
#else //根据平面的法向量计算欧拉角,进而计算旋转矩阵
//参数计算
SSG_EulerAngles eulerPra = planeNormalToEuler(planceFunc[0], planceFunc[1], planceFunc[2]);
//反射进行校正
eulerPra.roll = eulerPra.roll;
eulerPra.pitch = eulerPra.pitch;
eulerPra.yaw = eulerPra.yaw;
RotationMatrix rMatrix = eulerToRotationMatrix(eulerPra.yaw, eulerPra.pitch, eulerPra.roll);
#endif
planePara.planeCalib[0] = rMatrix.data[0][0];
planePara.planeCalib[1] = rMatrix.data[0][1];
planePara.planeCalib[2] = rMatrix.data[0][2];
planePara.planeCalib[3] = rMatrix.data[1][0];
planePara.planeCalib[4] = rMatrix.data[1][1];
planePara.planeCalib[5] = rMatrix.data[1][2];
planePara.planeCalib[6] = rMatrix.data[2][0];
planePara.planeCalib[7] = rMatrix.data[2][1];
planePara.planeCalib[8] = rMatrix.data[2][2];
planePara.invRMatrix[0] = invMatrix.data[0][0];
planePara.invRMatrix[1] = invMatrix.data[0][1];
planePara.invRMatrix[2] = invMatrix.data[0][2];
planePara.invRMatrix[3] = invMatrix.data[1][0];
planePara.invRMatrix[4] = invMatrix.data[1][1];
planePara.invRMatrix[5] = invMatrix.data[1][2];
planePara.invRMatrix[6] = invMatrix.data[2][0];
planePara.invRMatrix[7] = invMatrix.data[2][1];
planePara.invRMatrix[8] = invMatrix.data[2][2];
#if 0 //test: 两个矩阵的乘积必须是单位阵
double testMatrix[3][3];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
testMatrix[i][j] = 0;
for (int m = 0; m < 3; m++)
testMatrix[i][j] += invMatrix.data[i][m] * rMatrix.data[m][j];
}
}
#endif
//数据进行转换
SVzNLRangeD calibYRange = { 0, -1 };
SVzNLRangeD topYRange = { 0, -1 };
double sumMeanY = 0;
int sumSize = 0;
for (int i = 0, i_max = (int)groundPts.size(); i < i_max; i++)
{
cv::Point3f a_calibPt;
a_calibPt.x = (float)(groundPts[i].x * planePara.planeCalib[0] + groundPts[i].y * planePara.planeCalib[1] + groundPts[i].z * planePara.planeCalib[2]);
a_calibPt.y = (float)(groundPts[i].x * planePara.planeCalib[3] + groundPts[i].y * planePara.planeCalib[4] + groundPts[i].z * planePara.planeCalib[5]);
a_calibPt.z = (float)(groundPts[i].x * planePara.planeCalib[6] + groundPts[i].y * planePara.planeCalib[7] + groundPts[i].z * planePara.planeCalib[8]);
//z
if (calibYRange.max < calibYRange.min)
{
calibYRange.min = a_calibPt.y;
calibYRange.max = a_calibPt.y;
sumMeanY += a_calibPt.y;
sumSize++;
}
else
{
if (calibYRange.min > a_calibPt.y)
calibYRange.min = a_calibPt.y;
if (calibYRange.max < a_calibPt.y)
calibYRange.max = a_calibPt.y;
sumMeanY += a_calibPt.y;
sumSize++;
}
}
if (sumSize > 0)
sumMeanY = sumMeanY / (double)sumSize;
planePara.planeHeight = sumMeanY; // calibZRange.min;
return planePara;
}
SSG_planeCalibPara sg_getPlaneCalibPara2_ROI(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
SVzNL3DRangeD roi)
@ -2311,6 +2609,28 @@ void lineDataRT_vector(std::vector< SVzNL3DPosition>& a_line, const double* camP
}
return;
}
void HCamera_lineDataRT_vector(std::vector< SVzNL3DPosition>& a_line, const double* camPoseR, double groundH)
{
for (int i = 0; i < a_line.size(); i++)
{
SVzNL3DPoint a_pt = a_line[i].pt3D;
if (a_pt.z < 1e-4)
continue;
double x = a_pt.x * camPoseR[0] + a_pt.y * camPoseR[1] + a_pt.z * camPoseR[2];
double y = a_pt.x * camPoseR[3] + a_pt.y * camPoseR[4] + a_pt.z * camPoseR[5];
double z = a_pt.x * camPoseR[6] + a_pt.y * camPoseR[7] + a_pt.z * camPoseR[8];
if ((groundH > 0) && (y >= groundH)) //去除地面
z = 0;
a_pt.x = x;
a_pt.y = y;
a_pt.z = z;
a_line[i].pt3D = a_pt;
}
return;
}
void lineDataRT_RGBD(SVzNLXYZRGBDLaserLine* a_line, const double* camPoseR, double groundH)
{
for (int i = 0; i < a_line->nPointCnt; i++)

View File

@ -25,3 +25,6 @@
//뀔관
#define SX_BAG_TRAY_EMPTY -2201
//汽车轮眉高度测量
#define SX_ERR_INVALID_ARC -2301

View File

@ -205,6 +205,55 @@ if (i == 370)
return;
}
void wd_getLineDataIntervals(
std::vector<SVzNL3DPosition>& lineData,
const SSG_lineSegParam lineSegPara,
std::vector<SSG_RUN>& segs)
{
int runIdx = 1;
SSG_RUN a_run = { 0, -1, 0 }; //startIdx, len, lastIdx
double pre_z = 0;
double pre_y = 0;
int dataSize = (int)lineData.size();
for (int i = 0; i < dataSize; i++)
{
lineData[i].nPointIdx = 0; //转义使用
if (lineData[i].pt3D.z > 1e-4)
{
if (a_run.len < 0)
{
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
else
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
double y_diff = abs(lineData[i].pt3D.y - pre_y);
if ((z_diff < lineSegPara.segGapTh_z) && (y_diff < lineSegPara.segGapTh_y))
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
a_run.value = runIdx;
segs.push_back(a_run);
runIdx++;
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
pre_z = lineData[i].pt3D.z;
pre_y = lineData[i].pt3D.y;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
}
//滤除离群点z跳变门限方法
void sg_lineDataRemoveOutlier_changeOriginData(
SVzNL3DPosition* lineData,
@ -1500,6 +1549,233 @@ void sg_getLineCornerFeature(
return;
}
/// <summary>
/// 对激光线上由Mask(nPointIdx转义定义指定的点提取激光线上的拐点特征
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
/// </summary>
void sg_maskData_getLineCornerFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
int maskID,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
std::vector<SSG_basicFeature1D>& features)
{
//去除零点
int dataSize = (int)lineData.size();
std::vector< SVzNL3DPosition> vldPts;
for (int i = 0; i < dataSize; i++)
{
SVzNL3DPosition a_pt = lineData[i];
a_pt.nPointIdx = i;
if ( (lineData[i].pt3D.z > 1e-4) &&(lineData[i].nPointIdx == maskID))
vldPts.push_back(a_pt);
}
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if (vldPts[i].nPointIdx == 521)
int kkk = 1;
//前向寻找
int pre_i = -1;
for (int j = i - 1; j >= 0; j--)
{
double dist = sqrt(pow(vldPts[i].pt3D.y - vldPts[j].pt3D.y, 2) +
pow(vldPts[i].pt3D.z - vldPts[j].pt3D.z, 2));
if (dist >= cornerPara.scale * 4)
{
pre_i = -1;
break;
}
else if (dist >= cornerPara.scale)
{
pre_i = j;
break;
}
}
//后向寻找
int post_i = -1;
for (int j = i + 1; j < i_max; j++)
{
double dist = sqrt(pow(vldPts[i].pt3D.y - vldPts[j].pt3D.y, 2) +
pow(vldPts[i].pt3D.z - vldPts[j].pt3D.z, 2));
if (dist >= cornerPara.scale * 4)
{
post_i = -1;
break;
}
else if (dist >= cornerPara.scale)
{
post_i = j;
break;
}
}
//计算拐角
if ((pre_i < 0) || (post_i < 0))
{
corners[i].pntIdx = -1;
corners[i].forwardAngle = 0;
corners[i].backwardAngle = 0;
corners[i].corner = 0;
corners[i].forwardDiffZ = 0;
corners[i].backwardDiffZ = 0;
}
else
{
double tanValue_pre = (vldPts[i].pt3D.z - vldPts[pre_i].pt3D.z) / abs(vldPts[i].pt3D.y - vldPts[pre_i].pt3D.y);
double tanValue_post = (vldPts[post_i].pt3D.z - vldPts[i].pt3D.z) / abs(vldPts[post_i].pt3D.y - vldPts[i].pt3D.y);
double forwardAngle = atan(tanValue_post) * 180.0 / PI;
double backwardAngle = atan(tanValue_pre) * 180.0 / PI;
corners[i].pntIdx = i;
corners[i].forwardAngle = forwardAngle;
corners[i].backwardAngle = backwardAngle;
corners[i].corner = -(forwardAngle - backwardAngle); //图像坐标系与正常坐标系y方向相反所以有“-”号
corners[i].forwardDiffZ = vldPts[post_i].pt3D.z - vldPts[i].pt3D.z;
corners[i].backwardDiffZ = vldPts[i].pt3D.z - vldPts[pre_i].pt3D.z;
}
}
//搜索拐角极值
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
SSG_pntDirAngle* pre_data = NULL;
std::vector< SSG_pntDirAngle> cornerPeakP;
std::vector< SSG_pntDirAngle> cornerPeakM;
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if (i == 275)
int kkk = 1;
SSG_pntDirAngle* curr_data = &corners[i];
if (curr_data->pntIdx < 0)
{
if (i == i_max - 1) //最后一个
{
if (1 == _state) //上升
{
cornerPeakP.push_back(corners[eEdgePtIdx]);
}
else if (2 == _state) //下降
{
cornerPeakM.push_back(corners[eEdgePtIdx]);
}
}
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) //下降
{
cornerPeakP.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (cornerDiff > 0) // 上升
{
cornerPeakM.push_back(*pre_data);
_state = 1;
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//注意:最后一个不处理,为基座位置
//极小值点(峰顶)
//极值比较,在尺度窗口下寻找局部极值点
double square_distTh = 4 * cornerPara.scale * cornerPara.scale; //2倍的cornerScale。
for (int i = 0, i_max = (int)cornerPeakP.size(); i < i_max; i++)
{
if (cornerPeakP[i].corner < cornerPara.cornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPeakP[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPeakP[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[prePtIdx].pt3D.y, 2); // + pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (cornerPeakP[i].corner < cornerPeakP[j].corner)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPeakP[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPeakP[j].pntIdx;
double dist = pow(vldPts[cornerPtIdx].pt3D.y - vldPts[postPtIdx].pt3D.y, 2); // +pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (cornerPeakP[i].corner < cornerPeakP[j].corner)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
SSG_basicFeature1D a_feature;
if ((cornerPeakP[i].backwardAngle > cornerPara.jumpCornerTh_1) && (cornerPeakP[i].forwardAngle > -cornerPara.jumpCornerTh_2))
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
else if ((cornerPeakP[i].forwardAngle < -cornerPara.jumpCornerTh_1) && (cornerPeakP[i].backwardAngle < cornerPara.jumpCornerTh_2))
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
else
a_feature.featureType = LINE_FEATURE_CORNER_V;
a_feature.featureValue = cornerPeakP[i].corner;
a_feature.jumpPos = vldPts[cornerPtIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[cornerPtIdx].nPointIdx };
features.push_back(a_feature);
}
}
return;
}
/// <summary>
/// 提取激光线上的拐点特征水平端点视为Corner
/// seg端点z距离或y距离大于门限
@ -2558,8 +2834,6 @@ void wd_getRingArcFeature(
return;
}
// 最小点集数量(小于此数无法拟合直线)
const int MIN_POINT_COUNT = 3;
//使用端点直线,检查点到直线的距离,大于门限的分割
void split(
SSG_RUN a_run,
@ -2628,46 +2902,7 @@ void wd_surfaceLineSegment(
int dataSize = (int)lineData.size();
//去除零点
std::vector<SSG_RUN> segs;
int runIdx = 1;
SSG_RUN a_run = { 0, -1, 0 }; //startIdx, len, lastIdx
double pre_z = 0;
double pre_y = 0;
for (int i = 0; i < dataSize; i++)
{
if (lineData[i].pt3D.z > 1e-4)
{
if (a_run.len < 0)
{
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
else
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
double y_diff = abs(lineData[i].pt3D.y - pre_y);
if ((z_diff < lineSegPara.segGapTh_z) && (y_diff < lineSegPara.segGapTh_y))
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
a_run.value = runIdx;
segs.push_back(a_run);
runIdx++;
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
pre_z = lineData[i].pt3D.z;
pre_y = lineData[i].pt3D.y;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
wd_getLineDataIntervals(lineData, lineSegPara, segs);
//逐段处理
int segSize = (int)segs.size();

View File

@ -0,0 +1,492 @@
#include <vector>
#include "SG_baseDataType.h"
#include "SG_baseAlgo_Export.h"
#include "wheelArchHeigthMeasure_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_wheelArchHeigthMeasureVersion(void)
{
return m_strVersion.c_str();
}
//相机水平安装计算地面调平参数。
//相机Z轴基本平行地面时需要以地面为参照将相机调水平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara wd_horizonCamera_getGroundCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
return sg_HCameraVScan_getGroundCalibPara(scanLines);
}
//相机水平时姿态调平,并去除地面
void wd_horizonCamera_lineDataR(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH)
{
HCamera_lineDataRT_vector(a_line, camPoseR, groundH);
}
SVzNL3DPoint _ptRotate(SVzNL3DPoint pt3D, const 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;
}
bool compareByPtSize(const SWD_clustersInfo& a, const SWD_clustersInfo& b) {
return a.ptSize > b.ptSize;
}
//提取轮眉区域的下端点
void _getArcEndings(std::vector< std::vector<SVzNL3DPosition>>& scanLines, const int cluster_arc_id, std::vector<SVzNL2DPoint>& contourPts)
{
int lineNum = (int)scanLines.size();
for (int i = 0; i < lineNum; i++)
{
int ptNum = (int)scanLines[i].size();
int lastIdx = -1;
for (int j = 0; j < ptNum; j++)
{
if ((i == 380) && (j > 288))
int kkk = 1;
if ( (scanLines[i][j].nPointIdx == cluster_arc_id) && (scanLines[i][j].pt3D.z >1e-4))
lastIdx = j;
}
if (lastIdx >= 0)
{
SVzNL2DPoint a_pt = { i, lastIdx};
contourPts.push_back(a_pt);
}
}
}
//提取轮眉种子点定义为y最小的端点
int _getArcEndingsCenterPos(std::vector< std::vector<SVzNL3DPosition>>& scanLines, std::vector<SVzNL2DPoint>& contourPts)
{
double minY = DBL_MAX;
int seedPosIdx = 0;
for (int i = 0, i_max = (int)contourPts.size(); i < i_max; i++)
{
SVzNL2DPoint& a_pos = contourPts[i];
double y = scanLines[a_pos.x][a_pos.y].pt3D.y;
if ((minY > y) && (scanLines[a_pos.x][a_pos.y].pt3D.z > 1e-4))
{
seedPosIdx = i;
minY = y;
}
}
return seedPosIdx;
}
//使用端点直线,检查点到直线的距离,大于门限的分割
int computeMaxDistPos(
int ptStartIdx, int ptEndIdx,
std::vector< SVzNL3DPosition>& lineData)
{
SVzNL3DPoint pt1 = lineData[ptStartIdx].pt3D;
SVzNL3DPoint pt2 = lineData[ptEndIdx].pt3D;
if ((pt1.z < 1e-4) || (pt2.z < 1e-4))
return -1;
double _a, _b, _c;
compute2ptLine_2(
pt1.y, pt1.z,
pt2.y, pt2.z,
&_a, &_b, &_c);
//compute2ptLine(pt1, pt2, &_a, &_b, &_c);
double denominator = sqrt(_a * _a + _b * _b);
//归一化
_a = _a / denominator;
_b = _b / denominator;
_c = _c / denominator;
double maxDist = 0;
int maxPos = 0;
for (int i = ptStartIdx; i <= ptEndIdx; i++)
{
SVzNL3DPoint a_pt = lineData[i].pt3D;
if (a_pt.z > 1e-4)
{
double dist = abs(a_pt.y * _a + a_pt.z * _b + _c);
if (maxDist < dist)
{
maxDist = dist;
maxPos = i;
}
}
}
return maxPos;
}
void _extractArcFittingPts(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
std::vector<SVzNL2DPoint>& contourPts,
double chkWin,
int seedPosIdx,
std::vector< SVzNL2DPoint>& arcFittingPos)
{
int size = (int)contourPts.size();
arcFittingPos.push_back(contourPts[seedPosIdx]);
SVzNL3DPoint seedPt = scanLines[contourPts[seedPosIdx].x][contourPts[seedPosIdx].y].pt3D;
for (int i = seedPosIdx - 1; i >= 0; i--)
{
SVzNL2DPoint chkPos = contourPts[i];
SVzNL3DPoint chkPt = scanLines[chkPos.x][chkPos.y].pt3D;
double len = sqrt(pow(chkPt.x - seedPt.x, 2) + pow(chkPt.y - seedPt.y, 2));
if (len > chkWin)
break;
arcFittingPos.insert(arcFittingPos.begin(), chkPos);
}
for (int i = seedPosIdx + 1; i < size; i++)
{
SVzNL2DPoint chkPos = contourPts[i];
SVzNL3DPoint chkPt = scanLines[chkPos.x][chkPos.y].pt3D;
double len = sqrt(pow(chkPt.x - seedPt.x, 2) + pow(chkPt.y - seedPt.y, 2));
if (len > chkWin)
break;
arcFittingPos.push_back(chkPos);
}
//检查正确的端点检查20mm
double chkLen = 20;
for (int i = 0, i_max = (int)arcFittingPos.size(); i < i_max; i++)
{
SVzNL2DPoint chkPos = arcFittingPos[i];
SVzNL3DPoint chkPt = scanLines[chkPos.x][chkPos.y].pt3D;
int endIdx = chkPos.y;
int startIdx = endIdx;
for (int m = endIdx - 1; m >= 0; m--)
{
SVzNL3DPoint a_pt = scanLines[chkPos.x][m].pt3D;
if (a_pt.z > 1e-4)
{
double len = sqrt(pow(a_pt.y - chkPt.y, 2) + pow(a_pt.z - chkPt.z, 2));
if (len > chkLen)
break;
startIdx = m;
}
}
if (startIdx != endIdx)
{
int maxPos = computeMaxDistPos(startIdx, endIdx, scanLines[chkPos.x]);
SVzNL3DPoint max_pt = scanLines[chkPos.x][maxPos].pt3D;
double len1 = sqrt(pow(max_pt.y - chkPt.y, 2) + pow(max_pt.z - chkPt.z, 2));
//构建等腰三角形,重新寻找点到底边的距离最高点
for (int m = maxPos - 1; m >= 0; m--)
{
SVzNL3DPoint a_pt = scanLines[chkPos.x][m].pt3D;
if (a_pt.z > 1e-4)
{
double len = sqrt(pow(a_pt.y - max_pt.y, 2) + pow(a_pt.z - max_pt.z, 2));
if (len > len1)
break;
startIdx = m;
}
}
maxPos = computeMaxDistPos(startIdx, endIdx, scanLines[chkPos.x]);
arcFittingPos[i].y = maxPos;
}
}
}
SVzNL3DPoint _getWheelArcFittingPoint(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const int maskID,
std::vector< SVzNL2DPoint>& fittingPos,
SVzNL2DPoint& fittingPosition)
{
//upWheel, 在XY平面生成拟合点
std::vector<cv::Point2d> fittingPoints;
for (int i = 0, i_max = (int)fittingPos.size(); i < i_max; i++)
{
int lineIdx = fittingPos[i].x;
int ptIdx = fittingPos[i].y;
cv::Point2d a_pt = { scanLines[lineIdx][ptIdx].pt3D.x,scanLines[lineIdx][ptIdx].pt3D.y };
scanLines[lineIdx][ptIdx].nPointIdx = maskID;
fittingPoints.push_back(a_pt);
}
double a, b, c, mse, max_err;
bool fitResult = leastSquareParabolaFitEigen(
fittingPoints, a, b, c, mse, max_err);
//计算轮毂上顶点
SVzNL3DPoint fitPt;
fitPt.x = -b / (2 * a);
fitPt.y = (4 * a * c - b * b) / (4 * a);
//在轮廓点中寻找最近的点
SVzNL2DPoint subOptiPtPos = { 0,0 };
double minLen = DBL_MAX;
for (int i = 0, i_max = (int)fittingPos.size(); i < i_max; i++)
{
int lineIdx = fittingPos[i].x;
int ptIdx = fittingPos[i].y;
cv::Point2d a_pt = { scanLines[lineIdx][ptIdx].pt3D.x,scanLines[lineIdx][ptIdx].pt3D.y };
double len = sqrt(pow(fitPt.x - a_pt.x, 2) + pow(fitPt.y - a_pt.y, 2));
if (minLen > len)
{
subOptiPtPos = fittingPos[i];
minLen = len;
}
}
fittingPosition = subOptiPtPos;
fitPt.z = scanLines[subOptiPtPos.x][subOptiPtPos.y].pt3D.z;
return fitPt;
}
//轮眉高度测量
WD_wheelArchInfo wd_wheelArchHeigthMeasure(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
const SSG_lineSegParam lineSegPara,
const SSG_outlierFilterParam filterParam,
const SSG_treeGrowParam growParam,
const SSG_planeCalibPara groundCalibPara,
int* errCode)
{
*errCode = 0;
WD_wheelArchInfo result;
memset(&result, 0, sizeof(WD_wheelArchInfo));
int lineNum = (int)scanLines.size();
int linePtNum = (int)scanLines[0].size();
bool isGridData = true;
for (int line = 0; line < lineNum; line++)
{
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
if (linePtNum != (int)lineData.size())
isGridData = false;
//滤波,滤除异常点
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], linePtNum, filterParam);
}
//生成水平扫描
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;
}
}
//水平arc特征提取
for (int line = 0; line < linePtNum; line++)
{
if (line == 974)
int kkk = 1;
std::vector<SVzNL3DPosition>& lineData = hLines_raw[line];
//滤波,滤除异常点
int ptNum = (int)lineData.size();
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], ptNum, filterParam);
}
//聚类,聚类出前盖板和轮胎
std::vector<std::vector<SSG_featureClusteringInfo>> featureInfoMask;
std::vector<std::vector<SVzNL3DPoint>> feature3DInfo;
featureInfoMask.resize(lineNum);
feature3DInfo.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
featureInfoMask[i].resize(linePtNum);
feature3DInfo[i].resize(linePtNum);
}
//生成Mask
for (int line = 0; line < lineNum; line++)
{
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
for (int ptIdx = 0; ptIdx < linePtNum; ptIdx++)
{
if (scanLines[line][ptIdx].pt3D.z > 1e-4)
{
SSG_featureClusteringInfo a_mask;
memset(&a_mask, 0, sizeof(SSG_featureClusteringInfo));
a_mask.featurType = 1;
a_mask.lineIdx = line;
a_mask.ptIdx = ptIdx;
featureInfoMask[line][ptIdx] = a_mask;
feature3DInfo[line][ptIdx] = scanLines[line][ptIdx].pt3D;
}
}
}
//聚类
//采用迭代思想,回归思路进行高效聚类
std::vector<std::vector< SVzNL2DPoint>> clusters; //只记录位置
std::vector<SWD_clustersInfo> clustersInfo;
int clusterID = 1;
int clusterCheckWin = 5;
for (int y = 0; y < linePtNum; y++)
{
for (int x = 0; x < lineNum; x++)
{
SSG_featureClusteringInfo& a_featureInfo = featureInfoMask[x][y];
if ((0 == a_featureInfo.featurType) || (a_featureInfo.clusterID > 0)) //非特征或已经处理
continue;
SVzNL3DPoint& a_feature3DValue = feature3DInfo[x][y];
SVzNL3DRangeD a_clusterRoi;
a_clusterRoi.xRange.min = a_feature3DValue.x;
a_clusterRoi.xRange.max = a_feature3DValue.x;
a_clusterRoi.yRange.min = a_feature3DValue.y;
a_clusterRoi.yRange.max = a_feature3DValue.y;
a_clusterRoi.zRange.min = a_feature3DValue.z;
a_clusterRoi.zRange.max = a_feature3DValue.z;
SVzNL2DPoint a_seedPos = { x, y };
std::vector< SVzNL2DPoint> a_cluster;
a_cluster.push_back(a_seedPos);
wd_pointClustering2D(
featureInfoMask,//int记录特征标记和clusterID附加一个flag
feature3DInfo,//double,记录坐标信息
clusterCheckWin, //搜索窗口
growParam,//聚类条件
clusterID, //当前Cluster的ID
a_cluster, //result
a_clusterRoi
);
clusters.push_back(a_cluster);
SWD_clustersInfo a_info;
a_info.clusterIdx = clusterID;
a_info.ptSize = (int)a_cluster.size();
a_info.roi3D = a_clusterRoi;
clustersInfo.push_back(a_info);
clusterID++;
}
}
//聚类结果分析
//取最大的两个聚类。上面的聚类是前盖板,下面的是车轮
std::sort(clustersInfo.begin(), clustersInfo.end(), compareByPtSize);
int cluseter_wheel_id, cluster_arc_id;
if (clustersInfo[0].roi3D.yRange.max > clustersInfo[1].roi3D.yRange.max)
{
cluseter_wheel_id = clustersInfo[0].clusterIdx;
cluster_arc_id = clustersInfo[1].clusterIdx;
}
else
{
cluseter_wheel_id = clustersInfo[1].clusterIdx;
cluster_arc_id = clustersInfo[0].clusterIdx;
}
std::vector< SVzNL2DPoint>& cluster_wheel = clusters[cluseter_wheel_id - 1];
std::vector< SVzNL2DPoint>& cluster_arc = clusters[cluster_arc_id - 1];
for (int i = 0, i_max = (int)cluster_wheel.size(); i < i_max; i++)
{
int lineIdx = cluster_wheel[i].x;
int ptIdx = cluster_wheel[i].y;
scanLines[lineIdx][ptIdx].nPointIdx = 1;
}
for (int i = 0, i_max = (int)cluster_arc.size(); i < i_max; i++)
{
int lineIdx = cluster_arc[i].x;
int ptIdx = cluster_arc[i].y;
scanLines[lineIdx][ptIdx].nPointIdx = 2;
}
//寻找轮眉点
//(1)快速寻找下端点2取中间区域精确确定端点3抛物线拟合4计算轮眉点最高点
std::vector<SVzNL2DPoint> arcEndings;
_getArcEndings(scanLines, 2, arcEndings); //轮眉的ID是2
if (arcEndings.size() == 0)
{
*errCode = SX_ERR_INVALID_ARC;
return result;
}
int arcMidPos = _getArcEndingsCenterPos(scanLines, arcEndings);
//取两侧固定长度100mm内的点进行精确边界提取拟合
double chkWin = 100.0;
std::vector< SVzNL2DPoint> arcFittingPos;
_extractArcFittingPts(
scanLines,
arcEndings,
chkWin,
arcMidPos,
arcFittingPos);
//在XY平面生成拟合点
double outLineLen = 200;
SVzNL2DPoint arcPtPos;
SVzNL3DPoint arcPt = _getWheelArcFittingPoint(scanLines, 3, arcFittingPos, arcPtPos);
result.wheelArchPos = arcPt;
result.arcLine[0] = { arcPt.x - outLineLen, arcPt.y, arcPt.z };
result.arcLine[1] = { arcPt.x + outLineLen, arcPt.y, arcPt.z };
//提取轮毂特征V型槽。连长6mm角度最大的V型槽
std::vector< SVzNL2DPoint> upWheelPos;
std::vector< SVzNL2DPoint> downWheelPos;
int startLine = arcFittingPos[0].x;
int endLine = arcFittingPos.back().x;
SSG_cornerParam wheelCornerPara;
memset(&wheelCornerPara, 0, sizeof(SSG_cornerParam));
wheelCornerPara.scale = 6.0;
wheelCornerPara.cornerTh = 75.0;
for (int line = startLine; line <= endLine; line++)
{
std::vector<SSG_basicFeature1D> cornerFeatures;
sg_maskData_getLineCornerFeature(
scanLines[line],
line,
1, //车轮的ID为1
wheelCornerPara, //scale通常取bagH的1/4
cornerFeatures);
//取第一个为up
if (cornerFeatures.size() >= 2)
{
upWheelPos.push_back(cornerFeatures[0].jumpPos2D);
downWheelPos.push_back(cornerFeatures.back().jumpPos2D);
}
}
SVzNL2DPoint upPos;
SVzNL3DPoint upWheelPt = _getWheelArcFittingPoint(scanLines, 4, upWheelPos, upPos);
result.wheelUpPos = upWheelPt;
result.upLine[0] = { upWheelPt.x - outLineLen, upWheelPt.y, upWheelPt.z };
result.upLine[1] = { upWheelPt.x + outLineLen, upWheelPt.y, upWheelPt.z };
SVzNL2DPoint downPos;
SVzNL3DPoint downWheelPt = _getWheelArcFittingPoint(scanLines, 5, downWheelPos, downPos);
result.wheelDownPos = downWheelPt;
result.downLine[0] = { downWheelPt.x - outLineLen, downWheelPt.y, downWheelPt.z };
result.downLine[1] = { downWheelPt.x + outLineLen, downWheelPt.y, downWheelPt.z };
double centerY = (upWheelPt.y + downWheelPt.y) / 2;
int searchLine = (upPos.x + downPos.x) / 2;
double minDist = DBL_MAX;
int minPtIdx = 0;
for (int i = 0; i < (int)scanLines[searchLine].size(); i++)
{
if (scanLines[searchLine][i].pt3D.z > 1e-4)
{
double dist = abs(scanLines[searchLine][i].pt3D.y - centerY);
if (minDist > dist)
{
minDist = dist;
minPtIdx = i;
}
}
}
result.centerLine[0] = { downWheelPt.x - outLineLen, centerY, scanLines[searchLine][minPtIdx].pt3D.z };
result.centerLine[1] = { downWheelPt.x + outLineLen, centerY, scanLines[searchLine][minPtIdx].pt3D.z };
result.archToCenterHeigth = centerY - arcPt.y;
//将数据重新投射回原来的坐标系,以保持手眼标定结果正确
for (int i = 0; i < lineNum; i++)
lineDataRT_vector(scanLines[i], groundCalibPara.invRMatrix, -1);
//将检测结果重新投射回原来的坐标系
result.wheelArchPos = _ptRotate(result.wheelArchPos, groundCalibPara.invRMatrix);
result.wheelUpPos = _ptRotate(result.wheelUpPos, groundCalibPara.invRMatrix);
result.wheelDownPos = _ptRotate(result.wheelDownPos, groundCalibPara.invRMatrix);
for (int i = 0; i < 2; i++)
{
result.arcLine[i] = _ptRotate(result.arcLine[i], groundCalibPara.invRMatrix);
result.centerLine[i] = _ptRotate(result.centerLine[i], groundCalibPara.invRMatrix);
result.downLine[i] = _ptRotate(result.downLine[i], groundCalibPara.invRMatrix);
result.upLine[i] = _ptRotate(result.upLine[i], groundCalibPara.invRMatrix);
}
return result;
}

View File

@ -0,0 +1,43 @@
#pragma once
#include "SG_algo_Export.h"
#include <vector>
#define _OUTPUT_DEBUG_DATA 1
typedef struct
{
SVzNL3DPoint wheelArchPos;
SVzNL3DPoint wheelUpPos;
SVzNL3DPoint wheelDownPos;
SVzNL3DPoint arcLine[2];
SVzNL3DPoint upLine[2];
SVzNL3DPoint downLine[2];
SVzNL3DPoint centerLine[2];
double archToCenterHeigth;
}WD_wheelArchInfo;
//读版本号
SG_APISHARED_EXPORT const char* wd_wheelArchHeigthMeasureVersion(void);
//相机水平安装计算地面调平参数。。
//相机Z轴基本平行地面时需要以地面为参照将相机调水平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SG_APISHARED_EXPORT SSG_planeCalibPara wd_horizonCamera_getGroundCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines);
//相机水平时姿态调平,并去除地面
SG_APISHARED_EXPORT void wd_horizonCamera_lineDataR(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH);
//提取工件角点及定位长度信息
SG_APISHARED_EXPORT WD_wheelArchInfo wd_wheelArchHeigthMeasure(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_cornerParam cornerPara,
const SSG_lineSegParam lineSegPara,
const SSG_outlierFilterParam filterParam,
const SSG_treeGrowParam growParam,
const SSG_planeCalibPara groundCalibPara,
int* errCode);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{bbf5341e-8447-45e9-ada3-3e8a9b52f5ef}</ProjectGuid>
<RootNamespace>wheelArchHeigthMeasuretest</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(Platform)\$(Configuration)\</OutDir>
<IncludePath>..\..\thirdParty\VzNLSDK\Inc;..\sourceCode;..\sourceCode\inc;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(Platform)\$(Configuration)\</OutDir>
<IncludePath>..\..\thirdParty\VzNLSDK\Inc;..\sourceCode;..\sourceCode\inc;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\thirdParty\opencv320\build\include;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\..\thirdParty\opencv320\build\x64\vc14\lib;..\build\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>opencv_world320d.lib;wheelArchHeigthMeasure.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\thirdParty\opencv320\build\include;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>..\..\thirdParty\opencv320\build\x64\vc14\lib;..\build\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>opencv_world320.lib;wheelArchHeigthMeasure.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="wheelArchHeigthMeasure_test.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\sourceCode\wheelArchHeigthMeasure.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\sourceCode\wheelArchHeigthMeasure_Export.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{cfe11556-106a-4216-bf62-fda980528f7a}</ProjectGuid>
<RootNamespace>wheelArchHeigthMeaure</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>wheelArchHeigthMeasure</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(Platform)\$(Configuration)\</OutDir>
<IncludePath>..\..\thirdParty\VzNLSDK\Inc;..\..\thirdParty\opencv320\build\include;..\sourceCode;..\sourceCode\inc;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(Platform)\$(Configuration)\</OutDir>
<IncludePath>..\..\thirdParty\VzNLSDK\Inc;..\..\thirdParty\opencv320\build\include;..\sourceCode;..\sourceCode\inc;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;WHEELARCHHEIGTHMEAURE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;WHEELARCHHEIGTHMEAURE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;WHEELARCHHEIGTHMEAURE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\..\thirdParty\opencv320\build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalLibraryDirectories>..\..\thirdParty\opencv320\build\x64\vc14\lib;..\build\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<PerUserRedirection>true</PerUserRedirection>
<AdditionalDependencies>opencv_world320d.lib;baseAlgorithm.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;WHEELARCHHEIGTHMEAURE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\..\thirdParty\opencv320\build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalLibraryDirectories>..\..\thirdParty\opencv320\build\x64\vc14\lib;..\build\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>opencv_world320.lib;baseAlgorithm.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>