algoLib/sourceCode/YouJiang_sheetPositioning.cpp
2026-03-10 16:57:56 +08:00

422 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <vector>
#include "SG_baseDataType.h"
#include "SG_baseAlgo_Export.h"
#include "YouJiang_sheetPositioning_Export.h"
#include <opencv2/opencv.hpp>
#define DEBUG_OUT_IMAGE 1
std::string m_strVersion = "1.0.0";
const char* wd_sheetPositionVersion(void)
{
return m_strVersion.c_str();
}
//计算一个平面调平参数。
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara wd_sheetPosition_getBaseCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
return sg_getPlaneCalibPara2(scanLines);
}
//相机姿态调平,并去除地面
void wd_sheetPosition_lineDataR(
std::vector< SVzNL3DPosition>& a_line,
const double* camPoseR,
double groundH)
{
lineDataRT_vector(a_line, camPoseR, groundH);
}
//提取边缘封闭目标的边缘特征,输出聚类簇
void wd_computeClosedEdgeClusters(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SSG_planeCalibPara groundCalibPara,
const SWD_sheetAlgoParam algoParam,
int* errCode,
std::vector<std::vector< SVzNL2DPoint>>& clusters, //只记录位置
std::vector<SVzNL3DRangeD>& clustersRoi3D)
{
int lineNum = (int)scanLines.size();
if (lineNum == 0)
{
*errCode = SG_ERR_3D_DATA_NULL;
return;
}
bool isGridData = true;
int linePtNum = (int)scanLines[0].size();
for (int i = 0; i < lineNum; i++)
{
if (linePtNum != (int)scanLines[i].size())
isGridData = false;//行处理
//调平,去除地面
wd_sheetPosition_lineDataR(scanLines[i], groundCalibPara.planeCalib, -1);
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
//提取薄片上的圆孔
//垂直跳变特征提取
std::vector<std::vector<SSG_basicFeature1D>> lineJumpFeatures_v;
for (int line = 0; line < lineNum; line++)
{
if (line == 76)
int kkk = 1;
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
if (linePtNum != (int)lineData.size())
isGridData = false;
//滤波,滤除异常点
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], linePtNum, algoParam.filterParam);
std::vector<SSG_basicFeature1D> line_jumpFeatures;
int dataSize = (int)lineData.size();
/// 提取激光线上的Jumping特征
sg_getLineLJumpFeature_cornerMethod(
&lineData[0],
dataSize,
line,
algoParam.cornerParam,
true, //取上沿
line_jumpFeatures);
lineJumpFeatures_v.push_back(line_jumpFeatures);
}
if (false == isGridData)//数据不是网格格式
{
*errCode = SG_ERR_NOT_GRID_FORMAT;
return;
}
//生成水平扫描
std::vector<std::vector<SVzNL3DPosition>> hLines_raw;
hLines_raw.resize(linePtNum);
for (int i = 0; i < linePtNum; i++)
hLines_raw[i].resize(lineNum);
for (int line = 0; line < lineNum; line++)
{
for (int j = 0; j < linePtNum; j++)
{
scanLines[line][j].nPointIdx = 0; //将原始数据的序列清0会转义使用
hLines_raw[j][line] = scanLines[line][j];
hLines_raw[j][line].pt3D.x = scanLines[line][j].pt3D.y;
hLines_raw[j][line].pt3D.y = scanLines[line][j].pt3D.x;
}
}
//水平jump特征提取
std::vector<std::vector<SSG_basicFeature1D>> lineJumpFeatures_h;
int lineNum_h_raw = (int)hLines_raw.size();
for (int line = 0; line < lineNum_h_raw; line++)
{
if (line == 522)
int kkk = 1;
std::vector<SVzNL3DPosition>& lineData = hLines_raw[line];
//滤波,滤除异常点
int ptNum = (int)lineData.size();
sg_lineDataRemoveOutlier_changeOriginData(&lineData[0], ptNum, algoParam.filterParam);
std::vector<SSG_basicFeature1D> line_jumpFeatures;
int dataSize = (int)lineData.size();
/// 提取激光线上的Jumping特征
sg_getLineLJumpFeature_cornerMethod(
&lineData[0],
dataSize,
line,
algoParam.cornerParam,
true, //取上沿
line_jumpFeatures);
lineJumpFeatures_h.push_back(line_jumpFeatures);
}
//标注
std::vector<std::vector<SSG_featureClusteringInfo>> featureInfoMask;
std::vector<std::vector<SVzNL3DPoint>> feature3DInfo;
featureInfoMask.resize(lineNum);
feature3DInfo.resize(lineNum);
for (int i = 0; i < lineNum; i++)
{
featureInfoMask[i].resize(lineNum_h_raw);
feature3DInfo[i].resize(lineNum_h_raw);
}
//垂直标注
for (int line = 0; line < lineNum; line++)
{
if (line == 390)
int kkk = 1;
std::vector<SSG_basicFeature1D>& a_lineJumpFeature = lineJumpFeatures_v[line];
for (int m = 0, m_max = (int)a_lineJumpFeature.size(); m < m_max; m++)
{
int px = a_lineJumpFeature[m].jumpPos2D.x;
int py = a_lineJumpFeature[m].jumpPos2D.y;
SSG_featureClusteringInfo& a_featureInfo = featureInfoMask[px][py];
a_featureInfo.clusterID = 0;
a_featureInfo.featurType = 1;
a_featureInfo.featureIdx_v = m;
a_featureInfo.featureIdx_h = 0;
a_featureInfo.lineIdx = px;
a_featureInfo.ptIdx = py;
a_featureInfo.flag = 0;
SVzNL3DPoint& a_feature3D = feature3DInfo[px][py];
a_feature3D = a_lineJumpFeature[m].jumpPos;
scanLines[px][py].nPointIdx = 1;
}
}
//水平标注
for (int line = 0; line < lineNum_h_raw; line++)
{
std::vector<SSG_basicFeature1D>& a_lineJumpFeature = lineJumpFeatures_h[line];
for (int m = 0, m_max = (int)a_lineJumpFeature.size(); m < m_max; m++)
{
int py = a_lineJumpFeature[m].jumpPos2D.x;
int px = a_lineJumpFeature[m].jumpPos2D.y;
SSG_featureClusteringInfo& a_featureInfo = featureInfoMask[px][py];
if (a_featureInfo.featurType == 0)
{
a_featureInfo.clusterID = 0;
a_featureInfo.lineIdx = px;
a_featureInfo.ptIdx = py;
a_featureInfo.flag = 0;
}
a_featureInfo.featurType += 2;
a_featureInfo.featureIdx_h = m;
SVzNL3DPoint& a_feature3DValue = feature3DInfo[px][py];
a_feature3DValue = { a_lineJumpFeature[m].jumpPos.y, a_lineJumpFeature[m].jumpPos.x, a_lineJumpFeature[m].jumpPos.z };
scanLines[px][py].nPointIdx += 2;
}
}
//聚类
//采用迭代思想,回归思路进行高效聚类
int clusterID = 1;
int clusterCheckWin = 3;
for (int y = 0; y < lineNum_h_raw; y++)
{
for (int x = 0; x < lineNum; x++)
{
SSG_featureClusteringInfo& a_featureInfo = featureInfoMask[x][y];
if ((0 == a_featureInfo.featurType) || (a_featureInfo.clusterID > 0)) //非特征或已经处理
continue;
SVzNL3DPoint& a_feature3DValue = feature3DInfo[x][y];
SVzNL3DRangeD a_clusterRoi;
a_clusterRoi.xRange.min = a_feature3DValue.x;
a_clusterRoi.xRange.max = a_feature3DValue.x;
a_clusterRoi.yRange.min = a_feature3DValue.y;
a_clusterRoi.yRange.max = a_feature3DValue.y;
a_clusterRoi.zRange.min = a_feature3DValue.z;
a_clusterRoi.zRange.max = a_feature3DValue.z;
SVzNL2DPoint a_seedPos = { x, y };
std::vector< SVzNL2DPoint> a_cluster;
a_cluster.push_back(a_seedPos);
wd_gridPointClustering(
featureInfoMask,//int记录特征标记和clusterID附加一个flag
feature3DInfo,//double,记录坐标信息
clusterCheckWin, //搜索窗口
algoParam.growParam,//聚类条件
clusterID, //当前Cluster的ID
a_cluster, //result
a_clusterRoi
);
clusters.push_back(a_cluster);
clustersRoi3D.push_back(a_clusterRoi);
clusterID++;
}
}
}
//薄片定位
//使用语义模块匹配1提取特征2基于特征匹配薄片
void wd_YouJiang_getSheetPosition(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SWD_sheetTemplateParam templateParam,
const SSG_planeCalibPara groundCalibPara,
const SWD_sheetAlgoParam algoParam,
SWD_gripState* opState, //操作状态机。指示当前状态
int* errCode,
std::vector<SWD_sheetGrasper>& resultObjPositions)
{
std::vector<std::vector< SVzNL2DPoint>> clusters;//只记录位置
std::vector<SVzNL3DRangeD> clustersRoi3D;
//提取边缘封闭目标的边缘特征,输出聚类簇
wd_computeClosedEdgeClusters(
scanLines,
groundCalibPara,
algoParam,
errCode,
clusters, //只记录位置
clustersRoi3D);
int clusterNum = (int)clusters.size();
//投影和标注。投影的目的是用于模板匹配时的特征处理。以1mm为单位.记录clusterID
SVzNL3DRangeD roi3D = sg_getScanDataROI_vector(scanLines);
double projectionScale = 1.0; //以1mm为投影尺度
int projecction_xw = (int)((roi3D.xRange.max - roi3D.xRange.min) / projectionScale) + 1;
int projecction_yh = (int)((roi3D.yRange.max - roi3D.yRange.min) / projectionScale) + 1;
std::vector<std::vector<int>> projectionClusters;
projectionClusters.resize(projecction_xw);
for (int i = 0; i < projecction_yh; i++)
projectionClusters[i].resize(projecction_yh);
for (int i = 0; i < clusterNum; i++)
{
std::vector< SVzNL2DPoint>& a_cluster = clusters[i];
for (int j = 0; j < (int)a_cluster.size(); j++)
{
SVzNL2DPoint& a_pt = a_cluster[j];
SVzNL3DPosition& a_pt3d = scanLines[a_pt.x][a_pt.y];
int px = (int)((a_pt3d.pt3D.x - roi3D.xRange.min) / projectionScale);
int py = (int)((a_pt3d.pt3D.y - roi3D.yRange.min) / projectionScale);
if ((px < 0) || (px >= projecction_xw) || (py < 0) || (py >= projecction_yh))
int kkk = 1;
projectionClusters[px][py] = i + 1; //clusterID = index + 1
}
}
//聚类结果分析
//根据大小确定圆孔
std::vector<SVzNL3DPoint> clustersCenter;
clustersCenter.resize(clusters.size());
std::vector<int> validIndice;
double diameter_th = templateParam.holeDiameter * 0.1;
for (int i = 0; i < clusterNum; i++)
{
double xw = clustersRoi3D[i].xRange.max - clustersRoi3D[i].xRange.min;
double yh = clustersRoi3D[i].yRange.max - clustersRoi3D[i].yRange.min;
double xw_diff = abs(xw - templateParam.holeDiameter);
double yh_diff = abs(yh - templateParam.holeDiameter);
if ((xw_diff < diameter_th) && (yh_diff < diameter_th))
{
validIndice.push_back(i);
//计算中心点
}
}
//圆孔配对
//确定正反和抓取点
}
typedef struct
{
cv::RotatedRect rect;
cv::Point2f vertices[4];
}_RectParam;
//薄片按键高度测量
void wd_computeSheetKeyHeigth(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
const SWD_sheetKeyParam sheetKeyParam,
const SSG_planeCalibPara groundCalibPara,
const SWD_sheetAlgoParam algoParam,
SWD_gripState* opState, //操作状态机。指示当前状态
int* errCode,
std::vector<SWD_sheetGrasper>& resultObjPositions)
{
std::vector<std::vector< SVzNL2DPoint>> clusters;//只记录位置
std::vector<SVzNL3DRangeD> clustersRoi3D;
//提取边缘封闭目标的边缘特征,输出聚类簇
wd_computeClosedEdgeClusters(
scanLines,
groundCalibPara,
algoParam,
errCode,
clusters, //只记录位置
clustersRoi3D);
int clusterNum = (int)clusters.size();
//聚类结果分析
//根据大小确定薄片
const double sameSizeTh_w = sheetKeyParam.sheetSize.width * 0.05;
const double sameSizeTh_h = sheetKeyParam.sheetSize.height * 0.05;
const double zAreaWin = 5;
std::vector<_RectParam> clustersRect;
clustersRect.resize(clusters.size());
#if 1
std::vector<int> validIndice;
for (int i = 0; i < clusterNum; i++)
{
std::vector< SVzNL2DPoint>& a_cluster = clusters[i];
if (a_cluster.size() < 4)
continue;
//计算最小外接矩
std::vector<cv::Point2f> points;
SVzNLRect roi = { -1,-1,-1,-1 };
for (int j = 0; j < (int)a_cluster.size(); j++)
{
SVzNL2DPoint& a_pt = a_cluster[j];
SVzNL3DPosition& a_pt3d = scanLines[a_pt.x][a_pt.y];
//生成ROI
if (roi.left < 0)
roi = { a_pt.x ,a_pt.x , a_pt.y, a_pt.y };
else
{
if (roi.left > a_pt.x)
roi.left = a_pt.x;
if (roi.right < a_pt.x)
roi.right = a_pt.x;
if (roi.top > a_pt.y)
roi.top = a_pt.y;
if (roi.bottom < a_pt.y)
roi.bottom = a_pt.y;
}
float x = (float)a_pt3d.pt3D.x;
float y = (float)a_pt3d.pt3D.y;
points.push_back(cv::Point2f(x, y));
}
if (points.size() == 0)
continue;
// 最小外接矩形
cv::RotatedRect rect = minAreaRect(points);
cv::Point2f vertices[4];
rect.points(vertices);
double width = rect.size.width; //投影的宽和高, 对应袋子的长和宽
double height = rect.size.height;
if (width < height)
{
double tmp = height;
height = width;
width = tmp;
}
int cx = (roi.left + roi.right) / 2;
int cy = (roi.top + roi.bottom) / 2;
//判别
double w_diff = abs(width - sheetKeyParam.sheetSize.width);
double h_diff = abs(height - sheetKeyParam.sheetSize.height);
if ((w_diff < sameSizeTh_w) && (h_diff < sameSizeTh_h))
{
//寻找到目标
//取中心点以中心点附近区域计算Z
int cx = (roi.left + roi.right) / 2;
int cy = (roi.top + roi.bottom) / 2;
SVzNLRect meanZRoi = { cx - zAreaWin , cx + zAreaWin , cy - zAreaWin , cy + zAreaWin };
double meanZ = computeROIMeanZ(scanLines, meanZRoi);
//以Z值作为门限进行切割
double cutZ = meanZ - sheetKeyParam.nominalKeyHeight * 0.75;
int labelID = i + 100; //ID从100开始
computeROIZCutLabel(scanLines, roi, cutZ, labelID);
}
}
#endif
}