algoLib/sourceCode/SG_lineFeature.cpp

4272 lines
113 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;
}
//滤除离群点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;
}
//滤除离群点:点距离方法
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>
/// 获取扫描线中的斜坡,并记录斜坡中的最大的相邻点之间的跳变。
/// 同时记录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取窗口宽度内的最小值为有效最小值
/// 3V:在最小值点两个Win宽度一半内检查有没有对称的Slope。
/// 4L:在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>
/// 提取激光线上的拐点特征
/// 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>
/// 提取激光线上的拐点特征水平端点视为Corner
/// 根据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;
}
bool compareByIdx(const SSG_pntDirAngle& a, const SSG_pntDirAngle& b) {
return a.pntIdx < b.pntIdx;
}
/// <summary>
/// 提取激光线上的Jumping特征
/// nPointIdx被重新定义成Feature类型
/// 算法流程:
/// 1逐点计算前向角和后向角
/// 2逐点计算拐角顺时针为负逆时针为正
/// 3搜索正拐角的极大值。
/// 4判断拐角是否为跳变
/// </summary>
void sg_getLineJumpFeature_cornerMethod(
SVzNL3DPosition* lineData,
int dataSize,
int lineIdx,
const SSG_cornerParam cornerPara, //scale通常取bagH的1/4
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; //沿序号减小方向
}
}
//搜索拐角极值
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;
}
//注意:最后一个不处理,为基座位置
//极小值点(峰顶)
//极值比较,在尺度窗口下寻找局部极值点
std::vector< SSG_pntDirAngle> cornerFeatures;
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)
{
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_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)
{
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;
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;
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;
}
//手套的环的特征在扫描线的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;
}
/// <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;
}
#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;
}