Eigen库学习笔记(四)Eigen用于三维张量

平常中我们使用Eigen::MatrixXd二维矩阵。
Eigen的三维张量正处于开发中,在unsupported文件夹下,还没有放到正式代码中。
Eigen::Tensor来定义
IMPORTANT NOTE: The current developement version of Eigen (post-3.2) supports Tensors. This support is experimental and a moving target.
包含头文件:

#include <unsupported/Eigen/CXX11/Tensor>

1、示例:

Eigen::Tensor<double, 3> epsilon(3,3,3);
epsilon.setZero();
epsilon(0,1,2) = 1;
epsilon(1,2,0) = 1;
epsilon(2,0,1) = 1;
epsilon(1,0,2) = -1;
epsilon(2,1,0) = -1;
epsilon(0,2,1) = -1;
Eigen::Tensor<double, 4> grassmannIdentity(3,3,3,3);
grassmannIdentity.setZero();
// this is not the most efficient way to write such a product,
// but is the only way possible with the current feature set
for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
    for (int k = 0; k < 3; k++) {
      for (int l = 0; l < 3; l++) {
        for (int m = 0; m < 3; m++) {
          grassmannIdentity(i,j,l,m) += epsilon(i,j,k) * epsilon(k,l,m);
        }
      }
    }
  }
}

// verify
for (int i = 0; i < 3; i++) {
  for (int j = 0; j < 3; j++) {
    for (int l = 0; l < 3; l++) {
      for (int m = 0; m < 3; m++) {
        assert(grassmannIdentity(i,j,l,m) == (int(i == l) * int(j == m) - int(i == m) * int(j == l)));
      }
    }
  }
}

// dimensionalities
assert(epsilon.dimension(0) == 3);
assert(epsilon.dimension(1) == 3);
assert(epsilon.dimension(2) == 3);
auto dims = epsilon.dimensions();
assert(dims[0] == 3);
assert(dims[1] == 3);
assert(dims[2] == 3);

参考:官方文档Tensor support

2、3维张量

  // 定义一个2x3x4大小的矩阵
  Eigen::Tensor<float, 3> a(2, 3, 4);
  // 初始化为0
  a.setZero();
  // 访问元素
  a(0, 1, 0) = 12.0f;
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      for (int k = 0; k < 4; k++) {
        std::cout << a(i, j, k) << " ";
      }
      std::cout << std::endl;
    }
    std::cout << std::endl << std::endl;
  }
  // 输出维度
  std::cout<<a.dimension(0)<<" "<<a.dimension(1)<<" "<<a.dimension(2)<<std::endl;

输出结果:

0 0 0 0 
12 0 0 0 
0 0 0 0 

0 0 0 0 
0 0 0 0 
0 0 0 0

2 3 4

补充测试

#include <unsupported/Eigen/CXX11/Tensor>
#include <iostream>

int main()
{
// 定义一个2x3x4大小的矩阵
Eigen::Tensor<float, 3> a(2, 3, 4);
// 初始化为0
a.setZero();
// 访问元素
//a(0, 1, 0) = 12.0f;
for (int i = 0; i < 2; i++) {
	for (int j = 0; j < 3; j++) {
		for (int k = 0; k < 4; k++) {
			a(i, j, k) = i*3*4+j*4+k;
			std::cout << a(i, j, k) << " ";
		}
		std::cout << std::endl;
	}
	std::cout << std::endl << std::endl;
}
// 输出维度
std::cout<<a.dimension(0)<<" "<<a.dimension(1)<<" "<<a.dimension(2)<<std::endl;
std::cout << a;

}

输出

0 1 2 3
4 5 6 7
8 9 10 11


12 13 14 15
16 17 18 19
20 21 22 23


2 3 4
 0  4  8  1  5  9  2  6 10  3  7 11
12 16 20 13 17 21 14 18 22 15 19 23
--------------------------------
Process exited after 0.3212 seconds with return value 0

Press ANY key to exit...

3、固定大小矩阵TensorFixedSize

参考官方解释

The fixed sized equivalent of Eigen::Tensor<float, 3> t(3, 5, 7); is Eigen::TensorFixedSize<float, Size<3,5,7>> t;
  // 固定 大小的Size 2x3x4
  Eigen::TensorFixedSize<float, Eigen::Sizes<2, 3, 4>> b;
  // 每个元素都设置固定值
  b.setConstant(3.f);
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      for (int k = 0; k < 4; k++) {
        std::cout << b(i, j, k) << " ";
      }
      std::cout << std::endl;
    }
    std::cout << std::endl << std::endl;
  }

输出:

3 3 3 3 
3 3 3 3 
3 3 3 3 

3 3 3 3 
3 3 3 3 
3 3 3 3

4、常用函数API

1.维度

  Eigen::Tensor<float, 2> a(3, 4);
  std::cout << "Dims " << a.NumDimensions;
  //=> Dims 2

  Eigen::Tensor<float, 2> a(3, 4);
  int dim1 = a.dimension(1);
  std::cout << "Dim 1: " << dim1;
  //=> Dim 1: 4

2.形状

  Eigen::Tensor<float, 2> a(3, 4);
  const Eigen::Tensor<float, 2>::Dimensions& d = a.dimensions();
  std::cout << "Dim size: " << d.size << ", dim 0: " << d[0]
	     << ", dim 1: " << d[1];
  //=> Dim size: 2, dim 0: 3, dim 1: 4

3.矩阵元素个数

  Eigen::Tensor<float, 2> a(3, 4);
  std::cout << "Size: " << a.size();
  //=> Size: 12

4.初始化

  /// 1.
  // setConstant(const Scalar& val),用于把一个矩阵的所有元素设置成一个指定的常数。
  Eigen::Tensor<string, 2> a(2, 3);
  a.setConstant("yolo");
  std::cout << "String tensor: " << endl << a << endl << endl;
  //=>
  // String tensor:
  // yolo yolo yolo
  // yolo yolo yolo
 
  /// 2.
  // setZero() 全部置零
  a.setZero();

  /// 3.
  // setRandom() 随机初始化
  a.setRandom();
  std::cout << "Random: " << endl << a << endl << endl;
  //=>
  //Random:
  //  0.680375    0.59688  -0.329554    0.10794
  // -0.211234   0.823295   0.536459 -0.0452059
  // 0.566198  -0.604897  -0.444451   0.257742
   
  /// 4.
  // setValues({..initializer_list}) 从列表、数据初始化
  Eigen::Tensor<float, 2> a(2, 3);
  a.setValues({{0.0f, 1.0f, 2.0f}, {3.0f, 4.0f, 5.0f}});
  std::cout << "a" << endl << a << endl << endl;
  //=>
  // a
  // 0 1 2
  // 3 4 5
  
  //如果给定的数组数据,少于矩阵元素的个数,那么后面不足的元素其值不变:
  Eigen::Tensor<int, 2> a(2, 3);
  a.setConstant(1000);
  a.setValues({{10, 20, 30}});
  std::cout << "a" << endl << a << endl << endl;
  //=>
  // a
  // 10   20   30
  // 1000 1000 1000

参考: Eigen::Tensor使用,定义高维矩阵

参考:官方 张量说明 Eigen-unsupported 3.4.0 (git rev e3e74001f7c4bf95f0dde572e8a08c5b2918a3ab)

5、矩阵乘法与广播机制

使用Eigen的Tensor缘由
之所以使用Eigen的Tensor,主要是因为Tensorflow中的一个C++项目中,需要对graph中得到的Tensor进行数据后处理。而tensorflow的C++API中用到了Eigen这个矩阵运算库中的Tensor,所做了些笔记。该笔记主要参考[Eigen-unsupport这个官方的文档(英文),并以此为基础记录一些与numpy对应的函数与技巧。

Tensor的矩阵乘法操作

Eigen中的Tensor中重载了*并用于实现点对点相乘和与标量相乘。关于点对点相乘,需要注意的是两个tensor的shape必须相同才能相乘,如果需要像numpy那样实现大小矩阵相乘(广播机制),需要自己设置广播,如下:

#include <Eigen/Dense>
Eigen::Tensor<float, 2, 1> a(3, 2);
Eigen::Tensor<float, 2, 1> b(1, 2);
a.setConstant(2.0);
b.setConstant(3.0);
Eigen::array<int, 2> bcast = { 3, 1 };//第一维复制3倍
auto ab = a*b.broadcast(bcast);
cout<<ab<<endl;
-----------------------
6 6
6 6
6 6

如果是要实现Tensor的内积,则需要使用tensor的contract函数,可以实现高维的矩阵内积,如下:

Eigen::Tensor<int, 2> a(2, 3);
a.setValues({{1, 2, 3}, {6, 5, 4}});
Eigen::Tensor<int, 2> b(3, 2);
b.setValues({{1, 2}, {4, 5}, {5, 6}});
Eigen::array<Eigen::IndexPair<int>, 1> product_dims = { Eigen::IndexPair<int>(1, 0) };
Eigen::Tensor<int, 2> AB = a.contract(b, product_dims);
cout<<a<<endl<<b<<endl<<AB<<endl;
----------------------------------
1 2 3
6 5 4
1 2
4 5
5 6
24 30
46 61

Tensor的reshape操作

Tensor中的reshape靠TensorMap这种数据类型实现。

float nums[12] = { 0, 100, 200 , 300, 400, 500,600, 700, 800,900, 1000, 1100 };
Eigen::TensorMap<Eigen::Tensor<float, 2, 1>> a(nums, 4, 3);
Eigen::TensorMap<Eigen::Tensor<float, 2, 1>> b(a.data(), 2, 6);
----------------------
a
   0  100  200
 300  400  500
 600  700  800
 900 1000 1100
b
   0  100  200  300  400  500
 600  700  800  900 1000 1100

Tensor的堆叠(stack)操作

Tensor的堆叠,如两个(2,2)的tensor堆叠为(2,2,2),也可以实现拼接(4,2),(2,4)。

Eigen::Tensor<float, 2, 1> a(2, 2);
Eigen::Tensor<float, 2, 1> b(2, 2);
a.setConstant(1,0);
b.setConstant(2.0);
auto ab = a.concatenate(b,0);   //(4,2)
auto ab1 = a.concatenate(b,1);  //(2,4)
Eigen::DSizes<Eigen::DenseIndex, 3> three_dim(3,2,1);
Eigen::Tensor<float, 3, 1> ab2 = a.reshape(three_dim).concatenate(b.reshape(three_dim), 2)  //(2,2,2)

Tensor的find(where)操作
Tensor中没有matlab中的find或者python中numpy里的where,用于寻找满足条件的元素的下标。这里用到的思路是首先通过比较(><的符号Eigen已经重载),考虑到tensor的内存是连续存储的,通过遍历每个元素,计算满足的元素的下标。由于下面的代码中已经设置了Tensor为行优先存储,下标计算如下所示。

class getthree_index
{
public:
	vector<int>* fir_dim;
	vector<int>* sec_dim;
	vector<int>* thi_dim;
	int fir, sec, thi;
	int idx = 0;

	getthree_index(vector<int>* f, vector<int>* s, vector<int>* t, int fn, int sn, int tn)
	{
		fir_dim = f; sec_dim = s; thi_dim = t; fir = fn; sec = sn; thi = tn;
	};
	void operator()(bool i);
};

float nums[12] = { 0, 100, 200 , 300, 400, 500,600, 700, 800,900, 1000, 1100 };
Eigen::TensorMap<Eigen::Tensor<float, 3, 1>> a(nums, 2, 3, 2);
auto threshold = a.constant(400);
Eigen::Tensor<bool, 3, 1> res = a>threshold.eval();
vector<int> fir_dim;
vector<int> sec_dim;
vector<int> thi_dim;
auto shape = a.dimensions();
for_each(res.data(), res.data() + res.size(), getthree_index(&fir_dim, &sec_dim, &thi_dim, 
			int(shape[0]), int(shape[1]), int(shape[2])));

参考:Eigen中Tensor的使用技巧

要使用Eigen三维点云进行旋转,首先需要将点云数据表示为Eigen中的矩阵形式。然后,可以使用Eigen的旋转矩阵来进行旋转操作。下面是一个示例代码: ```cpp #include <Eigen/Core> #include <Eigen/Geometry> // 定义一个3D点云类型 typedef Eigen::Matrix<float, 3, Eigen::Dynamic> PointCloud; // 定义旋转角度和旋转轴 float angle = 45.0; // 旋转角度,单位为度 Eigen::Vector3f axis(0.0, 0.0, 1.0); // 旋转轴,这里以Z轴为例 // 创建一个点云对象并填充数据 PointCloud pointCloud; pointCloud.resize(3, numPoints); // 假设点云有numPoints个点 // 填充点云数据... // 将点云数据转换为Eigen矩阵形式 Eigen::Map<Eigen::Matrix3Xf> points(pointCloud.data(), 3, numPoints); // 创建旋转矩阵 float angleRad = angle * M_PI / 180.0; // 将角度转换为弧度 Eigen::AngleAxisf rotation(angleRad, axis); Eigen::Matrix3f rotationMatrix = rotation.toRotationMatrix(); // 进行点云旋转 points = rotationMatrix * points; // 将旋转后的点云数据重新存储回PointCloud对象中 pointCloud = points; ``` 在上述代码中,首先定义了一个`PointCloud`类型,表示三维点云。然后,通过`Eigen::Map`将点云数据转换为Eigen矩阵形式。接下来,创建了旋转矩阵,通过乘法操作将点云进行旋转。最后,将旋转后的点云数据重新存储回`PointCloud`对象中。 请根据实际情况修改代码中的点云数据和旋转角度、轴参数。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落花逐流水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值