algoLib/sourceCode/SG_baseFunc.cpp
jerryzeng 5d95855e2a rodAndBarDetection version 1.3.5 :
新的定位盘中心测量功能占将float运算改成double ,测试PC和3588差异
2026-06-06 21:51:01 +08:00

3359 lines
87 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>
#ifdef __WIN32
#include <corecrt_math_defines.h>
#endif // __WIN32
#include <cmath>
#include <unordered_map>
#include <Eigen/dense>
const double EPS = 1e-10;
SVzNL3DPoint vec3_cross(const SVzNL3DPoint& a, const SVzNL3DPoint& b)
{
SVzNL3DPoint c;
c.x = a.y * b.z - a.z * b.y;
c.y = a.z * b.x - a.x * b.z;
c.z = a.x * b.y - a.y * b.x;
return c;
}
// 向量数乘
SVzNL3DPoint vec3_multiply(const SVzNL3DPoint& a, const double s)
{
SVzNL3DPoint result = { a.x * s, a.y * s, a.z * s };
return result;
}
// 点乘 dot
double vec3_dotMultiply(const SVzNL3DPoint& a, const SVzNL3DPoint& b)
{
return (a.x * b.x + a.y * b.y + a.z * b.z);
}
// 模长
double vec3_length(const SVzNL3DPoint& a)
{
return sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
}
// 归一化(单位向量)
SVzNL3DPoint vec3_normalize(const SVzNL3DPoint& a)
{
SVzNL3DPoint result;
double len = vec3_length(a);
if (len < 1e-6)
result = { 0,0,0 };
else
result = { a.x / len, a.y / len, a.z / len };
return result;
}
// 计算两个向量夹角(返回 角度)
float vec3_computeVecAngle(const SVzNL3DPoint& a, const SVzNL3DPoint& b)
{
float l = vec3_length(a) * vec3_length(b);
if (l < 1e-6f)
return 0.0f; // 避免除零
float cosTheta = vec3_dotMultiply(a, b) / l;
cosTheta = std::max(std::min(cosTheta, 1.0f), -1.0f); // 防止数值越界
float rad = acosf(cosTheta);
float degree = rad * 180.0f / (float)M_PI;
return degree;
}
/**
* @brief 平面内向量 v 绕平面法向量 n 旋转 theta 弧度
* v 必须在平面内,自动使用简化版罗德里格斯)
*/
SVzNL3DPoint wd_rotateVectorInPlane(const SVzNL3DPoint& v, const SVzNL3DPoint& n, double theta)
{
SVzNL3DPoint k = vec3_normalize(n); // 旋转轴(单位法向量)
SVzNL3DPoint cross = vec3_cross(k, v); // k × v
double angle = theta * PI / 180.0;
double c = cos(angle);
double s = sin(angle);
// 简化公式v' = v cosθ + (k×v) sinθ
SVzNL3DPoint t1 = vec3_multiply(v, c);
SVzNL3DPoint t2 = vec3_multiply(cross, s);
SVzNL3DPoint result = { t1.x + t2.x, t1.y + t2.y, t1.z + t2.z };
return result;;
}
//逆时针旋转时 θ > 0 ;顺时针旋转时 θ < 0
SVzNL3DPoint wd_rotate2D(const SVzNL3DPoint& pt, const double angle)
{
double sinTheta = sin(PI * angle / 180);
double cosTheta = cos(PI * angle / 180);
SVzNL3DPoint rotatePt;
rotatePt.x = pt.x * cosTheta - pt.y * sinTheta;
rotatePt.y = pt.x * sinTheta + pt.y * cosTheta;
rotatePt.z = pt.z;
return rotatePt;
}
SVzNL3DRangeD sg_getScanDataROI(
//计算扫描ROI
SVzNL3DLaserLine* laser3DPoints,
int lineNum)
{
SVzNL3DRangeD roi;
roi.xRange = { 0, -1 };
roi.yRange = { 0, -1 };
roi.zRange = { 0, -1 };
for (int line = 0; line < lineNum; line++)
{
for (int i = 0; i < laser3DPoints[line].nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &laser3DPoints[line].p3DPosition[i];
if (pt3D->pt3D.z < 1e-4)
continue;
if (roi.xRange.max < roi.xRange.min)
{
roi.xRange.min = pt3D->pt3D.x;
roi.xRange.max = pt3D->pt3D.x;
}
else
{
if (roi.xRange.min > pt3D->pt3D.x)
roi.xRange.min = pt3D->pt3D.x;
if (roi.xRange.max < pt3D->pt3D.x)
roi.xRange.max = pt3D->pt3D.x;
}
//y
if (roi.yRange.max < roi.yRange.min)
{
roi.yRange.min = pt3D->pt3D.y;
roi.yRange.max = pt3D->pt3D.y;
}
else
{
if (roi.yRange.min > pt3D->pt3D.y)
roi.yRange.min = pt3D->pt3D.y;
if (roi.yRange.max < pt3D->pt3D.y)
roi.yRange.max = pt3D->pt3D.y;
}
//z
if (roi.zRange.max < roi.zRange.min)
{
roi.zRange.min = pt3D->pt3D.z;
roi.zRange.max = pt3D->pt3D.z;
}
else
{
if (roi.zRange.min > pt3D->pt3D.z)
roi.zRange.min = pt3D->pt3D.z;
if (roi.zRange.max < pt3D->pt3D.z)
roi.zRange.max = pt3D->pt3D.z;
}
}
}
return roi;
}
//计算扫描ROI: vecotr格式
SVzNL3DRangeD sg_getScanDataROI_vector(std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
SVzNL3DRangeD roi;
roi.xRange = { 0, -1 };
roi.yRange = { 0, -1 };
roi.zRange = { 0, -1 };
int lineNum = (int)scanLines.size();
for (int line = 0; line < lineNum; line++)
{
int nPositionCnt = (int)scanLines[line].size();
for (int i = 0; i < nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &scanLines[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
if (roi.xRange.max < roi.xRange.min)
{
roi.xRange.min = pt3D->pt3D.x;
roi.xRange.max = pt3D->pt3D.x;
}
else
{
if (roi.xRange.min > pt3D->pt3D.x)
roi.xRange.min = pt3D->pt3D.x;
if (roi.xRange.max < pt3D->pt3D.x)
roi.xRange.max = pt3D->pt3D.x;
}
//y
if (roi.yRange.max < roi.yRange.min)
{
roi.yRange.min = pt3D->pt3D.y;
roi.yRange.max = pt3D->pt3D.y;
}
else
{
if (roi.yRange.min > pt3D->pt3D.y)
roi.yRange.min = pt3D->pt3D.y;
if (roi.yRange.max < pt3D->pt3D.y)
roi.yRange.max = pt3D->pt3D.y;
}
//z
if (roi.zRange.max < roi.zRange.min)
{
roi.zRange.min = pt3D->pt3D.z;
roi.zRange.max = pt3D->pt3D.z;
}
else
{
if (roi.zRange.min > pt3D->pt3D.z)
roi.zRange.min = pt3D->pt3D.z;
if (roi.zRange.max < pt3D->pt3D.z)
roi.zRange.max = pt3D->pt3D.z;
}
}
}
return roi;
}
//计算点云ROI: vecotr格式
SVzNL3DRangeD wd_getPointCloudROI(std::vector<SVzNL3DPoint>& scanData)
{
SVzNL3DRangeD roi;
roi.xRange = { 0, -1 };
roi.yRange = { 0, -1 };
roi.zRange = { 0, -1 };
int nPositionCnt = (int)scanData.size();
for (int i = 0; i < nPositionCnt; i++)
{
SVzNL3DPoint& pt3D = scanData[i];
if (pt3D.z < 1e-4)
continue;
if (roi.xRange.max < roi.xRange.min)
{
roi.xRange.min = pt3D.x;
roi.xRange.max = pt3D.x;
}
else
{
if (roi.xRange.min > pt3D.x)
roi.xRange.min = pt3D.x;
if (roi.xRange.max < pt3D.x)
roi.xRange.max = pt3D.x;
}
//y
if (roi.yRange.max < roi.yRange.min)
{
roi.yRange.min = pt3D.y;
roi.yRange.max = pt3D.y;
}
else
{
if (roi.yRange.min > pt3D.y)
roi.yRange.min = pt3D.y;
if (roi.yRange.max < pt3D.y)
roi.yRange.max = pt3D.y;
}
//z
if (roi.zRange.max < roi.zRange.min)
{
roi.zRange.min = pt3D.z;
roi.zRange.max = pt3D.z;
}
else
{
if (roi.zRange.min > pt3D.z)
roi.zRange.min = pt3D.z;
if (roi.zRange.max < pt3D.z)
roi.zRange.max = pt3D.z;
}
}
return roi;
}
//计算点云的ROI和scale: vecotr格式
SWD_pointCloudPara wd_getPointCloudPara(std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
SWD_pointCloudPara para;
para.xRange = { 0, -1 };
para.yRange = { 0, -1 };
para.zRange = { 0, -1 };
para.scale_x = -1; //初始值
para.scale_y = -1;
int lineNum = (int)scanLines.size();
double x_scale = 0;
int x_scale_cnt = 0;
double y_scale = 0;
double y_scale_cnt = 0;
for (int line = 0; line < lineNum; line++)
{
int nPositionCnt = (int)scanLines[line].size();
for (int i = 0; i < nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &scanLines[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
if (i > 0)
{
if (scanLines[line][i - 1].pt3D.z > 1e-4)
{
y_scale += abs(pt3D->pt3D.y - scanLines[line][i - 1].pt3D.y);
y_scale_cnt++;
}
}
if (line > 0)
{
if (scanLines[line - 1][i].pt3D.z > 1e-4)
{
x_scale += abs(pt3D->pt3D.x - scanLines[line-1][i].pt3D.x);
x_scale_cnt++;
}
}
if (para.xRange.max < para.xRange.min)
{
para.xRange.min = pt3D->pt3D.x;
para.xRange.max = pt3D->pt3D.x;
}
else
{
if (para.xRange.min > pt3D->pt3D.x)
para.xRange.min = pt3D->pt3D.x;
if (para.xRange.max < pt3D->pt3D.x)
para.xRange.max = pt3D->pt3D.x;
}
//y
if (para.yRange.max < para.yRange.min)
{
para.yRange.min = pt3D->pt3D.y;
para.yRange.max = pt3D->pt3D.y;
}
else
{
if (para.yRange.min > pt3D->pt3D.y)
para.yRange.min = pt3D->pt3D.y;
if (para.yRange.max < pt3D->pt3D.y)
para.yRange.max = pt3D->pt3D.y;
}
//z
if (para.zRange.max < para.zRange.min)
{
para.zRange.min = pt3D->pt3D.z;
para.zRange.max = pt3D->pt3D.z;
}
else
{
if (para.zRange.min > pt3D->pt3D.z)
para.zRange.min = pt3D->pt3D.z;
if (para.zRange.max < pt3D->pt3D.z)
para.zRange.max = pt3D->pt3D.z;
}
}
}
if (x_scale_cnt > 0)
para.scale_x = x_scale / (double)x_scale_cnt;
if (y_scale_cnt > 0)
para.scale_y = y_scale / (double)y_scale_cnt;
return para;
}
//计算Z均值
double computeMeanZ(std::vector< SVzNL3DPoint>& pts)
{
int ptNum = (int)pts.size();
int vldNum = 0;
double sumZ = 0;
for (int i = 0; i < ptNum; i++)
{
if (pts[i].z > 1e-4)
{
sumZ += pts[i].z;
vldNum++;
}
}
if (vldNum > 0)
return (sumZ / vldNum);
else
return 0;
}
double computeROIMeanZ(std::vector<std::vector< SVzNL3DPosition>>& scanLines, SVzNLRect roi)
{
int vldNum = 0;
double sumZ = 0;
for (int line = roi.left; line <= roi.right; line++)
{
for (int i = roi.top; i <= roi.bottom; i++)
{
if (scanLines[line][i].pt3D.z > 1e-4)
{
sumZ += scanLines[line][i].pt3D.z;
vldNum++;
}
}
}
if (vldNum > 0)
return (sumZ / vldNum);
else
return 0;
}
void computeROIZCutLabel(std::vector<std::vector< SVzNL3DPosition>>& scanLines, SVzNLRect roi, double cutZ, int labelID)
{
for (int line = roi.left; line <= roi.right; line++)
{
for (int i = roi.top; i <= roi.bottom; i++)
{
if ( (scanLines[line][i].pt3D.z > 1e-4) && (scanLines[line][i].pt3D.z < cutZ))
scanLines[line][i].nPointIdx = labelID;
}
}
return;
}
SVzNL3DPoint computeLineCrossPt_abs(double a1, double b1, double c1, double a2, double b2, double c2)
{
SVzNL3DPoint crossPt;
crossPt.x = (c2 * b1 - c1 * b2) / (a1 * b2 - a2 * b1);
crossPt.y = (c2 * a1 - c1 * a2) / (b1 * a2 - b2 * a1);
crossPt.z = 0;
return crossPt;
}
//计算角度差值在0-180度范围
double computeAngleDiff(double theta1, double theta2)
{
double diff = theta1 - theta2;
if (diff < 0)
diff += 360;
if (diff > 180)
diff = 360 - diff;
return diff;
}
void compute2ptLine(SVzNL3DPoint pt1, SVzNL3DPoint pt2, double* _a, double* _b, double* _c)
{
*_a = pt2.y - pt1.y;
*_b = pt1.x - pt2.x;
*_c = pt2.x * pt1.y - pt1.x * pt2.y;
return;
}
void compute2ptLine_2(double x1, double y1, double x2, double y2, double* _a, double* _b, double* _c)
{
*_a = y2 - y1;
*_b = x1 - x2;
*_c = x2 * y1 - x1 * y2;
return;
}
//旋转45度后的直线方程
void rotateLine45Deg(
double _a, double _b, double _c,
double x0, double y0,
double* r_a, double* r_b, double* r_c)
{
// 旋转后直线的系数(基于数学推导)
*r_a = _a + _b;
*r_b = _b - _a;
*r_c = -(*r_a) * x0 - (*r_b) * y0;
return;
}
double getLineAngle(const double _a, const double _b, const double _c)
{
if (_a == 0)
return 0;
else if (_b == 0)
return 90;
else
{
double k = _a / _b;
double theta = atan(-k) + PI / 2;
theta = (theta * 180.0) / PI;
return theta;
}
}
//计算两点的2D距离
double compute2DLen(SVzNL3DPoint pt1, SVzNL3DPoint pt2)
{
double len = sqrt(pow(pt1.x - pt2.x, 2) + pow(pt1.y - pt2.y, 2));
return len;
}
//计算XY平面面的三角形顶角(p0的张角)
double computeXOYVertexAngle(SVzNL3DPoint p0, SVzNL3DPoint p1, SVzNL3DPoint p2)
{
double len_c = compute2DLen(p1, p2);
double len_a = compute2DLen(p0, p1);
double len_b = compute2DLen(p0, p2);
double cosAngle = (pow(len_a, 2) + pow(len_b, 2) - pow(len_c, 2)) / (2 * len_a * len_b);
double angle = acos(cosAngle);
angle = angle * 180 / M_PI;
if (angle < 0)
angle = angle + 180;
return angle;
}
// 计算向量的模长
double vecNorm(const SVzNL2DPointD& v) {
return sqrt(v.x * v.x + v.y * v.y);
}
// 向量归一化单位向量返回是否成功零向量返回false
bool vecNormalize(SVzNL2DPointD& v) {
double norm = vecNorm(v);
if (norm < EPS) { // 零向量,无法归一化
return false;
}
v.x /= norm;
v.y /= norm;
return true;
}
// 计算两个向量的点积
double vecDot(const SVzNL2DPointD& a, const SVzNL2DPointD& b) {
return a.x * b.x + a.y * b.y;
}
// 计算两个向量的2D叉积标量值
double vecCross(const SVzNL2DPointD& a, const SVzNL2DPointD& b) {
return a.x * b.y - a.y * b.x;
}
/**
* @brief 计算从向量a到向量b的**有方向旋转角**(范围:-π ~ π)
* @param a 源向量
* @param b 目标向量
* @param rotAngle 输出:旋转角(弧度),逆时针为正,顺时针为负
* @return true计算成功false零向量失败
*/
bool calcRotateAngle(const SVzNL2DPointD& a, const SVzNL2DPointD& b, double& rotAngle) {
SVzNL2DPointD aNorm = a;
SVzNL2DPointD bNorm = b;
// 归一化两个向量,零向量直接返回失败
if (!vecNormalize(aNorm) || !vecNormalize(bNorm)) {
std::cerr << "Error: 输入为零向量,无法计算旋转角!" << std::endl;
return false;
}
// 计算点积并钳位(避免浮点精度导致超出[-1,1]
double dot = vecDot(aNorm, bNorm);
if (dot < -1.0 + EPS)
dot = -1.0 + EPS;
if (dot > 1.0 - EPS)
dot = 1.0 - EPS;
// 点积求无方向夹角0 ~ π)
double angle = acos(dot);
// 叉积判断旋转方向
double cross = vecCross(aNorm, bNorm);
if (cross < -EPS) { // 顺时针,角度取负
rotAngle = -angle;
}
else { // 逆时针/共线,角度取正
rotAngle = angle;
}
return true;
}
double computePtDistToLine(double x0, double y0, double a, double b, double c)
{
double tmp = sqrt(pow(a, 2) + pow(b, 2));
double dist = abs(a * x0 + b * y0 + c) / tmp;
return dist;
}
//计算垂足点直线方程y = kx + b
SVzNL2DPointD sx_getFootPoint(double x0, double y0, double k, double b)
{
double A = k;
double B = -1;
double C = b;
SVzNL2DPointD foot;
foot.x = (B * B * x0 - A * B * y0 - A * C) / (A * A + B * B);
foot.y = (-A * B * x0 + A * A * y0 - B * C) / (A * A + B * B);
return foot;
}
//计算垂足点直线方程ax+by+c = 0
SVzNL2DPointD sx_getFootPoint_abc(double x0, double y0, double A, double B, double C)
{
SVzNL2DPointD foot;
foot.x = (B * B * x0 - A * B * y0 - A * C) / (A * A + B * B);
foot.y = (-A * B * x0 + A * A * y0 - B * C) / (A * A + B * B);
return foot;
}
#if 0
void icvprCcaByTwoPass(const cv::Mat& binImg, cv::Mat& lableImg)
{
// connected component analysis (4-component)
// use two-pass algorithm
// 1. first pass: label each foreground pixel with a label
// 2. second pass: visit each labeled pixel and merge neighbor labels
//
// foreground pixel: binImg(x,y) = 1
// background pixel: binImg(x,y) = 0
if (binImg.empty() ||
binImg.type() != CV_8UC1)
{
return;
}
// 1. first pass
lableImg.release();
binImg.convertTo(lableImg, CV_32SC1);
int label = 1; // start by 2
std::vector<int> labelSet;
labelSet.push_back(0); // background: 0
labelSet.push_back(1); // foreground: 1
int rows = binImg.rows - 1;
int cols = binImg.cols - 1;
for (int i = 1; i < rows; i++)
{
int* data_preRow = lableImg.ptr<int>(i - 1);
int* data_curRow = lableImg.ptr<int>(i);
for (int j = 1; j < cols; j++)
{
if (data_curRow[j] == 1)
{
std::vector<int> neighborLabels;
neighborLabels.reserve(2);
int leftPixel = data_curRow[j - 1];
int upPixel = data_preRow[j];
if (leftPixel > 1)
{
neighborLabels.push_back(leftPixel);
}
if (upPixel > 1)
{
neighborLabels.push_back(upPixel);
}
if (neighborLabels.empty())
{
labelSet.push_back(++label); // assign to a new label
data_curRow[j] = label;
labelSet[label] = label;
}
else
{
std::sort(neighborLabels.begin(), neighborLabels.end());
int smallestLabel = neighborLabels[0];
data_curRow[j] = smallestLabel;
// save equivalence
for (size_t k = 1; k < neighborLabels.size(); k++)
{
int tempLabel = neighborLabels[k];
int& oldSmallestLabel = labelSet[tempLabel];
if (oldSmallestLabel > smallestLabel)
{
labelSet[oldSmallestLabel] = smallestLabel;
oldSmallestLabel = smallestLabel;
}
else if (oldSmallestLabel < smallestLabel)
{
labelSet[smallestLabel] = oldSmallestLabel;
}
}
}
}
}
}
// update equivalent labels
// assigned with the smallest label in each equivalent label set
for (size_t i = 2; i < labelSet.size(); i++)
{
int curLabel = labelSet[i];
int preLabel = labelSet[curLabel];
while (preLabel != curLabel)
{
curLabel = preLabel;
preLabel = labelSet[preLabel];
}
labelSet[i] = curLabel;
}
// 2. second pass
for (int i = 0; i < rows; i++)
{
int* data = lableImg.ptr<int>(i);
for (int j = 0; j < cols; j++)
{
int& pixelLabel = data[j];
pixelLabel = labelSet[pixelLabel];
}
}
}
#endif
#if 0
//Bresenham算法
void line(int x0, int y0, int x1, int y1, TGAImage& image, TGAColor color) {
bool steep = false;
if (std::abs(x1 - x0) < std::abs(y1 - y0)) {
std::swap(x0, y0);
std::swap(x1, y1);
steep = true;
}
if (x0 > x1) {
std::swap(x0, x1);
std::swap(y0, y1);
}
int dx = x1 - x0;
int dy = y1 - y0;
int deltaY = std::abs(dy << 1);
int middle = dx;
int y = y0;
for (int x = x0; x <= x1; ++x) {
if (steep) {
image.set(y, x, color);
}
else {
image.set(x, y, color);
}
deltaY += std::abs(dy << 1);
if (deltaY >= middle) {
y += (y1 > y0 ? 1 : -1);
middle += std::abs(dx << 1);
}
}
}
#endif
//Bresenham算法
void drawLine(
int x0,
int y0,
int x1,
int y1,
std::vector<SVzNL2DPoint>& pts)
{
// 计算dx和dy的绝对值
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
// 确定步进方向
int sx = (x0 < x1) ? 1 : -1; // x方向步进
int sy = (y0 < y1) ? 1 : -1; // y方向步进
// 初始化误差变量结合dx和dy的符号
int err = dx - dy;
while (true) {
SVzNL2DPoint a_pt = { x0, y0 };
pts.push_back(a_pt);
// 到达终点时退出循环
if (x0 == x1 && y0 == y1) break;
int e2 = 2 * err; // 当前误差的两倍
// 根据误差决定步进方向
if (e2 > -dy) { // 误差倾向于x方向步进
err -= dy;
x0 += sx;
}
if (e2 < dx) { // 误差倾向于y方向步进
err += dx;
y0 += sy;
}
}
}
/// <summary>
/// 两步法标注
/// </summary>
/// <param name="bwImg"> 目标点为“1” 空白点为“0”</param>
/// <param name="labImg"> 标注结果。每个点为rgnID, ID从2开始 </param>
/// <param name="labelRgns"></param>
#if 0
void SG_TwoPassLabel(
const cv::Mat& bwImg,
cv::Mat& labImg,
std::vector<SSG_Region>& labelRgns,
int connectivity)
{
assert(bwImg.type() == CV_8UC1);
bwImg.convertTo(labImg, CV_32SC1);
int rows = bwImg.rows - 1;
int cols = bwImg.cols - 1;
//二值图像像素值为0或1为了不冲突label从2开始
int label = 2;
std::vector<int> labelSet;
labelSet.push_back(0);
labelSet.push_back(1);
//第一次扫描
int* data_prev = (int*)labImg.data;
int* data_cur = (int*)(labImg.data + labImg.step);
int left, up;//指针指向的像素点的左方点和上方点
int neighborLabels[2];
for (int i = 1; i < rows; i++)// 忽略第一行和第一列,其实可以将labImg的宽高加1然后在初始化为0就可以了
{
data_cur++;
data_prev++;
for (int j = 1; j < cols; j++, data_cur++, data_prev++)
{
if ((i == 1409) && (j == 432))
int kkk = 1;
if (*data_cur != 1)//当前点不为1扫描下一个点
continue;
left = *(data_cur - 1);
up = *data_prev;
int count = 0;
for (int curLabel : {left, up})
{
if (curLabel > 1)
neighborLabels[count++] = curLabel;
}
if (!count)//赋予一个新的label
{
labelSet.push_back(label);
*data_cur = label;
label++;
continue;
}
//将当前点标记设为左点和上点label的最小值
int smallestLabel = neighborLabels[0];
if (count == 2 && neighborLabels[1] < smallestLabel)
smallestLabel = neighborLabels[1];
*data_cur = smallestLabel;
//设置等价表,这里可能有点难理解
//左点有可能比上点小,也有可能比上点大,两种情况都要考虑,例如
//0 0 1 0 1 0 x x 2 x 3 x
//1 1 1 1 1 1 -> 4 4 2 2 2 2
//要将labelSet中3的位置设置为2
for (int k = 0; k < count; k++)
{
int neiLabel = neighborLabels[k];
int oldSmallestLabel = labelSet[neiLabel];
if (oldSmallestLabel > smallestLabel)
{
if ((oldSmallestLabel == 117) && (smallestLabel == 113))
int kkk = 1;
labelSet[oldSmallestLabel] = smallestLabel;
}
else if (oldSmallestLabel < smallestLabel)
{
if ((smallestLabel == 117) && (oldSmallestLabel == 113))
int kkk = 1;
if (labelSet[smallestLabel] != oldSmallestLabel)
{
}
labelSet[smallestLabel] = oldSmallestLabel;
}
}
}
data_cur++;
data_prev++;
}
//上面一步中,有的labelSet的位置还未设为最小值例如
//0 0 1 0 1 x x 2 x 3
//0 1 1 1 1 -> x 4 2 2 2
//1 1 1 0 1 5 4 2 x 2
//上面这波操作中把labelSet[4]设为2但labelSet[5]仍为4
//这里可以将labelSet[5]设为2
for (size_t i = 2; i < labelSet.size(); i++)
{
int curLabel = labelSet[i];
int prelabel = labelSet[curLabel];
while (prelabel != curLabel)
{
curLabel = prelabel;
prelabel = labelSet[prelabel];
}
labelSet[i] = curLabel;
}
//第二次扫描用labelSet进行更新最后一列
std::vector<SSG_Region*> labelInfo;
labelInfo.resize(labelSet.size(), nullptr);
data_cur = (int*)labImg.data;
for (int i = 0; i < labImg.rows; i++)
{
for (int j = 0; j < labImg.cols; j++)
{
*data_cur = labelSet[*data_cur];
if (*data_cur > 1) //有效label
{
//统计Region信息
SSG_Region* info_cur = (SSG_Region*)labelInfo[*data_cur];
if (nullptr == info_cur)
{
SSG_Region new_rgn = { {j,j,i,i}, 1, *data_cur };
labelRgns.push_back(new_rgn); //push_back()后vector中内存单元可能会被改动
for (int m = 0; m < labelRgns.size(); m++)
{
info_cur = &labelRgns[m];
labelInfo[info_cur->labelID] = info_cur;
}
}
else
{
assert(*data_cur == info_cur->labelID);
if (info_cur->roi.left > j)
info_cur->roi.left = j;
if (info_cur->roi.right < j)
info_cur->roi.right = j;
if (info_cur->roi.top > i)
info_cur->roi.top = i;
if (info_cur->roi.bottom < i)
info_cur->roi.bottom = i;
info_cur->ptCounter++;
}
}
data_cur++;
}
}
return;
}
#else
// 查找函数(带路径压缩)
int find(int x, std::vector<int>& parent) {
if (parent[x] != x) {
parent[x] = find(parent[x], parent);
}
return parent[x];
}
// 合并函数(按秩合并到较小根)
void unionSet(int x, int y, std::vector<int>& parent) {
int rootX = find(x, parent);
int rootY = find(y, parent);
if (rootX != rootY) {
if (rootX < rootY) {
parent[rootY] = rootX;
}
else {
parent[rootX] = rootY;
}
}
}
/**
* @brief 连通域标注函数
* @param image 输入二值图像0表示背景非0为前景
* @param labels 输出标签矩阵
* @param connectivity 连通性4或8
*/
void SG_TwoPassLabel(
const cv::Mat& bwImg,
cv::Mat& labImg,
std::vector<SSG_Region>& labelRgns,
int connectivity)
{
assert(bwImg.type() == CV_8UC1);
bwImg.convertTo(labImg, CV_32SC1);
if (bwImg.rows == 0)
return;
int rows = bwImg.rows - 1;
int cols = bwImg.cols - 1;
// 初始化并查集(最大可能标签数为像素总数)
int max_label = rows * cols;
std::vector<int> parent(max_label + 1);
for (int i = 0; i <= max_label; ++i) {
parent[i] = i;
}
//第一次扫描
int label_cnt = 2; // 当前最大标签,二值图像像素值为0或1为了不冲突label从2开始
int* data_prev = (int*)labImg.data;
int* data_cur = (int*)(labImg.data + labImg.step);
// 第一遍扫描:临时标签分配
for (int i = 1; i < rows; i++)
{
data_cur++;
data_prev++;
for (int j = 1; j < cols; j++, data_cur++, data_prev++)
{
if (*data_cur != 1)//当前点不为1扫描下一个点
continue;
int left = *(data_cur - 1);
int up = *data_prev;
int up_left = *(data_prev-1);
int up_right = *(data_prev + 1);
std::vector<int> neighbors;
auto add_neighbor = [&](int neiLabel) {
if (neiLabel != 0) {
neighbors.push_back(find(neiLabel, parent));
}
};
// 检查已处理邻域
if(up > 1)
add_neighbor(up); // 上
if( (left > 1) && (left != up))
add_neighbor(left); // 左
if (connectivity == 8)
{
if( (up_left > 1) && (up_left != up) && (up_left != left))
add_neighbor(up_left); // 左上
if( (up_right > 1) && (up_right != up) && (up_right != left) && (up_right != up_left))
add_neighbor(up_right); // 右上
}
if (neighbors.empty()) { // 新连通域
*data_cur = label_cnt++;
}
else { // 合并邻域
int min_root = *std::min_element(neighbors.begin(), neighbors.end());
*data_cur = min_root;
for (int root : neighbors)
{
if (root != min_root)
{
unionSet(root, min_root, parent);
}
}
}
}
data_cur++;
data_prev++;
}
for (int i = 2; i < label_cnt; i++)
parent[i] = find(parent[i], parent);
data_cur = (int*)labImg.data;
for (int i = 0; i < labImg.rows; i++)
{
for (int j = 0; j < labImg.cols; j++)
{
if (*data_cur > 1)
{
*data_cur = parent[*data_cur];
}
data_cur++;
}
}
std::vector<SSG_Region*> labelInfo;
labelInfo.resize(label_cnt, nullptr);
// (可选)重新映射为连续标签
std::unordered_map<int, int> label_map;
int new_label = 2;
data_cur = (int*)labImg.data;
for (int i = 0; i < labImg.rows; i++)
{
for (int j = 0; j < labImg.cols; j++)
{
if (j == 69)
int kkk = 1;
int lbl = *data_cur;
if (lbl > 1)
{
if (label_map.find(lbl) == label_map.end())
{
label_map[lbl] = new_label++;
}
*data_cur = label_map[lbl];
//统计Region信息
SSG_Region* info_cur = (SSG_Region*)labelInfo[*data_cur];
if (nullptr == info_cur)
{
SSG_Region new_rgn = { {j,j,i,i}, 1, *data_cur };
labelRgns.push_back(new_rgn); //push_back()后vector中内存单元可能会被改动
for (int m = 0; m < labelRgns.size(); m++)
{
info_cur = &labelRgns[m];
labelInfo[info_cur->labelID] = info_cur;
}
}
else
{
assert(*data_cur == info_cur->labelID);
if (info_cur->roi.left > j)
info_cur->roi.left = j;
if (info_cur->roi.right < j)
info_cur->roi.right = j;
if (info_cur->roi.top > i)
info_cur->roi.top = i;
if (info_cur->roi.bottom < i)
info_cur->roi.bottom = i;
info_cur->ptCounter++;
}
}
data_cur++;
}
}
}
#endif
// 函数从平面法向量计算欧拉角ZYX顺序
SSG_EulerAngles planeNormalToEuler(double A, double B, double C) {
SSG_EulerAngles angles = { 0, 0, 0 };
// 1. 归一化法向量
double length = std::sqrt(A * A + B * B + C * C);
if (length < 1e-7)
return angles;
double nx = A / length;
double ny = B / length;
double nz = C / length;
// 2. 计算俯仰角绕Y轴
angles.pitch = std::asin(nx) * (180.0 / M_PI); // 转为度数
// 3. 计算Roll绕X轴
const double cos_pitch = std::sqrt(1 - nx * nx); // 等价于cos(pitch)
if (cos_pitch > 1e-7) {
// 当cos_pitch非零时用atan2计算Roll
angles.roll = std::asin(-ny/ cos_pitch) * (180.0 / M_PI);
}
else {
// 当Pitch接近±π/2时Roll无法确定设为0
angles.roll = 0.0;
}
// 4. 假设yaw为0绕Z轴
angles.yaw= 0.0;
return angles;
}
// 定义3x3旋转矩阵结构体
struct RotationMatrix {
double data[3][3]; // 行优先存储 [row][col]
};
// 将角度转换为弧度
inline double degreesToRadians(double degrees) {
return degrees * M_PI / 180.0;
}
// 从欧拉角计算旋转矩阵 (ZYX顺序: 偏航Z -> 俯仰Y -> 横滚X)
RotationMatrix eulerToRotationMatrix(double yaw_deg, double pitch_deg, double roll_deg) {
RotationMatrix R;
// 角度转弧度
double yaw = degreesToRadians(yaw_deg);
double pitch = degreesToRadians(pitch_deg);
double roll = degreesToRadians(roll_deg);
// 预计算三角函数
double cy = cos(yaw);
double sy = sin(yaw);
double cp = cos(pitch);
double sp = sin(pitch);
double cr = cos(roll);
double sr = sin(roll);
// 计算旋转矩阵元素ZYX顺序 = Rz * Ry * Rx
R.data[0][0] = cy * cp;
R.data[0][1] = cy * sp * sr - sy * cr;
R.data[0][2] = cy * sp * cr + sy * sr;
R.data[1][0] = sy * cp;
R.data[1][1] = sy * sp * sr + cy * cr;
R.data[1][2] = sy * sp * cr - cy * sr;
R.data[2][0] = -sp;
R.data[2][1] = cp * sr;
R.data[2][2] = cp * cr;
return R;
}
// 定义三维向量结构体
struct Vector3 {
double x, y, z;
Vector3(double x_, double y_, double z_) : x(x_), y(y_), z(z_) {}
};
// 定义四元数结构体
struct Quaternion {
double w, x, y, z;
Quaternion(double w_, double x_, double y_, double z_)
: w(w_), x(x_), y(y_), z(z_) {}
};
// 计算两个向量的旋转四元数
Quaternion rotationBetweenVectors(const Vector3& a, const Vector3& b) {
// 归一化输入向量
const double eps = 1e-7;
double a_len = std::sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
double b_len = std::sqrt(b.x * b.x + b.y * b.y + b.z * b.z);
if (a_len < eps || b_len < eps) {
// 零向量无法定义旋转,返回单位四元数
return Quaternion(1.0, 0.0, 0.0, 0.0);
}
Vector3 a_norm(a.x / a_len, a.y / a_len, a.z / a_len);
Vector3 b_norm(b.x / b_len, b.y / b_len, b.z / b_len);
double cos_theta = a_norm.x * b_norm.x + a_norm.y * b_norm.y + a_norm.z * b_norm.z;
// 处理共线情况
if (cos_theta > 1.0 - eps) {
// 向量方向相同,无需旋转
return Quaternion(1.0, 0.0, 0.0, 0.0);
}
else if (cos_theta < -1.0 + eps) {
// 向量方向相反绕任意垂直轴旋转180度
Vector3 axis(1.0, 0.0, 0.0); // 默认选择X轴
if (std::abs(a_norm.y) < eps && std::abs(a_norm.z) < eps) {
// 如果a接近X轴则选择Y轴作为旋转轴
axis = Vector3(0.0, 1.0, 0.0);
}
return Quaternion(0.0, axis.x, axis.y, axis.z); // 180度旋转
}
// 计算旋转轴和半角
Vector3 axis = Vector3(
a_norm.y * b_norm.z - a_norm.z * b_norm.y,
a_norm.z * b_norm.x - a_norm.x * b_norm.z,
a_norm.x * b_norm.y - a_norm.y * b_norm.x
);
double axis_len = std::sqrt(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z);
if (axis_len < eps) { // 防止除零
return Quaternion(1.0, 0.0, 0.0, 0.0);
}
axis.x /= axis_len;
axis.y /= axis_len;
axis.z /= axis_len;
double half_cos = std::sqrt(0.5 * (1.0 + cos_theta));
double half_sin = std::sqrt(0.5 * (1.0 - cos_theta));
return Quaternion(
half_cos,
half_sin * axis.x,
half_sin * axis.y,
half_sin * axis.z
);
}
void quaternionToMatrix(const Quaternion& q, double mat[3][3]) {
double xx = q.x * q.x, yy = q.y * q.y, zz = q.z * q.z;
double xy = q.x * q.y, xz = q.x * q.z, yz = q.y * q.z;
double wx = q.w * q.x, wy = q.w * q.y, wz = q.w * q.z;
mat[0][0] = 1 - 2 * (yy + zz);
mat[0][1] = 2 * (xy - wz);
mat[0][2] = 2 * (xz + wy);
mat[1][0] = 2 * (xy + wz);
mat[1][1] = 1 - 2 * (xx + zz);
mat[1][2] = 2 * (yz - wx);
mat[2][0] = 2 * (xz - wy);
mat[2][1] = 2 * (yz + wx);
mat[2][2] = 1 - 2 * (xx + yy);
}
//计算一个平面调平参数。
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara sg_getPlaneCalibPara(
SVzNL3DLaserLine* laser3DPoints,
int lineNum)
{
//设置初始结果
double initCalib[9]= {
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 };
SSG_planeCalibPara planePara;
for (int i = 0; i < 9; i++)
planePara.planeCalib[i] = initCalib[i];
planePara.planeHeight = -1.0;
//统计z范围
SVzNLRangeD zRange = { 0, -1 }; //< Z范围
for (int line = 0; line < lineNum; line++)
{
for (int i = 0; i < laser3DPoints[line].nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &laser3DPoints[line].p3DPosition[i];
if (pt3D->pt3D.z < 1e-4)
continue;
//z
if (zRange.max < zRange.min)
{
zRange.min = pt3D->pt3D.z;
zRange.max = pt3D->pt3D.z;
}
else
{
if (zRange.min > pt3D->pt3D.z)
zRange.min = pt3D->pt3D.z;
if (zRange.max < pt3D->pt3D.z)
zRange.max = pt3D->pt3D.z;
}
}
}
//在Z方向进行统计取第一个极值
//以mm为单位简化量化
int zHistSize = (int)(zRange.max - zRange.min) + 1;
if (zHistSize == 0)
return planePara;
std::vector<int> zHist;
zHist.resize(zHistSize);
int totalPntSize = 0;
for (int line = 0; line < lineNum; line++)
{
for (int i = 0; i < laser3DPoints[line].nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &laser3DPoints[line].p3DPosition[i];
if (pt3D->pt3D.z < 1e-4)
continue;
totalPntSize++;
int histPos = (int)(pt3D->pt3D.z - zRange.min);
zHist[histPos] ++;
}
}
std::vector<int> zSumHist;
zSumHist.resize(zHistSize);
bool isSame = true;
//以厘米为单位进行累加
for (int i = 0; i < zHistSize; i++)
{
int sumValue = 0;
for (int j = i - 5; j <= i + 5; j++)
{
if ((j >= 0) && (j < zHistSize))
sumValue += zHist[j];
}
zSumHist[i] = sumValue;
if (i > 0)
{
if (sumValue != zSumHist[i - 1])
isSame = false;
}
}
if(true == isSame)
{
//不进行累加(如果累加,累加值相等)
for (int i = 0; i < zHistSize; i++)
zSumHist[i] = zHist[i];
}
//寻找极值
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
int pre_data = -1;
std::vector< SSG_intPair> pkTop;
std::vector< SSG_intPair> pkBtm;
std::vector<int> pkBtmBackIndexing;
pkBtmBackIndexing.resize(zHistSize);
for (int i = 0; i < zHistSize; i++)
pkBtmBackIndexing[i] = -1;
for (int i = 0; i < zHistSize; i++)
{
int curr_data = zSumHist[i];
if (pre_data < 0)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
continue;
}
eEdgePtIdx = i;
double z_diff = curr_data - pre_data;
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_i, pre_data});
_state = 2;
}
else if(i == (zHistSize-1))
pkTop.push_back({ i, curr_data });
break;
case 2: //下降
if (z_diff > 0) // 上升
{
int pkBtmIdx = (int)pkBtm.size();
pkBtmBackIndexing[pre_i] = pkBtmIdx;
pkBtm.push_back({ pre_i, pre_data });
_state = 1;
}
else if (i == (zHistSize - 1))
{
int pkBtmIdx = (int)pkBtm.size();
pkBtmBackIndexing[i] = pkBtmIdx;
pkBtm.push_back({ i, curr_data });
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//寻找第一个超过总点数1/3的极值点
if (pkTop.size() < 1)
return planePara;
int pntSizeTh = totalPntSize / 10;
SSG_intPair* vldPeak = NULL;
for (int i = 0, i_max = (int)pkTop.size(); i < i_max; i++)
{
if (pkTop[i].data_1 > pntSizeTh)
{
vldPeak = &pkTop[i];
break;
}
}
if (NULL == vldPeak)
return planePara;
//寻找开始和结束位置
//向前向后寻找
int preBtmIdx = -1;
for (int j = vldPeak->data_0 - 1; j >= 0; j--)
{
if (pkBtmBackIndexing[j] >= 0)
{
int idx = pkBtmBackIndexing[j];
if (pkBtm[idx].data_1 < (vldPeak->data_1 / 2))
{
preBtmIdx = j;
break;
}
}
}
int postBtmIdx = -1;
for (int j = vldPeak->data_0 + 1; j <zHistSize; j++)
{
if (pkBtmBackIndexing[j] >= 0)
{
int idx = pkBtmBackIndexing[j];
if (pkBtm[idx].data_1 < (vldPeak->data_1 / 2))
{
postBtmIdx = j;
break;
}
}
}
SVzNLRangeD topZRange;
if (preBtmIdx < 0)
topZRange.min = zRange.min;
else
topZRange.min = (float)preBtmIdx + zRange.min;
if (postBtmIdx < 0)
topZRange.max = zRange.max;
else
topZRange.max = (float)postBtmIdx + zRange.min;
//取数据
std::vector<cv::Point3d> Points3ds;
for (int line = 0; line < lineNum; line++)
{
for (int i = 0; i < laser3DPoints[line].nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &laser3DPoints[line].p3DPosition[i];
if (pt3D->pt3D.z < 1e-4)
continue;
if ((pt3D->pt3D.z >= topZRange.min) && (pt3D->pt3D.z <= topZRange.max))
{
cv::Point3d a_vldPt;
a_vldPt.x = pt3D->pt3D.x;
a_vldPt.y = pt3D->pt3D.y;
a_vldPt.z = pt3D->pt3D.z;
Points3ds.push_back(a_vldPt);
}
}
}
//平面拟合
std::vector<double> planceFunc;
//res: [0]=A, [1]= B, [2]=-1.0, [3]=C,
vzCaculateLaserPlane(Points3ds, planceFunc);
#if 1 //两个向量的旋转旋转,使用四元数法,
Vector3 a = Vector3(planceFunc[0], planceFunc[1], planceFunc[2]);
Vector3 b = Vector3(0, 0, -1.0);
Quaternion quanPara = rotationBetweenVectors(a, b);
RotationMatrix rMatrix;
quaternionToMatrix(quanPara, rMatrix.data);
//计算反向旋转矩阵
Quaternion invQuanPara = rotationBetweenVectors(b, a);
RotationMatrix invMatrix;
quaternionToMatrix(invQuanPara, invMatrix.data);
#else //根据平面的法向量计算欧拉角,进而计算旋转矩阵
//参数计算
SSG_EulerAngles eulerPra = planeNormalToEuler(planceFunc[0], planceFunc[1], planceFunc[2]);
//反射进行校正
eulerPra.roll = eulerPra.roll;
eulerPra.pitch = eulerPra.pitch;
eulerPra.yaw = eulerPra.yaw;
RotationMatrix rMatrix = eulerToRotationMatrix(eulerPra.yaw, eulerPra.pitch, eulerPra.roll);
#endif
planePara.planeCalib[0] = rMatrix.data[0][0];
planePara.planeCalib[1] = rMatrix.data[0][1];
planePara.planeCalib[2] = rMatrix.data[0][2];
planePara.planeCalib[3] = rMatrix.data[1][0];
planePara.planeCalib[4] = rMatrix.data[1][1];
planePara.planeCalib[5] = rMatrix.data[1][2];
planePara.planeCalib[6] = rMatrix.data[2][0];
planePara.planeCalib[7] = rMatrix.data[2][1];
planePara.planeCalib[8] = rMatrix.data[2][2];
planePara.invRMatrix[0] = invMatrix.data[0][0];
planePara.invRMatrix[1] = invMatrix.data[0][1];
planePara.invRMatrix[2] = invMatrix.data[0][2];
planePara.invRMatrix[3] = invMatrix.data[1][0];
planePara.invRMatrix[4] = invMatrix.data[1][1];
planePara.invRMatrix[5] = invMatrix.data[1][2];
planePara.invRMatrix[6] = invMatrix.data[2][0];
planePara.invRMatrix[7] = invMatrix.data[2][1];
planePara.invRMatrix[8] = invMatrix.data[2][2];
#if 0 //test: 两个矩阵的乘积必须是单位阵
double testMatrix[3][3];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
testMatrix[i][j] = 0;
for (int m = 0; m < 3; m++)
testMatrix[i][j] += invMatrix.data[i][m] * rMatrix.data[m][j];
}
}
#endif
//数据进行转换
SVzNLRangeD calibZRange = { 0, -1 };
topZRange = { 0, -1 };
for (int i = 0, i_max = (int)Points3ds.size(); i < i_max; i++)
{
//z
if (topZRange.max < topZRange.min)
{
topZRange.min = Points3ds[i].z;
topZRange.max = Points3ds[i].z;
}
else
{
if (topZRange.min > Points3ds[i].z)
topZRange.min = Points3ds[i].z;
if (topZRange.max < Points3ds[i].z)
topZRange.max = Points3ds[i].z;
}
cv::Point3f a_calibPt;
a_calibPt.x = (float)(Points3ds[i].x * planePara.planeCalib[0] + Points3ds[i].y * planePara.planeCalib[1] + Points3ds[i].z * planePara.planeCalib[2]);
a_calibPt.y = (float)(Points3ds[i].x * planePara.planeCalib[3] + Points3ds[i].y * planePara.planeCalib[4] + Points3ds[i].z * planePara.planeCalib[5]);
a_calibPt.z = (float)(Points3ds[i].x * planePara.planeCalib[6] + Points3ds[i].y * planePara.planeCalib[7] + Points3ds[i].z * planePara.planeCalib[8]);
//z
if (calibZRange.max < calibZRange.min)
{
calibZRange.min = a_calibPt.z;
calibZRange.max = a_calibPt.z;
}
else
{
if (calibZRange.min > a_calibPt.z)
calibZRange.min = a_calibPt.z;
if (calibZRange.max < a_calibPt.z)
calibZRange.max = a_calibPt.z;
}
}
planePara.planeHeight = calibZRange.min;
return planePara;
}
SSG_planeCalibPara adjustPlaneToXYPlane(double plane_A, double plane_B, double plane_C)
{
SSG_planeCalibPara calibPara;
//两个向量的旋转旋转,使用四元数法,
Vector3 a = Vector3(plane_A, plane_B, plane_C);
Vector3 b = Vector3(0, 0, -1.0);
Quaternion quanPara = rotationBetweenVectors(a, b);
RotationMatrix rMatrix;
quaternionToMatrix(quanPara, rMatrix.data);
//计算反向旋转矩阵
Quaternion invQuanPara = rotationBetweenVectors(b, a);
RotationMatrix invMatrix;
quaternionToMatrix(invQuanPara, invMatrix.data);
calibPara.planeCalib[0] = rMatrix.data[0][0];
calibPara.planeCalib[1] = rMatrix.data[0][1];
calibPara.planeCalib[2] = rMatrix.data[0][2];
calibPara.planeCalib[3] = rMatrix.data[1][0];
calibPara.planeCalib[4] = rMatrix.data[1][1];
calibPara.planeCalib[5] = rMatrix.data[1][2];
calibPara.planeCalib[6] = rMatrix.data[2][0];
calibPara.planeCalib[7] = rMatrix.data[2][1];
calibPara.planeCalib[8] = rMatrix.data[2][2];
calibPara.invRMatrix[0] = invMatrix.data[0][0];
calibPara.invRMatrix[1] = invMatrix.data[0][1];
calibPara.invRMatrix[2] = invMatrix.data[0][2];
calibPara.invRMatrix[3] = invMatrix.data[1][0];
calibPara.invRMatrix[4] = invMatrix.data[1][1];
calibPara.invRMatrix[5] = invMatrix.data[1][2];
calibPara.invRMatrix[6] = invMatrix.data[2][0];
calibPara.invRMatrix[7] = invMatrix.data[2][1];
calibPara.invRMatrix[8] = invMatrix.data[2][2];
return calibPara;
}
//计算一个平面调平参数。
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara sg_getPlaneCalibPara2(
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
//设置初始结果
double initCalib[9] = {
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 };
SSG_planeCalibPara planePara;
for (int i = 0; i < 9; i++)
planePara.planeCalib[i] = initCalib[i];
planePara.planeHeight = -1.0;
int lineNum = (int)scanLines.size();
//统计z范围
SVzNLRangeD zRange = { 0, -1 }; //< Z范围
for (int line = 0; line < lineNum; line++)
{
int nPositionCnt = (int)scanLines[line].size();
for (int i = 0; i < nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &scanLines[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
//z
if (zRange.max < zRange.min)
{
zRange.min = pt3D->pt3D.z;
zRange.max = pt3D->pt3D.z;
}
else
{
if (zRange.min > pt3D->pt3D.z)
zRange.min = pt3D->pt3D.z;
if (zRange.max < pt3D->pt3D.z)
zRange.max = pt3D->pt3D.z;
}
}
}
//在Z方向进行统计取第一个极值
//以mm为单位简化量化
int zHistSize = (int)(zRange.max - zRange.min) + 1;
if (zHistSize == 0)
return planePara;
std::vector<int> zHist;
zHist.resize(zHistSize);
int totalPntSize = 0;
for (int line = 0; line < lineNum; line++)
{
int nPositionCnt = (int)scanLines[line].size();
for (int i = 0; i < nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &scanLines[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
totalPntSize++;
int histPos = (int)(pt3D->pt3D.z - zRange.min);
zHist[histPos] ++;
}
}
std::vector<int> zSumHist;
zSumHist.resize(zHistSize);
bool isSame = true;
//以厘米为单位进行累加
for (int i = 0; i < zHistSize; i++)
{
int sumValue = 0;
for (int j = i - 5; j <= i + 5; j++)
{
if ((j >= 0) && (j < zHistSize))
sumValue += zHist[j];
}
zSumHist[i] = sumValue;
if (i > 0)
{
if (sumValue != zSumHist[i - 1])
isSame = false;
}
}
if (true == isSame)
{
//不进行累加(如果累加,累加值相等)
for (int i = 0; i < zHistSize; i++)
zSumHist[i] = zHist[i];
}
//寻找极值
int _state = 0;
int pre_i = -1;
int sEdgePtIdx = -1;
int eEdgePtIdx = -1;
int pre_data = -1;
std::vector< SSG_intPair> pkTop;
std::vector< SSG_intPair> pkBtm;
std::vector<int> pkBtmBackIndexing;
pkBtmBackIndexing.resize(zHistSize);
for (int i = 0; i < zHistSize; i++)
pkBtmBackIndexing[i] = -1;
for (int i = 0; i < zHistSize; i++)
{
int curr_data = zSumHist[i];
if (pre_data < 0)
{
sEdgePtIdx = i;
eEdgePtIdx = i;
pre_data = curr_data;
pre_i = i;
continue;
}
eEdgePtIdx = i;
double z_diff = curr_data - pre_data;
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_i, pre_data });
_state = 2;
}
else if (i == (zHistSize - 1))
pkTop.push_back({ i, curr_data });
break;
case 2: //下降
if (z_diff > 0) // 上升
{
int pkBtmIdx = (int)pkBtm.size();
pkBtmBackIndexing[pre_i] = pkBtmIdx;
pkBtm.push_back({ pre_i, pre_data });
_state = 1;
}
else if (i == (zHistSize - 1))
{
int pkBtmIdx = (int)pkBtm.size();
pkBtmBackIndexing[i] = pkBtmIdx;
pkBtm.push_back({ i, curr_data });
}
break;
default:
_state = 0;
break;
}
pre_data = curr_data;
pre_i = i;
}
//寻找第一个超过总点数1/3的极值点
if (pkTop.size() < 1)
return planePara;
int pntSizeTh = totalPntSize / 10;
SSG_intPair* vldPeak = NULL;
for (int i = 0, i_max = (int)pkTop.size(); i < i_max; i++)
{
if (pkTop[i].data_1 > pntSizeTh)
{
vldPeak = &pkTop[i];
break;
}
}
if (NULL == vldPeak)
return planePara;
//寻找开始和结束位置
//向前向后寻找
int preBtmIdx = -1;
for (int j = vldPeak->data_0 - 1; j >= 0; j--)
{
if (pkBtmBackIndexing[j] >= 0)
{
int idx = pkBtmBackIndexing[j];
if (pkBtm[idx].data_1 < (vldPeak->data_1 / 2))
{
preBtmIdx = j;
break;
}
}
}
int postBtmIdx = -1;
for (int j = vldPeak->data_0 + 1; j < zHistSize; j++)
{
if (pkBtmBackIndexing[j] >= 0)
{
int idx = pkBtmBackIndexing[j];
if (pkBtm[idx].data_1 < (vldPeak->data_1 / 2))
{
postBtmIdx = j;
break;
}
}
}
SVzNLRangeD topZRange;
if (preBtmIdx < 0)
topZRange.min = zRange.min;
else
topZRange.min = (float)preBtmIdx + zRange.min;
if (postBtmIdx < 0)
topZRange.max = zRange.max;
else
topZRange.max = (float)postBtmIdx + zRange.min;
//取数据
std::vector<cv::Point3d> Points3ds;
for (int line = 0; line < lineNum; line++)
{
int nPositionCnt = (int)scanLines[line].size();
for (int i = 0; i < nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &scanLines[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
if ((pt3D->pt3D.z >= topZRange.min) && (pt3D->pt3D.z <= topZRange.max))
{
cv::Point3d a_vldPt;
a_vldPt.x = pt3D->pt3D.x;
a_vldPt.y = pt3D->pt3D.y;
a_vldPt.z = pt3D->pt3D.z;
Points3ds.push_back(a_vldPt);
}
}
}
//平面拟合
std::vector<double> planceFunc;
vzCaculateLaserPlane(Points3ds, planceFunc);
#if 1 //两个向量的旋转旋转,使用四元数法,
Vector3 a = Vector3(planceFunc[0], planceFunc[1], planceFunc[2]);
Vector3 b = Vector3(0, 0, -1.0);
Quaternion quanPara = rotationBetweenVectors(a, b);
RotationMatrix rMatrix;
quaternionToMatrix(quanPara, rMatrix.data);
//计算反向旋转矩阵
Quaternion invQuanPara = rotationBetweenVectors(b, a);
RotationMatrix invMatrix;
quaternionToMatrix(invQuanPara, invMatrix.data);
#else //根据平面的法向量计算欧拉角,进而计算旋转矩阵
//参数计算
SSG_EulerAngles eulerPra = planeNormalToEuler(planceFunc[0], planceFunc[1], planceFunc[2]);
//反射进行校正
eulerPra.roll = eulerPra.roll;
eulerPra.pitch = eulerPra.pitch;
eulerPra.yaw = eulerPra.yaw;
RotationMatrix rMatrix = eulerToRotationMatrix(eulerPra.yaw, eulerPra.pitch, eulerPra.roll);
#endif
planePara.planeCalib[0] = rMatrix.data[0][0];
planePara.planeCalib[1] = rMatrix.data[0][1];
planePara.planeCalib[2] = rMatrix.data[0][2];
planePara.planeCalib[3] = rMatrix.data[1][0];
planePara.planeCalib[4] = rMatrix.data[1][1];
planePara.planeCalib[5] = rMatrix.data[1][2];
planePara.planeCalib[6] = rMatrix.data[2][0];
planePara.planeCalib[7] = rMatrix.data[2][1];
planePara.planeCalib[8] = rMatrix.data[2][2];
planePara.invRMatrix[0] = invMatrix.data[0][0];
planePara.invRMatrix[1] = invMatrix.data[0][1];
planePara.invRMatrix[2] = invMatrix.data[0][2];
planePara.invRMatrix[3] = invMatrix.data[1][0];
planePara.invRMatrix[4] = invMatrix.data[1][1];
planePara.invRMatrix[5] = invMatrix.data[1][2];
planePara.invRMatrix[6] = invMatrix.data[2][0];
planePara.invRMatrix[7] = invMatrix.data[2][1];
planePara.invRMatrix[8] = invMatrix.data[2][2];
#if 0 //test: 两个矩阵的乘积必须是单位阵
double testMatrix[3][3];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
testMatrix[i][j] = 0;
for (int m = 0; m < 3; m++)
testMatrix[i][j] += invMatrix.data[i][m] * rMatrix.data[m][j];
}
}
#endif
//数据进行转换
SVzNLRangeD calibZRange = { 0, -1 };
topZRange = { 0, -1 };
double sumMeanZ = 0;
int sumSize = 0;
for (int i = 0, i_max = (int)Points3ds.size(); i < i_max; i++)
{
//z
if (topZRange.max < topZRange.min)
{
topZRange.min = Points3ds[i].z;
topZRange.max = Points3ds[i].z;
}
else
{
if (topZRange.min > Points3ds[i].z)
topZRange.min = Points3ds[i].z;
if (topZRange.max < Points3ds[i].z)
topZRange.max = Points3ds[i].z;
}
cv::Point3f a_calibPt;
a_calibPt.x = (float)(Points3ds[i].x * planePara.planeCalib[0] + Points3ds[i].y * planePara.planeCalib[1] + Points3ds[i].z * planePara.planeCalib[2]);
a_calibPt.y = (float)(Points3ds[i].x * planePara.planeCalib[3] + Points3ds[i].y * planePara.planeCalib[4] + Points3ds[i].z * planePara.planeCalib[5]);
a_calibPt.z = (float)(Points3ds[i].x * planePara.planeCalib[6] + Points3ds[i].y * planePara.planeCalib[7] + Points3ds[i].z * planePara.planeCalib[8]);
//z
if (calibZRange.max < calibZRange.min)
{
calibZRange.min = a_calibPt.z;
calibZRange.max = a_calibPt.z;
sumMeanZ += a_calibPt.z;
sumSize++;
}
else
{
if (calibZRange.min > a_calibPt.z)
calibZRange.min = a_calibPt.z;
if (calibZRange.max < a_calibPt.z)
calibZRange.max = a_calibPt.z;
sumMeanZ += a_calibPt.z;
sumSize++;
}
}
if (sumSize > 0)
sumMeanZ = sumMeanZ / (double)sumSize;
planePara.planeHeight = sumMeanZ; // calibZRange.min;
return planePara;
}
//针对孔板计算一个平面调平参数:提取不经过孔的扫描线进行平面拟合
//数据输入中可以有一个地平面和参考调平平面,以最高的平面进行调平
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara sg_getHolePlaneCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
//设置初始结果
double initCalib[9] = {
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 };
SSG_planeCalibPara planePara;
for (int i = 0; i < 9; i++)
planePara.planeCalib[i] = initCalib[i];
planePara.planeHeight = -1.0;
SSG_lineSegParam segParam;
segParam.segGapTh_y = 1.0;
segParam.segGapTh_z = 1.0;
int lineNum = (int)scanLines.size();
std::vector<int> validLines;
for (int line = 0; line < lineNum; line++)
{
//去除零点
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(scanLines[line], segParam, segs);
if (segs.size() == 1)
validLines.push_back(line);
}
//取数据
std::vector<cv::Point3d> Points3ds;
for (int vline = 0; vline < (int)validLines.size(); vline++)
{
int line = validLines[vline];
int nPositionCnt = (int)scanLines[line].size();
for (int i = 0; i < nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &scanLines[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
cv::Point3d a_vldPt;
a_vldPt.x = pt3D->pt3D.x;
a_vldPt.y = pt3D->pt3D.y;
a_vldPt.z = pt3D->pt3D.z;
Points3ds.push_back(a_vldPt);
}
}
//平面拟合
std::vector<double> planceFunc;
vzCaculateLaserPlane(Points3ds, planceFunc);
#if 1 //两个向量的旋转旋转,使用四元数法,
Vector3 a = Vector3(planceFunc[0], planceFunc[1], planceFunc[2]);
Vector3 b = Vector3(0, 0, -1.0);
Quaternion quanPara = rotationBetweenVectors(a, b);
RotationMatrix rMatrix;
quaternionToMatrix(quanPara, rMatrix.data);
//计算反向旋转矩阵
Quaternion invQuanPara = rotationBetweenVectors(b, a);
RotationMatrix invMatrix;
quaternionToMatrix(invQuanPara, invMatrix.data);
#else //根据平面的法向量计算欧拉角,进而计算旋转矩阵
//参数计算
SSG_EulerAngles eulerPra = planeNormalToEuler(planceFunc[0], planceFunc[1], planceFunc[2]);
//反射进行校正
eulerPra.roll = eulerPra.roll;
eulerPra.pitch = eulerPra.pitch;
eulerPra.yaw = eulerPra.yaw;
RotationMatrix rMatrix = eulerToRotationMatrix(eulerPra.yaw, eulerPra.pitch, eulerPra.roll);
#endif
planePara.planeCalib[0] = rMatrix.data[0][0];
planePara.planeCalib[1] = rMatrix.data[0][1];
planePara.planeCalib[2] = rMatrix.data[0][2];
planePara.planeCalib[3] = rMatrix.data[1][0];
planePara.planeCalib[4] = rMatrix.data[1][1];
planePara.planeCalib[5] = rMatrix.data[1][2];
planePara.planeCalib[6] = rMatrix.data[2][0];
planePara.planeCalib[7] = rMatrix.data[2][1];
planePara.planeCalib[8] = rMatrix.data[2][2];
planePara.invRMatrix[0] = invMatrix.data[0][0];
planePara.invRMatrix[1] = invMatrix.data[0][1];
planePara.invRMatrix[2] = invMatrix.data[0][2];
planePara.invRMatrix[3] = invMatrix.data[1][0];
planePara.invRMatrix[4] = invMatrix.data[1][1];
planePara.invRMatrix[5] = invMatrix.data[1][2];
planePara.invRMatrix[6] = invMatrix.data[2][0];
planePara.invRMatrix[7] = invMatrix.data[2][1];
planePara.invRMatrix[8] = invMatrix.data[2][2];
#if 0 //test: 两个矩阵的乘积必须是单位阵
double testMatrix[3][3];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
testMatrix[i][j] = 0;
for (int m = 0; m < 3; m++)
testMatrix[i][j] += invMatrix.data[i][m] * rMatrix.data[m][j];
}
}
#endif
//数据进行转换
SVzNLRangeD calibZRange = { 0, -1 };
SVzNLRangeD topZRange = { 0, -1 };
double sumMeanZ = 0;
int sumSize = 0;
for (int i = 0, i_max = (int)Points3ds.size(); i < i_max; i++)
{
//z
if (topZRange.max < topZRange.min)
{
topZRange.min = Points3ds[i].z;
topZRange.max = Points3ds[i].z;
}
else
{
if (topZRange.min > Points3ds[i].z)
topZRange.min = Points3ds[i].z;
if (topZRange.max < Points3ds[i].z)
topZRange.max = Points3ds[i].z;
}
cv::Point3f a_calibPt;
a_calibPt.x = (float)(Points3ds[i].x * planePara.planeCalib[0] + Points3ds[i].y * planePara.planeCalib[1] + Points3ds[i].z * planePara.planeCalib[2]);
a_calibPt.y = (float)(Points3ds[i].x * planePara.planeCalib[3] + Points3ds[i].y * planePara.planeCalib[4] + Points3ds[i].z * planePara.planeCalib[5]);
a_calibPt.z = (float)(Points3ds[i].x * planePara.planeCalib[6] + Points3ds[i].y * planePara.planeCalib[7] + Points3ds[i].z * planePara.planeCalib[8]);
//z
if (calibZRange.max < calibZRange.min)
{
calibZRange.min = a_calibPt.z;
calibZRange.max = a_calibPt.z;
sumMeanZ += a_calibPt.z;
sumSize++;
}
else
{
if (calibZRange.min > a_calibPt.z)
calibZRange.min = a_calibPt.z;
if (calibZRange.max < a_calibPt.z)
calibZRange.max = a_calibPt.z;
sumMeanZ += a_calibPt.z;
sumSize++;
}
}
if (sumSize > 0)
sumMeanZ = sumMeanZ / (double)sumSize;
planePara.planeHeight = sumMeanZ; // calibZRange.min;
return planePara;
}
//水平安装相机垂直扫描模式地面调平
SSG_planeCalibPara sg_HCameraVScan_getGroundCalibPara(
std::vector< std::vector<SVzNL3DPosition>>& scanLines)
{
//设置初始结果
double initCalib[9] = {
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 };
SSG_planeCalibPara planePara;
for (int i = 0; i < 9; i++)
planePara.planeCalib[i] = initCalib[i];
planePara.planeHeight = -1.0;
//提取地面直线段
SSG_lineSegParam lineSegPara;
lineSegPara.distScale = 2.0;
lineSegPara.segGapTh_y = 5.0; //y方向间隔大于5mm认为是分段
lineSegPara.segGapTh_z = 10.0; //z方向间隔大于10mm认为是分段
std::vector<cv::Point3d> groundPts;
int lineNum = (int)scanLines.size();
for (int line = 0; line < lineNum; line++)
{
std::vector<SVzNL3DPosition>& lineData = scanLines[line];
int dataSize = (int)lineData.size();
//去除零点
std::vector<SSG_RUN> segs;
wd_getLineDataIntervals(lineData, lineSegPara, segs);
if (segs.size() == 0)
continue;
//对最后一段进行处理
SSG_RUN lastSeg = segs.back();
//直线分割
std::vector< SSG_RUN> segmentationLines;
split(lastSeg, lineData, lineSegPara.distScale, segmentationLines);
if (segmentationLines.size() == 0)
continue;
//检查最后一段的直线段的斜率
SSG_RUN lastLine = segmentationLines.back();
//计算斜率
int startIdx = lastLine.start;
int endIdx = lastLine.start + lastLine.len - 1;
double dy = abs(lineData[endIdx].pt3D.y - lineData[startIdx].pt3D.y) + 1e-8; //加扰防止dy为0
double dz = lineData[startIdx].pt3D.z - lineData[endIdx].pt3D.z;
if (dz > 0)
{
double tan_k = dz / dy;
if (tan_k > tan(PI / 3)) //大于60度合格
{
for (int i = startIdx; i <= endIdx; i++)
{
if (lineData[i].pt3D.z > 1e-4)
{
lineData[i].nPointIdx = 1;
cv::Point3d a_pt = cv::Point3d(lineData[i].pt3D.x, lineData[i].pt3D.y, lineData[i].pt3D.z);
groundPts.push_back(a_pt);
}
}
}
}
}
//平面拟合
std::vector<double> planceFunc;
vzCaculateLaserPlane(groundPts, planceFunc);
#if 1 //两个向量的旋转旋转,使用四元数法,
Vector3 a = Vector3(planceFunc[0], planceFunc[1], planceFunc[2]);
Vector3 b1 = Vector3(0, 1.0, 0);
Vector3 b2 = Vector3(0, -1.0, 0);
Quaternion quanPara_1 = rotationBetweenVectors(a, b1);
Quaternion quanPara_2 = rotationBetweenVectors(a, b2);
RotationMatrix rMatrix_1;
quaternionToMatrix(quanPara_1, rMatrix_1.data);
RotationMatrix rMatrix_2;
quaternionToMatrix(quanPara_2, rMatrix_2.data);
//计算反向旋转矩阵
Quaternion invQuanPara_1 = rotationBetweenVectors(b1, a);
Quaternion invQuanPara_2 = rotationBetweenVectors(b2, a);
RotationMatrix invMatrix_1;
quaternionToMatrix(invQuanPara_1, invMatrix_1.data);
RotationMatrix invMatrix_2;
quaternionToMatrix(invQuanPara_2, invMatrix_2.data);
#else //根据平面的法向量计算欧拉角,进而计算旋转矩阵
//参数计算
SSG_EulerAngles eulerPra = planeNormalToEuler(planceFunc[0], planceFunc[1], planceFunc[2]);
//反射进行校正
eulerPra.roll = eulerPra.roll;
eulerPra.pitch = eulerPra.pitch;
eulerPra.yaw = eulerPra.yaw;
RotationMatrix rMatrix = eulerToRotationMatrix(eulerPra.yaw, eulerPra.pitch, eulerPra.roll);
#endif
planePara.planeCalib[0] = rMatrix_1.data[0][0];
planePara.planeCalib[1] = rMatrix_1.data[0][1];
planePara.planeCalib[2] = rMatrix_1.data[0][2];
planePara.planeCalib[3] = rMatrix_1.data[1][0];
planePara.planeCalib[4] = rMatrix_1.data[1][1];
planePara.planeCalib[5] = rMatrix_1.data[1][2];
planePara.planeCalib[6] = rMatrix_1.data[2][0];
planePara.planeCalib[7] = rMatrix_1.data[2][1];
planePara.planeCalib[8] = rMatrix_1.data[2][2];
planePara.invRMatrix[0] = invMatrix_1.data[0][0];
planePara.invRMatrix[1] = invMatrix_1.data[0][1];
planePara.invRMatrix[2] = invMatrix_1.data[0][2];
planePara.invRMatrix[3] = invMatrix_1.data[1][0];
planePara.invRMatrix[4] = invMatrix_1.data[1][1];
planePara.invRMatrix[5] = invMatrix_1.data[1][2];
planePara.invRMatrix[6] = invMatrix_1.data[2][0];
planePara.invRMatrix[7] = invMatrix_1.data[2][1];
planePara.invRMatrix[8] = invMatrix_1.data[2][2];
//数据进行转换
SVzNLRangeD calibYRange = { 0, -1 };
SVzNLRangeD topYRange = { 0, -1 };
double sumMeanY = 0;
int sumSize = 0;
for (int i = 0, i_max = (int)groundPts.size(); i < i_max; i++)
{
cv::Point3f a_calibPt;
a_calibPt.x = (float)(groundPts[i].x * planePara.planeCalib[0] + groundPts[i].y * planePara.planeCalib[1] + groundPts[i].z * planePara.planeCalib[2]);
a_calibPt.y = (float)(groundPts[i].x * planePara.planeCalib[3] + groundPts[i].y * planePara.planeCalib[4] + groundPts[i].z * planePara.planeCalib[5]);
a_calibPt.z = (float)(groundPts[i].x * planePara.planeCalib[6] + groundPts[i].y * planePara.planeCalib[7] + groundPts[i].z * planePara.planeCalib[8]);
//z
if (calibYRange.max < calibYRange.min)
{
calibYRange.min = a_calibPt.y;
calibYRange.max = a_calibPt.y;
sumMeanY += a_calibPt.y;
sumSize++;
}
else
{
if (calibYRange.min > a_calibPt.y)
calibYRange.min = a_calibPt.y;
if (calibYRange.max < a_calibPt.y)
calibYRange.max = a_calibPt.y;
sumMeanY += a_calibPt.y;
sumSize++;
}
}
if (sumSize > 0)
sumMeanY = sumMeanY / (double)sumSize;
if (sumMeanY < 0)
{
planePara.planeCalib[0] = rMatrix_2.data[0][0];
planePara.planeCalib[1] = rMatrix_2.data[0][1];
planePara.planeCalib[2] = rMatrix_2.data[0][2];
planePara.planeCalib[3] = rMatrix_2.data[1][0];
planePara.planeCalib[4] = rMatrix_2.data[1][1];
planePara.planeCalib[5] = rMatrix_2.data[1][2];
planePara.planeCalib[6] = rMatrix_2.data[2][0];
planePara.planeCalib[7] = rMatrix_2.data[2][1];
planePara.planeCalib[8] = rMatrix_2.data[2][2];
planePara.invRMatrix[0] = invMatrix_2.data[0][0];
planePara.invRMatrix[1] = invMatrix_2.data[0][1];
planePara.invRMatrix[2] = invMatrix_2.data[0][2];
planePara.invRMatrix[3] = invMatrix_2.data[1][0];
planePara.invRMatrix[4] = invMatrix_2.data[1][1];
planePara.invRMatrix[5] = invMatrix_2.data[1][2];
planePara.invRMatrix[6] = invMatrix_2.data[2][0];
planePara.invRMatrix[7] = invMatrix_2.data[2][1];
planePara.invRMatrix[8] = invMatrix_2.data[2][2];
//数据进行转换
calibYRange = { 0, -1 };
topYRange = { 0, -1 };
sumMeanY = 0;
sumSize = 0;
for (int i = 0, i_max = (int)groundPts.size(); i < i_max; i++)
{
cv::Point3f a_calibPt;
a_calibPt.x = (float)(groundPts[i].x * planePara.planeCalib[0] + groundPts[i].y * planePara.planeCalib[1] + groundPts[i].z * planePara.planeCalib[2]);
a_calibPt.y = (float)(groundPts[i].x * planePara.planeCalib[3] + groundPts[i].y * planePara.planeCalib[4] + groundPts[i].z * planePara.planeCalib[5]);
a_calibPt.z = (float)(groundPts[i].x * planePara.planeCalib[6] + groundPts[i].y * planePara.planeCalib[7] + groundPts[i].z * planePara.planeCalib[8]);
//z
if (calibYRange.max < calibYRange.min)
{
calibYRange.min = a_calibPt.y;
calibYRange.max = a_calibPt.y;
sumMeanY += a_calibPt.y;
sumSize++;
}
else
{
if (calibYRange.min > a_calibPt.y)
calibYRange.min = a_calibPt.y;
if (calibYRange.max < a_calibPt.y)
calibYRange.max = a_calibPt.y;
sumMeanY += a_calibPt.y;
sumSize++;
}
}
if (sumSize > 0)
sumMeanY = sumMeanY / (double)sumSize;
}
planePara.planeHeight = sumMeanY; // calibZRange.min;
return planePara;
}
SSG_planeCalibPara wd_computeRTMatrix(SVzNL3DPoint& vector1, SVzNL3DPoint& vector2)
{
Vector3 a = Vector3(vector1.x, vector1.y, vector1.z);
Vector3 b = Vector3(vector2.x, vector2.y, vector2.z);
Quaternion quanPara = rotationBetweenVectors(a, b);
RotationMatrix rMatrix;
quaternionToMatrix(quanPara, rMatrix.data);
//计算反向旋转矩阵
Quaternion invQuanPara = rotationBetweenVectors(b, a);
RotationMatrix invMatrix;
quaternionToMatrix(invQuanPara, invMatrix.data);
SSG_planeCalibPara calibPara;
calibPara.planeCalib[0] = rMatrix.data[0][0];
calibPara.planeCalib[1] = rMatrix.data[0][1];
calibPara.planeCalib[2] = rMatrix.data[0][2];
calibPara.planeCalib[3] = rMatrix.data[1][0];
calibPara.planeCalib[4] = rMatrix.data[1][1];
calibPara.planeCalib[5] = rMatrix.data[1][2];
calibPara.planeCalib[6] = rMatrix.data[2][0];
calibPara.planeCalib[7] = rMatrix.data[2][1];
calibPara.planeCalib[8] = rMatrix.data[2][2];
calibPara.invRMatrix[0] = invMatrix.data[0][0];
calibPara.invRMatrix[1] = invMatrix.data[0][1];
calibPara.invRMatrix[2] = invMatrix.data[0][2];
calibPara.invRMatrix[3] = invMatrix.data[1][0];
calibPara.invRMatrix[4] = invMatrix.data[1][1];
calibPara.invRMatrix[5] = invMatrix.data[1][2];
calibPara.invRMatrix[6] = invMatrix.data[2][0];
calibPara.invRMatrix[7] = invMatrix.data[2][1];
calibPara.invRMatrix[8] = invMatrix.data[2][2];
calibPara.planeHeight = 0;
return calibPara;
}
SSG_planeCalibPara sg_getPlaneCalibPara2_ROI(
std::vector< std::vector<SVzNL3DPosition>>& scanLines,
SVzNL3DRangeD roi)
{
//设置初始结果
double initCalib[9] = {
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 };
SSG_planeCalibPara planePara;
for (int i = 0; i < 9; i++)
planePara.planeCalib[i] = initCalib[i];
planePara.planeHeight = -1.0;
int lineNum = (int)scanLines.size();
//取数据
std::vector<cv::Point3d> Points3ds;
for (int line = 0; line < lineNum; line++)
{
int nPositionCnt = (int)scanLines[line].size();
for (int i = 0; i < nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &scanLines[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
bool isValid = false;
if ((pt3D->pt3D.x >= roi.xRange.min) && (pt3D->pt3D.x <= roi.xRange.max) &&
(pt3D->pt3D.y >= roi.yRange.min) && (pt3D->pt3D.y <= roi.yRange.max) &&
(pt3D->pt3D.z >= roi.zRange.min) && (pt3D->pt3D.y <= roi.zRange.max))
{
cv::Point3d a_vldPt;
a_vldPt.x = pt3D->pt3D.x;
a_vldPt.y = pt3D->pt3D.y;
a_vldPt.z = pt3D->pt3D.z;
Points3ds.push_back(a_vldPt);
}
}
}
//平面拟合
std::vector<double> planceFunc;
vzCaculateLaserPlane(Points3ds, planceFunc);
#if 1 //两个向量的旋转旋转,使用四元数法,
Vector3 a = Vector3(planceFunc[0], planceFunc[1], planceFunc[2]);
Vector3 b = Vector3(0, 0, -1.0);
Quaternion quanPara = rotationBetweenVectors(a, b);
RotationMatrix rMatrix;
quaternionToMatrix(quanPara, rMatrix.data);
//计算反向旋转矩阵
Quaternion invQuanPara = rotationBetweenVectors(b, a);
RotationMatrix invMatrix;
quaternionToMatrix(invQuanPara, invMatrix.data);
#else //根据平面的法向量计算欧拉角,进而计算旋转矩阵
//参数计算
SSG_EulerAngles eulerPra = planeNormalToEuler(planceFunc[0], planceFunc[1], planceFunc[2]);
//反射进行校正
eulerPra.roll = eulerPra.roll;
eulerPra.pitch = eulerPra.pitch;
eulerPra.yaw = eulerPra.yaw;
RotationMatrix rMatrix = eulerToRotationMatrix(eulerPra.yaw, eulerPra.pitch, eulerPra.roll);
#endif
planePara.planeCalib[0] = rMatrix.data[0][0];
planePara.planeCalib[1] = rMatrix.data[0][1];
planePara.planeCalib[2] = rMatrix.data[0][2];
planePara.planeCalib[3] = rMatrix.data[1][0];
planePara.planeCalib[4] = rMatrix.data[1][1];
planePara.planeCalib[5] = rMatrix.data[1][2];
planePara.planeCalib[6] = rMatrix.data[2][0];
planePara.planeCalib[7] = rMatrix.data[2][1];
planePara.planeCalib[8] = rMatrix.data[2][2];
planePara.invRMatrix[0] = invMatrix.data[0][0];
planePara.invRMatrix[1] = invMatrix.data[0][1];
planePara.invRMatrix[2] = invMatrix.data[0][2];
planePara.invRMatrix[3] = invMatrix.data[1][0];
planePara.invRMatrix[4] = invMatrix.data[1][1];
planePara.invRMatrix[5] = invMatrix.data[1][2];
planePara.invRMatrix[6] = invMatrix.data[2][0];
planePara.invRMatrix[7] = invMatrix.data[2][1];
planePara.invRMatrix[8] = invMatrix.data[2][2];
#if 0 //test: 两个矩阵的乘积必须是单位阵
double testMatrix[3][3];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
testMatrix[i][j] = 0;
for (int m = 0; m < 3; m++)
testMatrix[i][j] += invMatrix.data[i][m] * rMatrix.data[m][j];
}
}
#endif
//数据进行转换
SVzNLRangeD calibZRange = { 0, -1 };
for (int i = 0, i_max = (int)Points3ds.size(); i < i_max; i++)
{
cv::Point3f a_calibPt;
a_calibPt.x = (float)(Points3ds[i].x * planePara.planeCalib[0] + Points3ds[i].y * planePara.planeCalib[1] + Points3ds[i].z * planePara.planeCalib[2]);
a_calibPt.y = (float)(Points3ds[i].x * planePara.planeCalib[3] + Points3ds[i].y * planePara.planeCalib[4] + Points3ds[i].z * planePara.planeCalib[5]);
a_calibPt.z = (float)(Points3ds[i].x * planePara.planeCalib[6] + Points3ds[i].y * planePara.planeCalib[7] + Points3ds[i].z * planePara.planeCalib[8]);
//z
if (calibZRange.max < calibZRange.min)
{
calibZRange.min = a_calibPt.z;
calibZRange.max = a_calibPt.z;
}
else
{
if (calibZRange.min > a_calibPt.z)
calibZRange.min = a_calibPt.z;
if (calibZRange.max < a_calibPt.z)
calibZRange.max = a_calibPt.z;
}
}
planePara.planeHeight = calibZRange.min;
return planePara;
}
//计算一个平面调平参数。
//以数据输入中ROI以内的点进行平面拟合计算调平参数
//旋转矩阵为调平参数,即将平面法向调整为垂直向量的参数
SSG_planeCalibPara sg_getPlaneCalibPara_ROIs(
SVzNL3DLaserLine* laser3DPoints,
int lineNum,
std::vector<SVzNL3DRangeD>& ROIs)
{
//设置初始结果
double initCalib[9] = {
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0 };
SSG_planeCalibPara planePara;
for (int i = 0; i < 9; i++)
planePara.planeCalib[i] = initCalib[i];
planePara.planeHeight = -1.0;
//取数据
std::vector<cv::Point3d> Points3ds;
for (int line = 0; line < lineNum; line++)
{
for (int i = 0; i < laser3DPoints[line].nPositionCnt; i++)
{
SVzNL3DPosition* pt3D = &laser3DPoints[line].p3DPosition[i];
if (pt3D->pt3D.z < 1e-4)
continue;
bool isValid = false;
for (int m = 0, m_max = (int)ROIs.size(); m < m_max; m++)
{
if ((pt3D->pt3D.x >= ROIs[m].xRange.min) && (pt3D->pt3D.x <= ROIs[m].xRange.max) &&
(pt3D->pt3D.y >= ROIs[m].yRange.min) && (pt3D->pt3D.y <= ROIs[m].yRange.max) &&
(pt3D->pt3D.z >= ROIs[m].zRange.min) && (pt3D->pt3D.y <= ROIs[m].zRange.max))
{
isValid = true;
break;
}
}
if (false == isValid)
continue;
cv::Point3d a_vldPt;
a_vldPt.x = pt3D->pt3D.x;
a_vldPt.y = pt3D->pt3D.y;
a_vldPt.z = pt3D->pt3D.z;
Points3ds.push_back(a_vldPt);
}
}
//平面拟合
std::vector<double> planceFunc;
vzCaculateLaserPlane(Points3ds, planceFunc);
#if 1 //两个向量的旋转旋转,使用四元数法,
Vector3 a = Vector3(planceFunc[0], planceFunc[1], planceFunc[2]);
Vector3 b = Vector3(0, 0, -1.0);
Quaternion quanPara = rotationBetweenVectors(a, b);
RotationMatrix rMatrix;
quaternionToMatrix(quanPara, rMatrix.data);
//计算反向旋转矩阵
Quaternion invQuanPara = rotationBetweenVectors(b, a);
RotationMatrix invMatrix;
quaternionToMatrix(invQuanPara, invMatrix.data);
#else //根据平面的法向量计算欧拉角,进而计算旋转矩阵
//参数计算
SSG_EulerAngles eulerPra = planeNormalToEuler(planceFunc[0], planceFunc[1], planceFunc[2]);
//反射进行校正
eulerPra.roll = eulerPra.roll;
eulerPra.pitch = eulerPra.pitch;
eulerPra.yaw = eulerPra.yaw;
RotationMatrix rMatrix = eulerToRotationMatrix(eulerPra.yaw, eulerPra.pitch, eulerPra.roll);
#endif
planePara.planeCalib[0] = rMatrix.data[0][0];
planePara.planeCalib[1] = rMatrix.data[0][1];
planePara.planeCalib[2] = rMatrix.data[0][2];
planePara.planeCalib[3] = rMatrix.data[1][0];
planePara.planeCalib[4] = rMatrix.data[1][1];
planePara.planeCalib[5] = rMatrix.data[1][2];
planePara.planeCalib[6] = rMatrix.data[2][0];
planePara.planeCalib[7] = rMatrix.data[2][1];
planePara.planeCalib[8] = rMatrix.data[2][2];
planePara.invRMatrix[0] = invMatrix.data[0][0];
planePara.invRMatrix[1] = invMatrix.data[0][1];
planePara.invRMatrix[2] = invMatrix.data[0][2];
planePara.invRMatrix[3] = invMatrix.data[1][0];
planePara.invRMatrix[4] = invMatrix.data[1][1];
planePara.invRMatrix[5] = invMatrix.data[1][2];
planePara.invRMatrix[6] = invMatrix.data[2][0];
planePara.invRMatrix[7] = invMatrix.data[2][1];
planePara.invRMatrix[8] = invMatrix.data[2][2];
#if 0 //test: 两个矩阵的乘积必须是单位阵
double testMatrix[3][3];
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
testMatrix[i][j] = 0;
for (int m = 0; m < 3; m++)
testMatrix[i][j] += invMatrix.data[i][m] * rMatrix.data[m][j];
}
}
#endif
//数据进行转换
SVzNLRangeD calibZRange = { 0, -1 };
for (int i = 0, i_max = (int)Points3ds.size(); i < i_max; i++)
{
cv::Point3f a_calibPt;
a_calibPt.x = (float)(Points3ds[i].x * planePara.planeCalib[0] + Points3ds[i].y * planePara.planeCalib[1] + Points3ds[i].z * planePara.planeCalib[2]);
a_calibPt.y = (float)(Points3ds[i].x * planePara.planeCalib[3] + Points3ds[i].y * planePara.planeCalib[4] + Points3ds[i].z * planePara.planeCalib[5]);
a_calibPt.z = (float)(Points3ds[i].x * planePara.planeCalib[6] + Points3ds[i].y * planePara.planeCalib[7] + Points3ds[i].z * planePara.planeCalib[8]);
//z
if (calibZRange.max < calibZRange.min)
{
calibZRange.min = a_calibPt.z;
calibZRange.max = a_calibPt.z;
}
else
{
if (calibZRange.min > a_calibPt.z)
calibZRange.min = a_calibPt.z;
if (calibZRange.max < a_calibPt.z)
calibZRange.max = a_calibPt.z;
}
}
planePara.planeHeight = calibZRange.min;
return planePara;
}
// 从旋转矩阵计算欧拉角Z-Y-X顺序
SSG_EulerAngles rotationMatrixToEulerZYX(const double R[3][3]) {
SSG_EulerAngles angles;
// 计算俯仰角pitchθ
angles.pitch = asin(-R[2][0]); // asin返回弧度
// 检查万向节锁cosθ接近0
const double epsilon = 1e-6;
if (abs(cos(angles.pitch)) > epsilon) {
// 无万向节锁正常计算yaw和roll
angles.yaw = atan2(R[1][0], R[0][0]);
angles.roll = atan2(R[2][1], R[2][2]);
}
else {
// 万向节锁约定roll=0计算yaw
angles.roll = 0.0;
angles.yaw = atan2(-R[0][1], R[1][1]);
}
// 将弧度转换为角度
const double rad2deg = 180.0 / M_PI;
angles.yaw *= rad2deg;
angles.pitch *= rad2deg;
angles.roll *= rad2deg;
return angles;
}
// 从欧拉角计算旋转矩阵Z-Y-X顺序
void eulerToRotationMatrixZYX(const SSG_EulerAngles& angles, double R[3][3]) {
// 将角度转换为弧度
const double deg2rad = M_PI / 180.0;
const double yaw = angles.yaw * deg2rad;
const double pitch = angles.pitch * deg2rad;
const double roll = angles.roll * deg2rad;
// 预计算三角函数值
const double cy = cos(yaw), sy = sin(yaw);
const double cp = cos(pitch), sp = sin(pitch);
const double cr = cos(roll), sr = sin(roll);
#if 0
// 绕Z轴旋转矩阵
double Rz[3][3] = {
{cy, -sy, 0},
{sy, cy, 0},
{0, 0, 1}
};
// 绕Y轴旋转矩阵
double Ry[3][3] = {
{cp, 0, sp},
{0, 1, 0},
{-sp, 0, cp}
};
// 绕X轴旋转矩阵
double Rx[3][3] = {
{1, 0, 0},
{0, cr, -sr},
{0, sr, cr}
};
// 矩阵相乘顺序R = Rz * Ry * Rx
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
// 先计算 Rz * Ry
double temp[3][3] = { 0 };
for (int k = 0; k < 3; ++k) {
temp[i][j] += Rz[i][k] * Ry[k][j];
}
// 再与 Rx 相乘
R[i][j] = 0;
for (int k = 0; k < 3; ++k) {
R[i][j] += temp[i][k] * Rx[k][j];
}
}
}
#endif
// 优化后的直接计算公式(避免中间矩阵)
R[0][0] = cy * cp;
R[0][1] = cy * sp * sr - sy * cr;
R[0][2] = cy * sp * cr + sy * sr;
R[1][0] = sy * cp;
R[1][1] = sy * sp * sr + cy * cr;
R[1][2] = sy * sp * cr - cy * sr;
R[2][0] = -sp;
R[2][1] = cp * sr;
R[2][2] = cp * cr;
}
//根据相机姿态对相机采集的3D数据进行旋转(没有平移),将数据调整为俯视状态
///camPoseR为3x3矩阵
void lineDataRT(SVzNL3DLaserLine* a_line, const double* camPoseR, double groundH)
{
for (int i = 0; i < a_line->nPositionCnt; i++)
{
SVzNL3DPoint a_pt = a_line->p3DPosition[i].pt3D;
if (a_pt.z < 1e-4)
continue;
double x = a_pt.x * camPoseR[0] + a_pt.y * camPoseR[1] + a_pt.z * camPoseR[2];
double y = a_pt.x * camPoseR[3] + a_pt.y * camPoseR[4] + a_pt.z * camPoseR[5];
double z = a_pt.x * camPoseR[6] + a_pt.y * camPoseR[7] + a_pt.z * camPoseR[8];
if ((groundH > 0) && (z > groundH)) //去除地面
z = 0;
a_pt.x = x;
a_pt.y = y;
a_pt.z = z;
a_line->p3DPosition[i].pt3D = a_pt;
}
return;
}
void lineDataRT_vector(std::vector< SVzNL3DPosition>& a_line, const double* camPoseR, double groundH)
{
for (int i = 0; i < (int)a_line.size(); i++)
{
SVzNL3DPoint a_pt = a_line[i].pt3D;
if (a_pt.z < 1e-4)
continue;
double x = a_pt.x * camPoseR[0] + a_pt.y * camPoseR[1] + a_pt.z * camPoseR[2];
double y = a_pt.x * camPoseR[3] + a_pt.y * camPoseR[4] + a_pt.z * camPoseR[5];
double z = a_pt.x * camPoseR[6] + a_pt.y * camPoseR[7] + a_pt.z * camPoseR[8];
if ((groundH > 0) && (z > groundH)) //去除地面
z = 0;
a_pt.x = x;
a_pt.y = y;
a_pt.z = z;
a_line[i].pt3D = a_pt;
}
return;
}
void HCamera_lineDataRT_vector(std::vector< SVzNL3DPosition>& a_line, const double* camPoseR, double groundH)
{
for (int i = 0; i < a_line.size(); i++)
{
SVzNL3DPoint a_pt = a_line[i].pt3D;
if (a_pt.z < 1e-4)
continue;
double x = a_pt.x * camPoseR[0] + a_pt.y * camPoseR[1] + a_pt.z * camPoseR[2];
double y = a_pt.x * camPoseR[3] + a_pt.y * camPoseR[4] + a_pt.z * camPoseR[5];
double z = a_pt.x * camPoseR[6] + a_pt.y * camPoseR[7] + a_pt.z * camPoseR[8];
if ((groundH > 0) && (y >= groundH)) //去除地面
z = 0;
a_pt.x = x;
a_pt.y = y;
a_pt.z = z;
a_line[i].pt3D = a_pt;
}
return;
}
void lineDataRT_RGBD(SVzNLXYZRGBDLaserLine* a_line, const double* camPoseR, double groundH)
{
for (int i = 0; i < a_line->nPointCnt; i++)
{
SVzNLPointXYZRGBA a_pt = a_line->p3DPoint[i];
if (a_pt.z < 1e-4)
continue;
double x = a_pt.x * camPoseR[0] + a_pt.y * camPoseR[1] + a_pt.z * camPoseR[2];
double y = a_pt.x * camPoseR[3] + a_pt.y * camPoseR[4] + a_pt.z * camPoseR[5];
double z = a_pt.x * camPoseR[6] + a_pt.y * camPoseR[7] + a_pt.z * camPoseR[8];
if ((groundH > 0) && (z > groundH)) //去除地面
z = 0;
a_pt.x = (float)x;
a_pt.y = (float)y;
a_pt.z = (float)z;
a_line->p3DPoint[i] = a_pt;
}
return;
}
//对栅格化数据进行XY平面上的投影二值量化并对量化产生的空白点进行插值
void pointClout2DProjection(
std::vector< std::vector<SVzNL3DPosition>>& gridScanData,
SVzNLRangeD x_range,
SVzNLRangeD y_range,
double scale,
double cuttingGrndZ,
int edgeSkip,
double inerPolateDistTh, //插值阈值。大于此阈值的不进行量化插值
cv::Mat& projectionData,//投影量化数据初始化为一个极大值1e+6
cv::Mat& backIndexing //标记坐标索引用于回找3D坐标
)
{
int lineNum = (int)gridScanData.size();
if (lineNum == 0)
return;
int nPointCnt = (int)gridScanData[0].size();
for (int line = 0; line < lineNum; line++)
{
int pre_x = -1, pre_y = -1;
SVzNL3DPosition* prePt = NULL;
for (int i = 0; i < nPointCnt; i++)
{
SVzNL3DPosition* pt3D = &gridScanData[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
if ((cuttingGrndZ > 0) && (pt3D->pt3D.z > cuttingGrndZ))
continue;
double x = pt3D->pt3D.x;
double y = pt3D->pt3D.y;
int px = (int)(x - x_range.min)/scale + edgeSkip;
int py = (int)(y - y_range.min)/scale + edgeSkip;
cv::Vec2i v2i_exist = backIndexing.at<cv::Vec2i>(py, px);
#if 0
if ((v2i_exist[0] > 0) || (v2i_exist[1] > 0)) //多个点重复投影到同一个点上,只保留一个有效点
{
pt3D->pt3D.z = 0; //invalidate
}
else
#endif
{
cv::Vec2i v2i = { line, i };
backIndexing.at<cv::Vec2i>(py, px) = v2i;
projectionData.at<float>(py, px) = 1e+6;
//垂直插值
if (prePt)
{
//计算距离,超过一定距离则不插值
double dist = sqrt(pow(pt3D->pt3D.x - prePt->pt3D.x, 2) +
pow(pt3D->pt3D.y - prePt->pt3D.y, 2) +
pow(pt3D->pt3D.z - prePt->pt3D.z, 2));
if (dist < inerPolateDistTh)
{
std::vector<SVzNL2DPoint> interPts;
drawLine(
pre_x,
pre_y,
px,
py,
interPts);
for (int m = 0, m_max = (int)interPts.size(); m < m_max; m++)
projectionData.at<float>(interPts[m].y, interPts[m].x) = 1e+6;
}
}
prePt = pt3D;
pre_x = px;
pre_y = py;
}
}
}
//水平插值
int pixWin = (int)(inerPolateDistTh / scale);
for (int y = 0; y < projectionData.rows; y++)
{
int pre_x = -1;
for (int x = 0; x < projectionData.cols; x++)
{
double value = projectionData.at<float>(y, x);
if (value > 1e-4)
{
if (pre_x >= 0)
{
//插值
int x_diff = x - pre_x;
if ((x_diff > 1) && (x_diff < pixWin))
{
for (int m = pre_x + 1; m < x; m++)
projectionData.at<float>(y, m) = 1e+6;
}
}
pre_x = x;
}
}
}
}
//对栅格化数据进行XY平面上的投影量化Z值保留并对量化产生的空白点进行插值
void pointCloud2DQuantization(
std::vector< std::vector<SVzNL3DPosition>>& gridScanData,
SVzNLRangeD x_range,
SVzNLRangeD y_range,
double scale,
int edgeSkip,
double inerPolateDistTh, //插值阈值。大于此阈值的不进行量化插值
std::vector<std::vector<SVzNL3DPoint>>& quantiData, //量化数据初始化为一个极大值1e+6
std::vector<std::vector<SVzNL2DPoint>>& backIndexing //标记坐标索引用于回找3D坐标
)
{
int lineNum = (int)gridScanData.size();
if (lineNum == 0)
return;
//计算量化大小并初始化
int x_cols = (int)((x_range.max - x_range.min) / scale) + 1 + edgeSkip * 2;
int y_rows = (int)((y_range.max - y_range.min) / scale) + 1 + edgeSkip * 2;
quantiData.resize(x_cols);
backIndexing.resize(x_cols);
double quantiXStart = x_range.min - edgeSkip * scale;
double quantiYStart = y_range.min - edgeSkip * scale;
for (int i = 0; i < x_cols; i++)
{
quantiData[i].resize(y_rows);
for (int j = 0; j < y_rows; j++)
quantiData[i][j] = {i * scale + quantiXStart + scale/2, j * scale + quantiYStart + scale / 2 , 0};
backIndexing[i].resize(y_rows);
std::fill(backIndexing[i].begin(), backIndexing[i].end(), SVzNL2DPoint{0,0});
}
int nPointCnt = (int)gridScanData[0].size();
for (int line = 0; line < lineNum; line++)
{
int pre_x = -1, pre_y = -1;
SVzNL3DPosition* prePt = NULL;
for (int i = 0; i < nPointCnt; i++)
{
SVzNL3DPosition* pt3D = &gridScanData[line][i];
if (pt3D->pt3D.z < 1e-4)
continue;
double x = pt3D->pt3D.x;
double y = pt3D->pt3D.y;
int px = (int)(x - x_range.min) / scale + edgeSkip;
int py = (int)(y - y_range.min) / scale + edgeSkip;
SVzNL2DPoint indexing_exist = backIndexing[px][py]; //按列存储,和扫描线方向一致
#if 0
if ((v2i_exist[0] > 0) || (v2i_exist[1] > 0)) //多个点重复投影到同一个点上,只保留一个有效点
{
pt3D->pt3D.z = 0; //invalidate
}
else
#endif
{
SVzNL2DPoint v2i = { line, i };
backIndexing[px][py] = v2i;
quantiData[px][py].z = pt3D->pt3D.z;
//垂直插值
if (prePt)
{
//计算距离,超过一定距离则不插值
double dist = sqrt(pow(pt3D->pt3D.x - prePt->pt3D.x, 2) +
pow(pt3D->pt3D.y - prePt->pt3D.y, 2) +
pow(pt3D->pt3D.z - prePt->pt3D.z, 2));
if (dist < inerPolateDistTh)
{
std::vector<SVzNL2DPoint> interPts;
drawLine(
pre_x,
pre_y,
px,
py,
interPts);
for (int m = 0, m_max = (int)interPts.size(); m < m_max; m++)
{
double k1=1.0, k2=0.0;
if (py != pre_y)
{
k1 = ((double)(interPts[m].y - pre_y)) / ((double)(py - pre_y));
k2 = 1.0 - k1;
}
double inter_z = k1 * pt3D->pt3D.z + k2 * prePt->pt3D.z;
quantiData[interPts[m].x][interPts[m].y].z = inter_z;
}
}
}
prePt = pt3D;
pre_x = px;
pre_y = py;
}
}
}
//水平插值
int pixWin = (int)(inerPolateDistTh / scale);
int cols = (int)quantiData[0].size();
int rows = (int)quantiData.size();
for (int y = 0; y < cols; y++) //和激光扫描方向一致
{
int pre_x = -1;
double pre_value = -1;
for (int x = 0; x < rows; x++)
{
double value = quantiData[x][y].z;
if (value > 1e-4)
{
if (pre_x >= 0)
{
//插值
int x_diff = x - pre_x;
if ((x_diff > 1) && (x_diff < pixWin))
{
for (int m = pre_x + 1; m < x; m++)
{
double k1 = ((double)(m - pre_x)) / ((double)x_diff);
double k2 = 1.0 - k1;
double inter_z = k1 * value + k2 * pre_value;
quantiData[x][y].z = inter_z;
}
}
}
pre_x = x;
pre_value = value;
}
}
}
}
//对空间两组对应点计算旋转平移矩阵
// Eigen库实现
void caculateRT(
const std::vector<cv::Point3d>& pts1,
const std::vector<cv::Point3d>& pts2,
cv::Mat& R, cv::Mat& T,
cv::Point3d& C1, cv::Point3d& C2)
{
//【1】 求中心点
cv::Point3d p1, p2;
int N = pts1.size();
for (int i = 0; i < N; i++)
{
p1 += pts1[i];
p2 += pts2[i];
}
p1 = cv::Point3d(cv::Vec3d(p1) / N);
p2 = cv::Point3d(cv::Vec3d(p2) / N);
C1 = p1;
C2 = p2;
// 【2】得到去中心坐标
std::vector<cv::Point3d> q1(N), q2(N);
for (int i = 0; i < N; i++)
{
q1[i] = pts1[i] - p1;
q2[i] = pts2[i] - p2;
}
//【3】计算需要进行奇异值分解的 W = sum(qi * qi转置) compute q1*q2^T
Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
for (int i = 0; i < N; i++)
W += Eigen::Vector3d(q1[i].x, q1[i].y, q1[i].z) * Eigen::Vector3d(q2[i].x, q2[i].y, q2[i].z).transpose();
// 【4】对 W 进行SVD 奇异值分解
Eigen::JacobiSVD<Eigen::Matrix3d> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
Eigen::Matrix3d U = svd.matrixU();
Eigen::Matrix3d V = svd.matrixV();
// 【5】计算旋转 和平移矩阵 R 和 t, R= V *M* UT
double det = (U * V.transpose()).determinant();
Eigen::Matrix3d M;
M << 1, 0, 0, 0, 1, 0, 0, 0, det;
Eigen::Matrix3d R_ = V * M * (U.transpose());
// t = p' - R * p
Eigen::Vector3d t_ = Eigen::Vector3d(p2.x, p2.y, p2.z) - R_ * Eigen::Vector3d(p1.x, p1.y, p1.z);
// 【6】格式转换 convert to cv::Mat
R = (cv::Mat_<double>(3, 3) <<
R_(0, 0), R_(0, 1), R_(0, 2),
R_(1, 0), R_(1, 1), R_(1, 2),
R_(2, 0), R_(2, 1), R_(2, 2)
);
T = (cv::Mat_<double>(3, 1) << t_(0, 0), t_(1, 0), t_(2, 0));
return;
}
//计算点旋转平移后的位置
void pointRT(const cv::Mat& R, const cv::Mat& T,
const cv::Point3d& originPt, const cv::Point3d& rtOriginPT, //RT(旋转平移)前后的质心
const cv::Point3d& pt, cv::Point3d& rtPt) //RT前后的点
{
Eigen::Matrix3d _R;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
_R(i, j) = R.at<double>(i, j);
}
}
Eigen::Vector3d _T = Eigen::Vector3d(T.at<double>(0, 0), T.at<double>(1, 0), T.at<double>(2, 0));
Eigen::Vector3d vec_origin = Eigen::Vector3d(originPt.x, originPt.y, originPt.z);
Eigen::Vector3d vec_rtOrigin = Eigen::Vector3d(rtOriginPT.x, rtOriginPT.y, rtOriginPT.z);
Eigen::Vector3d vec_pt = Eigen::Vector3d(pt.x, pt.y, pt.z);
Eigen::Vector3d result = _R * (vec_pt - vec_origin) + vec_rtOrigin;
rtPt.x = result(0);
rtPt.y = result(1);
rtPt.z = result(2);
return;
}
//计算点旋转平移后的位置
void pointRT_2(const cv::Mat& R, const cv::Mat& T,
const cv::Point3d& pt, cv::Point3d& rtPt) //RT前后的点
{
Eigen::Matrix3d _R;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
_R(i, j) = R.at<double>(i, j);
}
}
Eigen::Vector3d _T = Eigen::Vector3d(T.at<double>(0, 0), T.at<double>(1, 0), T.at<double>(2, 0));
Eigen::Vector3d vec_pt = Eigen::Vector3d(pt.x, pt.y, pt.z);
Eigen::Vector3d result = _R * vec_pt + _T;
rtPt.x = result(0);
rtPt.y = result(1);
rtPt.z = result(2);
return;
}
//计算点旋转后的位置
void pointRotate(const cv::Mat& R,
const cv::Point3d& pt, cv::Point3d& rtPt) //Rotate前后的点
{
Eigen::Matrix3d _R;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
_R(i, j) = R.at<double>(i, j);
}
}
Eigen::Vector3d vec_pt = Eigen::Vector3d(pt.x, pt.y, pt.z);
Eigen::Vector3d result = _R * vec_pt;
rtPt.x = result(0);
rtPt.y = result(1);
rtPt.z = result(2);
return;
}
void WD_EulerRpyToRotation(double rpy[3], double matrix3d[9]) {
double cos0 = cos(rpy[0] * PI / 180);
double sin0 = sin(rpy[0] * PI / 180);
double cos1 = cos(rpy[1] * PI / 180);
double sin1 = sin(rpy[1] * PI / 180);
double cos2 = cos(rpy[2] * PI / 180);
double sin2 = sin(rpy[2] * PI / 180);
matrix3d[0] = cos2 * cos1;
matrix3d[1] = cos2 * sin1 * sin0 - sin2 * cos0;
matrix3d[2] = cos2 * sin1 * cos0 + sin2 * sin0;
matrix3d[3] = sin2 * cos1;
matrix3d[4] = sin2 * sin1 * sin0 + cos2 * cos0;
matrix3d[5] = sin2 * sin1 * cos0 - cos2 * sin0;
matrix3d[6] = -sin1;
matrix3d[7] = cos1 * sin0;
matrix3d[8] = cos1 * cos0;
return;
}
void WD_EulerRpyToDirVectors(double rpy[3],std::vector<cv::Point3d>& dirVectors) {
double cos0 = cos(rpy[0] * PI / 180);
double sin0 = sin(rpy[0] * PI / 180);
double cos1 = cos(rpy[1] * PI / 180);
double sin1 = sin(rpy[1] * PI / 180);
double cos2 = cos(rpy[2] * PI / 180);
double sin2 = sin(rpy[2] * PI / 180);
double matrix3d[9];
matrix3d[0] = cos2 * cos1;
matrix3d[1] = cos2 * sin1 * sin0 - sin2 * cos0;
matrix3d[2] = cos2 * sin1 * cos0 + sin2 * sin0;
matrix3d[3] = sin2 * cos1;
matrix3d[4] = sin2 * sin1 * sin0 + cos2 * cos0;
matrix3d[5] = sin2 * sin1 * cos0 - cos2 * sin0;
matrix3d[6] = -sin1;
matrix3d[7] = cos1 * sin0;
matrix3d[8] = cos1 * cos0;
cv::Point3d vx, vy, vz;
vx.x = matrix3d[0];
vy.x = matrix3d[1];
vz.x = matrix3d[2];
vx.y = matrix3d[3];
vy.y = matrix3d[4];
vz.y = matrix3d[5];
vx.z = matrix3d[6];
vy.z = matrix3d[7];
vz.z = matrix3d[8];
dirVectors.push_back(vx);
dirVectors.push_back(vy);
dirVectors.push_back(vz);
return;
}
void scanLinesSmooting3x3(
std::vector< std::vector<SVzNL3DPosition>>& gridDataInput,
std::vector< std::vector<SVzNL3DPosition>>& smoothingData
)
{
int lineNum = (int)gridDataInput.size();
int linePtNum = (int)gridDataInput[0].size();
for (int line = 0; line < lineNum; line++)
{
for (int ptIdx = 0; ptIdx < linePtNum; ptIdx++)
{
smoothingData[line][ptIdx] = gridDataInput[line][ptIdx];
if (gridDataInput[line][ptIdx].pt3D.z > 1e-4)
{
double sumZ = 0;
int num = 0;
for (int i = line - 1; i <= line + 1; i++)
{
for (int j = ptIdx - 1; j <= ptIdx + 1; j++)
{
if ((i >= 0) && (i < lineNum) && (j >= 0) && (j < linePtNum))
{
if (gridDataInput[i][j].pt3D.z > 1e-4)
{
sumZ += gridDataInput[i][j].pt3D.z;
num++;
}
}
}
}
smoothingData[line][ptIdx].pt3D.z = sumZ / num;
}
}
}
return;
}
#if 0
#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include <Eigen/SVD>
// Define a struct for 3D points
struct Point3D {
double x, y, z;
};
// Function to perform 3D line fitting using SVD
void fitLine3D(const std::vector<Point3D>& points, Eigen::Vector3d& centroid, Eigen::Vector3d& direction) {
int n = points.size();
if (n < 2) {
std::cerr << "Need at least 2 points to fit a line." << std::endl;
return;
}
// 1. Calculate Centroid
centroid.setZero();
for (const auto& p : points) {
centroid += Eigen::Vector3d(p.x, p.y, p.z);
}
centroid /= n;
// 2. Center the data and build the data matrix
Eigen::MatrixXd data_matrix(n, 3);
for (int i = 0; i < n; ++i) {
data_matrix.row(i) << points[i].x - centroid(0),
points[i].y - centroid(1),
points[i].z - centroid(2);
}
// 3. Apply SVD
// We compute the SVD of the centered data matrix
Eigen::JacobiSVD<Eigen::MatrixXd> svd(data_matrix, Eigen::ComputeThinV);
// 4. Extract the direction vector
// The right singular vector corresponding to the largest singular value (first column of V)
// gives the direction of the best-fit line.
direction = svd.matrixV().col(0);
}
int main() {
// Sample data points
std::vector<Point3D> points = {
{1.0, 2.0, 3.0},
{2.0, 3.0, 4.0},
{3.0, 4.0, 5.0},
{4.0, 5.0, 6.0},
{5.0, 6.0, 7.0}
};
Eigen::Vector3d centroid;
Eigen::Vector3d direction;
fitLine3D(points, centroid, direction);
std::cout << "Centroid (point on the line): " << centroid.transpose() << std::endl;
std::cout << "Direction vector of the line: " << direction.transpose() << std::endl;
std::cout << "Equation of the line: P(t) = Centroid + t * Direction" << std::endl;
return 0;
}
template<class Vector3>
std::pair < Vector3, Vector3 > best_line_from_points(const std::vector<Vector3>& c)
{
// copy coordinates to matrix in Eigen format
size_t num_atoms = c.size();
Eigen::Matrix< Vector3::Scalar, Eigen::Dynamic, Eigen::Dynamic > centers(num_atoms, 3);
for (size_t i = 0; i < num_atoms; ++i) centers.row(i) = c[i];
Vector3 origin = centers.colwise().mean();
Eigen::MatrixXd centered = centers.rowwise() - origin.transpose();
Eigen::MatrixXd cov = centered.adjoint() * centered;
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eig(cov);
Vector3 axis = eig.eigenvectors().col(2).normalized();
return std::make_pair(origin, axis);
}
#endif