#include "SG_baseDataType.h" #include "SG_baseAlgo_Export.h" #include typedef struct { int flag; int sPtIdx; int ePtIdx; SVzNL3DPosition startPt; SVzNL3DPosition endPt; }SSG_jump; typedef struct { int pntIdx; double forwardAngle; //前向角 double backwardAngle; //后向角 double corner; //拐角 double forwardDiffZ; double backwardDiffZ; }SSG_pntDirAngle; 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& input, int smoothWin, std::vector& output) { output.resize(input.size()); // 预分配足够的空间 std::copy(input.begin(), input.end(), output.begin()); int size = 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; } //滤除离群点:z跳变门限方法 void sg_lineDataRemoveOutlier(SVzNL3DPosition* lineData, int dataSize, SSG_outlierFilterParam filterParam, std::vector& filerData, std::vector& 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 = 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 = 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& filerData, std::vector& 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 = 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; } /// /// 获取扫描线中的斜坡,并记录斜坡中的最大的相邻点之间的跳变。 /// 同时记录Ending。当没有中间袋子,只有左右袋子时,会有两组Ending。Ending之间的最小间距设置为袋子宽度的1/4 /// /// 扫描线数据,兼容栅格数据格式 /// 扫描线数据长度 /// 有效斜坡长度。小于此长度的斜坡被视为噪声 /// 输出检测到的斜坡 void _getSlopes(SVzNL3DPosition* lineData, int dataSize, const double validSlopeH, double minEndingGap, std::vector< SSG_slope>& slopes, std::vector& 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)); } /// /// 提取激光线上的L型跳变和V型跳变特征 /// nPointIdx被重新定义成Feature类型 /// 算法流程: /// (1)提取扫描线中的Slope(超过门限高度的slope为有效slope),局部最小值,以及局部Z跳变最大的点 /// (2)取窗口宽度内的最小值为有效最小值 /// (3)V:在最小值点两个Win宽度一半内检查有没有对称的Slope。 /// (4)L:在Slope中检测Z跳变最大的点,判断角度。 /// /// 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 endings; //22024.12.16: 注意:当没有中间袋子,只有左右袋子时,有两组Ending。 _getSlopes(lineData, dataSize, slopeParam.validSlopeH, slopeParam.minEndingGap, slopes, endings); //首先检查L型边界:本身具有足够坡度,与下一段形成明显坡度差 //再检查是否可以形成V型特征 for (int i = 0, i_max = 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 = 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; } /// /// 提取激光线上的拐点特征 /// nPointIdx被重新定义成Feature类型 /// 算法流程: /// (1)逐点计算前向角和后向角 /// (2)逐点计算拐角,顺时针为负,逆时针为正 /// (3)搜索正拐角的极大值。 /// (4)判断拐角是否为跳变 /// 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 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); } //计算前向角和后向角 std::vector< SSG_pntDirAngle> corners; corners.resize(vldPts.size()); for (int i = 0, i_max = vldPts.size(); i < i_max; i++) { //前向寻找 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 = 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 = 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 = 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 < cornerPara.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; } } for (int i = 0, i_max = 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; } /// /// 提取激光线上的极值点(极大值点和极小值点) /// /// 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 = 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 = 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; } /// /// 提取激光线上的下跳变点(Z值变大的跳变) /// /// 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; } /// /// 提取激光线上的圆柱形特征 /// nPointIdx被重新定义成Feature类型 /// 算法流程: /// (1)逐点计算前向角和后向角 /// (2)逐点计算拐角,顺时针为负,逆时针为正 /// (3)搜索正拐角的极大值。 /// (4)判断拐角是否为跳变 /// void sg_getLineUpperSemiCircleFeature( SVzNL3DPosition* lineData, int dataSize, int lineIdx, const double sieveDiameter, const SSG_slopeParam slopePara, std::vector< SSG_featureSemiCircle>& line_features) { if (lineIdx == 54) int kkk = 1; //去除零点 std::vector< SVzNL3DPosition> vldPts; std::vector 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 = 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 endingFlag; endingFlag.resize(dataSize); for (int i = 0; i < dataSize; i++) endingFlag[i] = 0; for (int i = 0,i_max=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 = segMax.size(); m < m_max; m++) { segMax[m].jumpPos2D.y += idx_1; localMax.push_back(segMax[m]); } for (int m = 0, m_max = 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 = 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 = 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 validZWinPeakP; double square_distTh = 4 * slopePara.LSlopeZWin * slopePara.LSlopeZWin; //2倍的cornerScale。 for (int i = 0, i_max = 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 validZWinPeakM; for (int i = 0, i_max = 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进行配对 for (int i = 0, i_max = 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.midY = lineData[a_feature.midPtIdx].pt3D.y; 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 = preIdx; m <= postIdx; 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.midY = lineData[a_feature.midPtIdx].pt3D.y; 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 = preIdx; m <= postIdx; m++) { if (0 == endingFlag[m]) endingFlag[m] = 1; } } } } } } 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; } /// /// 使用模板法提取直角特征 /// 水平向下直角特征:拐点左侧deltaZ在一个很小的范围内;拐点右侧deltaY在一个很小的范围内 /// /// /// /// /// /// /// 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 types; std::vector 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; }