algoLib/sourceCode/SG_lineFeature.cpp

8202 lines
214 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 "SG_baseDataType.h"
#include "SG_baseAlgo_Export.h"
#include <vector>
typedef struct
{
int flag;
int sPtIdx;
int ePtIdx;
SVzNL3DPosition startPt;
SVzNL3DPosition endPt;
}SSG_jump;
typedef struct
{
int flag;
int startPtIdx;
int endPtIdx;
SVzNL3DPosition startPt;
SVzNL3DPosition endPt;
SSG_jump maxJump;
}SSG_slope;
typedef struct
{
int startPtIdx;
int endPtIdx;
double zDiff;
}SSG_zWin;
SSG_meanVar _computeMeanVar(double* data, int size)
{
double sum_x = 0;
double sum_square = 0;
int num = 0;
for (int i = 0; i < size; i++)
{
sum_x += data[i];
sum_square += data[i] * data[i];
num++;
}
SSG_meanVar a_meanVar = { 0, 0 };
if (num > 0)
{
a_meanVar.mean = sum_x / num;
a_meanVar.var = sum_square / num - a_meanVar.mean * a_meanVar.mean;
if (a_meanVar.var < 0)
a_meanVar.var = 0;
else
a_meanVar.var = sqrt(a_meanVar.var);
}
return a_meanVar;
}
void sg_lineDataSmoothing(
std::vector<SVzNL3DPosition>& input,
int smoothWin,
std::vector<SVzNL3DPosition>& output)
{
output.resize(input.size()); // 预分配足够的空间
std::copy(input.begin(), input.end(), output.begin());
int size = (int)input.size();
for (int i = 0; i < size; i++)
{
if (input[i].pt3D.z > 1e-4)
{
int n = 0;
double sum_z = 0;
for (int j = i-smoothWin / 2; j <= i+smoothWin / 2; j++)
{
if ((j >= 0) && (j < size))
{
if (input[j].pt3D.z > 1e-4)
{
sum_z += input[j].pt3D.z;
n++;
}
}
}
output[i].pt3D.z = sum_z / n;
}
}
return;
}
//对扫描线按分段进行平滑。
void sg_lineSegSmoothing(
std::vector<SVzNL3DPosition>& input,
double seg_y_deltaTh, //分段的Y间隔。大于此间隔,为新的分段
double seg_z_deltaTh,//分段的Z间隔。大于此间隔,为新的分段
int smoothWin,
std::vector<SVzNL3DPosition>& output)
{
//output.resize(input.size()); // 预分配足够的空间
//std::copy(input.begin(), input.end(), output.begin());
int dataSize = (int)input.size();
//计算分段
std::vector<std::vector<SVzNL3DPosition>> segs;
std::vector<SVzNL3DPosition> a_seg;
int segStart = -1;
for (int i = 0; i < dataSize; i++)
{
SVzNL3DPosition a_pt = input[i];
//seg判断
if (a_pt.pt3D.z > 1e-4)
{
if (segStart < 0)
{
a_seg.push_back(a_pt);
segStart = 1;
}
else //检查两点距离
{
SVzNL3DPosition pre_pt = a_seg.back();
double diff_z = abs(a_pt.pt3D.z - pre_pt.pt3D.z);
double diff_y = abs(a_pt.pt3D.y - pre_pt.pt3D.y);
if ((diff_y > seg_y_deltaTh) || (diff_z > seg_z_deltaTh))
{
segs.push_back(a_seg);
a_seg.clear();
a_seg.push_back(a_pt);
}
else
a_seg.push_back(a_pt);
}
}
else
a_seg.push_back(a_pt);
}
//last
if (a_seg.size() > 0)
segs.push_back(a_seg);
//分段平滑
for (int i = 0, i_max = (int)segs.size(); i < i_max; i++)
{
std::vector<SVzNL3DPosition> seg_output;
sg_lineDataSmoothing(segs[i], smoothWin, seg_output);
output.insert(output.end(), seg_output.begin(), seg_output.end());
}
return;
}
//滤除离群点:z跳变门限方法
void sg_lineDataRemoveOutlier(
SVzNL3DPosition* lineData,
int dataSize,
SSG_outlierFilterParam filterParam,
std::vector<SVzNL3DPosition>& filerData,
std::vector<int>& noisePts)
{
filerData.resize(dataSize);
std::vector< SSG_RUN> continueRuns;
SSG_RUN a_run = {0, -1, 0}; //startIdx, len, lastIdx
double pre_z = 0;
for (int i = 0; i < dataSize; i++)
{
if (i == 370)
int kkk = 1;
filerData[i] = lineData[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);
if (z_diff < filterParam.continuityTh)
{
a_run.len++;
a_run.value = i;
}
else
{
continueRuns.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
pre_z = lineData[i].pt3D.z;
}
}
if(a_run.len > 0)
continueRuns.push_back(a_run);
for (int i = 0, i_max = (int)continueRuns.size(); i < i_max; i++)
{
if (continueRuns[i].len < filterParam.outlierTh) //噪声
{
for (int j = continueRuns[i].start; j <= continueRuns[i].value; j++)
{
filerData[j].pt3D.z = 0;
noisePts.push_back(j);//记录序号,用于在原始数据上去除噪声
}
}
}
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);
}
//提取空点段。与wd_getLineDataIntervals()正好相反
void wd_getLineDataNullIntervals(
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
SVzNL3DPosition preData = { -1, {0, 0, 0} };
int dataSize = (int)lineData.size();
for (int i = 0; i < dataSize; i++)
{
if (lineData[i].pt3D.z < 1e-4)
{
//若前一个不为空,RUN的起点
if ( (preData.nPointIdx >= 0) && (preData.pt3D.z > 1e-4))
{
a_run.start = i-1;
a_run.len = 2;
a_run.value = i;
}
}
else
{
//若前一个为空,RUN的终点
if ((preData.nPointIdx >= 0) && (preData.pt3D.z < 1e-4) &&(a_run.start >= 0))
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
//判断长度
double gap = abs(lineData[i].pt3D.y - lineData[a_run.start].pt3D.y);
if(gap < lineSegPara.segGapTh_y)
segs.push_back(a_run);
a_run = { 0, -1, 0 };
}
}
preData = lineData[i];
}
}
//滤除离群点:z跳变门限方法
void sg_lineDataRemoveOutlier_changeOriginData(
SVzNL3DPosition* lineData,
int dataSize,
SSG_outlierFilterParam filterParam)
{
std::vector< SSG_RUN> continueRuns;
SSG_RUN a_run = { 0, -1, 0 }; //startIdx, len, lastIdx
double pre_z = 0;
for (int i = 0; i < dataSize; i++)
{
if (i == 370)
int kkk = 1;
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);
if (z_diff < filterParam.continuityTh)
{
a_run.len++;
a_run.value = i;
}
else
{
continueRuns.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
pre_z = lineData[i].pt3D.z;
}
}
if (a_run.len > 0)
continueRuns.push_back(a_run);
for (int i = 0, i_max = (int)continueRuns.size(); i < i_max; i++)
{
if (continueRuns[i].len < filterParam.outlierTh) //噪声
{
for (int j = continueRuns[i].start; j <= continueRuns[i].value; j++)
{
lineData[j].pt3D.z = 0;
}
}
}
return;
}
//滤除离群点:z跳变门限方法, vecotr对象
void wd_vectorDataRemoveOutlier_overwrite(
std::vector<SVzNL3DPosition>& a_line,
SSG_outlierFilterParam filterParam)
{
int dataSize = (int)a_line.size();
std::vector< SSG_RUN> continueRuns;
SSG_RUN a_run = { 0, -1, 0 }; //startIdx, len, lastIdx
double pre_z = 0;
for (int i = 0; i < dataSize; i++)
{
if (i == 370)
int kkk = 1;
if (a_line[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(a_line[i].pt3D.z - pre_z);
if (z_diff < filterParam.continuityTh)
{
a_run.len++;
a_run.value = i;
}
else
{
continueRuns.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
pre_z = a_line[i].pt3D.z;
}
}
if (a_run.len > 0)
continueRuns.push_back(a_run);
for (int i = 0, i_max = (int)continueRuns.size(); i < i_max; i++)
{
if (continueRuns[i].len < filterParam.outlierTh) //噪声
{
for (int j = continueRuns[i].start; j <= continueRuns[i].value; j++)
{
a_line[j].pt3D.z = 0;
}
}
}
return;
}
//滤除离群点:点距离方法
void sg_lineDataRemoveOutlier_ptDistMethod(SVzNL3DPosition* lineData, int dataSize, SSG_outlierFilterParam filterParam, std::vector<SVzNL3DPosition>& filerData, std::vector<int>& noisePts)
{
filerData.resize(dataSize);
std::vector< SSG_RUN> continueRuns;
SSG_RUN a_run = { 0, -1, 0 }; //startIdx, len, lastIdx
SVzNL3DPoint pre_pt = {0,0,0};
for (int i = 0; i < dataSize; i++)
{
if (i == 370)
int kkk = 1;
filerData[i] = lineData[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
{
#if 1
double pt_dist = sqrt(pow(lineData[i].pt3D.x - pre_pt.x, 2) +
pow(lineData[i].pt3D.y - pre_pt.y, 2) +
pow(lineData[i].pt3D.z - pre_pt.z, 2));
if (pt_dist < filterParam.continuityTh)
{
a_run.len++;
a_run.value = i;
}
else
{
continueRuns.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
#else
double pt_dy = abs(lineData[i].pt3D.y - pre_pt.y);
double pt_dz = abs(lineData[i].pt3D.z - pre_pt.z);
if (pt_dy * 3 > pt_dz)
{
a_run.len++;
a_run.value = i;
}
else
{
continueRuns.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
#endif
}
pre_pt = lineData[i].pt3D;
}
}
if (a_run.len > 0)
continueRuns.push_back(a_run);
for (int i = 0, i_max = (int)continueRuns.size(); i < i_max; i++)
{
if (continueRuns[i].len < filterParam.outlierTh) //噪声
{
for (int j = continueRuns[i].start; j <= continueRuns[i].value; j++)
{
filerData[j].pt3D.z = 0;
noisePts.push_back(j);//记录序号,用于在原始数据上去除噪声
}
}
}
return;
}
/// <summary>
/// 提取激光线上的极值点(极大值点和极小值点)
///
/// </summary>
void sg_getLineLocalPeaks(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double scaleWin,
std::vector< SSG_basicFeature1D>& localMax,
std::vector< SSG_basicFeature1D>& localMin)
{
if (dataSize < 2)
return;
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
SVzNL3DPosition* pre_data = NULL;
std::vector< SVzNL3DPosition> pkTop;
std::vector< SVzNL3DPosition> pkBtm;
for (int i = 0; i < dataSize; i++)
{
lineData[i].nPointIdx = i;
SVzNL3DPosition* curr_data = &lineData[i];
if (curr_data->pt3D.z < 1e-4)
{
if (i == dataSize - 1) //最后一个
{
if (1 == _state) //上升
{
pkTop.push_back(lineData[eEdgePtIdx]);
}
else if (2 == _state) //下降
{
pkBtm.push_back(lineData[eEdgePtIdx]);
}
}
continue;
}
if (NULL == pre_data)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
continue;
}
eEdgePtIdx = i;
double z_diff = curr_data->pt3D.z - pre_data->pt3D.z;
switch (_state)
{
case 0: //初态
if (z_diff > 0) //下降
{
_state = 2;
}
else if (z_diff < 0) //上升
{
_state = 1;
}
break;
case 1: //上升
if (z_diff > 0) //下降
{
pkTop.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (z_diff < 0) // 上升
{
pkBtm.push_back(*pre_data);
_state = 1;
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//注意:最后一个不处理,为基座位置
//极小值点(峰顶)
//极值比较,在尺度窗口下寻找局部极值点
double square_distTh = scaleWin * scaleWin;
for (int i = 0, i_max = (int)pkTop.size(); i < i_max; i++)
{
bool isPeak = true;
//向前搜索
for (int j = i - 1; j >= 0; j--)
{
double dist = pow(pkTop[i].pt3D.y - pkTop[j].pt3D.y, 2); // + pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (pkTop[i].pt3D.z > pkTop[j].pt3D.z)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
for (int j = i + 1; j < i_max; j++)
{
double dist = pow(pkTop[i].pt3D.y - pkTop[j].pt3D.y, 2); // +pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (pkTop[i].pt3D.z > pkTop[j].pt3D.z)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
a_feature.jumpPos = pkTop[i].pt3D;
a_feature.jumpPos2D = { lineIdx, pkTop[i].nPointIdx };
localMin.push_back(a_feature);
}
}
//极大值点(谷底)
for (int i = 0, i_max = (int)pkBtm.size(); i < i_max; i++)
{
bool isPeak = true;
//向前搜索
for (int j = i - 1; j >= 0; j--)
{
double dist = pow(pkBtm[i].pt3D.y - pkBtm[j].pt3D.y, 2); // +pow(pkBtm[i].pt3D.x - pkBtm[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (pkBtm[i].pt3D.z < pkBtm[j].pt3D.z)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
for (int j = i + 1; j < i_max; j++)
{
double dist = pow(pkBtm[i].pt3D.y - pkBtm[j].pt3D.y, 2); // +pow(pkBtm[i].pt3D.x - pkBtm[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (pkBtm[i].pt3D.z < pkBtm[j].pt3D.z)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_BOTTOM;
a_feature.jumpPos = pkBtm[i].pt3D;
a_feature.jumpPos2D = { lineIdx, pkBtm[i].nPointIdx };
localMax.push_back(a_feature);
}
}
return;
}
void sg_getLineLocalPeaks_2(
std::vector<SVzNL3DPosition>& lineData,
int lineIdx,
const double scaleWin,
std::vector< SSG_basicFeature1D>& localZMax,
std::vector< SSG_basicFeature1D>& localZMin)
{
int dataSize = (int)lineData.size();
if (dataSize < 2)
return;
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
SVzNL3DPosition* pre_data = NULL;
std::vector< SVzNL3DPosition> zTop;
std::vector< SVzNL3DPosition> zBtm;
for (int i = 0; i < dataSize; i++)
{
if (i == 141)
int kkk = 1;
lineData[i].nPointIdx = i;
SVzNL3DPosition* curr_data = &lineData[i];
if (curr_data->pt3D.z < 1e-4)
{
if (i == dataSize - 1) //最后一个
{
if (1 == _state) //上升
{
zTop.push_back(lineData[eEdgePtIdx]);
}
else if (2 == _state) //下降
{
zBtm.push_back(lineData[eEdgePtIdx]);
}
}
continue;
}
if (NULL == pre_data)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
continue;
}
eEdgePtIdx = i;
double z_diff = curr_data->pt3D.z - pre_data->pt3D.z;
switch (_state)
{
case 0: //初态
if (z_diff < 0) //下降
{
_state = 2;
}
else if (z_diff > 0) //上升
{
_state = 1;
}
break;
case 1: //上升
if (z_diff <0) //下降
{
zTop.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (z_diff > 0) // 上升
{
zBtm.push_back(*pre_data);
_state = 1;
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//注意:最后一个不处理,为基座位置
//极小值点(峰顶)
//极值比较,在尺度窗口下寻找局部极值点
double square_distTh = scaleWin * scaleWin;
for (int i = 0, i_max = (int)zTop.size(); i < i_max; i++)
{
bool isPeak = true;
//向前搜索
for (int j = i - 1; j >= 0; j--)
{
double dist = pow(zTop[i].pt3D.y - zTop[j].pt3D.y, 2) + pow(zTop[i].pt3D.z - zTop[j].pt3D.z, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (zTop[i].pt3D.z < zTop[j].pt3D.z)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
for (int j = i + 1; j < i_max; j++)
{
double dist = pow(zTop[i].pt3D.y - zTop[j].pt3D.y, 2) + pow(zTop[i].pt3D.z - zTop[j].pt3D.z, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (zTop[i].pt3D.z < zTop[j].pt3D.z)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
a_feature.jumpPos = zTop[i].pt3D;
a_feature.jumpPos2D = { lineIdx, zTop[i].nPointIdx };
localZMax.push_back(a_feature);
}
}
//极大值点(谷底)
for (int i = 0, i_max = (int)zBtm.size(); i < i_max; i++)
{
bool isPeak = true;
//向前搜索
for (int j = i - 1; j >= 0; j--)
{
double dist = pow(zBtm[i].pt3D.y - zBtm[j].pt3D.y, 2) + pow(zBtm[i].pt3D.z - zBtm[j].pt3D.z, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (zBtm[i].pt3D.z > zBtm[j].pt3D.z)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
for (int j = i + 1; j < i_max; j++)
{
double dist = pow(zBtm[i].pt3D.y - zBtm[j].pt3D.y, 2); pow(zBtm[i].pt3D.z - zBtm[j].pt3D.z, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (zBtm[i].pt3D.z > zBtm[j].pt3D.z)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_BOTTOM;
a_feature.jumpPos = zBtm[i].pt3D;
a_feature.jumpPos2D = { lineIdx, zBtm[i].nPointIdx };
localZMin.push_back(a_feature);
}
}
return;
}
/// <summary>
/// 获取扫描线中的斜坡,并记录斜坡中的最大的相邻点之间的跳变。
/// 同时记录Ending。当没有中间袋子,只有左右袋子时,会有两组Ending。Ending之间的最小间距设置为袋子宽度的1/4
/// </summary>
/// <param name="lineData">扫描线数据,兼容栅格数据格式</param>
/// <param name="dataSize">扫描线数据长度</param>
/// <param name="validSlopeH">有效斜坡长度。小于此长度的斜坡被视为噪声</param>
/// <param name="slopes">输出检测到的斜坡</param>
void _getSlopes(SVzNL3DPosition* lineData, int dataSize, const double validSlopeH, double minEndingGap,
std::vector< SSG_slope>& slopes, std::vector<SVzNLRange>& endings)
{
if (dataSize < 2)
return;
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
int nullPtNum = 0;
SVzNL3DPosition* pre_data = NULL;
SSG_slope a_slope = { 0, 0, 0, {0, {0,0,0}}, {0, {0,0,0}}, { 0, 0, 0, {0, {0,0,0}}, {0, {0,0,0}}} };
for (int i = 0; i < dataSize; i++)
{
if (i >= 317)
int kkk = 1;
SVzNL3DPosition* curr_data = &lineData[i];
if (curr_data->pt3D.z < 1e-4)
{
nullPtNum++;
if (i == dataSize - 1) //最后一个
{
if (sEdgePtIdx >= 0)
{
SVzNLRange a_range = { sEdgePtIdx , eEdgePtIdx };
endings.push_back(a_range);
}
}
continue;
}
if (NULL == pre_data)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
nullPtNum = 0;
continue;
}
if (i == dataSize - 1) //最后一个
{
if (sEdgePtIdx >= 0)
{
SVzNLRange a_range = { sEdgePtIdx , i };
endings.push_back(a_range);
}
}
else
{
if (nullPtNum > 0)
{
double y_diff = abs(curr_data->pt3D.y - pre_data->pt3D.y);
if ((y_diff > minEndingGap) && (sEdgePtIdx >= 0))
{
SVzNLRange a_range = { sEdgePtIdx , eEdgePtIdx };
endings.push_back(a_range);
sEdgePtIdx = i;
}
nullPtNum = 0;
}
}
eEdgePtIdx = i;
double z_diff = curr_data->pt3D.z - pre_data->pt3D.z;
switch (_state)
{
case 0: //初态
if (z_diff > 0) //下降
{
a_slope.startPtIdx = pre_i;
a_slope.endPtIdx = i;
a_slope.startPt = *pre_data;
a_slope.endPt = *curr_data;
a_slope.maxJump.sPtIdx = pre_i;
a_slope.maxJump.ePtIdx = i;
a_slope.maxJump.startPt = *pre_data;
a_slope.maxJump.endPt = *curr_data;
_state = 2;
}
else if (z_diff < 0) //上升
{
a_slope.startPtIdx = pre_i;
a_slope.endPtIdx = i;
a_slope.startPt = *pre_data;
a_slope.endPt = *curr_data;
a_slope.maxJump.sPtIdx = pre_i;
a_slope.maxJump.ePtIdx = i;
a_slope.maxJump.startPt = *pre_data;
a_slope.maxJump.endPt = *curr_data;
_state = 1;
}
break;
case 1: //上升
if (z_diff > 0) //下降
{
double height = a_slope.startPt.pt3D.z - a_slope.endPt.pt3D.z;
if (height >= validSlopeH)
{
a_slope.flag = 0;
a_slope.maxJump.flag = 0;
slopes.push_back(a_slope);
}
a_slope.startPtIdx = pre_i;
a_slope.endPtIdx = i;
a_slope.startPt = *pre_data;
a_slope.endPt = *curr_data;
a_slope.maxJump.sPtIdx = pre_i;
a_slope.maxJump.ePtIdx = i;
a_slope.maxJump.startPt = *pre_data;
a_slope.maxJump.endPt = *curr_data;
_state = 2;
}
else
{
a_slope.endPtIdx = i;
a_slope.endPt = *curr_data;
double maxDelta = a_slope.maxJump.startPt.pt3D.z - a_slope.maxJump.endPt.pt3D.z;
if (maxDelta < -z_diff)
{
a_slope.maxJump.sPtIdx = pre_i;
a_slope.maxJump.ePtIdx = i;
a_slope.maxJump.startPt = *pre_data;
a_slope.maxJump.endPt = *curr_data;
}
}
break;
case 2: //下降
if (z_diff < 0) // 上升
{
double height = a_slope.endPt.pt3D.z - a_slope.startPt.pt3D.z;
if (height >= validSlopeH)
{
a_slope.flag = 0;
a_slope.maxJump.flag = 0;
slopes.push_back(a_slope);
}
a_slope.startPtIdx = pre_i;
a_slope.endPtIdx = i;
a_slope.startPt = *pre_data;
a_slope.endPt = *curr_data;
a_slope.maxJump.sPtIdx = pre_i;
a_slope.maxJump.ePtIdx = i;
a_slope.maxJump.startPt = *pre_data;
a_slope.maxJump.endPt = *curr_data;
_state = 1;
}
else
{
a_slope.endPtIdx = i;
a_slope.endPt = *curr_data;
double maxDelta = a_slope.maxJump.endPt.pt3D.z - a_slope.maxJump.startPt.pt3D.z;
if (maxDelta < z_diff)
{
a_slope.maxJump.sPtIdx = pre_i;
a_slope.maxJump.ePtIdx = i;
a_slope.maxJump.startPt = *pre_data;
a_slope.maxJump.endPt = *curr_data;
}
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//最后一个
bool chkLast = true;
if (slopes.size() > 0)
{
if (slopes.back().startPtIdx == a_slope.startPtIdx)
chkLast = false;
}
if (true == chkLast)
{
if (_state == 1)//上升
{
double height = a_slope.startPt.pt3D.z - a_slope.endPt.pt3D.z;
if (height >= validSlopeH)
{
a_slope.flag = 0;
a_slope.maxJump.flag = 0;
slopes.push_back(a_slope);
}
}
else if (_state == 2)//下降
{
double height = a_slope.endPt.pt3D.z - a_slope.startPt.pt3D.z;
if (height >= validSlopeH)
{
a_slope.flag = 0;
a_slope.maxJump.flag = 0;
slopes.push_back(a_slope);
}
}
}
return;
}
SVzNL3DPosition _getZNearestPt(SVzNL3DPosition* lineData, int startIdx, int endIdx, double nominalZ)
{
SVzNL3DPosition rsltPt = { 0, {0,0,0} };
double diffZ_min = -1;
for (int i = startIdx; i <= endIdx; i++)
{
double diffZ = abs(nominalZ - lineData[i].pt3D.z);
if (diffZ_min < 0)
{
diffZ_min = diffZ;
rsltPt = lineData[i];
}
else
{
if (diffZ_min > diffZ)
{
diffZ_min = diffZ;
rsltPt = lineData[i];
}
}
}
return rsltPt;
}
bool _chkVFeature(SVzNL3DPosition* lineData, int dataSize, SSG_slope* down_slope, SSG_slope* up_slope, const SSG_VFeatureParam valleyPara)
{
bool isValidValley = false;
//寻找最低点
SVzNL3DPosition valleyPt;
//检测两个Slope是否在Z向有重叠
if ((down_slope->endPt.pt3D.z > up_slope->endPt.pt3D.z) &&
(up_slope->startPt.pt3D.z > down_slope->startPt.pt3D.z))
{
if (down_slope->endPt.pt3D.z >= up_slope->startPt.pt3D.z) //down_slope的EndPt是最低点
{
valleyPt = down_slope->endPt;//down_slope的EndPt是最低点
double chkZ_diff = abs(up_slope->startPt.pt3D.z - valleyPt.pt3D.z);
//在dwon_slope中寻找与up_slope的startPt的z高度最接近的点
SVzNL3DPosition counterPt = _getZNearestPt(lineData, down_slope->startPtIdx, down_slope->endPtIdx, up_slope->startPt.pt3D.z);
if (chkZ_diff < valleyPara.valleyMinH) //需要检测valleyMinH的宽度
{
SVzNL3DPosition minH_pt1 = _getZNearestPt(lineData, down_slope->startPtIdx, down_slope->endPtIdx, down_slope->endPt.pt3D.z - valleyPara.valleyMinH/2);
SVzNL3DPosition minH_pt2 = _getZNearestPt(lineData, up_slope->startPtIdx, up_slope->endPtIdx, down_slope->endPt.pt3D.z - valleyPara.valleyMinH/2);
double valleyW = abs(minH_pt1.pt3D.y - minH_pt2.pt3D.y);
if (valleyW < valleyPara.valleyMaxW)
isValidValley = true;
}
else//up_slope的最低点高于valleyMinH, 不需要检测valleyMinH的宽度
{
//检查Valley的宽高比
//在up_slope中寻找与down_slope的endPt的z高度最接近的点
double valleyW = abs(counterPt.pt3D.y - up_slope->startPt.pt3D.z);
if (valleyW < valleyPara.valleyMaxW)
isValidValley = true;
}
}
else //up_slope的startPt是最低点
{
valleyPt = up_slope->startPt;//up_slope的startPt是最低点
double chkZ_diff = abs(down_slope->endPt.pt3D.z - valleyPt.pt3D.z);
//在up_slope中寻找与down_slope的endPt的z高度最接近的点
SVzNL3DPosition counterPt = _getZNearestPt(lineData, up_slope->startPtIdx, up_slope->endPtIdx, down_slope->endPt.pt3D.z);
if (chkZ_diff < valleyPara.valleyMinH) //需要检测valleyMinH的宽度
{
SVzNL3DPosition minH_pt1 = _getZNearestPt(lineData, down_slope->startPtIdx, down_slope->endPtIdx, up_slope->startPt.pt3D.z - valleyPara.valleyMinH);
SVzNL3DPosition minH_pt2 = _getZNearestPt(lineData, up_slope->startPtIdx, up_slope->endPtIdx, up_slope->startPt.pt3D.z - valleyPara.valleyMinH);
double valleyW = abs(minH_pt1.pt3D.y - minH_pt2.pt3D.y);
if (valleyW < valleyPara.valleyMaxW)
isValidValley = true;
}
else//up_slope的最低点高于valleyMinH, 不需要检测valleyMinH的宽度
{
//检查Valley的宽高比
//在up_slope中寻找与down_slope的endPt的z高度最接近的点
double valleyW = abs(counterPt.pt3D.y - up_slope->startPt.pt3D.z);
if (valleyW < valleyPara.valleyMaxW)
isValidValley = true;
}
}
}
return isValidValley;
}
double _computerMaxSlopeK(SVzNL3DPosition* lineData, int dataSize, SSG_jump maxJump, double zWinLen)
{
//从maxJumPos向两端各延伸1cm,计算坡度
double z0 = maxJump.startPt.pt3D.z;
double z_start = z0;
double y_start = maxJump.startPt.pt3D.y;
double z1 = maxJump.endPt.pt3D.z;
double z_end = z1;
double y_end = maxJump.endPt.pt3D.y;
#if 1
if (maxJump.sPtIdx > 0)
{
y_start = lineData[maxJump.sPtIdx-1].pt3D.y;
z_start = lineData[maxJump.sPtIdx - 1].pt3D.z;
}
if (maxJump.ePtIdx < dataSize - 1)
{
y_end = lineData[maxJump.ePtIdx + 1].pt3D.y;
z_end = lineData[maxJump.ePtIdx + 1].pt3D.z;
}
#else
//在窗口内计算坡度以排除最大下降点本身z波动的影响
if (z0 < z1) //下降:对于L型斜坡,最大下降点应该在endPt端,再往下应该是平坦段,因此窗口要从startPt往前找。
{
double max_dz = 0;
for (int i = maxJump.sPtIdx; i >= 0; i--)
{
if (lineData[i].pt3D.z > 1e-4)
{
double dz = abs(z0 - lineData[i].pt3D.z);
if (max_dz < dz)
{
z_start = lineData[i].pt3D.z;
y_start = lineData[i].pt3D.y;
max_dz = dz;
}
if (dz > zWinLen)
break;
}
}
}
else //上升:对于L型斜坡,最大下降点应该在startPt端,再往前应该是平坦段,因此窗口要从endPt往后找。
{
double max_dz = 0;
for (int i = maxJump.ePtIdx; i < dataSize; i++)
{
if (lineData[i].pt3D.z > 1e-4)
{
double dz = abs(z0 - lineData[i].pt3D.z);
if (max_dz < dz)
{
z_end = lineData[i].pt3D.z;
y_end = lineData[i].pt3D.y;
max_dz = dz;
}
if (dz > zWinLen)
break;
}
}
}
#endif
return abs((z_start - z_end) / (y_start - y_end));
}
/// <summary>
/// 提取激光线上的L型跳变和V型跳变特征
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)提取扫描线中的Slope(超过门限高度的slope为有效slope),局部最小值,以及局部Z跳变最大的点
/// (2)取窗口宽度内的最小值为有效最小值
/// (3)V:在最小值点两个Win宽度一半内检查有没有对称的Slope。
/// (4)L:在Slope中检测Z跳变最大的点,判断角度。
///
/// </summary>
void sg_getLineLVFeature(SVzNL3DPosition* lineData, int dataSize, int lineIdx, const SSG_slopeParam slopeParam, const SSG_VFeatureParam valleyPara, SSG_lineFeature* line_features)
{
std::vector< SSG_slope> slopes;
//提取扫描线中的Slope(超过门限高度的slope为有效slope)
std::vector<SVzNLRange> endings; //22024.12.16: 注意:当没有中间袋子,只有左右袋子时,有两组Ending。
_getSlopes(lineData, dataSize, slopeParam.validSlopeH, slopeParam.minEndingGap, slopes, endings);
//首先检查L型边界:本身具有足够坡度,与下一段形成明显坡度差
//再检查是否可以形成V型特征
for (int i = 0, i_max = (int)slopes.size(); i < i_max; i++)
{
SSG_slope* a_slope = &slopes[i];
if (a_slope->flag > 0)
continue;
double curr_slopeH = a_slope->endPt.pt3D.z - a_slope->startPt.pt3D.z;
double curr_maxJumpH = a_slope->maxJump.endPt.pt3D.z - a_slope->maxJump.startPt.pt3D.z;
int curr_self_type = 0;
SSG_basicFeature1D self_slope;
memset(&self_slope, 0, sizeof(SSG_basicFeature1D));
if (abs(curr_maxJumpH) > slopeParam.minLJumpH)
{
if (curr_maxJumpH > 0) //下降
{
curr_self_type = LINE_FEATURE_L_JUMP_H2L;
self_slope.featureType = LINE_FEATURE_L_JUMP_H2L;
self_slope.jumpPos = a_slope->maxJump.startPt.pt3D;
self_slope.jumpPos2D = { lineIdx, a_slope->maxJump.sPtIdx };
}
else
{
curr_self_type = LINE_FEATURE_L_JUMP_L2H;
self_slope.featureType = LINE_FEATURE_L_JUMP_L2H;
self_slope.jumpPos = a_slope->maxJump.endPt.pt3D;
self_slope.jumpPos2D = { lineIdx,a_slope->maxJump.ePtIdx };
}
}
else if (abs(curr_slopeH) > slopeParam.minLJumpH)
{
//计算最大坡度
double maxSlopeK = _computerMaxSlopeK(lineData, dataSize, a_slope->maxJump, slopeParam.LSlopeZWin);
//abs(slopeH / (a_slope->endPt.pt3D.y - a_slope->startPt.pt3D.y));
if (maxSlopeK > 2) //对应63度,在此角度下,相邻点在1mm时,点的波动基本不会导致slope的反转
{
if (curr_slopeH > 0) //下降
{
bool nearEdgeFlag = false;
for (int m = 0; m < endings.size(); m++)
{
int gap_0 = abs(a_slope->endPtIdx - endings[m].nMin);
int gap_1 = abs(a_slope->endPtIdx - endings[m].nMax);
if ( (gap_0 <= 2) || (gap_1 <= 2))
{
nearEdgeFlag = true;
break;
}
}
if (false == nearEdgeFlag)
{
curr_self_type = LINE_FEATURE_L_SLOPE_H2L;
self_slope.featureType = LINE_FEATURE_L_SLOPE_H2L;
self_slope.jumpPos = a_slope->maxJump.endPt.pt3D; //a_slope->endPt.pt3D;
self_slope.jumpPos2D = { lineIdx, a_slope->maxJump.ePtIdx };//a_slope->endPtIdx };
}
}
else
{
bool nearEdgeFlag = false;
for (int m = 0; m < endings.size(); m++)
{
int gap_0 = abs(a_slope->startPtIdx - endings[m].nMin);
int gap_1 = abs(a_slope->startPtIdx - endings[m].nMax);
if ((gap_0 <= 2) || (gap_1 <= 2))
{
nearEdgeFlag = true;
break;
}
}
if (false == nearEdgeFlag)
{
curr_self_type = LINE_FEATURE_L_SLOPE_L2H;
self_slope.featureType = LINE_FEATURE_L_SLOPE_L2H;
self_slope.jumpPos = a_slope->maxJump.startPt.pt3D; //a_slope->startPt.pt3D;
self_slope.jumpPos2D = { lineIdx, a_slope->maxJump.sPtIdx }; //a_slope->startPtIdx };
}
}
}
}
//考虑下一个特征,迭代检查
int nxt_self_type = 0;
int complex_type = 0; //复合特征
SSG_basicFeature1D nxt_self_slope;
memset(&nxt_self_slope, 0, sizeof(SSG_basicFeature1D));
SSG_basicFeature1D a_valley;
memset(&a_valley, 0, sizeof(SSG_basicFeature1D));
if (i < i_max - 1) //检查能否与下一个配对
{
SSG_slope* nxt_slope = &slopes[i + 1];
double z_diff = a_slope->endPt.pt3D.z - a_slope->startPt.pt3D.z;
double nxt_z_diff = nxt_slope->endPt.pt3D.z - nxt_slope->startPt.pt3D.z;
double curr_dz = abs(z_diff);
double nxt_dz = abs(nxt_z_diff);
if ( (z_diff > 0) && (nxt_z_diff < 0) && (0 == nxt_slope->flag)) //下降slope
{
//检查是否可以配对
bool isValley = _chkVFeature(lineData, dataSize, a_slope, nxt_slope, valleyPara);
if (true == isValley) //&& (false == isLSlope))
{
a_slope->flag = 1;
nxt_slope->flag = 1;
complex_type = LINE_FEATURE_V_SLOPE;
a_valley.featureType = LINE_FEATURE_V_SLOPE;
if (a_slope->endPt.pt3D.z >= nxt_slope->startPt.pt3D.z)
{
a_valley.jumpPos = a_slope->endPt.pt3D;
a_valley.jumpPos2D = { lineIdx, a_slope->endPtIdx };
}
else
{
a_valley.jumpPos = nxt_slope->startPt.pt3D;
a_valley.jumpPos2D = { lineIdx, nxt_slope->startPtIdx };
}
}
}
if (complex_type > 0)//检查下一个slope的type
{
double nxt_slopeH = nxt_slope->endPt.pt3D.z - nxt_slope->startPt.pt3D.z;
double nxt_maxJumpH = nxt_slope->maxJump.endPt.pt3D.z - nxt_slope->maxJump.startPt.pt3D.z;
if (abs(nxt_maxJumpH) > slopeParam.minLJumpH)
{
if (nxt_maxJumpH > 0) //下降
{
nxt_self_type = LINE_FEATURE_L_JUMP_H2L;
nxt_self_slope.featureType = LINE_FEATURE_L_JUMP_H2L;
nxt_self_slope.jumpPos = nxt_slope->maxJump.startPt.pt3D;
nxt_self_slope.jumpPos2D = { lineIdx, nxt_slope->maxJump.sPtIdx };
}
else
{
nxt_self_type = LINE_FEATURE_L_JUMP_L2H;
nxt_self_slope.featureType = LINE_FEATURE_L_JUMP_L2H;
nxt_self_slope.jumpPos = nxt_slope->maxJump.endPt.pt3D;
nxt_self_slope.jumpPos2D = { lineIdx,nxt_slope->maxJump.ePtIdx };
}
}
else if (abs(nxt_slopeH) > slopeParam.minLJumpH)
{
//计算最大坡度
double nxt_maxSlopeK = _computerMaxSlopeK(lineData, dataSize, nxt_slope->maxJump, slopeParam.LSlopeZWin);
//abs(slopeH / (nxt_slope->endPt.pt3D.y - nxt_slope->startPt.pt3D.y));
if (nxt_maxSlopeK > 2) //对应63度,在此角度下,相邻点在1mm时,点的波动基本不会导致slope的反转
{
if (nxt_slopeH > 0) //下降
{
bool nearEdgeFlag = false;
for (int m = 0; m < endings.size(); m++)
{
int gap_0 = abs(nxt_slope->endPtIdx - endings[m].nMin);
int gap_1 = abs(nxt_slope->endPtIdx - endings[m].nMax);
if ((gap_0 <= 2) || (gap_1 <= 2))
{
nearEdgeFlag = true;
break;
}
}
if (false == nearEdgeFlag)
{
nxt_self_type = LINE_FEATURE_L_SLOPE_H2L;
nxt_self_slope.featureType = LINE_FEATURE_L_SLOPE_H2L;
nxt_self_slope.jumpPos = nxt_slope->maxJump.endPt.pt3D;//nxt_slope->endPt.pt3D;
nxt_self_slope.jumpPos2D = { lineIdx, nxt_slope->maxJump.ePtIdx };//nxt_slope->endPtIdx };
}
}
else
{
bool nearEdgeFlag = false;
for (int m = 0; m < endings.size(); m++)
{
int gap_0 = abs(nxt_slope->startPtIdx - endings[m].nMin);
int gap_1 = abs(nxt_slope->startPtIdx - endings[m].nMax);
if ((gap_0 <= 2) || (gap_1 <= 2))
{
nearEdgeFlag = true;
break;
}
}
if (false == nearEdgeFlag)
{
nxt_self_type = LINE_FEATURE_L_SLOPE_L2H;
nxt_self_slope.featureType = LINE_FEATURE_L_SLOPE_L2H;
nxt_self_slope.jumpPos = nxt_slope->maxJump.startPt.pt3D; //nxt_slope->startPt.pt3D;
nxt_self_slope.jumpPos2D = { lineIdx, nxt_slope->maxJump.sPtIdx }; //nxt_slope->startPtIdx };
}
}
}
}
}
}
//处理同时存在的情况
if (complex_type == 0)
{
if (curr_self_type > 0)
{
line_features->features.push_back(self_slope);
//标记
a_slope->flag = 1;
if ((LINE_FEATURE_L_JUMP_H2L == curr_self_type) || (LINE_FEATURE_L_JUMP_L2H == curr_self_type))
a_slope->maxJump.flag = 1;
}
if (nxt_self_type > 0)
{
line_features->features.push_back(nxt_self_slope);
//标记
slopes[i + 1].flag = 1;
if ((LINE_FEATURE_L_JUMP_H2L == nxt_self_type) || (LINE_FEATURE_L_JUMP_L2H == nxt_self_type))
slopes[i + 1].maxJump.flag = 1;
}
}
else //complex_type > 0
{
const double sameFeatureDistTh = 5.0;
const double slope_jump_th = 40.0;
SSG_slope* nxt_slope = &slopes[i + 1];
//标记
a_slope->flag = 1;
nxt_slope->flag = 1;
if ((curr_self_type == 0) && (nxt_self_type == 0))
{
line_features->features.push_back(a_valley);
}
else if ((curr_self_type > 0) && (nxt_self_type == 0))
{
SVzNL3DPoint valley_lowest = a_valley.jumpPos;
SVzNL3DPoint curr_slope_lowest;
if (LINE_FEATURE_L_JUMP_H2L == curr_self_type)
curr_slope_lowest = a_slope->maxJump.endPt.pt3D;
else if (LINE_FEATURE_L_JUMP_L2H == curr_self_type)
curr_slope_lowest = a_slope->maxJump.startPt.pt3D;
else
curr_slope_lowest = self_slope.jumpPos;
double valley_slope_dist_0 = abs(valley_lowest.y - curr_slope_lowest.y);
if (valley_slope_dist_0 > sameFeatureDistTh) //同时存在
{
line_features->features.push_back(self_slope);
line_features->features.push_back(a_valley);
}
else
{
//检查最高点高差。高差大于
double highest_diff = a_slope->startPt.pt3D.z - nxt_slope->endPt.pt3D.z;
if (abs(highest_diff) < slope_jump_th) //小于40mm(袋子高度的1/4),视为valley; 反之,视为Slope
{
if ((LINE_FEATURE_L_JUMP_H2L == curr_self_type) || (LINE_FEATURE_L_JUMP_L2H == curr_self_type))//JUMP优先
line_features->features.push_back(self_slope);
else
line_features->features.push_back(a_valley);
}
else
line_features->features.push_back(self_slope);
}
}
else if ((curr_self_type == 0) && (nxt_self_type > 0))
{
SVzNL3DPoint valley_lowest = a_valley.jumpPos;
SVzNL3DPoint nxt_slope_lowest;
if (LINE_FEATURE_L_JUMP_H2L == nxt_self_type)
nxt_slope_lowest = nxt_slope->maxJump.endPt.pt3D;
else if (LINE_FEATURE_L_JUMP_L2H == nxt_self_type)
nxt_slope_lowest = nxt_slope->maxJump.startPt.pt3D;
else
nxt_slope_lowest = nxt_self_slope.jumpPos;
double valley_slope_dist_1 = abs(valley_lowest.y - nxt_slope_lowest.y);
if (valley_slope_dist_1 > sameFeatureDistTh) //同时存在
{
line_features->features.push_back(a_valley);
line_features->features.push_back(nxt_self_slope);
}
else // ((curr_self_type > 0) && (nxt_self_type > 0))
{
//检查最高点高差。高差大于
double highest_diff = a_slope->startPt.pt3D.z - nxt_slope->endPt.pt3D.z;
if (abs(highest_diff) < slope_jump_th) //小于40mm(袋子高度的1/4),视为valley; 反之,视为Slope
{
if ((LINE_FEATURE_L_JUMP_H2L == nxt_self_type) || (LINE_FEATURE_L_JUMP_L2H == nxt_self_type)) //JUMP优先
line_features->features.push_back(nxt_self_slope);
else
line_features->features.push_back(a_valley);
}
else
line_features->features.push_back(nxt_self_slope);
}
}
else
{
SVzNL3DPoint valley_lowest = a_valley.jumpPos;
SVzNL3DPoint curr_slope_lowest;
if (LINE_FEATURE_L_JUMP_H2L == curr_self_type)
curr_slope_lowest = a_slope->maxJump.endPt.pt3D;
else if (LINE_FEATURE_L_JUMP_L2H == curr_self_type)
curr_slope_lowest = a_slope->maxJump.startPt.pt3D;
else
curr_slope_lowest = self_slope.jumpPos;
double valley_slope_dist_0 = abs(valley_lowest.y - curr_slope_lowest.y);
SVzNL3DPoint nxt_slope_lowest;
if (LINE_FEATURE_L_JUMP_H2L == nxt_self_type)
nxt_slope_lowest = nxt_slope->maxJump.endPt.pt3D;
else if (LINE_FEATURE_L_JUMP_L2H == nxt_self_type)
nxt_slope_lowest = nxt_slope->maxJump.startPt.pt3D;
else
nxt_slope_lowest = nxt_self_slope.jumpPos;
double valley_slope_dist_1= abs(valley_lowest.y - nxt_slope_lowest.y);
if ( (valley_slope_dist_0 > sameFeatureDistTh) && (valley_slope_dist_1 > sameFeatureDistTh)) //同时存在
{
line_features->features.push_back(self_slope);
line_features->features.push_back(a_valley);
line_features->features.push_back(nxt_self_slope);
}
else if ((valley_slope_dist_0 > sameFeatureDistTh) && (valley_slope_dist_1 <= sameFeatureDistTh))
{
line_features->features.push_back(self_slope);
if((LINE_FEATURE_L_JUMP_H2L == nxt_self_type) || (LINE_FEATURE_L_JUMP_L2H == nxt_self_type)) //JUMP优先
line_features->features.push_back(nxt_self_slope);
else
line_features->features.push_back(a_valley);
}
else if ((valley_slope_dist_0 <= sameFeatureDistTh) && (valley_slope_dist_1 > sameFeatureDistTh))
{
if((LINE_FEATURE_L_JUMP_H2L == curr_self_type) || (LINE_FEATURE_L_JUMP_L2H == curr_self_type))//JUMP优先
line_features->features.push_back(self_slope);
else
line_features->features.push_back(a_valley);
line_features->features.push_back(nxt_self_slope);
}
else //合并为一个
{
//检查最高点高差。高差大于
double highest_diff = a_slope->startPt.pt3D.z - nxt_slope->endPt.pt3D.z;
if (abs(highest_diff) < slope_jump_th) //小于40mm(袋子高度的1/4),视为valley; 反之,视为Slope
{
line_features->features.push_back(a_valley);
}
else
{
if (highest_diff > 0) //L2H slope
{
if (self_slope.jumpPos.z > nxt_self_slope.jumpPos.z)
{
self_slope.featureType = LINE_FEATURE_L_SLOPE_L2H;
line_features->features.push_back(self_slope);
}
else
{
nxt_self_slope.featureType = LINE_FEATURE_L_SLOPE_L2H;
line_features->features.push_back(nxt_self_slope);
}
}
else //H2L slope
{
if (self_slope.jumpPos.z > nxt_self_slope.jumpPos.z)
{
self_slope.featureType = LINE_FEATURE_L_SLOPE_H2L;
line_features->features.push_back(self_slope);
}
else
{
nxt_self_slope.featureType = LINE_FEATURE_L_SLOPE_H2L;
line_features->features.push_back(nxt_self_slope);
}
}
}
}
}
}
}
//检查连续的JUMP是否可能合并
int i = (int)line_features->features.size() - 2;
while(i >= 0)
{
if (i < ((int)line_features->features.size() - 1))
{
SSG_basicFeature1D* curr_feature = &line_features->features[i];
SSG_basicFeature1D* nxt_feature = &line_features->features[i + 1];
if ((curr_feature->featureType == nxt_feature->featureType) &&
((LINE_FEATURE_L_JUMP_H2L == curr_feature->featureType) ||
(LINE_FEATURE_L_JUMP_L2H == curr_feature->featureType)))
{
//计算两个的距离
double dist = abs(curr_feature->jumpPos.y - nxt_feature->jumpPos.y);
if (dist < 20) //距离小于20mm,合并
{
if (LINE_FEATURE_L_JUMP_L2H)
*curr_feature = *nxt_feature;
line_features->features.erase(line_features->features.begin() + i + 1);
}
}
}
i--;
}
//添加开始和结束边界
for (int i = 0; i < endings.size(); i++)
{
SSG_basicFeature1D an_edge;
memset(&an_edge, 0, sizeof(SSG_basicFeature1D));
an_edge.featureType = LINE_FEATURE_LINE_ENDING_0;
an_edge.jumpPos = lineData[endings[i].nMin].pt3D;
an_edge.jumpPos2D = { lineIdx, endings[i].nMin };
line_features->endings.push_back(an_edge);
//line_features.insert(line_features.begin(), an_edge); //头部
//尾部
an_edge.featureType = LINE_FEATURE_LINE_ENDING_1;
an_edge.jumpPos = lineData[endings[i].nMax].pt3D;
an_edge.jumpPos2D = { lineIdx, endings[i].nMax };
line_features->endings.push_back(an_edge);
}
return;
}
/// <summary>
/// 提取激光线上的拐点特征
/// seg端点:z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void sg_getLineCornerFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
SSG_lineFeature* line_features)
{
line_features->lineIdx = lineIdx;
if (lineIdx == 538)
int kkk = 1;
//去除零点
std::vector< SVzNL3DPosition> vldPts;
std::vector<SSG_RUN> segs;
//修改seg的定义。seg端点是两点间距离大于门限的点
int segStart = -1, segEnd = -1;
for (int i = 0; i < dataSize; i++)
{
if ( (lineIdx == 399)&&(i==568))
int kkk = 1;
SVzNL3DPosition a_pt = lineData[i];
a_pt.nPointIdx = i;
if (lineData[i].pt3D.z > 1e-4)
vldPts.push_back(a_pt);
//seg判断
if (lineData[i].pt3D.z > 1e-4)
{
if (segStart < 0)
segStart = i;
else //检查两点距离
{
SVzNL3DPosition pre_pt = lineData[i-1];
double diff_z = abs(a_pt.pt3D.z - pre_pt.pt3D.z);
if (diff_z > cornerPara.minEndingGap_z)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
segStart = i;
}
}
segEnd = i;
}
else
{
if (segStart >= 0)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
segStart = -1;
segEnd = -1;
}
}
}
//last
if (segStart >= 0)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
}
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if ((lineIdx == 399) && (i == 419))
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)
{
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)
{
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.jumpPos = vldPts[cornerPtIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[cornerPtIdx].nPointIdx };
line_features->features.push_back(a_feature);
}
}
//添加开始和结束边界
//检查seg是否需要合并;向后合并
for (int i = 0, i_max = (int)segs.size(); i < i_max - 1; i++)
{
SSG_RUN* nxt_seg = &segs[i + 1];
SSG_RUN* curr_seg = &segs[i];
int idx_1 = curr_seg->start + curr_seg->len - 1;
int idx_2 = nxt_seg->start;
double y_diff = abs(lineData[idx_1].pt3D.y - lineData[idx_2].pt3D.y);
double z_diff = abs(lineData[idx_1].pt3D.z - lineData[idx_2].pt3D.z);
if ( (y_diff < cornerPara.minEndingGap) && (z_diff < cornerPara.minEndingGap_z)) //合并
{
int idx_end = nxt_seg->start + nxt_seg->len - 1;
nxt_seg->start = curr_seg->start;
nxt_seg->len = idx_end - curr_seg->start + 1;
curr_seg->value = 0;
}
}
for (int i = 0, i_max = (int)segs.size(); i < i_max; i++)
{
if (0 == segs[i].value) //被合并
continue;
int idx_1 = segs[i].start;
int idx_2 = segs[i].start + segs[i].len - 1;
SSG_basicFeature1D an_edge;
memset(&an_edge, 0, sizeof(SSG_basicFeature1D));
an_edge.featureType = LINE_FEATURE_LINE_ENDING_0;
an_edge.jumpPos = lineData[idx_1].pt3D;
an_edge.jumpPos2D = { lineIdx, idx_1 };
line_features->endings.push_back(an_edge);
//line_features.insert(line_features.begin(), an_edge); //头部
//尾部
an_edge.featureType = LINE_FEATURE_LINE_ENDING_1;
an_edge.jumpPos = lineData[idx_2].pt3D;
an_edge.jumpPos2D = { lineIdx, idx_2 };
line_features->endings.push_back(an_edge);
}
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距离大于门限
/// 根据Seg进行corner提取
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void sg_getLineCornerFeature_BQ(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
double refSteppingZ,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
std::vector<SSG_basicFeature1D>& line_features)
{
//去除零点
std::vector< SVzNL3DPosition> vldPts;
std::vector< int> vldPtSegIdx;
std::vector<SSG_RUN> segs;
std::vector<int> backIndexing;
backIndexing.resize(dataSize);
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 (i == 370)
int kkk = 1;
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 < cornerPara.minEndingGap_z) && (y_diff < cornerPara.minEndingGap))
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
a_run.value = runIdx;
runIdx++;
segs.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
int bIdx = (int)vldPts.size();
backIndexing[i] = bIdx;
vldPts.push_back(lineData[i]);
vldPtSegIdx.push_back(runIdx);
pre_z = lineData[i].pt3D.z;
pre_y = lineData[i].pt3D.y;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
//将点置标志
for (int i = 0, i_max = (int)segs.size(); i < i_max; i++)
{
int idx1 = segs[i].start;
int idx2 = segs[i].start + segs[i].len - 1;
lineData[idx1].nPointIdx |= 0x100000;
lineData[idx2].nPointIdx |= 0x200000;
}
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if ((lineIdx == 399) && (i == 419))
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) && (vldPtSegIdx[i] == vldPtSegIdx[j]))
{
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) && (vldPtSegIdx[i] == vldPtSegIdx[j]))
{
post_i = j;
break;
}
}
//计算拐角
double tanValue_pre = 0;
if (pre_i >= 0)
tanValue_pre = (vldPts[i].pt3D.z - vldPts[pre_i].pt3D.z) / abs(vldPts[i].pt3D.y - vldPts[pre_i].pt3D.y);
double tanValue_post = 0;
if (post_i >= 0)
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;
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
{
corners[i].pntIdx = i;
corners[i].forwardAngle = forwardAngle;
corners[i].backwardAngle = backwardAngle;
if ((pre_i >= 0) && (post_i >= 0))
{
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;
}
else
{
corners[i].corner = 0;
corners[i].forwardDiffZ = 0;
corners[i].backwardDiffZ = 0;
}
}
}
//搜索拐角极值
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;
a_feature.featureType = 0;
if (abs(cornerPeakP[i].backwardAngle) < cornerPara.jumpCornerTh_1)
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
else if (abs(cornerPeakP[i].forwardAngle) < cornerPara.jumpCornerTh_1)
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
if (a_feature.featureType > 0)
{
int idx = vldPts[cornerPtIdx].nPointIdx;
lineData[idx].nPointIdx |= (a_feature.featureType << 16);
}
}
}
//添加Feature
for (int i = 0; i < dataSize; i++)
{
SVzNL3DPosition a_pt = lineData[i];
int flag = a_pt.nPointIdx >> 16;
int endFlag = flag >> 4;
flag &= 0x0f;
if (flag > 0)
{
double diffZ = abs(a_pt.pt3D.z - refSteppingZ);
if (diffZ < 10.0)
{
SSG_basicFeature1D a_feature;
memset(&a_feature, 0, sizeof(SSG_basicFeature1D));
a_feature.featureType = flag;
a_feature.jumpPos = a_pt.pt3D;
a_feature.jumpPos2D = { lineIdx, i };
line_features.push_back(a_feature);
}
}
else if (1 == endFlag)
{
int cornerIdx = backIndexing[i];
if (abs(corners[cornerIdx].forwardAngle) < cornerPara.jumpCornerTh_1)
{
//double diffZ = abs(a_pt.pt3D.z - refSteppingZ);
//if (diffZ < 10.0)
{
SSG_basicFeature1D a_feature;
memset(&a_feature, 0, sizeof(SSG_basicFeature1D));
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
a_feature.jumpPos = a_pt.pt3D;
a_feature.jumpPos2D = { lineIdx, i };
line_features.push_back(a_feature);
}
}
}
else if (2 == endFlag)
{
int cornerIdx = backIndexing[i];
if (abs(corners[cornerIdx].backwardAngle) < cornerPara.jumpCornerTh_1)
{
//double diffZ = abs(a_pt.pt3D.z - refSteppingZ);
//if (diffZ < 10.0)
{
SSG_basicFeature1D a_feature;
memset(&a_feature, 0, sizeof(SSG_basicFeature1D));
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
a_feature.jumpPos = a_pt.pt3D;
a_feature.jumpPos2D = { lineIdx, i };
line_features.push_back(a_feature);
}
}
}
}
return;
}
/// <summary>
/// 提取激光线上的拐点特征,地面端点附近的拐点视为合格拐点
/// 根据Seg进行corner提取
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void sg_getLineContourCornerFeature_groundMask_BQ(
std::vector<SVzNL3DPosition>& lineData,
std::vector<int> groundMask,
int lineIdx,
double contourWin, //ground边缘范围
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
std::vector<SSG_basicFeature1D>& line_features)
{
int dataSize = (int)lineData.size();
//去除零点
std::vector< SVzNL3DPosition> vldPts;
std::vector< int> vldPtSegIdx;
std::vector<SSG_RUN> segs;
std::vector<int> backIndexing;
backIndexing.resize(dataSize);
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 (i == 370)
int kkk = 1;
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 < cornerPara.minEndingGap_z) && (y_diff < cornerPara.minEndingGap))
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
a_run.value = runIdx;
runIdx++;
segs.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
int bIdx = (int)vldPts.size();
backIndexing[i] = bIdx;
vldPts.push_back(lineData[i]);
vldPtSegIdx.push_back(runIdx);
pre_z = lineData[i].pt3D.z;
pre_y = lineData[i].pt3D.y;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if ((lineIdx == 399) && (i == 419))
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) && (vldPtSegIdx[i] == vldPtSegIdx[j]))
{
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) && (vldPtSegIdx[i] == vldPtSegIdx[j]))
{
post_i = j;
break;
}
}
//计算拐角
double tanValue_pre = 0;
if (pre_i >= 0)
tanValue_pre = (vldPts[i].pt3D.z - vldPts[pre_i].pt3D.z) / abs(vldPts[i].pt3D.y - vldPts[pre_i].pt3D.y);
double tanValue_post = 0;
if (post_i >= 0)
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;
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
{
corners[i].pntIdx = i;
corners[i].forwardAngle = forwardAngle;
corners[i].backwardAngle = backwardAngle;
if ((pre_i >= 0) && (post_i >= 0))
{
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;
}
else
{
corners[i].corner = 0;
corners[i].forwardDiffZ = 0;
corners[i].backwardDiffZ = 0;
}
}
}
//搜索拐角极值
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) //下降
{
if (pre_data->corner > 0)
cornerPeakP.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (cornerDiff > 0) // 上升
{
if (pre_data->corner < 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)cornerPeakM.size(); i < i_max; i++)
{
if (abs(cornerPeakM[i].corner) < cornerPara.cornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPeakM[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPeakM[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 (abs(cornerPeakM[i].corner) < abs(cornerPeakM[j].corner))
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPeakM[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPeakM[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 (abs(cornerPeakM[i].corner) < abs(cornerPeakM[j].corner))
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
//判断是否是跳变到水平平面上
SSG_basicFeature1D a_feature;
memset(&a_feature, 0, sizeof(SSG_basicFeature1D));
a_feature.featureType = 0;
if (abs(cornerPeakM[i].backwardAngle) < cornerPara.jumpCornerTh_1)
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
else if (abs(cornerPeakM[i].forwardAngle) < cornerPara.jumpCornerTh_1)
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
if (a_feature.featureType > 0)
{
int idx = cornerPtIdx; //vldPts[cornerPtIdx].nPointIdx;
//Mask处理,寻找Ground边缘
bool vldContour = false;
int stepping = 1;
bool stop1 = false;
bool stop2 = false;
while (1)
{
if (false == stop1)
{
int idx_1 = idx + stepping;
if (idx_1 < (int)vldPts.size())
{
double dist = sqrt(pow(vldPts[idx].pt3D.x - vldPts[idx_1].pt3D.x, 2) +
pow(vldPts[idx].pt3D.y - vldPts[idx_1].pt3D.y, 2));
int raw_idx = vldPts[idx_1].nPointIdx;
if (dist > contourWin)
stop1 = true;
else if (1 == groundMask[raw_idx])
vldContour = true;
}
else
stop1 = true;
}
if (false == stop2)
{
int idx_2 = idx - stepping;
if (idx_2 >= 0)
{
double dist = sqrt(pow(vldPts[idx].pt3D.x - vldPts[idx_2].pt3D.x, 2) +
pow(vldPts[idx].pt3D.y - vldPts[idx_2].pt3D.y, 2));
int raw_idx = vldPts[idx_2].nPointIdx;
if (dist > contourWin)
stop2 = true;
else if (1 == groundMask[raw_idx])
vldContour = true;
}
else
stop2 = true;
}
if ((true == vldContour) || ((true == stop1) && (true == stop2)))
break;
stepping++;
}
if (true == vldContour)
{
int raw_idx = vldPts[idx].nPointIdx;
a_feature.jumpPos = lineData[raw_idx].pt3D;
a_feature.jumpPos2D = { lineIdx, raw_idx };
a_feature.featureValue = cornerPeakM[i].corner;
line_features.push_back(a_feature);
}
}
}
}
return;
}
//提取Gap特征。Gap特征:由两个负的Corner构成的区域
void sg_getLineGapFeature(
std::vector<SVzNL3DPosition>& lineData, //扫描线
int lineIdx, //当前扫描线序号
const SSG_cornerParam cornerPara,
const SVzNLRangeD gapParam,
std::vector<SSG_basicFeatureGap>& line_gaps)
{
int dataSize = (int)lineData.size();
//去除零点
std::vector< SVzNL3DPosition> vldPts;
//std::vector< int> vldPtSegIdx;
std::vector<SSG_RUN_EX> segs;
std::vector<int> backIndexing;
backIndexing.resize(dataSize);
int runIdx = 1;
SSG_RUN_EX a_run = { 0, -1, 0, false, false }; //startIdx, len, lastIdx
double pre_z = 0;
double pre_y = 0;
for (int i = 0; i < dataSize; i++)
{
lineData[i].nPointIdx = i; //重新编序号
if (lineData[i].pt3D.z > 1e-4)
{
int bIdx = (int)vldPts.size();
backIndexing[i] = bIdx;
vldPts.push_back(lineData[i]);
if (a_run.len < 0)
{
a_run.start_zRising = true;
a_run.start = bIdx;
a_run.len = 1;
a_run.end_zRising = true;
a_run.value = i;
}
else
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
if (z_diff < cornerPara.minEndingGap_z)
{
a_run.len = bIdx - a_run.start + 1;
a_run.value = i;
}
else
{
bool next_zRising;
if (pre_z > lineData[i].pt3D.z)
{
a_run.end_zRising = true;
next_zRising = false;
}
else
{
a_run.end_zRising = false;
next_zRising = true;
}
a_run.value = runIdx;
runIdx++;
segs.push_back(a_run);
a_run.start = bIdx;
a_run.start_zRising = next_zRising;
a_run.len = 1;
a_run.value = i;
a_run.end_zRising = true;
}
}
//vldPtSegIdx.push_back(runIdx);
pre_z = lineData[i].pt3D.z;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
//逐段进行
int segSize = (int)segs.size();
for (int segIdx = 0; segIdx < segSize; segIdx++)
{
int vPtIdxStart = segs[segIdx].start;
int vPtIdxEnd = vPtIdxStart + segs[segIdx].len - 1;
for (int i = vPtIdxStart; i <= vPtIdxEnd; i++)
{
//前向寻找
int pre_i = -1;
for (int j = i - 1; j >= vPtIdxStart; 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)
{
pre_i = j;
break;
}
}
//后向寻找
int post_i = -1;
for (int j = i + 1; j <= vPtIdxEnd; 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)
{
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) //下降
{
if (pre_data->corner > 0)
cornerPeakP.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (cornerDiff > 0) // 上升
{
if (pre_data->corner < 0)
cornerPeakM.push_back(*pre_data);
_state = 1;
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//合并
//极值比较,在尺度窗口下寻找局部极值点
std::vector< SSG_pntDirAngle> vldCornerPeaks;
double square_distTh = cornerPara.scale * cornerPara.scale; //2倍的cornerScale。
for (int i = 0, i_max = (int)cornerPeakM.size(); i < i_max; i++)
{
if (abs(cornerPeakM[i].corner) < cornerPara.cornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPeakM[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPeakM[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 (abs(cornerPeakM[i].corner) < abs(cornerPeakM[j].corner))
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPeakM[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPeakM[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 (abs(cornerPeakM[i].corner) < abs(cornerPeakM[j].corner))
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
vldCornerPeaks.push_back(cornerPeakM[i]);
}
}
//配对
for (int i = 0, i_max = (int)vldCornerPeaks.size(); i < i_max-1; i++)
{
SSG_pntDirAngle& corner_1 = vldCornerPeaks[i];
int ptIdx_1 = corner_1.pntIdx;
if (corner_1.type < 0)
continue;
SSG_pntDirAngle& corner_2 = vldCornerPeaks[i + 1];
int ptIdx_2 = corner_2.pntIdx;
double dist = sqrt(pow(vldPts[ptIdx_1].pt3D.y - vldPts[ptIdx_2].pt3D.y, 2) +pow(vldPts[ptIdx_1].pt3D.x - vldPts[ptIdx_2].pt3D.x, 2));
if ((dist > gapParam.min) && (dist < gapParam.max))
{
SSG_basicFeatureGap a_gap;
memset(&a_gap, 0, sizeof(SSG_basicFeatureGap));
a_gap.gapPt_0 = vldPts[ptIdx_1];
a_gap.gapPt_1 = vldPts[ptIdx_2];
a_gap.lineIdx = lineIdx;
line_gaps.push_back(a_gap);
corner_2.type = -1;
}
}
return;
}
/// <summary>
/// 提取激光线上的拐点特征,水平端点视为Corner
/// seg端点:z距离或y距离大于门限
/// 根据Seg进行corner提取
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void sg_getLineContour_BQ(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
double refSteppingZ,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
std::vector<SSG_basicFeature1D>& line_features)
{
//去除零点
std::vector< SVzNL3DPosition> vldPts;
std::vector< int> vldPtSegIdx;
std::vector<SSG_RUN> segs;
std::vector<int> backIndexing;
backIndexing.resize(dataSize);
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 (i == 370)
int kkk = 1;
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 < cornerPara.minEndingGap_z) && (y_diff < cornerPara.minEndingGap))
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
a_run.value = runIdx;
runIdx++;
segs.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
int bIdx = (int)vldPts.size();
backIndexing[i] = bIdx;
vldPts.push_back(lineData[i]);
vldPtSegIdx.push_back(runIdx);
pre_z = lineData[i].pt3D.z;
pre_y = lineData[i].pt3D.y;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
//将点置标志
for (int i = 0, i_max = (int)segs.size(); i < i_max; i++)
{
int idx1 = segs[i].start;
int idx2 = segs[i].start + segs[i].len - 1;
lineData[idx1].nPointIdx |= 0x100000;
lineData[idx2].nPointIdx |= 0x200000;
}
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if ((lineIdx == 399) && (i == 419))
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) && (vldPtSegIdx[i] == vldPtSegIdx[j]))
{
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) && (vldPtSegIdx[i] == vldPtSegIdx[j]))
{
post_i = j;
break;
}
}
//计算拐角
double tanValue_pre = 0;
if (pre_i >= 0)
tanValue_pre = (vldPts[i].pt3D.z - vldPts[pre_i].pt3D.z) / abs(vldPts[i].pt3D.y - vldPts[pre_i].pt3D.y);
double tanValue_post = 0;
if (post_i >= 0)
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;
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
{
corners[i].pntIdx = i;
corners[i].forwardAngle = forwardAngle;
corners[i].backwardAngle = backwardAngle;
if ((pre_i >= 0) && (post_i >= 0))
{
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;
}
else
{
corners[i].corner = 0;
corners[i].forwardDiffZ = 0;
corners[i].backwardDiffZ = 0;
}
}
}
//搜索拐角极值
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;
a_feature.featureType = 0;
if (abs(cornerPeakP[i].backwardAngle) < cornerPara.jumpCornerTh_1)
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
else if (abs(cornerPeakP[i].forwardAngle) < cornerPara.jumpCornerTh_1)
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
if (a_feature.featureType > 0)
{
int idx = vldPts[cornerPtIdx].nPointIdx;
lineData[idx].nPointIdx |= (a_feature.featureType << 16);
}
}
}
//添加Feature
for (int i = 0; i < dataSize; i++)
{
SVzNL3DPosition a_pt = lineData[i];
int flag = a_pt.nPointIdx >> 16;
int endFlag = flag >> 4;
flag &= 0x0f;
if (flag > 0)
{
double diffZ = abs(a_pt.pt3D.z - refSteppingZ);
if (diffZ < 10.0)
{
SSG_basicFeature1D a_feature;
memset(&a_feature, 0, sizeof(SSG_basicFeature1D));
a_feature.featureType = flag;
a_feature.jumpPos = a_pt.pt3D;
a_feature.jumpPos2D = { lineIdx, i };
line_features.push_back(a_feature);
}
}
else if (1 == endFlag)
{
int cornerIdx = backIndexing[i];
if (abs(corners[cornerIdx].forwardAngle) < cornerPara.jumpCornerTh_1)
{
//double diffZ = abs(a_pt.pt3D.z - refSteppingZ);
//if (diffZ < 10.0)
{
SSG_basicFeature1D a_feature;
memset(&a_feature, 0, sizeof(SSG_basicFeature1D));
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
a_feature.jumpPos = a_pt.pt3D;
a_feature.jumpPos2D = { lineIdx, i };
line_features.push_back(a_feature);
}
}
}
else if (2 == endFlag)
{
int cornerIdx = backIndexing[i];
if (abs(corners[cornerIdx].backwardAngle) < cornerPara.jumpCornerTh_1)
{
//double diffZ = abs(a_pt.pt3D.z - refSteppingZ);
//if (diffZ < 10.0)
{
SSG_basicFeature1D a_feature;
memset(&a_feature, 0, sizeof(SSG_basicFeature1D));
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
a_feature.jumpPos = a_pt.pt3D;
a_feature.jumpPos2D = { lineIdx, i };
line_features.push_back(a_feature);
}
}
}
}
return;
}
/// <summary>
/// 提取激光线上的特征:跳变、低于z阈值、V及L型,用于粒径检测(PSM)
/// seg端点:z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void wd_getLineCornerFeature_PSM(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double groundZ, //地平面Z,大于grondZ的点视为目标边界
const SSG_cornerParam cornerPara,
SSG_lineFeature* line_features)
{
line_features->lineIdx = lineIdx;
if (lineIdx == 538)
int kkk = 1;
//对groundZ进行截断
for (int i = 0; i < dataSize; i++)
{
if (lineData[i].pt3D.z > groundZ)
lineData[i].pt3D.z = 0;
}
//去除零点
std::vector< SVzNL3DPosition> vldPts;
std::vector< int> vldPtSegIdx;
std::vector<SSG_RUN_EX> segs;
std::vector<int> backIndexing;
backIndexing.resize(dataSize);
int runIdx = 1;
SSG_RUN_EX a_run = { 0, -1, 0, false, false }; //startIdx, len, lastIdx
double pre_z = 0;
double pre_y = 0;
for (int i = 0; i < dataSize; i++)
{
if (i == 370)
int kkk = 1;
if (lineData[i].pt3D.z > 1e-4)
{
if (a_run.len < 0)
{
a_run.start_zRising = true;
a_run.start = i;
a_run.len = 1;
a_run.end_zRising = true;
a_run.value = i;
}
else
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
if (z_diff < cornerPara.minEndingGap_z)
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
bool next_zRising;
if (pre_z > lineData[i].pt3D.z)
{
a_run.end_zRising = true;
next_zRising = false;
}
else
{
a_run.end_zRising = false;
next_zRising = true;
}
a_run.value = runIdx;
runIdx++;
segs.push_back(a_run);
a_run.start = i;
a_run.start_zRising = next_zRising;
a_run.len = 1;
a_run.value = i;
a_run.end_zRising = true;
}
}
int bIdx = (int)vldPts.size();
backIndexing[i] = bIdx;
vldPts.push_back(lineData[i]);
vldPtSegIdx.push_back(runIdx);
pre_z = lineData[i].pt3D.z;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
//将点置标志
for (int i = 0, i_max = (int)segs.size(); i < i_max; i++)
{
int idx1 = segs[i].start;
int idx2 = segs[i].start + segs[i].len - 1;
lineData[idx1].nPointIdx |= 0x100000;
lineData[idx2].nPointIdx |= 0x200000;
}
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if ((lineIdx == 399) && (i == 419))
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)
{
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)
{
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.jumpPos = vldPts[cornerPtIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[cornerPtIdx].nPointIdx };
line_features->features.push_back(a_feature);
}
}
//添加开始和结束边界
//检查seg是否需要合并;向后合并
for (int i = 0, i_max = (int)segs.size(); i < i_max - 1; i++)
{
SSG_RUN_EX* nxt_seg = &segs[i + 1];
SSG_RUN_EX* curr_seg = &segs[i];
int idx_1 = curr_seg->start + curr_seg->len - 1;
int idx_2 = nxt_seg->start;
double y_diff = abs(lineData[idx_1].pt3D.y - lineData[idx_2].pt3D.y);
double z_diff = abs(lineData[idx_1].pt3D.z - lineData[idx_2].pt3D.z);
if ((y_diff < cornerPara.minEndingGap) && (z_diff < cornerPara.minEndingGap_z)) //合并
{
int idx_end = nxt_seg->start + nxt_seg->len - 1;
nxt_seg->start = curr_seg->start;
nxt_seg->len = idx_end - curr_seg->start + 1;
curr_seg->value = 0;
}
}
for (int i = 0, i_max = (int)segs.size(); i < i_max; i++)
{
if (0 == segs[i].value) //被合并
continue;
int idx_1 = segs[i].start;
int idx_2 = segs[i].start + segs[i].len - 1;
SSG_basicFeature1D an_edge;
memset(&an_edge, 0, sizeof(SSG_basicFeature1D));
an_edge.featureType = LINE_FEATURE_LINE_ENDING_0;
an_edge.jumpPos = lineData[idx_1].pt3D;
an_edge.jumpPos2D = { lineIdx, idx_1 };
line_features->endings.push_back(an_edge);
//line_features.insert(line_features.begin(), an_edge); //头部
//尾部
an_edge.featureType = LINE_FEATURE_LINE_ENDING_1;
an_edge.jumpPos = lineData[idx_2].pt3D;
an_edge.jumpPos2D = { lineIdx, idx_2 };
line_features->endings.push_back(an_edge);
}
return;
}
bool compareByPtIdx(const SSG_basicFeature1D& a, const SSG_basicFeature1D& b) {
return a.jumpPos2D.y < b.jumpPos2D.y;
}
//根据距离连续性分段
void wd_lineDataSegment_dist(
std::vector< SWD3DPointPostion>& lineData,
std::vector<SSG_RUN>& segs,
const double maxDistTh,
const int minSegSize
)
{
int dataSize = (int)lineData.size();
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].point.z > 1e-4)
{
if (a_run.len < 0)
{
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
else
{
double dist = sqrt(pow(lineData[i].point.y - pre_y, 2) + pow(lineData[i].point.z - pre_z, 2));
if (dist < maxDistTh)
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
a_run.value = runIdx;
runIdx++;
if(a_run.len >= minSegSize)
segs.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
pre_y = lineData[i].point.y;
pre_z = lineData[i].point.z;
}
}
if (a_run.len > 0)
{
if(a_run.len >= minSegSize)
segs.push_back(a_run);
}
}
//根据距离连续性分段
void wd_lineDataSegment_dist_2(
std::vector< SVzNL3DPosition>& lineData,
std::vector<SSG_RUN>& segs,
const double maxDistTh,
const int minSegSize
)
{
int dataSize = (int)lineData.size();
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 dist = sqrt(pow(lineData[i].pt3D.y - pre_y, 2) + pow(lineData[i].pt3D.z - pre_z, 2));
if (dist < maxDistTh)
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
a_run.value = runIdx;
runIdx++;
if (a_run.len >= minSegSize)
segs.push_back(a_run);
a_run.start = i;
a_run.len = 1;
a_run.value = i;
}
}
pre_y = lineData[i].pt3D.y;
pre_z = lineData[i].pt3D.z;
}
}
if (a_run.len > 0)
{
if (a_run.len >= minSegSize)
segs.push_back(a_run);
}
}
//根据Z连续性分段
void wd_lineDataSegment_zDist(
std::vector< SVzNL3DPosition>& lineData,
std::vector< SVzNL3DPosition>& vldPts,
std::vector<SSG_RUN_EX>& segs,
std::vector<int>& backIndexing,
const SSG_cornerParam cornerPara
)
{
int dataSize = (int)lineData.size();
int runIdx = 1;
SSG_RUN_EX a_run = { 0, -1, 0, false, false }; //startIdx, len, lastIdx
double pre_z = 0;
double pre_y = 0;
for (int i = 0; i < dataSize; i++)
{
lineData[i].nPointIdx = i; //重新编序号
if (lineData[i].pt3D.z > 1e-4)
{
int bIdx = (int)vldPts.size();
backIndexing[i] = bIdx;
vldPts.push_back(lineData[i]);
if (a_run.len < 0)
{
a_run.start_zRising = true;
a_run.start = bIdx;
a_run.len = 1;
a_run.end_zRising = true;
a_run.value = i;
}
else
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
if (z_diff < cornerPara.minEndingGap_z)
{
a_run.len = bIdx - a_run.start + 1;
a_run.value = i;
}
else
{
bool next_zRising;
if (pre_z > lineData[i].pt3D.z)
{
a_run.end_zRising = true;
next_zRising = false;
}
else
{
a_run.end_zRising = false;
next_zRising = true;
}
a_run.value = runIdx;
runIdx++;
segs.push_back(a_run);
a_run.start = bIdx;
a_run.start_zRising = next_zRising;
a_run.len = 1;
a_run.value = i;
a_run.end_zRising = true;
}
}
//vldPtSegIdx.push_back(runIdx);
pre_z = lineData[i].pt3D.z;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
}
//逐段计算方向角
void _computeDirAngle_perSeg(
std::vector< SVzNL3DPosition>& vldPts,
std::vector<SSG_RUN_EX>& segs,
const SSG_cornerParam cornerPara,
std::vector< SSG_pntDirAngle>& ptDirAngles
)
{
ptDirAngles.resize(vldPts.size());
for (int i = 0; i < (int)vldPts.size(); i++)
{
ptDirAngles[i].pntIdx = -1;
ptDirAngles[i].type = -1;
ptDirAngles[i].forwardPntIdx = 0;
ptDirAngles[i].backwardPntIdx = 0;
ptDirAngles[i].forwardAngle = 0;
ptDirAngles[i].backwardAngle = 0;
ptDirAngles[i].corner = 0;
ptDirAngles[i].forwardDiffZ = 0;
ptDirAngles[i].backwardDiffZ = 0;
}
//逐段进行
int segSize = (int)segs.size();
for (int segIdx = 0; segIdx < segSize; segIdx++)
{
int vPtIdxStart = segs[segIdx].start;
int vPtIdxEnd = vPtIdxStart + segs[segIdx].len - 1;
for (int i = vPtIdxStart; i <= vPtIdxEnd; i++)
{
//前向寻找
int pre_i = -1;
for (int j = i - 1; j >= vPtIdxStart; 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)
{
pre_i = j;
break;
}
}
//后向寻找
int post_i = -1;
for (int j = i + 1; j <= vPtIdxEnd; 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)
{
post_i = j;
break;
}
}
//计算拐角
if ((pre_i >= 0) && (post_i >= 0))
{
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;
ptDirAngles[i].pntIdx = i;
ptDirAngles[i].type = segIdx;
ptDirAngles[i].forwardPntIdx = post_i;
ptDirAngles[i].backwardPntIdx = pre_i;
ptDirAngles[i].forwardAngle = forwardAngle;
ptDirAngles[i].backwardAngle = backwardAngle;
ptDirAngles[i].corner = -(forwardAngle - backwardAngle); //图像坐标系与正常坐标系y方向相反,所以有“-”号
ptDirAngles[i].forwardDiffZ = vldPts[post_i].pt3D.z - vldPts[i].pt3D.z;
ptDirAngles[i].backwardDiffZ = vldPts[i].pt3D.z - vldPts[pre_i].pt3D.z;
}
}
}
}
//逐段计算方向角
void _computeDirAngle_perSeg_2(
std::vector< SVzNL3DPosition>& vldPts,
std::vector<SSG_RUN>& segs,
const SSG_cornerParam cornerPara,
std::vector< SSG_pntDirAngle>& ptDirAngles
)
{
ptDirAngles.resize(vldPts.size());
for (int i = 0; i < (int)vldPts.size(); i++)
{
ptDirAngles[i].pntIdx = -1;
ptDirAngles[i].type = -1;
ptDirAngles[i].forwardPntIdx = 0;
ptDirAngles[i].backwardPntIdx = 0;
ptDirAngles[i].forwardAngle = 0;
ptDirAngles[i].backwardAngle = 0;
ptDirAngles[i].corner = 0;
ptDirAngles[i].forwardDiffZ = 0;
ptDirAngles[i].backwardDiffZ = 0;
}
//逐段进行
int segSize = (int)segs.size();
for (int segIdx = 0; segIdx < segSize; segIdx++)
{
int vPtIdxStart = segs[segIdx].start;
int vPtIdxEnd = vPtIdxStart + segs[segIdx].len - 1;
for (int i = vPtIdxStart; i <= vPtIdxEnd; i++)
{
//前向寻找
int pre_i = -1;
for (int j = i - 1; j >= vPtIdxStart; 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)
{
pre_i = j;
break;
}
}
//后向寻找
int post_i = -1;
for (int j = i + 1; j <= vPtIdxEnd; 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)
{
post_i = j;
break;
}
}
//计算拐角
if ((pre_i >= 0) && (post_i >= 0))
{
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;
ptDirAngles[i].pntIdx = i;
ptDirAngles[i].type = segIdx;
ptDirAngles[i].forwardPntIdx = post_i;
ptDirAngles[i].backwardPntIdx = pre_i;
ptDirAngles[i].forwardAngle = forwardAngle;
ptDirAngles[i].backwardAngle = backwardAngle;
ptDirAngles[i].corner = -(forwardAngle - backwardAngle); //图像坐标系与正常坐标系y方向相反,所以有“-”号
ptDirAngles[i].forwardDiffZ = vldPts[post_i].pt3D.z - vldPts[i].pt3D.z;
ptDirAngles[i].backwardDiffZ = vldPts[i].pt3D.z - vldPts[pre_i].pt3D.z;
}
}
}
}
//计算方向角(不分段)
void wd_computeDirAngle_wholeLine(
std::vector< SVzNL3DPosition>& line_data,
const SSG_cornerParam cornerPara,
std::vector< SSG_pntDirAngle>& ptDirAngles
)
{
ptDirAngles.resize(line_data.size());
int dataSize = (int)line_data.size();
for (int i = 0; i < dataSize; i++)
{
if (i == 930)
int kkk = 1;
if (line_data[i].pt3D.z < 1e-4)
{
ptDirAngles[i].pntIdx = i;
ptDirAngles[i].forwardPntIdx = 0;
ptDirAngles[i].backwardPntIdx = 0;
ptDirAngles[i].type = -1;
ptDirAngles[i].forwardAngle = 0;
ptDirAngles[i].backwardAngle = 0;
ptDirAngles[i].corner = 0;
ptDirAngles[i].curr_z = 0;
ptDirAngles[i].forwardDiffZ = 0;
ptDirAngles[i].backwardDiffZ = 0;
continue;
}
//前向寻找
int pre_i = -1;
double pre_dist = 0;
for (int j = i - 1; j >= 0; j--)
{
if (line_data[j].pt3D.z < 1e-4)
continue;
pre_dist = sqrt(pow(line_data[i].pt3D.y - line_data[j].pt3D.y, 2) +
pow(line_data[i].pt3D.z - line_data[j].pt3D.z, 2));
if (pre_dist >= cornerPara.scale)
{
pre_i = j;
break;
}
}
//后向寻找
int post_i = -1;
double post_dist = 0;
for (int j = i + 1; j < dataSize; j++)
{
if (line_data[j].pt3D.z < 1e-4)
continue;
post_dist = sqrt(pow(line_data[i].pt3D.y - line_data[j].pt3D.y, 2) +
pow(line_data[i].pt3D.z - line_data[j].pt3D.z, 2));
if (post_dist >= cornerPara.scale)
{
post_i = j;
break;
}
}
//计算拐角
if ((pre_i < 0) || (post_i < 0))
{
ptDirAngles[i].pntIdx = i;
ptDirAngles[i].type = -1;
ptDirAngles[i].forwardAngle = 0;
ptDirAngles[i].backwardAngle = 0;
ptDirAngles[i].forwardPntIdx = 0;
ptDirAngles[i].backwardPntIdx = 0;
ptDirAngles[i].corner = 0;
ptDirAngles[i].curr_z = 0;
ptDirAngles[i].forwardDiffZ = 0;
ptDirAngles[i].backwardDiffZ = 0;
}
else
{
double tanValue_pre = (line_data[i].pt3D.z - line_data[pre_i].pt3D.z) / abs(line_data[i].pt3D.y - line_data[pre_i].pt3D.y);
double tanValue_post = (line_data[post_i].pt3D.z - line_data[i].pt3D.z) / abs(line_data[post_i].pt3D.y - line_data[i].pt3D.y);
double forwardAngle = atan(tanValue_post) * 180.0 / PI;
double backwardAngle = atan(tanValue_pre) * 180.0 / PI;
ptDirAngles[i].pntIdx = i;
ptDirAngles[i].forwardPntIdx = post_i;
ptDirAngles[i].backwardPntIdx = pre_i;
ptDirAngles[i].type = 0;
ptDirAngles[i].forwardAngle = forwardAngle;
ptDirAngles[i].backwardAngle = backwardAngle;
ptDirAngles[i].corner = -(forwardAngle - backwardAngle); //图像坐标系与正常坐标系y方向相反,所以有“-”号
ptDirAngles[i].forwardDiffZ = line_data[post_i].pt3D.z - line_data[i].pt3D.z;
ptDirAngles[i].backwardDiffZ = line_data[i].pt3D.z - line_data[pre_i].pt3D.z;
ptDirAngles[i].curr_z = line_data[i].pt3D.z;
}
}
}
/// <summary>
/// 提取激光线上的圆环的上半段弧。宽度由圆环的宽度确定
/// seg端点:z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void wd_getRingArcFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_cornerParam cornerPara,
SVzNLRangeD ringArcWidth, //环宽度
std::vector<SWD_segFeature>& line_ringArcs //环
)
{
int dataSize = (int)lineData.size();
//去除零点
std::vector< SVzNL3DPosition> vldPts;
//std::vector< int> vldPtSegIdx;
std::vector<SSG_RUN_EX> segs;
std::vector<int> backIndexing;
backIndexing.resize(dataSize);
//根据z连续性分段
wd_lineDataSegment_zDist(lineData, vldPts, segs, backIndexing, cornerPara);
//计算前向角和后向角
std::vector< SSG_pntDirAngle> ptDirAngles;
_computeDirAngle_perSeg(vldPts, segs, cornerPara, ptDirAngles);
//逐段寻找连续的负corner段。Arc上的每一个点都是负的corner。
int vldPtSize = (int)vldPts.size();
int startIdx = -1;
int endIdx = -1;
double startAngle = 0;
double endAngle = 0;
int segSize = (int)segs.size();
for (int segIdx = 0; segIdx < segSize; segIdx++)
{
int vPtIdxStart = segs[segIdx].start;
int vPtIdxEnd = vPtIdxStart + segs[segIdx].len - 1;
for (int i = vPtIdxStart; i <= vPtIdxEnd; i++)
{
if (ptDirAngles[i].corner < 0)
{
if (startIdx < 0)
{
startIdx = i;
endIdx = i;
startAngle = ptDirAngles[i].backwardAngle;
endAngle = ptDirAngles[i].forwardAngle;
}
else
{
endIdx = i;
endAngle = ptDirAngles[i].forwardAngle;
if (i == dataSize - 1)
{
double totalCorner = abs(endAngle - startAngle);
double len = sqrt(pow(vldPts[startIdx].pt3D.y - vldPts[endIdx].pt3D.y, 2) +
pow(vldPts[startIdx].pt3D.z - vldPts[endIdx].pt3D.z, 2));
if ((totalCorner > cornerPara.cornerTh) && (len >= ringArcWidth.min) &&
(len <= ringArcWidth.max))
{
int preIdx = startIdx - 1;
if (preIdx < vPtIdxStart)
preIdx = vPtIdxStart;
int postIdx = endIdx + 1;
if (postIdx > vPtIdxEnd)
postIdx = vPtIdxEnd;
//判断整个seg是不是Arc
SWD_segFeature a_ringFeautre;
memset(&a_ringFeautre, 0, sizeof(SWD_segFeature));
a_ringFeautre.lineIdx = lineIdx;
if ((ptDirAngles[preIdx].pntIdx < 0) && (ptDirAngles[postIdx].pntIdx < 0))
{
a_ringFeautre.startPt = vldPts[vPtIdxStart].pt3D;
a_ringFeautre.endPt = vldPts[vPtIdxEnd].pt3D;
a_ringFeautre.startPtIdx = vldPts[vPtIdxStart].nPointIdx;
a_ringFeautre.endPtIdx = vldPts[vPtIdxEnd].nPointIdx;
}
else
{
a_ringFeautre.startPt = vldPts[startIdx].pt3D;
a_ringFeautre.endPt = vldPts[endIdx].pt3D;
a_ringFeautre.startPtIdx = vldPts[startIdx].nPointIdx;
a_ringFeautre.endPtIdx = vldPts[endIdx].nPointIdx;
}
a_ringFeautre.featureValue = totalCorner;
line_ringArcs.push_back(a_ringFeautre);
}
}
}
}
else
{
if (startIdx >= 0)
{
double totalCorner = abs(endAngle - startAngle);
double len = sqrt(pow(vldPts[startIdx].pt3D.y - vldPts[endIdx].pt3D.y, 2) +
pow(vldPts[startIdx].pt3D.z - vldPts[endIdx].pt3D.z, 2));
if ((totalCorner > cornerPara.cornerTh) && (len >= ringArcWidth.min) &&
(len <= ringArcWidth.max))
{
int preIdx = startIdx - 1;
if (preIdx < vPtIdxStart)
preIdx = vPtIdxStart;
int postIdx = endIdx + 1;
if (postIdx > vPtIdxEnd)
postIdx = vPtIdxEnd;
//判断整个seg是不是Arc
SWD_segFeature a_ringFeautre;
memset(&a_ringFeautre, 0, sizeof(SWD_segFeature));
a_ringFeautre.lineIdx = lineIdx;
if ((ptDirAngles[preIdx].pntIdx < 0) && (ptDirAngles[postIdx].pntIdx < 0))
{
a_ringFeautre.startPt = vldPts[vPtIdxStart].pt3D;
a_ringFeautre.endPt = vldPts[vPtIdxEnd].pt3D;
a_ringFeautre.startPtIdx = vldPts[vPtIdxStart].nPointIdx;
a_ringFeautre.endPtIdx = vldPts[vPtIdxEnd].nPointIdx;
}
else
{
a_ringFeautre.startPt = vldPts[startIdx].pt3D;
a_ringFeautre.endPt = vldPts[endIdx].pt3D;
a_ringFeautre.startPtIdx = vldPts[startIdx].nPointIdx;
a_ringFeautre.endPtIdx = vldPts[endIdx].nPointIdx;
}
a_ringFeautre.featureValue = totalCorner;
line_ringArcs.push_back(a_ringFeautre);
}
}
startIdx = -1;
endIdx = -1;
startAngle = 0;
endAngle = 0;
}
}
}
return;
}
//此处考虑到ZPeak会有波动,考虑到方向角计算是以尺度为基准,以方向角为准
bool _chkRodArcFeature(
int pkIdx, double searchWin,
std::vector< SSG_pntDirAngle>& ptDirAngles,
std::vector< SVzNL3DPosition>& lineData,
const double arcPerPointCornerMinValue, //arc上每个点的转角最小值
const double arcTotalCornerMinValue, //整个Arc的转角最小值
SSG_intPair& a_rodPos)
{
//pkIdx可能会有波动
double realPkCorner = abs(ptDirAngles[pkIdx].forwardAngle + ptDirAngles[pkIdx].backwardAngle);
int validCorner = ptDirAngles[pkIdx].type;
int realPkIdx = pkIdx;
//向前搜索
for (int i = pkIdx - 1; i >= 0; i--)
{
if (lineData[i].pt3D.z < 1e-4)
continue;
if (ptDirAngles[i].type < 0)
continue;
double dist = sqrt(pow(lineData[pkIdx].pt3D.y - lineData[i].pt3D.y, 2) + pow(lineData[pkIdx].pt3D.z - lineData[i].pt3D.z, 2));
if (dist > searchWin)
break;
if (ptDirAngles[i].pntIdx >= 0)
{
double flatAngle = abs(ptDirAngles[i].forwardAngle + ptDirAngles[i].backwardAngle);
if (validCorner < 0)
{
realPkCorner = flatAngle;
realPkIdx = i;
validCorner = ptDirAngles[i].type;
}
else if (realPkCorner > flatAngle)
{
realPkCorner = flatAngle;
realPkIdx = i;
}
}
}
//向后搜索
for (int i = pkIdx + 1; i < (int)lineData.size(); i++)
{
if (lineData[i].pt3D.z < 1e-4)
continue;
if (ptDirAngles[i].type < 0)
continue;
double dist = sqrt(pow(lineData[pkIdx].pt3D.y - lineData[i].pt3D.y, 2) + pow(lineData[pkIdx].pt3D.z - lineData[i].pt3D.z, 2));
if (dist > searchWin)
break;
if (ptDirAngles[i].pntIdx >= 0)
{
double flatAngle = abs(ptDirAngles[i].forwardAngle + ptDirAngles[i].backwardAngle);
if (validCorner < 0)
{
realPkCorner = flatAngle;
realPkIdx = i;
validCorner = ptDirAngles[i].type;
}
else if (realPkCorner > flatAngle)
{
realPkCorner = flatAngle;
realPkIdx = i;
}
}
}
if (validCorner < 0)
return false;
if ((ptDirAngles[realPkIdx].forwardAngle > 0) && (ptDirAngles[realPkIdx].backwardAngle < 0) &&
(realPkCorner <10) &&(ptDirAngles[realPkIdx].corner < -arcPerPointCornerMinValue)) //前向角为正,后向角为负,两者值基本相等(<10), 拐角为负,绝对值大于10度
{
//向前搜索
int pre_idx = -1;
for (int i = realPkIdx - 1; i >= 0; i--)
{
if ((ptDirAngles[i].type < 0) || (ptDirAngles[i].pntIdx < 0))
continue;
else if (validCorner != ptDirAngles[i].type)
break;
if (ptDirAngles[i].corner < -arcPerPointCornerMinValue)
pre_idx = i;
else
break;
}
//向后搜索
int post_idx = -1;
for (int i = realPkIdx + 1; i < (int)ptDirAngles.size(); i++)
{
if ((ptDirAngles[i].type < 0) || (ptDirAngles[i].pntIdx < 0))
continue;
else if (validCorner != ptDirAngles[i].type)
break;
if (ptDirAngles[i].corner < -arcPerPointCornerMinValue)
post_idx = i;
else
break;
}
if ((pre_idx >= 0) && (post_idx >= 0))
{
double totalCornerAngle = ptDirAngles[post_idx].forwardAngle - ptDirAngles[pre_idx].backwardAngle;
if (totalCornerAngle > arcTotalCornerMinValue) //整个圆弧需要至少60度的扇区。
{
a_rodPos.idx = realPkIdx;
a_rodPos.data_0 = pre_idx;
a_rodPos.data_1 = post_idx;
return true;
}
}
}
return false;
}
/// <summary>
/// 提取激光线上的圆环的上半段弧。
/// seg端点:z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)搜索同方向的拐角连续段
/// (2)搜索Z极值,
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
///
/// </summary>
void wd_getRodArcFeature_peakCornerMethod(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const double peakChkWin,
const SSG_cornerParam cornerPara,
std::vector<SWD_rodArcFeature>& line_rodArcs //环
)
{
double arcPerPointCornerMinValue = 5; //arc上每个点的转角最小值
double arcTotalCornerMinValue = 30; //整个Arc的转角最小值
//计算前向角和后向角
std::vector< SSG_pntDirAngle> ptDirAngles;
wd_computeDirAngle_wholeLine( lineData, cornerPara, ptDirAngles);
int dataSize = (int)lineData.size();
//搜索z极值。
std::vector< SSG_basicFeature1D> localZMax;
std::vector< SSG_basicFeature1D> localZMin;
sg_getLineLocalPeaks_2( lineData, lineIdx, peakChkWin, localZMax, localZMin);
//逐个极小值点判断
int peakNum = (int)localZMin.size();
for (int i = 0; i < peakNum; i++)
{
int pkIdx = localZMin[i].jumpPos2D.y;
SSG_intPair a_rodPos;
bool isArc = _chkRodArcFeature(
pkIdx, cornerPara.scale,
ptDirAngles, lineData,
arcPerPointCornerMinValue, arcTotalCornerMinValue,
a_rodPos);
if (true == isArc)
{
SWD_rodArcFeature a_rodArc;
memset(&a_rodArc, 0, sizeof(SWD_rodArcFeature));
a_rodArc.lineIdx = lineIdx;
a_rodArc.startPtIdx = a_rodPos.data_0;
a_rodArc.endPtIdx = a_rodPos.data_1;
a_rodArc.peakPtIdx = a_rodPos.idx;
a_rodArc.startPt = lineData[a_rodPos.data_0].pt3D;
a_rodArc.endPt = lineData[a_rodPos.data_1].pt3D;
a_rodArc.peakPt = lineData[a_rodPos.idx].pt3D;
a_rodArc.featureValue = ptDirAngles[a_rodPos.idx].corner;
line_rodArcs.push_back(a_rodArc);
}
}
return;
}
/// <summary>
/// 提取激光线上的圆环的上半段弧。
/// seg端点:z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐段计算前向角和后向角
/// (2)搜索同方向的拐角连续段
/// (2)搜索Z极值,
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
///
/// </summary>
void wd_getRodArcFeature_segmentPeakCornerMethod(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const double maxDistTh,
const int minSegSize,
const double peakChkWin,
const SSG_cornerParam cornerPara,
std::vector<SWD_rodArcFeature>& line_rodArcs //环
)
{
double arcPerPointCornerMinValue = 5; //arc上每个点的转角最小值
double arcTotalCornerMinValue = 30; //整个Arc的转角最小值
std::vector<SSG_RUN> segs;
wd_lineDataSegment_dist_2(
lineData,
segs,
maxDistTh,
minSegSize
);
//计算前向角和后向角
std::vector< SSG_pntDirAngle> ptDirAngles;
_computeDirAngle_perSeg_2(lineData, segs, cornerPara, ptDirAngles);
int dataSize = (int)lineData.size();
//搜索z极值。
std::vector< SSG_basicFeature1D> localZMax;
std::vector< SSG_basicFeature1D> localZMin;
sg_getLineLocalPeaks_2(lineData, lineIdx, peakChkWin, localZMax, localZMin);
//逐个极小值点判断
int peakNum = (int)localZMin.size();
SSG_intPair prePos = { -1, -1, -1 };
for (int i = 0; i < peakNum; i++)
{
int pkIdx = localZMin[i].jumpPos2D.y;
SSG_intPair a_rodPos;
bool isArc = _chkRodArcFeature(
pkIdx, cornerPara.scale,
ptDirAngles, lineData,
arcPerPointCornerMinValue, arcTotalCornerMinValue,
a_rodPos);
if ( (true == isArc) && (prePos.idx != a_rodPos.idx))
{
SWD_rodArcFeature a_rodArc;
memset(&a_rodArc, 0, sizeof(SWD_rodArcFeature));
a_rodArc.lineIdx = lineIdx;
a_rodArc.startPtIdx = a_rodPos.data_0;
a_rodArc.endPtIdx = a_rodPos.data_1;
a_rodArc.peakPtIdx = a_rodPos.idx;
a_rodArc.startPt = lineData[a_rodPos.data_0].pt3D;
a_rodArc.endPt = lineData[a_rodPos.data_1].pt3D;
a_rodArc.peakPt = lineData[a_rodPos.idx].pt3D;
a_rodArc.featureValue = ptDirAngles[a_rodPos.idx].corner;
line_rodArcs.push_back(a_rodArc);
prePos = a_rodPos;
}
}
return;
}
//提取corner极值(较早实现函数可以使用此函数进行代码优化)
void wd_searchCornerPeaks(
std::vector< SSG_pntDirAngle>& corners,
std::vector< SVzNL3DPosition>& vldPts,
const double minCornerTh,
double cornerMergeScale,
std::vector< SSG_pntDirAngle>& cornerPeakP,
std::vector< SSG_pntDirAngle>& cornerPeakM
)
{
int cornerSize = (int)corners.size();
std::vector< SSG_pntDirAngle> cornerPk_P;
std::vector< SSG_pntDirAngle> cornerPk_M;
//搜索拐角极值
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
SSG_pntDirAngle* pre_data = NULL;
for (int i = 0, i_max = cornerSize; 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) //上升
{
if(corners[eEdgePtIdx].corner > 0)
cornerPk_P.push_back(corners[eEdgePtIdx]);
}
else if (2 == _state) //下降
{
if(corners[eEdgePtIdx].corner < 0)
cornerPk_M.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) //下降
{
if (pre_data->corner > 0)
cornerPk_P.push_back(*pre_data);
_state = 2;
}
break;
case 2: //下降
if (cornerDiff > 0) // 上升
{
if (pre_data->corner < 0)
cornerPk_M.push_back(*pre_data);
_state = 1;
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//注意:最后一个不处理,为基座位置
//极小值点(峰顶)
//极值比较,在尺度窗口下寻找局部极值点
double square_distTh = cornerMergeScale * cornerMergeScale; //2倍的cornerScale。
for (int i = 0, i_max = (int)cornerPk_P.size(); i < i_max; i++)
{
if (cornerPk_P[i].corner < minCornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPk_P[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPk_P[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 (cornerPk_P[i].corner < cornerPk_P[j].corner)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPk_P[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPk_P[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 (cornerPk_P[i].corner < cornerPk_P[j].corner)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
cornerPeakP.push_back(cornerPk_P[i]);
}
for (int i = 0, i_max = (int)cornerPk_M.size(); i < i_max; i++)
{
if (abs(cornerPk_M[i].corner) < minCornerTh)
continue;
bool isPeak = true;
//向前搜索
int cornerPtIdx = cornerPk_M[i].pntIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = cornerPk_M[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 (abs(cornerPk_M[i].corner) < abs(cornerPk_M[j].corner))
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
cornerPtIdx = cornerPk_M[i].pntIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = cornerPk_M[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 (abs(cornerPk_M[i].corner) < abs(cornerPk_M[j].corner))
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
cornerPeakM.push_back(cornerPk_M[i]);
}
}
/// <summary>
/// 提取激光线上的拐点特征。是在PSM, LVTypeFeature, BQ等拐点算法的基础上的版本。
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void wd_getLineCorerFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_cornerParam cornerPara,
std::vector<SSG_basicFeature1D>& line_cornerFeatures //拐点特征
)
{
int dataSize = (int)lineData.size();
//去除零点
std::vector< SVzNL3DPosition> vldPts;
//std::vector< int> vldPtSegIdx;
std::vector<SSG_RUN_EX> segs;
std::vector<int> backIndexing;
backIndexing.resize(dataSize);
int runIdx = 1;
SSG_RUN_EX a_run = { 0, -1, 0, false, false }; //startIdx, len, lastIdx
double pre_z = 0;
double pre_y = 0;
for (int i = 0; i < dataSize; i++)
{
if (i == 1850)
int kkk = 1;
lineData[i].nPointIdx = i; //重新编序号
if (lineData[i].pt3D.z > 1e-4)
{
int bIdx = (int)vldPts.size();
backIndexing[i] = bIdx;
vldPts.push_back(lineData[i]);
if (a_run.len < 0)
{
a_run.start_zRising = true;
a_run.start = bIdx;
a_run.len = 1;
a_run.end_zRising = true;
a_run.value = i;
}
else
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
if (z_diff < cornerPara.minEndingGap_z)
{
a_run.len = bIdx - a_run.start + 1;
a_run.value = i;
}
else
{
bool next_zRising;
if (pre_z > lineData[i].pt3D.z)
{
a_run.end_zRising = true;
next_zRising = false;
}
else
{
a_run.end_zRising = false;
next_zRising = true;
}
a_run.value = runIdx;
runIdx++;
segs.push_back(a_run);
a_run.start = bIdx;
a_run.start_zRising = next_zRising;
a_run.len = 1;
a_run.value = i;
a_run.end_zRising = true;
}
}
//vldPtSegIdx.push_back(runIdx);
pre_z = lineData[i].pt3D.z;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
#if 0
//将点置标志
for (int i = 0, i_max = (int)segs.size(); i < i_max; i++)
{
int idx1 = segs[i].start;
int idx2 = segs[i].start + segs[i].len - 1;
lineData[idx1].nPointIdx |= 0x100000;
lineData[idx2].nPointIdx |= 0x200000;
}
#endif
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
//逐段进行
int segSize = (int)segs.size();
for (int segIdx = 0; segIdx < segSize; segIdx++)
{
int vPtIdxStart = segs[segIdx].start;
int vPtIdxEnd = vPtIdxStart + segs[segIdx].len - 1;
for (int i = vPtIdxStart; i <= vPtIdxEnd; i++)
{
if (i == 1587)
int kkk = 1;
//前向寻找
int pre_i = -1;
for (int j = i - 1; j >= vPtIdxStart; 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)
{
pre_i = j;
break;
}
}
//后向寻找
int post_i = -1;
for (int j = i + 1; j <= vPtIdxEnd; 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)
{
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;
}
}
}
std::vector< SSG_pntDirAngle> cornerPeakP;
std::vector< SSG_pntDirAngle> cornerPeakM;
double cornerMergeScale = cornerPara.scale * 2;
//提取corner极值
wd_searchCornerPeaks(
corners,
vldPts,
cornerPara.cornerTh,
cornerMergeScale,
cornerPeakP,
cornerPeakM
);
for (int i = 0, i_max = (int)cornerPeakP.size(); i < i_max; i++)
{
SSG_basicFeature1D a_feature;
if ((abs(cornerPeakP[i].backwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakP[i].forwardAngle) > cornerPara.jumpCornerTh_2))
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
else if ((abs(cornerPeakP[i].forwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakP[i].backwardAngle) > cornerPara.jumpCornerTh_2))
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
else
a_feature.featureType = LINE_FEATURE_CORNER_V;
int cornerPtIdx = cornerPeakP[i].pntIdx;
a_feature.jumpPos = vldPts[cornerPtIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[cornerPtIdx].nPointIdx };
line_cornerFeatures.push_back(a_feature);
}
return;
}
/// 提取激光线上的拐点特征。是在PSM, LVTypeFeature, BQ等拐点算法的基础上的版本。
/// 使用平均点距进行加速
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void wd_getLineCorerFeature_accelerate(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_cornerParam cornerPara,
const double pointInterval,
std::vector<SSG_RUN_EX>& segs,
std::vector<SSG_basicFeature1D>& line_cornerFeatures //拐点特征
)
{
int dataSize = (int)lineData.size();
//去除零点
int runIdx = 1;
SSG_RUN_EX a_run = { 0, -1, 0, false, false }; //startIdx, len, lastIdx
double pre_z = 0;
double pre_y = 0;
for (int i = 0; i < dataSize; i++)
{
if (i == 1100)
int kkk = 1;
lineData[i].nPointIdx = i; //重新编序号
if (lineData[i].pt3D.z > 1e-4)
{
if (a_run.len < 0)
{
a_run.start_zRising = true;
a_run.start = i;
a_run.len = 1;
a_run.end_zRising = true;
a_run.value = i;
}
else
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
if (z_diff < cornerPara.minEndingGap_z)
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
else
{
bool next_zRising;
if (pre_z > lineData[i].pt3D.z)
{
a_run.end_zRising = true;
next_zRising = false;
}
else
{
a_run.end_zRising = false;
next_zRising = true;
}
a_run.value = runIdx;
runIdx++;
segs.push_back(a_run);
a_run.start = i;
a_run.start_zRising = next_zRising;
a_run.len = 1;
a_run.value = i;
a_run.end_zRising = true;
}
}
//vldPtSegIdx.push_back(runIdx);
pre_z = lineData[i].pt3D.z;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(lineData.size());
//逐段进行
int ptSkipping = cornerPara.scale / pointInterval;
int segSize = (int)segs.size();
for (int segIdx = 0; segIdx < segSize; segIdx++)
{
int vPtIdxStart = segs[segIdx].start;
int vPtIdxEnd = vPtIdxStart + segs[segIdx].len - 1;
for (int i = vPtIdxStart; i <= vPtIdxEnd; i++)
{
if (i == 1100)
int kkk = 1;
if (lineData[i].pt3D.z < 1e-4)
{
corners[i].pntIdx = -1;
corners[i].forwardAngle = 0;
corners[i].backwardAngle = 0;
corners[i].corner = 0;
corners[i].forwardDiffZ = 0;
corners[i].backwardDiffZ = 0;
continue;
}
//前向寻找
int pre_i = -1;
int searchStart = i - ptSkipping;
if (searchStart >= vPtIdxStart)
{
for (int j = searchStart; j < i; j++)
{
if (lineData[j].pt3D.z > 1e-4)
{
double dist = sqrt(pow(lineData[i].pt3D.y - lineData[j].pt3D.y, 2) +
pow(lineData[i].pt3D.z - lineData[j].pt3D.z, 2));
if (dist < cornerPara.scale)
break;
else
pre_i = j;
}
}
}
//后向寻找
int post_i = -1;
int searchEnd = i + ptSkipping;
if (searchEnd <= vPtIdxEnd)
{
for (int j = searchEnd; j > i; j--)
{
if (lineData[j].pt3D.z > 1e-4)
{
double dist = sqrt(pow(lineData[i].pt3D.y - lineData[j].pt3D.y, 2) +
pow(lineData[i].pt3D.z - lineData[j].pt3D.z, 2));
if (dist < cornerPara.scale)
break;
else
post_i = j;
}
}
}
//计算拐角
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 = (lineData[i].pt3D.z - lineData[pre_i].pt3D.z) / abs(lineData[i].pt3D.y - lineData[pre_i].pt3D.y);
double tanValue_post = (lineData[post_i].pt3D.z - lineData[i].pt3D.z) / abs(lineData[post_i].pt3D.y - lineData[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 = lineData[post_i].pt3D.z - lineData[i].pt3D.z;
corners[i].backwardDiffZ = lineData[i].pt3D.z - lineData[pre_i].pt3D.z;
}
}
}
std::vector< SSG_pntDirAngle> cornerPeakP;
std::vector< SSG_pntDirAngle> cornerPeakM;
double cornerMergeScale = cornerPara.scale * 2;
//提取corner极值
wd_searchCornerPeaks(
corners,
lineData,
cornerPara.cornerTh,
cornerMergeScale,
cornerPeakP,
cornerPeakM
);
for (int i = 0, i_max = (int)cornerPeakP.size(); i < i_max; i++)
{
SSG_basicFeature1D a_feature;
if ((abs(cornerPeakP[i].backwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakP[i].forwardAngle) > cornerPara.jumpCornerTh_2))
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
else if ((abs(cornerPeakP[i].forwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakP[i].backwardAngle) > cornerPara.jumpCornerTh_2))
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
else
a_feature.featureType = LINE_FEATURE_CORNER_V;
int cornerPtIdx = cornerPeakP[i].pntIdx;
a_feature.jumpPos = lineData[cornerPtIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, lineData[cornerPtIdx].nPointIdx };
line_cornerFeatures.push_back(a_feature);
}
return;
}
SSG_pntDirAngle _computeCornerAngle(int ptIdx, int startIdx, int endIdx, double scale, std::vector< SVzNL3DPosition>& lineData)
{
SSG_pntDirAngle a_corner;
memset(&a_corner, 0, sizeof(SSG_pntDirAngle));
if (lineData[ptIdx].pt3D.z < 1e-4)
{
a_corner.pntIdx = -1;
a_corner.forwardAngle = 0;
a_corner.backwardAngle = 0;
a_corner.corner = 0;
a_corner.forwardDiffZ = 0;
a_corner.backwardDiffZ = 0;
return a_corner;
}
//前向寻找
int pre_i = -1;
for (int j = ptIdx-1; j >= startIdx; j--)
{
if (lineData[j].pt3D.z > 1e-4)
{
double dist = sqrt(pow(lineData[ptIdx].pt3D.y - lineData[j].pt3D.y, 2) +
pow(lineData[ptIdx].pt3D.z - lineData[j].pt3D.z, 2));
if (dist >= scale)
break;
else
pre_i = j;
}
}
//后向寻找
int post_i = -1;
for (int j = ptIdx+1; j < endIdx; j++)
{
if (lineData[j].pt3D.z > 1e-4)
{
double dist = sqrt(pow(lineData[ptIdx].pt3D.y - lineData[j].pt3D.y, 2) +
pow(lineData[ptIdx].pt3D.z - lineData[j].pt3D.z, 2));
if (dist >= scale)
break;
else
post_i = j;
}
}
//计算拐角
if ((pre_i < 0) || (post_i < 0))
{
a_corner.pntIdx = -1;
a_corner.forwardAngle = 0;
a_corner.backwardAngle = 0;
a_corner.corner = 0;
a_corner.forwardDiffZ = 0;
a_corner.backwardDiffZ = 0;
}
else
{
double tanValue_pre = (lineData[ptIdx].pt3D.z - lineData[pre_i].pt3D.z) / abs(lineData[ptIdx].pt3D.y - lineData[pre_i].pt3D.y);
double tanValue_post = (lineData[post_i].pt3D.z - lineData[ptIdx].pt3D.z) / abs(lineData[post_i].pt3D.y - lineData[ptIdx].pt3D.y);
double forwardAngle = atan(tanValue_post) * 180.0 / PI;
double backwardAngle = atan(tanValue_pre) * 180.0 / PI;
a_corner.pntIdx = ptIdx;
a_corner.forwardAngle = forwardAngle;
a_corner.backwardAngle = backwardAngle;
a_corner.corner = -(forwardAngle - backwardAngle); //图像坐标系与正常坐标系y方向相反,所以有“-”号
a_corner.forwardDiffZ = lineData[post_i].pt3D.z - lineData[ptIdx].pt3D.z;
a_corner.backwardDiffZ = lineData[ptIdx].pt3D.z - lineData[pre_i].pt3D.z;
}
return a_corner;
}
#if 0
//对一个面的边缘特征进行提取。
//此算法首先获取扫描线上各个段,然后对段的端点进行处理,得到边缘特征
void wd_getSurfaceEdgeFeatures(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_lineSegParam lineSegPara,
const SSG_cornerParam cornerPara,
double edgeAngleTh, //边缘的判断角度。当一个端点的前向角或后向角大于此门限,将拐点最大点视为边缘,反之,端点是边缘点
std::vector<SWDIndexing3DPoint>& edgeFeatures)
{
int dataSize = (int)lineData.size();
//去除零点
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(lineData, lineSegPara, segs);
//逐段处理,对端点进行处理
int segSize = (int)segs.size();
for (int si = 0; si < segSize; si++)
{
int endingIdx_s = segs[si].start;
int endingIdx_e = segs[si].start + segs[si].len - 1;
//后向寻找
int post_i = -1;
for (int j = endingIdx_s + 1; j <= endingIdx_e; j++)
{
if (lineData[j].pt3D.z > 1e-4)
{
double dist = sqrt(pow(lineData[endingIdx_s].pt3D.y - lineData[j].pt3D.y, 2) +
pow(lineData[endingIdx_s].pt3D.z - lineData[j].pt3D.z, 2));
if (dist >= lineSegPara.distScale)
{
post_i = j;
break;
}
}
}
double tanValue_post = (lineData[post_i].pt3D.z - lineData[endingIdx_s].pt3D.z) / abs(lineData[post_i].pt3D.y - lineData[endingIdx_s].pt3D.y);
double forwardAngle = atan(tanValue_post) * 180.0 / PI;
if (forwardAngle > edgeAngleTh)
{
//需要寻找拐点,作为端点
std::vector<SSG_pntDirAngle> corners;
for (int j = endingIdx_s + 1; j < endingIdx_e; j++)
{
SSG_pntDirAngle a_corner = _computeCornerAngle(j, endingIdx_s, endingIdx_e, lineSegPara.distScale, lineData);
corners.push_back(a_corner);
double cornerMergeScale = cornerPara.scale * 2;
std::vector< SSG_pntDirAngle> cornerPeakP;
std::vector< SSG_pntDirAngle> cornerPeakM;
_searchCornerPeaks(
corners,
lineData,
cornerPara,
cornerMergeScale,
cornerPeakP,
cornerPeakM
);
//取cornerPeakM的第一个极值点
}
}
else
{
}
std::vector< SSG_RUN> segmentationLines;
split(segs[si], lineData, lineSegPara.maxDist, segmentationLines);
//对于每个分段,只能有一个合格的直线段
std::vector< SSG_featureSemiCircle> candiLines;
for (int m = 0, m_max = (int)segmentationLines.size(); m < m_max; m++)
{
SSG_featureSemiCircle a_seg;
a_seg.lineIdx = lineIdx;
a_seg.startPtIdx = segmentationLines[m].start;
a_seg.endPtIdx = segmentationLines[m].start + segmentationLines[m].len - 1;
SVzNL3DPoint ptStart = lineData[a_seg.startPtIdx].pt3D;
SVzNL3DPoint ptEnd = lineData[a_seg.endPtIdx].pt3D;
double len = sqrt(pow(ptStart.x - ptEnd.x, 2) + pow(ptStart.y - ptEnd.y, 2) + pow(ptStart.z - ptEnd.z, 2));
if ((len >= lineLenRange.min) && (len <= lineLenRange.max))
{
int midPtIdx = (a_seg.startPtIdx + a_seg.endPtIdx) / 2;
bool validMid = true;
if (lineData[midPtIdx].pt3D.z < 1e-4)
{
validMid = false;
int chkWin = 1;
while (1)
{
int idx = midPtIdx + chkWin;
if (idx >= a_seg.endPtIdx)
break;
if (lineData[idx].pt3D.z > 1e-4)
{
midPtIdx = idx;
validMid = true;
break;
}
idx = midPtIdx - chkWin;
if (idx <= a_seg.startPtIdx)
break;
if (lineData[idx].pt3D.z > 1e-4)
{
midPtIdx = idx;
validMid = true;
break;
}
chkWin++;
}
}
if (true == validMid)
{
a_seg.midPtIdx = midPtIdx;
a_seg.midPt = lineData[midPtIdx].pt3D;
candiLines.push_back(a_seg);
}
}
}
//转成SSG_featureSemiCircle格式
if (candiLines.size() > 0)
{
//选择一个水平角度更小的
double minTan = 0;
int bestIdx = -1;
for (int m = 0, m_max = (int)candiLines.size(); m < m_max; m++)
{
SSG_featureSemiCircle& a_seg = candiLines[m];
SVzNL3DPoint ptStart = lineData[a_seg.startPtIdx].pt3D;
SVzNL3DPoint ptEnd = lineData[a_seg.endPtIdx].pt3D;
double tank = abs((ptEnd.z - ptStart.z) / (ptEnd.y - ptStart.y));
if (bestIdx < 0)
{
minTan = tank;
bestIdx = m;
}
else
{
if (minTan > tank)
{
minTan = tank;
bestIdx = m;
}
}
}
lineSegs.push_back(candiLines[bestIdx]);
for (int m = 0, m_max = (int)candiLines.size(); m < m_max; m++)
{
if (m != bestIdx)
invlaidLineSegs.push_back(candiLines[m]);
}
}
else if (candiLines.size() == 1)
lineSegs.push_back(candiLines[0]);
}
}
#endif
//提取凸起段
void wd_getLineRaisedFeature(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
SSG_raisedFeatureParam raisedFeaturePara, //凸起参数
std::vector<SWD_segFeature>& line_raisedFeatures //凸起
)
{
int dataSize = (int)lineData.size();
int runIdx = 1;
SSG_RUN_EX a_run = { 0, -1, 0, false, false }; //startIdx, len, lastIdx
double pre_z = -1;
double pre_y = 0;
for (int i = 0; i < dataSize; i++)
{
if (i == 1733)
int kkk = 1;
lineData[i].nPointIdx = i; //重新编序号
if (lineData[i].pt3D.z > 1e-4)
{
if(pre_z > 0)
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
double y_diff = abs(lineData[i].pt3D.y - pre_y);
double k = z_diff / y_diff;
if ( (z_diff > raisedFeaturePara.minJumpZ) && (k > raisedFeaturePara.minK)
&& (lineData[i].pt3D.z < pre_z))
{
a_run.start = i;
a_run.start_zRising = true;
a_run.len = 1;
a_run.value = i;
a_run.end_zRising = false;
}
else if ((z_diff > raisedFeaturePara.minJumpZ) && (k > raisedFeaturePara.minK)
&& (lineData[i].pt3D.z > pre_z))
{
if (a_run.len > 0)
{
a_run.end_zRising = true;
int sIdx = a_run.start;
int eIdx = a_run.start + a_run.len - 1;
double width = abs(lineData[sIdx].pt3D.y - lineData[eIdx].pt3D.y);
if ((width >= raisedFeaturePara.widthRange.min) &&
(width <= raisedFeaturePara.widthRange.max))
{
SWD_segFeature a_raisedFeature;
memset(&a_raisedFeature, 0, sizeof(SWD_segFeature));
a_raisedFeature.lineIdx = lineIdx;
a_raisedFeature.startPtIdx = sIdx;
a_raisedFeature.endPtIdx = eIdx;
a_raisedFeature.startPt = lineData[sIdx].pt3D;
a_raisedFeature.endPt = lineData[eIdx].pt3D;
a_raisedFeature.featureValue = width;
line_raisedFeatures.push_back(a_raisedFeature);
}
}
a_run = { 0, -1, 0, false, false }; //startIdx, len, lastIdx
}
else
{
a_run.len = i - a_run.start + 1;
a_run.value = i;
}
}
//vldPtSegIdx.push_back(runIdx);
pre_y = lineData[i].pt3D.y;
pre_z = lineData[i].pt3D.z;
}
}
}
//提取指定跳变高度的特征点
void wd_getSpecifiedHeightJumping(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
SVzNLRangeD jumpHeight, //跳变高度参数
std::vector<SSG_basicFeature1D>& line_jumpFeatures //跳变
)
{
int dataSize = (int)lineData.size();
double height = (jumpHeight.min + jumpHeight.max) / 2;
//当两个点的间隔大于jumpHeight, 即使高差为heigth,此时的角度也小于45度,此时按分段端点处理
std::vector< SSG_RUN> segs;
int runIdx = 1;
SSG_RUN a_run = { 0, -1, 0 }; //startIdx, len, lastIdx
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 y_diff = abs(lineData[i].pt3D.y - pre_y);
if (y_diff < height)
{
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_y = lineData[i].pt3D.y;
}
}
if (a_run.len > 0)
segs.push_back(a_run);
int segNum = (int)segs.size();
for (int si = 0; si < segNum; si++)
{
//将端点加入
int startIdx = segs[si].start;
int endIdx = segs[si].start + segs[si].len - 1;
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
a_feature.jumpPos = lineData[startIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, startIdx };
line_jumpFeatures.push_back(a_feature);
double pre_z = 0;
int preIdx = -1;
for (int i = startIdx; i <= endIdx; i++)
{
if (i == 1850)
int kkk = 1;
if (lineData[i].pt3D.z > 1e-4)
{
if (preIdx >= 0)
{
double z_diff = abs(lineData[i].pt3D.z - pre_z);
if ((z_diff >= jumpHeight.min) && (z_diff <= jumpHeight.max))
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
if (lineData[i].pt3D.z < pre_z)
{
a_feature.jumpPos = lineData[i].pt3D;
a_feature.jumpPos2D = { lineIdx, i };
}
else
{
a_feature.jumpPos = lineData[preIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, preIdx };
}
line_jumpFeatures.push_back(a_feature);
}
}
preIdx = i;
pre_z = lineData[i].pt3D.z;
}
}
//将最后的端点加入
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
a_feature.jumpPos = lineData[endIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, endIdx };
line_jumpFeatures.push_back(a_feature);
}
}
//使用端点直线,检查点到直线的距离,大于门限的分割
void split(
SSG_RUN a_run,
std::vector< SVzNL3DPosition>& lineData,
const double maxError,
std::vector< SSG_RUN>& lineSegs)
{
if (a_run.len < MIN_POINT_COUNT)
return;
int start = a_run.start;
int end = a_run.start + a_run.len - 1;
SVzNL3DPoint pt1 = lineData[start].pt3D;
SVzNL3DPoint pt2 = lineData[end].pt3D;
if ((pt1.z < 1e-4) || (pt2.z < 1e-4))
return;
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 = start; i <= end; 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;
}
}
}
if (maxDist < maxError)
{
// 误差满足要求,加入线段集合
lineSegs.push_back(a_run);
return;
}
SSG_RUN run_1 = { a_run.start, maxPos-a_run.start + 1, 0 };
SSG_RUN run_2 = { maxPos, a_run.start + a_run.len - maxPos, 0 };
split(run_1, lineData, maxError, lineSegs);
split(run_2, lineData, maxError, lineSegs);
}
//直线特征提取:对split-and-merge法作了简化,以起点终点直线代替拟合直线
//此算法对端面直线提取作了优化,每个分段只保留一个最佳直线段(去掉相邻的导角形成的直线,并记录)
void wd_surfaceLineSegment(
std::vector< SVzNL3DPosition>& lineData,
int lineIdx,
SVzNLRangeD lineLenRange,
const SSG_lineSegParam lineSegPara,
std::vector<SSG_featureSemiCircle>&lineSegs,
std::vector<SSG_featureSemiCircle>& invlaidLineSegs)
{
int dataSize = (int)lineData.size();
//去除零点
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(lineData, lineSegPara, segs);
//逐段处理
int segSize = (int)segs.size();
for (int si = 0; si < segSize; si++)
{
std::vector< SSG_RUN> segmentationLines;
split(segs[si], lineData, lineSegPara.distScale, segmentationLines);
//对于每个分段,只能有一个合格的直线段
std::vector< SSG_featureSemiCircle> candiLines;
for (int m = 0, m_max = (int)segmentationLines.size(); m < m_max; m++)
{
SSG_featureSemiCircle a_seg;
a_seg.lineIdx = lineIdx;
a_seg.startPtIdx = segmentationLines[m].start;
a_seg.endPtIdx = segmentationLines[m].start + segmentationLines[m].len - 1;
SVzNL3DPoint ptStart = lineData[a_seg.startPtIdx].pt3D;
SVzNL3DPoint ptEnd = lineData[a_seg.endPtIdx].pt3D;
double len = sqrt(pow(ptStart.x - ptEnd.x, 2) + pow(ptStart.y - ptEnd.y, 2) + pow(ptStart.z - ptEnd.z, 2));
if ((len >= lineLenRange.min) && (len <= lineLenRange.max))
{
int midPtIdx = (a_seg.startPtIdx + a_seg.endPtIdx) / 2;
bool validMid = true;
if (lineData[midPtIdx].pt3D.z < 1e-4)
{
validMid = false;
int chkWin = 1;
while (1)
{
int idx = midPtIdx + chkWin;
if (idx >= a_seg.endPtIdx)
break;
if (lineData[idx].pt3D.z > 1e-4)
{
midPtIdx = idx;
validMid = true;
break;
}
idx = midPtIdx - chkWin;
if (idx <= a_seg.startPtIdx)
break;
if (lineData[idx].pt3D.z > 1e-4)
{
midPtIdx = idx;
validMid = true;
break;
}
chkWin++;
}
}
if (true == validMid)
{
a_seg.midPtIdx = midPtIdx;
a_seg.midPt = lineData[midPtIdx].pt3D;
candiLines.push_back(a_seg);
}
}
}
//转成SSG_featureSemiCircle格式
if (candiLines.size() > 0)
{
//选择一个水平角度更小的
double minTan = 0;
int bestIdx = -1;
for (int m = 0, m_max = (int)candiLines.size(); m < m_max; m++)
{
SSG_featureSemiCircle& a_seg = candiLines[m];
SVzNL3DPoint ptStart = lineData[a_seg.startPtIdx].pt3D;
SVzNL3DPoint ptEnd = lineData[a_seg.endPtIdx].pt3D;
double tank = abs((ptEnd.z - ptStart.z) / (ptEnd.y - ptStart.y));
if (bestIdx < 0)
{
minTan = tank;
bestIdx = m;
}
else
{
if (minTan > tank)
{
minTan = tank;
bestIdx = m;
}
}
}
lineSegs.push_back(candiLines[bestIdx]);
for (int m = 0, m_max = (int)candiLines.size(); m < m_max; m++)
{
if (m != bestIdx)
invlaidLineSegs.push_back(candiLines[m]);
}
}
else if (candiLines.size() == 1)
lineSegs.push_back(candiLines[0]);
}
}
bool compareByIdx(const SSG_pntDirAngle& a, const SSG_pntDirAngle& b) {
return a.pntIdx < b.pntIdx;
}
/// <summary>
/// 提取激光线上的Z形Jumping特征, 用于搭接焊缝
/// seg端点:z距离大于门限
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void sg_getLineZJumpFeature_cornerMethod(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_cornerParam cornerPara,
const bool getTopEdge,
std::vector< SSG_basicFeature1D>& jumpFeatures)
{
//去除零点
std::vector< SVzNL3DPosition> vldPts;
std::vector<SSG_RUN> segs;
//修改seg的定义。seg端点是两点间距离大于门限的点
int segStart = -1, segEnd = -1;
for (int i = 0; i < dataSize; i++)
{
SVzNL3DPosition a_pt = lineData[i];
a_pt.nPointIdx = i;
if (lineData[i].pt3D.z > 1e-4)
vldPts.push_back(a_pt);
//seg判断
if (lineData[i].pt3D.z > 1e-4)
{
if (segStart < 0)
segStart = i;
else //检查两点距离
{
SVzNL3DPosition pre_pt = lineData[i - 1];
double diff_z = abs(a_pt.pt3D.z - pre_pt.pt3D.z);
if (diff_z > cornerPara.minEndingGap_z)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
segStart = i;
}
}
segEnd = i;
}
else
{
if (segStart >= 0)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
segStart = -1;
segEnd = -1;
}
}
}
//last
if (segStart >= 0)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
}
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if (i == 875)
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)
{
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)
{
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;
corners[i].forward_z = 0;
corners[i].backward_z = 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;
corners[i].forward_z = vldPts[post_i].pt3D.z; //沿序号增长方向
corners[i].backward_z = vldPts[pre_i].pt3D.z; //沿序号减小方向
}
}
//搜索拐角极值
std::vector< SSG_pntDirAngle> cornerPeakP;
std::vector< SSG_pntDirAngle> cornerPeakM;
double cornerMergeScale = cornerPara.scale * 2;
wd_searchCornerPeaks(
corners,
vldPts,
cornerPara.cornerTh,
cornerMergeScale,
cornerPeakP,
cornerPeakM
);
std::vector< SSG_pntDirAngle> cornerFeatures;
for (int i = 0; i < (int)cornerPeakP.size(); i++)
{
if ((abs(cornerPeakP[i].backwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakP[i].forwardAngle) > cornerPara.jumpCornerTh_2))
{
cornerPeakP[i].type = LINE_FEATURE_RIGHT_ANGLE_HR;
cornerFeatures.push_back(cornerPeakP[i]);
}
else if ((abs(cornerPeakP[i].forwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakP[i].backwardAngle) > cornerPara.jumpCornerTh_2))
{
cornerPeakP[i].type = LINE_FEATURE_RIGHT_ANGLE_FH;
cornerFeatures.push_back(cornerPeakP[i]);
}
}
for (int i = 0; i < (int)cornerPeakM.size(); i++)
{
if ((abs(cornerPeakM[i].backwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakM[i].forwardAngle) > cornerPara.jumpCornerTh_2))
{
cornerPeakM[i].type = LINE_FEATURE_RIGHT_ANGLE_HF;
cornerFeatures.push_back(cornerPeakM[i]);
}
else if ((abs(cornerPeakM[i].forwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakM[i].backwardAngle) > cornerPara.jumpCornerTh_2))
{
cornerPeakM[i].type = LINE_FEATURE_RIGHT_ANGLE_RH;
cornerFeatures.push_back(cornerPeakM[i]);
}
}
//排序
std::sort(cornerFeatures.begin(), cornerFeatures.end(), compareByIdx);
//进行组合获得Jump
for (int i = 0, i_max = (int)cornerFeatures.size(); i < i_max-1; i++)
{
if (cornerFeatures[i].type == LINE_FEATURE_RIGHT_ANGLE_HR)
{
if (cornerFeatures[i + 1].type == LINE_FEATURE_RIGHT_ANGLE_RH)
{
int pntIdx_1 = cornerFeatures[i].pntIdx;
int pntIdx_2 = cornerFeatures[i+1].pntIdx;
//计算距离和跳变高度
double dist = abs(vldPts[pntIdx_1].pt3D.y - vldPts[pntIdx_2].pt3D.y);
double height = abs(cornerFeatures[i + 1].forward_z - cornerFeatures[i].backward_z);
if ((dist < cornerPara.minEndingGap) && (height > cornerPara.minEndingGap_z))
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
if (true == getTopEdge)
{
a_feature.jumpPos = vldPts[pntIdx_2].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[pntIdx_2].nPointIdx }; //取
a_feature.featureValue = cornerFeatures[i+1].forward_z;
}
else
{
a_feature.jumpPos = vldPts[pntIdx_1].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[pntIdx_1].nPointIdx }; //取
a_feature.featureValue = cornerFeatures[i].backward_z;
}
jumpFeatures.push_back(a_feature);
}
}
}
else if (cornerFeatures[i].type == LINE_FEATURE_RIGHT_ANGLE_HF)
{
if (cornerFeatures[i + 1].type == LINE_FEATURE_RIGHT_ANGLE_FH)
{
int pntIdx_1 = cornerFeatures[i].pntIdx;
int pntIdx_2 = cornerFeatures[i + 1].pntIdx;
//计算距离和跳变高度
double dist = abs(vldPts[pntIdx_1].pt3D.y - vldPts[pntIdx_2].pt3D.y);
double height = abs(cornerFeatures[i + 1].forward_z - cornerFeatures[i].backward_z);
if ((dist < cornerPara.minEndingGap) && (height > cornerPara.minEndingGap_z))
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
if (true == getTopEdge)
{
a_feature.jumpPos = vldPts[pntIdx_1].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[pntIdx_1].nPointIdx };
a_feature.featureValue = cornerFeatures[i].backward_z;
}
else
{
a_feature.jumpPos = vldPts[pntIdx_2].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[pntIdx_2].nPointIdx };
a_feature.featureValue = cornerFeatures[i + 1].forward_z;
}
jumpFeatures.push_back(a_feature);
}
}
}
}
return;
}
/// <summary>
/// 提取激光线上的L形Jumping特征
/// 特征定义由绝对定义修改为相对定义
/// </summary>
void sg_getLineLJumpFeature_cornerMethod(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_cornerParam cornerPara,
const bool getTopEdge,
std::vector< SSG_basicFeature1D>& jumpFeatures)
{
//去除零点
std::vector< SVzNL3DPosition> vldPts;
std::vector<SSG_RUN> segs;
//修改seg的定义。seg端点是两点间距离大于门限的点
int segStart = -1, segEnd = -1;
for (int i = 0; i < dataSize; i++)
{
SVzNL3DPosition a_pt = lineData[i];
a_pt.nPointIdx = i;
if (lineData[i].pt3D.z > 1e-4)
vldPts.push_back(a_pt);
//seg判断
if (lineData[i].pt3D.z > 1e-4)
{
if (segStart < 0)
segStart = i;
else //检查两点距离
{
SVzNL3DPosition pre_pt = lineData[i - 1];
double diff_z = abs(a_pt.pt3D.z - pre_pt.pt3D.z);
if (diff_z > cornerPara.minEndingGap_z)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
segStart = i;
}
}
segEnd = i;
}
else
{
if (segStart >= 0)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
segStart = -1;
segEnd = -1;
}
}
}
//last
if (segStart >= 0)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
}
//计算前向角和后向角
std::vector< SSG_pntDirAngle> corners;
corners.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if (i == 875)
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)
{
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)
{
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;
corners[i].forward_z = 0;
corners[i].backward_z = 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;
corners[i].forward_z = vldPts[post_i].pt3D.z; //沿序号增长方向
corners[i].backward_z = vldPts[pre_i].pt3D.z; //沿序号减小方向
}
}
//搜索拐角极值
std::vector< SSG_pntDirAngle> cornerPeakP;
std::vector< SSG_pntDirAngle> cornerPeakM;
double cornerMergeScale = cornerPara.scale;
wd_searchCornerPeaks(
corners,
vldPts,
cornerPara.cornerTh,
cornerMergeScale,
cornerPeakP,
cornerPeakM
);
std::vector< SSG_pntDirAngle> cornerFeatures;
for (int i = 0; i < (int)cornerPeakP.size(); i++)
{
double angle_diff1 = abs(cornerPeakP[i].forwardAngle) - abs(cornerPeakP[i].backwardAngle);
double angle_diff2 = abs(cornerPeakP[i].backwardAngle) - abs(cornerPeakP[i].forwardAngle);
//if ((abs(cornerPeakP[i].backwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakP[i].forwardAngle) > cornerPara.jumpCornerTh_2))
if( angle_diff1 > cornerPara.jumpCornerTh_1)
{
cornerPeakP[i].type = LINE_FEATURE_RIGHT_ANGLE_HR;
cornerFeatures.push_back(cornerPeakP[i]);
}
//else if ((abs(cornerPeakP[i].forwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakP[i].backwardAngle) > cornerPara.jumpCornerTh_2))
else if(angle_diff2 > cornerPara.jumpCornerTh_1)
{
cornerPeakP[i].type = LINE_FEATURE_RIGHT_ANGLE_FH;
cornerFeatures.push_back(cornerPeakP[i]);
}
}
for (int i = 0; i < (int)cornerPeakM.size(); i++)
{
double angle_diff1 = abs(cornerPeakM[i].forwardAngle) - abs(cornerPeakM[i].backwardAngle);
double angle_diff2 = abs(cornerPeakM[i].backwardAngle) - abs(cornerPeakM[i].forwardAngle);
//if ((abs(cornerPeakM[i].backwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakM[i].forwardAngle) > cornerPara.jumpCornerTh_2))
if (angle_diff1 > cornerPara.jumpCornerTh_1)
{
cornerPeakM[i].type = LINE_FEATURE_RIGHT_ANGLE_HF;
cornerFeatures.push_back(cornerPeakM[i]);
}
//else if ((abs(cornerPeakM[i].forwardAngle) < cornerPara.jumpCornerTh_1) && (abs(cornerPeakM[i].backwardAngle) > cornerPara.jumpCornerTh_2))
else if (angle_diff2 > cornerPara.jumpCornerTh_1)
{
cornerPeakM[i].type = LINE_FEATURE_RIGHT_ANGLE_RH;
cornerFeatures.push_back(cornerPeakM[i]);
}
}
//排序
std::sort(cornerFeatures.begin(), cornerFeatures.end(), compareByIdx);
//获得Jump
for (int i = 0, i_max = (int)cornerFeatures.size(); i < i_max - 1; i++)
{
int pntIdx = cornerFeatures[i].pntIdx;
if (false == getTopEdge) //取下沿
{
if ((cornerFeatures[i].type == LINE_FEATURE_RIGHT_ANGLE_FH) || (cornerFeatures[i].type == LINE_FEATURE_RIGHT_ANGLE_HR))
{
double thickness = abs(cornerFeatures[i].forward_z - cornerFeatures[i].backward_z);
if (thickness > cornerPara.minEndingGap_z)
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
a_feature.jumpPos = vldPts[pntIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[pntIdx].nPointIdx }; //取
if (cornerFeatures[i + 1].type == LINE_FEATURE_RIGHT_ANGLE_FH)
a_feature.featureValue = cornerFeatures[i].forward_z;
else
a_feature.featureValue = cornerFeatures[i].backward_z;
jumpFeatures.push_back(a_feature);
}
}
}
else //取上沿
{
if ((cornerFeatures[i].type == LINE_FEATURE_RIGHT_ANGLE_HF) || (cornerFeatures[i].type == LINE_FEATURE_RIGHT_ANGLE_RH))
{
double thickness = abs(cornerFeatures[i].forward_z - cornerFeatures[i].backward_z);
if (thickness > cornerPara.minEndingGap_z)
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_L_JUMP_L2H;
a_feature.jumpPos = vldPts[pntIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[pntIdx].nPointIdx }; //取
if (cornerFeatures[i].type == LINE_FEATURE_RIGHT_ANGLE_HF)
a_feature.featureValue = cornerFeatures[i].backward_z;
else
a_feature.featureValue = cornerFeatures[i].forward_z;
jumpFeatures.push_back(a_feature);
}
}
}
}
return;
}
//手套的环的特征:在扫描线的Peak点的前后一定范围内,存在前向角从正到负的变化范围超过150度(门限值)
void wd_getLineGloveArcs(
std::vector<SVzNL3DPosition>& lineData,
int lineIdx,
const SSG_gloveArcParam arcPara,
std::vector<SSG_basicFeature1D>& gloveArcs)
{
//去除零点
std::vector< SVzNL3DPosition> vldPts;
int dataSize = (int)lineData.size();
if (dataSize < 10) //点太少,无法检出特征
return;
for (int i = 0; i < dataSize; i++)
{
if ((lineIdx == 399) && (i == 568))
int kkk = 1;
SVzNL3DPosition a_pt = lineData[i];
a_pt.nPointIdx = i;
if (lineData[i].pt3D.z > 1e-4)
vldPts.push_back(a_pt);
}
//在同一尺度下计算前向角和后向角,以及极大值点。
std::vector< SSG_pntDirAngle> corners; //逐点计算前向角
corners.resize(vldPts.size());
std::vector< int> localMax; //寻找极大值点
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if ((lineIdx == 0) && (i == 2585))
int kkk = 1;
double pre_stepDist = 0;
//前向寻找
bool isMax = true;
int pre_i = -1;
for (int j = i - 1; j >= 0; j--)
{
if ((true == isMax) &&(vldPts[i].pt3D.z <= vldPts[j].pt3D.z))
isMax = false;
double dist = sqrt(pow(vldPts[i].pt3D.y - vldPts[j].pt3D.y, 2) + pow(vldPts[i].pt3D.z - vldPts[j].pt3D.z, 2));
if (j == (i - 1))
pre_stepDist = dist;
if (dist >= arcPara.scale_angle)
{
pre_i = j;
break;
}
}
//后向寻找
double post_stepDist = 0;
int post_i = -1;
for (int j = i + 1; j < i_max; j++)
{
if (( true == isMax) && (vldPts[i].pt3D.z < vldPts[j].pt3D.z))
isMax = false;
double dist = sqrt(pow(vldPts[i].pt3D.y - vldPts[j].pt3D.y, 2) + pow(vldPts[i].pt3D.z - vldPts[j].pt3D.z, 2));
if (j == (i + 1))
post_stepDist = dist;
if (dist >= arcPara.scale_angle)
{
post_i = j;
break;
}
}
if (true == isMax)
localMax.push_back(i);
//计算拐角
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;
corners[i].pre_stepDist = pre_stepDist;
corners[i].post_stepDist = post_stepDist;
}
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);
corners[i].forwardDiffZ = vldPts[post_i].pt3D.z - vldPts[i].pt3D.z;
corners[i].backwardDiffZ = vldPts[i].pt3D.z - vldPts[pre_i].pt3D.z;
corners[i].pre_stepDist = pre_stepDist;
corners[i].post_stepDist = post_stepDist;
}
}
//在极大值前后搜索
for (int i = 0, i_max = (int)localMax.size(); i < i_max; i++)
{
if (i == 31)
int kkk = 1;
int peakIdx = localMax[i];
//向前搜索正的最大前向角
double pre_sumLen = 0;
double pre_angleMax = 0;
for (int j = peakIdx - 1; j >= 0; j--)
{
if (pre_angleMax < corners[j].forwardAngle)
pre_angleMax = corners[j].forwardAngle;
pre_sumLen += corners[j].post_stepDist;
if (pre_sumLen >= arcPara.scale_corner)
break;
}
//向后搜索负的最大前向角
double post_angleMax = corners[peakIdx].forwardAngle;
double post_sumLen = corners[peakIdx].post_stepDist;
for (int j = peakIdx + 1; j < (int)vldPts.size(); j++)
{
post_sumLen += corners[j].post_stepDist;
if (post_sumLen >= arcPara.scale_corner)
break;
if (post_angleMax > corners[j].forwardAngle)
post_angleMax = corners[j].forwardAngle;
}
//计算最大转角
double totalTurn = pre_angleMax - post_angleMax;
if (totalTurn >= arcPara.cornerTh) //有效ARC
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
a_feature.jumpPos = vldPts[peakIdx].pt3D;
a_feature.jumpPos2D = { lineIdx, vldPts[peakIdx].nPointIdx };
a_feature.featureValue = totalTurn;
gloveArcs.push_back(a_feature);
}
}
return;
}
#if 0
typedef struct
{
double holeLen;
double holeCenterY;
double holeCenterX;
int holeSIdx;
int holeEIdx;
}_HoleInfo;
typedef struct
{
double maxZ;
double minZ;
double pk_y;
double pk_x;
int pkIdx;
}_Feature;
//对调平后的直线寻找突起。采用分段法寻找。
void sg_getFlatLineLocalPeaks_vector(
std::vector<SVzNL3DPosition>& lineData,
int lineIdx,
const double scaleWin,
const double minPkHeighth,
const double holeR,
std::vector< SSG_basicFeature1D>& localMax)
{
int dataSize = lineData.size();
if (dataSize < 2)
return;
double maxZ = 0;
double subScale = scaleWin / 3.0;
std::vector<_Feature> subFeatures;
int maxIdx = -1;
int minIdx = -1;
bool startVld = false;
double startY = 0;
int preVldPt = 0;
int holePtNum = 0;
_HoleInfo a_hole = { 0.0, 0.0, 0.0, -1, -1 };
for (int i = 0; i < dataSize; i++)
{
lineData[i].nPointIdx = 0;
if ((lineIdx == 1219) && (i == 2722))
int kkk = 1;
if (lineData[i].pt3D.z < 1e-4)
{
if (true == startVld)
holePtNum++;
}
else
{
if (maxZ < lineData[i].pt3D.z)
maxZ = lineData[i].pt3D.z;
if (false == startVld)
{
startVld = true;
startY = lineData[i].pt3D.y;
}
//检查孔洞
if ((holePtNum > 0) && (preVldPt >= 0))
{
double holeLen = lineData[i].pt3D.y - lineData[preVldPt].pt3D.y;
if (holeLen > holeR / 3)
{
_HoleInfo currHole;
currHole.holeLen = holeLen;
currHole.holeCenterY = (lineData[i].pt3D.y + lineData[preVldPt].pt3D.y) / 2;
currHole.holeCenterX = (lineData[i].pt3D.x + lineData[preVldPt].pt3D.x) / 2;
currHole.holeSIdx = preVldPt;
currHole.holeEIdx = i;
if (a_hole.holeSIdx >= 0)
{
//检查孔洞是否可以合并,如果不能合并,则取大的孔洞
double hole_dist = currHole.holeCenterY - a_hole.holeCenterY;
if (hole_dist < holeR * 2)
{
a_hole.holeEIdx = currHole.holeEIdx;
a_hole.holeLen = lineData[a_hole.holeEIdx].pt3D.y - lineData[a_hole.holeSIdx].pt3D.y;
a_hole.holeCenterY = (lineData[a_hole.holeEIdx].pt3D.y + lineData[a_hole.holeSIdx].pt3D.y) / 2;
}
else if (a_hole.holeLen < currHole.holeLen)
a_hole = currHole;
}
else
a_hole = currHole;
}
//重新检查孔洞
holePtNum = 0;
}
double diffY = lineData[i].pt3D.y - startY;
if (diffY >= subScale) //分段检查
{
if (a_hole.holeSIdx >= 0)
{
_Feature a_subFeature;
a_subFeature.minZ = -1;
a_subFeature.maxZ = -1;
a_subFeature.pk_y = a_hole.holeCenterY;
a_subFeature.pk_x = a_hole.holeCenterX;
a_subFeature.pkIdx = (a_hole.holeSIdx + a_hole.holeEIdx) / 2;
subFeatures.push_back(a_subFeature);
}
else if (maxIdx >= 0)
{
double depth = lineData[maxIdx].pt3D.z - lineData[minIdx].pt3D.z;
if (depth > minPkHeighth / 2)
{
_Feature a_subFeature;
a_subFeature.minZ = lineData[minIdx].pt3D.z;
a_subFeature.maxZ = lineData[maxIdx].pt3D.z;
a_subFeature.pk_x = lineData[maxIdx].pt3D.x;
a_subFeature.pk_y = lineData[maxIdx].pt3D.y;
a_subFeature.pkIdx = maxIdx;
subFeatures.push_back(a_subFeature);
}
}
//分段重新开始
maxIdx = -1;
minIdx = -1;
startY = lineData[i].pt3D.y;
a_hole = { 0.0, 0.0, 0.0, -1, -1 };
}
if (maxIdx < 0)
maxIdx = i;
else
{
if (lineData[maxIdx].pt3D.z < lineData[i].pt3D.z)
maxIdx = i;
}
if (minIdx < 0)
minIdx = i;
else
{
if (lineData[minIdx].pt3D.z > lineData[i].pt3D.z)
minIdx = i;
}
//处理最后一个数据
if (i == dataSize - 1)
{
if (a_hole.holeSIdx >= 0)
{
_Feature a_subFeature;
a_subFeature.minZ = -1;
a_subFeature.maxZ = -1;
a_subFeature.pk_x = a_hole.holeCenterX;
a_subFeature.pk_y = a_hole.holeCenterY;
a_subFeature.pkIdx = (a_hole.holeSIdx + a_hole.holeEIdx) / 2;
subFeatures.push_back(a_subFeature);
}
else if (maxIdx >= 0)
{
_Feature a_subFeature;
a_subFeature.minZ = lineData[minIdx].pt3D.z;
a_subFeature.maxZ = lineData[maxIdx].pt3D.z;
a_subFeature.pk_x = lineData[maxIdx].pt3D.x;
a_subFeature.pk_y = lineData[maxIdx].pt3D.y;
a_subFeature.pkIdx = maxIdx;
subFeatures.push_back(a_subFeature);
}
}
preVldPt = i;
}
}
for (int i = 0, i_max = subFeatures.size(); i < i_max; i++)
{
_Feature* currFeature = &subFeatures[i];
if (currFeature->pkIdx < 0)
continue;
if (currFeature->maxZ < 0) // 孔洞
{
bool isCombined = false;
if (i < i_max - 1) //向后合并
{
_Feature* nxtFeature = &subFeatures[i + 1];
//检查孔洞是否可以合并,如果不能合并,则取大的孔洞
double hole_dist = nxtFeature->pk_y - currFeature->pk_y;
if (hole_dist < holeR*2) //合并
{
if (nxtFeature->maxZ < 0)
{
nxtFeature->pkIdx = (currFeature->pkIdx + nxtFeature->pkIdx) / 2;
nxtFeature->pk_x = (currFeature->pk_x + nxtFeature->pk_x) / 2;
nxtFeature->pk_y = (currFeature->pk_y + nxtFeature->pk_y) / 2;
}
currFeature->pkIdx = -1; //合并标记
isCombined = true;
}
}
if (false == isCombined)
{
//生成特征
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
a_feature.jumpPos2D = { lineIdx, currFeature->pkIdx };
a_feature.jumpPos.x = currFeature->pk_x;
a_feature.jumpPos.y = currFeature->pk_y;
a_feature.jumpPos.z = maxZ;
localMax.push_back(a_feature);
lineData[currFeature->pkIdx].nPointIdx = LINE_FEATURE_PEAK_TOP; //nPointIdx被转义使用
lineData[currFeature->pkIdx].pt3D = a_feature.jumpPos;
}
}
else
{
bool isBigger_1 = true; //比前一个大
bool isBigger_2 = true; //比后一个大
double minZ = currFeature->minZ;
if (i > 0)
{
_Feature* preFeature = &subFeatures[i - 1];
if (preFeature->maxZ < 0)
{
if(preFeature->pkIdx >= 0)
isBigger_1 = false;
}
else
{
if (currFeature->minZ > preFeature->minZ)
minZ = preFeature->minZ;
if (currFeature->maxZ < preFeature->maxZ)
isBigger_1 = false;
}
}
if (i < i_max - 1)
{
_Feature* nxtFeature = &subFeatures[i + 1];
if (nxtFeature->maxZ < 0)
{
double hole_dist = nxtFeature->pk_y - currFeature->pk_y;
if (hole_dist >= holeR * 2)
isBigger_2 = false;
else
nxtFeature->pkIdx = -1; //后面的孔被合并
}
else
{
if (currFeature->minZ > nxtFeature->minZ)
minZ = nxtFeature->minZ;
if (currFeature->maxZ < nxtFeature->maxZ)
isBigger_2 = false;
}
}
if ((true == isBigger_1) && (true == isBigger_2))
{
double z_diff = currFeature->maxZ - minZ;
if (z_diff > minPkHeighth) //合格的突起
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
a_feature.jumpPos2D = { lineIdx, currFeature->pkIdx };
a_feature.jumpPos = lineData[currFeature->pkIdx].pt3D;
localMax.push_back(a_feature);
lineData[currFeature->pkIdx].nPointIdx = LINE_FEATURE_PEAK_TOP; //nPointIdx被转义使用
}
}
}
}
return;
}
#else
//对调平后的直线寻找突起。
//根据斜率寻找:寻找跳变、斜坡、Gap,进行配对
typedef struct
{
int type;
int ptIdxS;
int ptIdxE;
int sIdx;
int eIdx;
SVzNL3DPoint start;
SVzNL3DPoint end;
double delta_y;
double delta_z;
double k;
}_FeatureInfo;
int _findSegMaxZPt(
std::vector<SVzNL3DPosition>& lineData,
int searchStart,
bool searchToHead,
double searchWin,
double* meanZ,
bool* foundJump)
{
int dataSize = (int)lineData.size();
double start_y = lineData[searchStart].pt3D.y;
double start_z = lineData[searchStart].pt3D.z;
if (start_z < 1e-4)
return -1;
double max_z = start_z;
int max_idx = searchStart;
double sum_z = max_z;
int sum_num = 1;
int step = searchToHead == true ? -1 : 1;
int idx = searchStart;
while (1)
{
idx += step;
if ((idx <= 0) || (idx >= dataSize))
break;
if (lineData[idx].nPointIdx == LINE_FEATURE_NUM)
{
*foundJump = true;
break;
}
double pt_y = lineData[idx].pt3D.y;
double pt_z = lineData[idx].pt3D.z;
if (pt_z > 1e-4)
{
double dist = abs(pt_y - start_y);
if (dist > searchWin)
{
*meanZ = sum_z / sum_num;
return max_idx;
}
else
{
sum_z += pt_z;
sum_num++;
if (max_z < pt_z)
{
max_z = pt_z;
max_idx = idx;
}
}
}
}
return -1;
}
void sg_getFlatLineLocalPeaks_vector(
std::vector<SVzNL3DPosition>& lineData,
int lineIdx,
const double scaleWin,
const double minPkHeighth,
const double holeR,
std::vector< SSG_basicFeature1D>& localMax)
{
int dataSize = (int)lineData.size();
if (dataSize < 2)
return;
//加速,进行抽样
//抽样的点中去除空点
double slopeZDeltaTH = 0.2; //slope的z变化门限,大于此门限为有限slope
int dwnSample = 1;
int sampleNum = dataSize / dwnSample;
std::vector<SVzNL3DPosition> sampleData;
for (int i = 0; i < sampleNum; i++)
{
SVzNL3DPosition a_sample = lineData[i * dwnSample];
a_sample.nPointIdx = i * dwnSample;
if(a_sample.pt3D.z > 1e-4)
sampleData.push_back(a_sample);
}
sampleNum = (int)sampleData.size();
std::vector< _FeatureInfo> allSubFeatures;
allSubFeatures.resize(sampleNum);
for (int i = 0; i < sampleNum; i++) //初始化
allSubFeatures[i].type = LINE_FEATURE_UNDEF;
int segEnd = 0;
for (int i = 0; i < sampleNum-1; i++)
{
SVzNL3DPosition* samplePtr = &sampleData[i];
if ((lineIdx == 0) && (i == 2290))
int kkk = 1;
//检查相邻两点是否为跳变或gap
SVzNL3DPosition* nxtSamplePtr = &sampleData[i+1];
//double d_delta = sqrt(pow(nxtSamplePtr->pt3D.y - samplePtr->pt3D.y, 2) + pow(nxtSamplePtr->pt3D.z - samplePtr->pt3D.z, 2));
double y_delta = abs(nxtSamplePtr->pt3D.y - samplePtr->pt3D.y);
double z_delta = nxtSamplePtr->pt3D.z - samplePtr->pt3D.z;
if ((y_delta > holeR / 2) || (abs(z_delta) > minPkHeighth))
{
_FeatureInfo a_subFeature;
a_subFeature.delta_y = y_delta;
a_subFeature.delta_z = z_delta;
a_subFeature.k = z_delta / y_delta;
a_subFeature.ptIdxS = i;
a_subFeature.ptIdxE = i+1;
a_subFeature.sIdx = sampleData[i].nPointIdx;
a_subFeature.eIdx = sampleData[i + 1].nPointIdx;
a_subFeature.start = samplePtr->pt3D;
a_subFeature.end = nxtSamplePtr->pt3D;
if(z_delta > 0)
a_subFeature.type = LINE_FEATURE_L_JUMP_L2H;
else
a_subFeature.type = LINE_FEATURE_L_JUMP_H2L;
allSubFeatures[i] = a_subFeature;
segEnd = i + 1;
}
else
{
//寻找尺度端点
for (int j = segEnd; j < sampleNum; j++)
{
SVzNL3DPosition* postSamplePtr = &sampleData[j];
double deltaY = abs(postSamplePtr->pt3D.y - samplePtr->pt3D.y);
if (deltaY >= scaleWin)
{
double deltaZ = postSamplePtr->pt3D.z - samplePtr->pt3D.z;
if (abs(deltaZ) > slopeZDeltaTH)
int kkk = 1;
_FeatureInfo a_subFeature;
a_subFeature.delta_y = deltaY;
a_subFeature.delta_z = deltaZ;
a_subFeature.k = deltaZ / deltaY;
a_subFeature.ptIdxS = i;
a_subFeature.ptIdxE = j;
a_subFeature.sIdx = sampleData[i].nPointIdx;
a_subFeature.eIdx = sampleData[j].nPointIdx;
a_subFeature.start = samplePtr->pt3D;
a_subFeature.end = postSamplePtr->pt3D;
if ( (abs(a_subFeature.k) > 0.5) && (abs(deltaZ) > slopeZDeltaTH))
{
if (deltaZ > 0)
a_subFeature.type = LINE_FEATURE_L_SLOPE_L2H;
else
a_subFeature.type = LINE_FEATURE_L_SLOPE_H2L;
}
else
a_subFeature.type = LINE_FEATURE_UNDEF;
allSubFeatures[i] = a_subFeature;
segEnd = j;
break;
}
}
}
}
//处理JUMP, 将相邻的LINE_FEATURE_L_SLOPE_L2H无效
for (int i = 0; i < sampleNum; i++)
{
if (allSubFeatures[i].type == LINE_FEATURE_L_JUMP_L2H)
{
//将相邻的LINE_FEATURE_L_SLOPE_L2H无效
for (int j = i + 1; j < sampleNum; j++)
{
if (allSubFeatures[j].type == LINE_FEATURE_L_SLOPE_L2H)
allSubFeatures[j].type = LINE_FEATURE_UNDEF;
else
break;
}
}
else if (allSubFeatures[i].type == LINE_FEATURE_L_JUMP_H2L)
{
//将相邻的LINE_FEATURE_L_SLOPE_L2H无效
for (int j = i - 1; j >= 0; j--)
{
if (allSubFeatures[j].type == LINE_FEATURE_L_SLOPE_H2L)
allSubFeatures[j].type = LINE_FEATURE_UNDEF;
else
break;
}
}
}
#if 0
std::vector< _FeatureInfo> subFeatures;
for (int i = 0; i < sampleNum; i++)
{
if (allSubFeatures[i].type != LINE_FEATURE_UNDEF)
subFeatures.push_back(allSubFeatures[i]);
}
_FeatureInfo a_nullFeature;
memset(&a_nullFeature, 0, sizeof(_FeatureInfo));
subFeatures.push_back(a_nullFeature); //在结尾补上一个空,序列处理时会正确处理最后一个
int subfeatureSize = subFeatures.size();
#endif
//将合格的subFeature挑选出来
int pre_type = LINE_FEATURE_UNDEF;
_FeatureInfo a_vldFeature;
std::vector< _FeatureInfo> vldFeatures;
for (int i = 0; i < sampleNum; i++)
{
if (pre_type == LINE_FEATURE_UNDEF)
{
if ( (allSubFeatures[i].type == LINE_FEATURE_L_JUMP_H2L) ||
(allSubFeatures[i].type == LINE_FEATURE_L_JUMP_L2H))
{
vldFeatures.push_back(allSubFeatures[i]);
pre_type = LINE_FEATURE_UNDEF;
}
else
{
a_vldFeature = allSubFeatures[i];
pre_type = allSubFeatures[i].type;
}
}
else
{
if (allSubFeatures[i].type == LINE_FEATURE_UNDEF)
{
vldFeatures.push_back(a_vldFeature);
}
else
{
if (((a_vldFeature.k < 0) && (allSubFeatures[i].k < 0)) ||
((a_vldFeature.k > 0) && (allSubFeatures[i].k > 0)))
{
if (abs(a_vldFeature.k) < abs(allSubFeatures[i].k))
a_vldFeature = allSubFeatures[i];
}
else
{
vldFeatures.push_back(a_vldFeature);
a_vldFeature = allSubFeatures[i];
}
}
pre_type = allSubFeatures[i].type;
}
}
//配对,生成特征
//优先对Jump进行配对
std::vector<SSG_basicFeature1D> featureBuffer;
featureBuffer.resize(vldFeatures.size());
for (int i = 0; i < vldFeatures.size(); i++) //初始化
featureBuffer[i].featureType = LINE_FEATURE_UNDEF;
for (int i = 0, i_max = (int)vldFeatures.size(); i < i_max; i++)
{
if (vldFeatures[i].type == LINE_FEATURE_L_JUMP_H2L)
{
//判断z的幅度
if (abs(vldFeatures[i].delta_z) < minPkHeighth) //没有起伏,可以匹配后面的JUMP
{
//生成特征
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
int centerIdx = (vldFeatures[i].sIdx + vldFeatures[i].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i].end.z) / 2;
int start = vldFeatures[i].sIdx;
int end = vldFeatures[i].eIdx;
//尝试匹配后面的JUMP
if (i < i_max - 1)
{
if ((vldFeatures[i + 1].type == LINE_FEATURE_L_JUMP_H2L) ||
(vldFeatures[i + 1].type == LINE_FEATURE_L_JUMP_L2H))
{
//判断距离
double dist = abs(vldFeatures[i + 1].start.y - vldFeatures[i].end.y);
if (dist < holeR) //合并
{
centerIdx = (vldFeatures[i].sIdx + vldFeatures[i+1].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i+1].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i+1].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i+1].end.z) / 2;
vldFeatures[i + 1].type = LINE_FEATURE_UNDEF;
end = vldFeatures[i+1].eIdx;
}
}
}
featureBuffer[i] = a_feature;
lineData[start].nPointIdx = LINE_FEATURE_NUM; //起点和终点作标识。使用LINE_FEATURE_NUM作标识码
lineData[end].nPointIdx = LINE_FEATURE_NUM;
}
else //有明显下降,只能匹配后面没有起伏的JUMP
{
//生成特征
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
int centerIdx = (vldFeatures[i].sIdx + vldFeatures[i].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i].end.z) / 2;
int start = vldFeatures[i].sIdx;
int end = vldFeatures[i].eIdx;
if (i < i_max - 1)
{
if ((vldFeatures[i + 1].type == LINE_FEATURE_L_JUMP_H2L) ||
(vldFeatures[i + 1].type == LINE_FEATURE_L_JUMP_L2H))
{
if (abs(vldFeatures[i + 1].delta_z) < minPkHeighth) //只匹配没有起伏的
{
//判断距离
double dist = abs(vldFeatures[i + 1].start.y - vldFeatures[i].end.y);
if (dist < holeR) //合并
{
centerIdx = (vldFeatures[i].sIdx + vldFeatures[i + 1].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i + 1].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i + 1].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i + 1].end.z) / 2;
vldFeatures[i + 1].type = LINE_FEATURE_UNDEF;
end = vldFeatures[i + 1].eIdx;
}
}
}
}
featureBuffer[i] = a_feature;
lineData[start].nPointIdx = LINE_FEATURE_NUM; //起点和终点作标识。使用LINE_FEATURE_NUM作标识码
lineData[end].nPointIdx = LINE_FEATURE_NUM;
}
}
else if (vldFeatures[i].type == LINE_FEATURE_L_JUMP_L2H)
{
//判断z的幅度
if (abs(vldFeatures[i].delta_z) < minPkHeighth) //没有起伏,可以匹配后面的JUMP
{
//直接匹配后面的JUMP
//生成特征
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
int centerIdx = (vldFeatures[i].sIdx + vldFeatures[i].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i].end.z) / 2;
int start = vldFeatures[i].sIdx;
int end = vldFeatures[i].eIdx;
//尝试匹配后面的JUMP
if (i < i_max - 1)
{
if ((vldFeatures[i + 1].type == LINE_FEATURE_L_JUMP_H2L) ||
(vldFeatures[i + 1].type == LINE_FEATURE_L_JUMP_L2H))
{
//判断距离
double dist = abs(vldFeatures[i + 1].start.y - vldFeatures[i].end.y);
if (dist < holeR) //合并
{
centerIdx = (vldFeatures[i].sIdx + vldFeatures[i + 1].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i + 1].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i + 1].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i + 1].end.z) / 2;
vldFeatures[i + 1].type = LINE_FEATURE_UNDEF;
end = vldFeatures[i + 1].eIdx;
}
}
}
featureBuffer[i] = a_feature;
lineData[start].nPointIdx = LINE_FEATURE_NUM; //起点和终点作标识。使用LINE_FEATURE_NUM作标识码
lineData[end].nPointIdx = LINE_FEATURE_NUM;
}
else //有起伏的,可以匹配后面的下降JUMP和没有起伏的JUMP和SLOPE
{
//生成特征
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
int centerIdx = (vldFeatures[i].sIdx + vldFeatures[i].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i].end.z) / 2;
int start = vldFeatures[i].sIdx;
int end = vldFeatures[i].eIdx;
if (i < i_max - 1)
{
if ((vldFeatures[i + 1].type == LINE_FEATURE_L_JUMP_H2L) ||
((vldFeatures[i + 1].type == LINE_FEATURE_L_JUMP_L2H) && (abs(vldFeatures[i+1].delta_z) < minPkHeighth)) ||
(vldFeatures[i + 1].type == LINE_FEATURE_L_SLOPE_H2L))
{
//判断距离
double dist = abs(vldFeatures[i + 1].start.y - vldFeatures[i].end.y);
if (dist < holeR) //合并
{
centerIdx = (vldFeatures[i].sIdx + vldFeatures[i + 1].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i + 1].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i + 1].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i + 1].end.z) / 2;
vldFeatures[i + 1].type = LINE_FEATURE_UNDEF;
end = vldFeatures[i + 1].eIdx;
}
}
}
featureBuffer[i] = a_feature;
lineData[start].nPointIdx = LINE_FEATURE_NUM; //起点和终点作标识。使用LINE_FEATURE_NUM作标识码
lineData[end].nPointIdx = LINE_FEATURE_NUM;
}
}
}
//对SLOPE进行配对
for (int i = 0, i_max = (int)vldFeatures.size(); i < i_max; i++)
{
if (vldFeatures[i].type == LINE_FEATURE_L_SLOPE_L2H)
{
if (i < i_max - 1)
{
double dist = abs(vldFeatures[i + 1].start.y - vldFeatures[i].end.y);
if ((dist < holeR) && (vldFeatures[i + 1].type == LINE_FEATURE_L_SLOPE_H2L))
{
//生成特征
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
int centerIdx = (vldFeatures[i].sIdx + vldFeatures[i + 1].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i + 1].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i + 1].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i + 1].end.z) / 2;
vldFeatures[i + 1].type = LINE_FEATURE_UNDEF;
featureBuffer[i] = a_feature;
}
else
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
#if 0
int centerIdx = (vldFeatures[i].sIdx + vldFeatures[i].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i].end.z) / 2;
#endif
//在HoleR范围内寻找Peak点
double mean_z = -1;
bool foundJump = false;
int pkIdx = _findSegMaxZPt(lineData, vldFeatures[i].eIdx, false, holeR*1.5, &mean_z, &foundJump);
//反向寻找是否有Jump
double meanZ_inv = -1;
bool foundJump_inv = false;
int pkIdx_inv = _findSegMaxZPt(lineData, vldFeatures[i].sIdx, true, holeR * 1.5, &meanZ_inv, &foundJump_inv);
if ( (pkIdx >= 0)&&(false == foundJump_inv))
{
double mean_depth = mean_z - vldFeatures[i].start.z;
if (pkIdx_inv >= 0)
mean_depth = mean_z - meanZ_inv;
if (mean_depth >= slopeZDeltaTH/2)
{
a_feature.jumpPos2D = { lineIdx, pkIdx };
a_feature.jumpPos = lineData[pkIdx].pt3D;
featureBuffer[i] = a_feature;
}
}
}
}
}
else if (vldFeatures[i].type == LINE_FEATURE_L_SLOPE_H2L)
{
//在HoleR范围内寻找Peak点
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_PEAK_TOP;
#if 0
int centerIdx = (vldFeatures[i].sIdx + vldFeatures[i].eIdx) / 2;
a_feature.jumpPos2D = { lineIdx, centerIdx };
a_feature.jumpPos.x = (vldFeatures[i].start.x + vldFeatures[i].end.x) / 2;
a_feature.jumpPos.y = (vldFeatures[i].start.y + vldFeatures[i].end.y) / 2;
a_feature.jumpPos.z = (vldFeatures[i].start.z + vldFeatures[i].end.z) / 2;
#endif
//在HoleR范围内寻找Peak点
double mean_z = -1;
bool foundJump = false;
int pkIdx = _findSegMaxZPt(lineData, vldFeatures[i].sIdx, true, holeR*1.5, &mean_z, &foundJump);
//反向寻找是否有Jump
double meanZ_inv = -1;
bool foundJump_inv = false;
int pkIdx_inv = _findSegMaxZPt(lineData, vldFeatures[i].eIdx, false, holeR * 1.5, &meanZ_inv, &foundJump_inv);
if ((pkIdx >= 0) && (false == foundJump_inv))
{
double mean_depth = mean_z - vldFeatures[i].start.z;
if (pkIdx_inv >= 0)
mean_depth = mean_z - meanZ_inv;
if (mean_depth >= slopeZDeltaTH/2)
{
a_feature.jumpPos2D = { lineIdx, pkIdx };
a_feature.jumpPos = lineData[pkIdx].pt3D;
featureBuffer[i] = a_feature;
}
}
}
}
for (int i = 0, i_max = (int)vldFeatures.size(); i < i_max; i++)
{
if (featureBuffer[i].featureType != LINE_FEATURE_UNDEF)
{
localMax.push_back(featureBuffer[i]);
if (lineData[featureBuffer[i].jumpPos2D.y].pt3D.z < 1e-4)
lineData[featureBuffer[i].jumpPos2D.y].pt3D = featureBuffer[i].jumpPos;
lineData[featureBuffer[i].jumpPos2D.y].nPointIdx = LINE_FEATURE_PEAK_TOP; //nPointIdx被转义使用
}
}
return;
}
#endif
/// <summary>
/// 提取激光线上的下跳变点(Z值变大的跳变)
///
/// </summary>
void sg_getLineDownJumps(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double jumpTh,
std::vector< SSG_basicFeature1D>& downJumps)
{
if (dataSize < 2)
return;
int pre_i = -1;
SVzNL3DPosition* pre_data = NULL;
for (int i = 0; i < dataSize; i++)
{
if (i == 830)
int kkk = 1;
lineData[i].nPointIdx = i;
SVzNL3DPosition* curr_data = &lineData[i];
if (curr_data->pt3D.z < 1e-4)
{
continue;
}
if (NULL == pre_data)
{
pre_data = curr_data;
pre_i = i;
continue;
}
double z_diff = curr_data->pt3D.z - pre_data->pt3D.z;
if (z_diff > jumpTh)
{
SSG_basicFeature1D a_feature;
a_feature.featureType = LINE_FEATURE_L_JUMP_H2L;
a_feature.jumpPos = lineData[pre_i].pt3D;
a_feature.jumpPos2D = { lineIdx, lineData[pre_i].nPointIdx };
downJumps.push_back(a_feature);
}
pre_data = curr_data;
pre_i = i;
}
return;
}
int _getSegPeak(SVzNL3DPosition* lineData, int segStartIdx, int segEndIdx)
{
double pkZ = -1;
int pkZIdx = -1;
for (int i = segStartIdx; i <= segEndIdx; i++)
{
if (lineData[i].pt3D.z < 1e-4)
continue;
if (pkZ < 0)
{
pkZ = lineData[i].pt3D.z;
pkZIdx = i;
}
else
{
if (pkZ > lineData[i].pt3D.z)
{
pkZ = lineData[i].pt3D.z;
pkZIdx = i;
}
}
}
return pkZIdx;
}
int _getYNearestPtIdx(SVzNL3DPosition* lineData, int segStartIdx, int segEndIdx, double midY)
{
double minDiff = -1;
int pkZIdx = -1;
for (int i = segStartIdx; i <= segEndIdx; i++)
{
if (lineData[i].pt3D.z < 1e-4)
continue;
double yDiff = abs(lineData[i].pt3D.y - midY);
if (minDiff < 0)
{
minDiff = yDiff;
pkZIdx = i;
}
else
{
if (minDiff > yDiff)
{
minDiff = yDiff;
pkZIdx = i;
}
}
}
return pkZIdx;
}
/// <summary>
/// 提取激光线上的圆柱形特征
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// (1)逐点计算前向角和后向角
/// (2)逐点计算拐角,顺时针为负,逆时针为正
/// (3)搜索正拐角的极大值。
/// (4)判断拐角是否为跳变
/// </summary>
void sg_getLineUpperSemiCircleFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const double sieveDiameter,
const SSG_slopeParam slopePara,
std::vector< SSG_featureSemiCircle>& result_features,
cv::Mat& holeMask) //根据holeMask进行合并
{
if (lineIdx == 194)
int kkk = 1;
//去除零点
std::vector< SVzNL3DPosition> vldPts;
std::vector<SSG_RUN> segs;
int segStart = -1, segEnd = -1;
for (int i = 0; i < dataSize; i++)
{
SVzNL3DPosition a_pt = lineData[i];
a_pt.nPointIdx = i;
if (lineData[i].pt3D.z > 1e-4)
{
if (segStart < 0)
segStart = i;
segEnd = i;
vldPts.push_back(a_pt);
}
else
{
if (segStart >= 0)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
segStart = -1;
segEnd = -1;
}
}
}
//last
if (segStart >= 0)
{
SSG_RUN a_run;
a_run.start = segStart;
a_run.len = segEnd - segStart + 1;
a_run.value = 1;
segs.push_back(a_run);
}
//检查seg是否需要合并;向后合并
for (int i = 0, i_max = (int)segs.size(); i < i_max - 1; i++)
{
SSG_RUN* nxt_seg = &segs[i + 1];
SSG_RUN* curr_seg = &segs[i];
int idx_1 = curr_seg->start + curr_seg->len - 1;
int idx_2 = nxt_seg->start;
double y_diff = abs(lineData[idx_1].pt3D.y - lineData[idx_2].pt3D.y);
if (y_diff < slopePara.minEndingGap) //合并
{
int idx_end = nxt_seg->start + nxt_seg->len - 1;
nxt_seg->start = curr_seg->start;
nxt_seg->len = idx_end - curr_seg->start + 1;
curr_seg->value = 0;
}
}
//获取localPeak
std::vector< SSG_basicFeature1D> localMax;
std::vector< SSG_basicFeature1D> localMin;
std::vector<int> endingFlag;
endingFlag.resize(dataSize);
for (int i = 0; i < dataSize; i++)
endingFlag[i] = 0;
for (int i = 0,i_max= (int)segs.size(); i < i_max; i++)
{
if (0 == segs[i].value) //被合并
continue;
int idx_1 = segs[i].start;
int idx_2 = segs[i].start + segs[i].len - 1;
endingFlag[idx_1] = 2;
endingFlag[idx_2] = 3;
std::vector< SSG_basicFeature1D> segMax;
std::vector< SSG_basicFeature1D> segMin;
sg_getLineLocalPeaks(
&lineData[idx_1],
segs[i].len,
lineIdx,
slopePara.LSlopeZWin,
segMax,
segMin);
for (int m = 0, m_max = (int)segMax.size(); m < m_max; m++)
{
segMax[m].jumpPos2D.y += idx_1;
localMax.push_back(segMax[m]);
}
for (int m = 0, m_max = (int)segMin.size(); m < m_max; m++)
{
segMin[m].jumpPos2D.y += idx_1;
localMin.push_back(segMin[m]);
}
}
//计算slope的Z变化
std::vector< SSG_zWin> slopes; //记录窗口长度内的z变化
slopes.resize(vldPts.size());
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
//后向寻找
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 >= slopePara.LSlopeZWin)
{
post_i = j;
break;
}
}
//计算slope
if (post_i < 0)
{
slopes[i].startPtIdx = i;
slopes[i].endPtIdx = -1;
slopes[i].zDiff = 0;
}
else
{
slopes[i].startPtIdx = i;
slopes[i].endPtIdx = post_i;
slopes[i].zDiff = vldPts[post_i].pt3D.z - vldPts[i].pt3D.z;
}
}
//搜索zWin极值
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
SSG_zWin* pre_data = NULL;
std::vector< SSG_zWin> zWinPeakP; //正的zWin
std::vector< SSG_zWin> zWinPeakM; //负的zWin
SSG_zWin* maxPkPlus = NULL;
SSG_zWin* maxPkMinus = NULL;
for (int i = 0, i_max = (int)vldPts.size(); i < i_max; i++)
{
if (i == 275)
int kkk = 1;
SSG_zWin* curr_data = &slopes[i];
if ( curr_data->endPtIdx < 0)
{
if ( (i == i_max - 1) && (pre_data != NULL)) //最后一个
{
if (1 == _state) //上升
{
if (pre_data->zDiff > 0)
zWinPeakP.push_back(slopes[eEdgePtIdx]);
}
else if (2 == _state) //下降
{
if (pre_data->zDiff < 0)
zWinPeakM.push_back(slopes[eEdgePtIdx]);
}
}
continue;
}
if (NULL == pre_data)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
continue;
}
eEdgePtIdx = i;
double zDiffDelta = curr_data->zDiff - pre_data->zDiff;
switch (_state)
{
case 0: //初态
if (zDiffDelta < 0) //下降
{
if (curr_data->zDiff < 0)
{
maxPkMinus = curr_data;
_state = 2;
}
}
else if (zDiffDelta > 0) //上升
{
if (curr_data->zDiff > 0)
{
maxPkPlus = curr_data;
_state = 1;
}
}
break;
case 1: //上升
if (zDiffDelta < 0) //下降
{
if(maxPkPlus)
zWinPeakP.push_back(*maxPkPlus);
maxPkPlus = NULL;
if (curr_data->zDiff < 0)
maxPkMinus = curr_data;
else
maxPkMinus = NULL;
_state = 2;
}
else
{
//记录最大slope
if (curr_data->zDiff > 0)
{
if (NULL == maxPkPlus)
maxPkPlus = curr_data;
else
{
if(maxPkPlus->zDiff < curr_data->zDiff)
maxPkPlus = curr_data;
}
}
}
break;
case 2: //下降
if (zDiffDelta > 0) // 上升
{
if (maxPkMinus)
zWinPeakM.push_back(*maxPkMinus);
maxPkMinus = NULL;
if (curr_data->zDiff > 0)
maxPkPlus = curr_data;
else
maxPkPlus = NULL;
_state = 1;
}
else
{
//记录最大slope
if (curr_data->zDiff < 0)
{
if (NULL == maxPkMinus)
maxPkMinus = curr_data;
else
{
if (maxPkMinus->zDiff > curr_data->zDiff)
maxPkMinus = curr_data;
}
}
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//极值比较,在尺度窗口下寻找局部极值点
std::vector<SSG_zWin> validZWinPeakP;
double square_distTh = 4 * slopePara.LSlopeZWin * slopePara.LSlopeZWin; //2倍的cornerScale。
for (int i = 0, i_max = (int)zWinPeakP.size(); i < i_max; i++)
{
if (zWinPeakP[i].zDiff < slopePara.validSlopeH)
continue;
bool isPeak = true;
//向前搜索
int currPtIdx = zWinPeakP[i].startPtIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = zWinPeakP[j].startPtIdx;
double dist = pow(vldPts[currPtIdx].pt3D.y - vldPts[prePtIdx].pt3D.y, 2); // + pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (zWinPeakP[i].zDiff < zWinPeakP[j].zDiff)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
currPtIdx = zWinPeakP[i].startPtIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = zWinPeakP[j].startPtIdx;
double dist = pow(vldPts[currPtIdx].pt3D.y - vldPts[postPtIdx].pt3D.y, 2); // +pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (zWinPeakP[i].zDiff < zWinPeakP[j].zDiff)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
SSG_zWin vldPk;
vldPk.startPtIdx = vldPts[zWinPeakP[i].startPtIdx].nPointIdx;
vldPk.endPtIdx = vldPts[zWinPeakP[i].endPtIdx].nPointIdx;
vldPk.zDiff = zWinPeakP[i].zDiff;
validZWinPeakP.push_back(vldPk);
//endingFlag[vldPk.startPtIdx] = 4;
}
}
//极值比较,在尺度窗口下寻找局部极值点
std::vector<SSG_zWin> validZWinPeakM;
for (int i = 0, i_max = (int)zWinPeakM.size(); i < i_max; i++)
{
if (zWinPeakM[i].zDiff > -slopePara.validSlopeH)
continue;
bool isPeak = true;
//向前搜索
int currPtIdx = zWinPeakM[i].startPtIdx;
for (int j = i - 1; j >= 0; j--)
{
int prePtIdx = zWinPeakM[j].startPtIdx;
double dist = pow(vldPts[currPtIdx].pt3D.y - vldPts[prePtIdx].pt3D.y, 2); // + pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2) ;
if (dist > square_distTh) //超出尺度窗口
break;
if (zWinPeakM[i].zDiff > zWinPeakM[j].zDiff)
{
isPeak = false;
break;
}
}
//向后搜索
if (true == isPeak)
{
currPtIdx = zWinPeakM[i].startPtIdx;
for (int j = i + 1; j < i_max; j++)
{
int postPtIdx = zWinPeakM[j].startPtIdx;
double dist = pow(vldPts[currPtIdx].pt3D.y - vldPts[postPtIdx].pt3D.y, 2); // +pow(pkTop[i].pt3D.x - pkTop[j].pt3D.x, 2);
if (dist > square_distTh) //超出尺度窗口
break;
if (zWinPeakM[i].zDiff > zWinPeakM[j].zDiff)
{
isPeak = false;
break;
}
}
}
if (true == isPeak)
{
SSG_zWin vldPk;
vldPk.startPtIdx = vldPts[zWinPeakM[i].startPtIdx].nPointIdx;
vldPk.endPtIdx = vldPts[zWinPeakM[i].endPtIdx].nPointIdx;
vldPk.zDiff = zWinPeakM[i].zDiff;
validZWinPeakM.push_back(vldPk);
//endingFlag[vldPk.endPtIdx] = 5;
}
}
//对validCornerPeakP进行配对
std::vector< SSG_featureSemiCircle> line_features;
for (int i = 0, i_max = (int)localMin.size(); i < i_max; i++)
{
int ptIdx = localMin[i].jumpPos2D.y;
if (endingFlag[ptIdx] > 0) //已经被处理
continue;
//向前搜索
int preIdx = -1;
for (int j = ptIdx - 1; j >= 0; j--)
{
if (endingFlag[j] > 0)
{
preIdx = j;
break;
}
}
//身后搜索
int postIdx = -1;
for (int j = ptIdx + 1; j < dataSize; j++)
{
if (endingFlag[j] > 0)
{
postIdx = j;
break;
}
}
if ((preIdx >= 0) && (postIdx >= 0))
{
int preFlag = endingFlag[preIdx];
int postFlag = endingFlag[postIdx];
if ((2 == preFlag ) && (3 == postFlag)) //段落情况,筛网悬空会出现这种情况
{
double yDist = abs(lineData[preIdx].pt3D.y - lineData[postIdx].pt3D.y);
if (yDist < sieveDiameter * 4) //valid
{
SSG_featureSemiCircle a_feature;
a_feature.flag = 0;
a_feature.startPtIdx = preIdx;
a_feature.endPtIdx = postIdx;
double midY = (lineData[preIdx].pt3D.y + lineData[postIdx].pt3D.y) / 2.0;
a_feature.width = yDist;
a_feature.lineIdx = lineIdx;
a_feature.midPtIdx = _getYNearestPtIdx(lineData, preIdx, postIdx, midY);
a_feature.midPt = lineData[a_feature.midPtIdx].pt3D;
if (a_feature.midPtIdx >= 0)
{
int pkIdx = _getSegPeak(lineData, preIdx, postIdx);
a_feature.pkHeight = lineData[pkIdx].pt3D.z;
line_features.push_back(a_feature);
for (int m = a_feature.startPtIdx; m <= a_feature.endPtIdx; m++)
{
if (0 == endingFlag[m])
endingFlag[m] = 1;
}
}
}
}
else if ( ((5 == preFlag) && (4 == postFlag)) || //有底板情况,筛网放置在一个平面上会出现这种情况
((2 == preFlag) && (4 == postFlag)) ||
((5 == preFlag) && (3 == postFlag)))
{
double yDist = abs(lineData[preIdx].pt3D.y - lineData[postIdx].pt3D.y);
if (yDist < sieveDiameter * 4) //valid
{
SSG_featureSemiCircle a_feature;
a_feature.flag = 0;
a_feature.startPtIdx = preIdx;
a_feature.endPtIdx = postIdx;
a_feature.width = abs(lineData[preIdx].pt3D.y - lineData[postIdx].pt3D.y);
a_feature.lineIdx = lineIdx;
double midY = (lineData[preIdx].pt3D.y + lineData[postIdx].pt3D.y) / 2.0;
a_feature.midPtIdx = _getYNearestPtIdx(lineData, preIdx, postIdx, midY);
a_feature.midPt = lineData[a_feature.midPtIdx].pt3D;
if (a_feature.midPtIdx >= 0)
{
int pkPtIdx = _getSegPeak(lineData, preIdx, postIdx);
a_feature.pkHeight = lineData[pkPtIdx].pt3D.z;
line_features.push_back(a_feature);
for (int m = a_feature.startPtIdx; m <= a_feature.endPtIdx; m++)
{
if (0 == endingFlag[m])
endingFlag[m] = 1;
}
}
}
}
}
}
//根据孔洞目标进行合并
for (int i = 0, i_max = (int)line_features.size(); i < i_max; i++)
{
if (FEATURE_FLAG_INVALID == line_features[i].flag)
continue;
if (i < i_max - 1)
{
int idx1 = line_features[i].endPtIdx + 1;
int holeId1 = holeMask.at<int>(idx1, lineIdx);
int idx2 = line_features[i + 1].startPtIdx - 1;
int holeId2 = holeMask.at<int>(idx2, lineIdx);
if( (holeId1 > 0) && (holeId1 == holeId2))
{
//合并
line_features[i].endPtIdx = line_features[i + 1].endPtIdx;
double midY = (lineData[line_features[i].startPtIdx].pt3D.y + lineData[line_features[i].endPtIdx].pt3D.y) / 2;
line_features[i].midPtIdx = _getYNearestPtIdx(lineData, line_features[i].startPtIdx, line_features[i].endPtIdx, midY);
line_features[i].midPt = lineData[line_features[i].midPtIdx].pt3D;
line_features[i].width = abs(lineData[line_features[i].startPtIdx].pt3D.y - lineData[line_features[i].endPtIdx].pt3D.y);
int pkPtIdx = _getSegPeak(lineData, line_features[i].startPtIdx, line_features[i].endPtIdx);
line_features[i].pkHeight = lineData[pkPtIdx].pt3D.z;
result_features.push_back(line_features[i]);
line_features[i + 1].flag = FEATURE_FLAG_INVALID;
}
else
{
result_features.push_back(line_features[i]);
}
}
}
return;
}
//计算检查长度时使用街区距离以避免太多的平方和开方运算
//如果z方向为Jump返回True。Jump定义Seed前一个点z的变化大于门限
bool _getPtPreMaxDelta(
SVzNL3DPosition* lineData,
int dataSize,
int seedIdx,
const double chkLen,
double* maxDeltaY,
double* maxDeltaZ,
int* endingIdx)
{
double seedY = lineData[seedIdx].pt3D.y;
double seedZ = lineData[seedIdx].pt3D.z;
*maxDeltaY = 0; //初始化成0
*maxDeltaZ = 0;
*endingIdx = -1;//初始化成无效值,表示没有足够长度
if (seedIdx == 0) //无法向前检查
return false;
double max_dy = -1;
double max_dz = -1;
for (int i = seedIdx - 1; i >= 0; i--)
{
SVzNL3DPoint* a_pt = &(lineData[i].pt3D);
if (a_pt->z > 1e-4)
{
double dy = abs(a_pt->y - seedY);
double dz = abs(a_pt->z - seedZ);
double dist = dy + dz;
if (dist > chkLen)
{
*endingIdx = i;
*maxDeltaY = max_dy;
*maxDeltaZ = max_dz;
if (i == seedIdx - 1)
return true;
else
return false;
}
if (max_dy < 0)
{
max_dy = dy;
max_dz = dz;
}
else
{
max_dy = max_dy < dy ? dy : max_dy;
max_dz = max_dz < dz ? dz : max_dz;
}
}
}
*endingIdx = -1;//没有足够长度
return false;
}
//计算检查长度时使用街区距离以避免太多的平方和开方运算
//如果z方向为Jump返回True。Jump定义Seed前一个点z的变化大于门限
bool _getPtPostMaxDelta(
SVzNL3DPosition* lineData,
int dataSize,
int seedIdx,
const double chkLen,
double* maxDeltaY,
double* maxDeltaZ,
int* endingIdx)
{
double seedY = lineData[seedIdx].pt3D.y;
double seedZ = lineData[seedIdx].pt3D.z;
*maxDeltaY = 0; //初始化成0
*maxDeltaZ = 0;
*endingIdx = -1;//初始化成无效值,表示没有足够长度
if (seedIdx == dataSize-1) //无法向后检查
return false;
double max_dy = -1;
double max_dz = -1;
for (int i = seedIdx + 1; i < dataSize; i++)
{
SVzNL3DPoint* a_pt = &(lineData[i].pt3D);
if (a_pt->z > 1e-4)
{
double dy = abs(a_pt->y - seedY);
double dz = abs(a_pt->z - seedZ);
double dist = dy + dz;
if (dist > chkLen)
{
*endingIdx = i;
*maxDeltaY = max_dy;
*maxDeltaZ = max_dz;
if (i == seedIdx - 1)
return true;
else
return false;
}
if (max_dy < 0)
{
max_dy = dy;
max_dz = dz;
}
else
{
max_dy = max_dy < dy ? dy : max_dy;
max_dz = max_dz < dz ? dz : max_dz;
}
}
}
*endingIdx = -1;//表示没有足够长度
return false;
}
/// <summary>
/// 使用模板法提取直角特征
/// 水平向下直角特征:拐点左侧deltaZ在一个很小的范围内;拐点右侧deltaY在一个很小的范围内
/// </summary>
/// <param name="lineData"></param>
/// <param name="dataSize"></param>
/// <param name="lineIdx"></param>
/// <param name="slopeParam"></param>
/// <param name="valleyPara"></param>
/// <param name="line_features"></param>
void sg_getLineRigthAngleFeature(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_lineRightAngleParam templatePara_HF,
const SSG_lineRightAngleParam templatePara_FH,
const SSG_lineRightAngleParam templatePara_HR,
const SSG_lineRightAngleParam templatePara_RH,
SSG_lineFeature* line_features)
{
double chkLen = templatePara_HF.H_len > templatePara_HF.V_len ? templatePara_HF.V_len : templatePara_HF.H_len;
chkLen = chkLen > templatePara_FH.H_len ? templatePara_FH.H_len : chkLen;
chkLen = chkLen > templatePara_FH.V_len ? templatePara_FH.V_len : chkLen;
chkLen = chkLen > templatePara_HR.H_len ? templatePara_HR.H_len : chkLen;
chkLen = chkLen > templatePara_HR.V_len ? templatePara_HR.V_len : chkLen;
chkLen = chkLen > templatePara_RH.H_len ? templatePara_RH.H_len : chkLen;
chkLen = chkLen > templatePara_RH.V_len ? templatePara_RH.V_len : chkLen;
double maxDelta = templatePara_HF.maxDelta < templatePara_FH.maxDelta ? templatePara_FH.maxDelta : templatePara_HF.maxDelta;
maxDelta = maxDelta < templatePara_HR.maxDelta ? templatePara_HR.maxDelta : maxDelta;
maxDelta = maxDelta < templatePara_RH.maxDelta ? templatePara_RH.maxDelta : maxDelta;
std::vector<int> types;
std::vector<int> jumpFlags;
types.resize(dataSize);
jumpFlags.resize(dataSize);
for (int i = 0; i < dataSize; i++)
{
if (i == 98)
int kkk = 1;
types[i] = 0;
jumpFlags[i] = 0;
double maxDeltaY_pre = 0;
double maxDeltaZ_pre = 0;
int endingIdx_pre = -1;
double maxDeltaY_post = 0;
double maxDeltaZ_post = 0;
int endingIdx_post = -1;
if (lineData[i].pt3D.z < 1e-4)
continue;
//首先以最小的检查长度进行直角判断
bool isJump_pre = _getPtPreMaxDelta(
lineData,
dataSize,
i,
chkLen,
&maxDeltaY_pre,
&maxDeltaZ_pre,
&endingIdx_pre);
bool isJump_post = _getPtPostMaxDelta(
lineData,
dataSize,
i,
chkLen,
&maxDeltaY_post,
&maxDeltaZ_post,
&endingIdx_post);
//共四种直角:水平-向下;向下-水平;水平-向上;向上-水平
if ((endingIdx_pre < 0) || (endingIdx_post < 0)) //没有足够长度
continue;
if (true == isJump_pre)
maxDeltaY_pre = 0; //实际可能会很大
if (true == isJump_post)
maxDeltaY_post = 0; //实际可能会很大
if ((maxDeltaZ_pre < maxDelta) && (maxDeltaY_post < maxDelta)) //可能为HX型(水平-垂直)
{
if (lineData[endingIdx_post].pt3D.z > lineData[i].pt3D.z) //下降
{
//使用templatePara_HX的参数重新进行迭代计算
isJump_pre = _getPtPreMaxDelta(
lineData,
dataSize,
i,
templatePara_HF.H_len,
&maxDeltaY_pre,
&maxDeltaZ_pre,
&endingIdx_pre);
isJump_post = _getPtPostMaxDelta(
lineData,
dataSize,
i,
templatePara_HF.V_len,
&maxDeltaY_post,
&maxDeltaZ_post,
&endingIdx_post);
if (true == isJump_pre)
maxDeltaY_pre = 0; //实际可能会很大
if (true == isJump_post)
maxDeltaY_post = 0; //实际可能会很大
if ((maxDeltaZ_pre < templatePara_HF.maxDelta) && (maxDeltaY_post < templatePara_HF.maxDelta))
{
if ((true == isJump_pre) || (true == isJump_post))
jumpFlags[i] = 1;
types[i] = LINE_FEATURE_RIGHT_ANGLE_HF;
}
}
else //上升
{
//使用templatePara_HX的参数重新进行迭代计算
isJump_pre = _getPtPreMaxDelta(
lineData,
dataSize,
i,
templatePara_HR.H_len,
&maxDeltaY_pre,
&maxDeltaZ_pre,
&endingIdx_pre);
isJump_post = _getPtPostMaxDelta(
lineData,
dataSize,
i,
templatePara_HR.V_len,
&maxDeltaY_post,
&maxDeltaZ_post,
&endingIdx_post);
if (true == isJump_pre)
maxDeltaY_pre = 0; //实际可能会很大
if (true == isJump_post)
maxDeltaY_post = 0; //实际可能会很大
if ((maxDeltaZ_pre < templatePara_HF.maxDelta) && (maxDeltaY_post < templatePara_HF.maxDelta))
{
if ((true == isJump_pre) || (true == isJump_post))
jumpFlags[i] = 1;
types[i] = LINE_FEATURE_RIGHT_ANGLE_HR;;
}
}
}
else if ((maxDeltaY_pre < maxDelta) && (maxDeltaZ_post < maxDelta)) //可能为XH型(垂直-水平)
{
if (lineData[i].pt3D.z > lineData[endingIdx_pre].pt3D.z) //下降
{
//使用templatePara_XH的参数重新进行迭代计算
isJump_pre = _getPtPreMaxDelta(
lineData,
dataSize,
i,
templatePara_FH.V_len,
&maxDeltaY_pre,
&maxDeltaZ_pre,
&endingIdx_pre);
isJump_post = _getPtPostMaxDelta(
lineData,
dataSize,
i,
templatePara_FH.H_len,
&maxDeltaY_post,
&maxDeltaZ_post,
&endingIdx_post);
if (true == isJump_pre)
maxDeltaY_pre = 0; //实际可能会很大
if (true == isJump_post)
maxDeltaY_post = 0; //实际可能会很大
if ((maxDeltaY_pre < templatePara_FH.maxDelta) && (maxDeltaZ_post < templatePara_FH.maxDelta))
{
if ((true == isJump_pre) || (true == isJump_post))
jumpFlags[i] = 1;
types[i] = LINE_FEATURE_RIGHT_ANGLE_FH;
}
}
else//上升
{
//使用templatePara_XH的参数重新进行迭代计算
isJump_pre = _getPtPreMaxDelta(
lineData,
dataSize,
i,
templatePara_RH.V_len,
&maxDeltaY_pre,
&maxDeltaZ_pre,
&endingIdx_pre);
isJump_post = _getPtPostMaxDelta(
lineData,
dataSize,
i,
templatePara_RH.H_len,
&maxDeltaY_post,
&maxDeltaZ_post,
&endingIdx_post);
if (true == isJump_pre)
maxDeltaY_pre = 0; //实际可能会很大
if (true == isJump_post)
maxDeltaY_post = 0; //实际可能会很大
if ((maxDeltaY_pre < templatePara_RH.maxDelta) && (maxDeltaZ_post < templatePara_RH.maxDelta))
{
if ((true == isJump_pre) || (true == isJump_post))
jumpFlags[i] = 1;
types[i] = LINE_FEATURE_RIGHT_ANGLE_RH;
}
}
}
}
//对连续的同类型直角进行处理
int i = 0;
int pre_type = -1;
while(i < dataSize)
{
int sIdx = i;
int eIdx = i;
if (types[i] != pre_type)
{
int sIdx = i;
int eIdx = i;
int jumpPos = jumpFlags[i] == 1 ? i : -1; //跳变位置
for (int j = i; j < dataSize; j++)
{
if (types[j] != types[i])
break;
else
{
eIdx = j;
if (jumpFlags[j] == 1)
jumpPos = j;
}
}
if (types[i] > 0)
{
SSG_basicFeature1D a_rightAngle;
a_rightAngle.featureType = types[i];
if (jumpPos >= 0)
{
a_rightAngle.jumpPos2D = { lineIdx, jumpPos };
a_rightAngle.jumpPos = lineData[jumpPos].pt3D;
}
else
{
int pos = (sIdx + eIdx) / 2;//取中点
if (lineData[pos].pt3D.z < 1e-4)
{
int w = pos - sIdx;
for (int m = 1; m <= w; m++)
{
if (lineData[pos + m].pt3D.z > 1e-4)
{
pos = pos + m;
break;
}
else if (lineData[pos - m].pt3D.z > 1e-4)
{
pos = pos - m;
break;
}
}
}
a_rightAngle.jumpPos2D = { lineIdx, pos };
a_rightAngle.jumpPos = lineData[pos].pt3D;
}
line_features->features.push_back(a_rightAngle);
}
pre_type = types[i];
i = eIdx + 1;
}
else
i++;
}
return;
}