c++ 将std::vector转换< double>为std::vector< Eigen:Vector3d>

p4tfgftt  于 2023-03-25  发布在  其他
关注(0)|答案(1)|浏览(357)

我想知道如何将std::vector<double>转换为std::vector<Eigen:Vector3d>。上下文是我正在从HDF 5文件中阅读点云数据(x,y,z点),该文件已被展平为1d数组,转换为std::vector<double>,因此std::vector<double>始终具有nx 3个点。
我使用的是libeigen 3-dev(3.3.7)。
之前,我从h5文件中阅读数据到std::vector<double>中,然后使用Eigen::Map转换为Eigen::VectorXd,如下所示:

Eigen::VectorXd readH5Data(H5::DataSet &dataset, H5::VarLenType memType, H5::DataSpace &dataspace, H5::DataSpace &memspace, int index) {

    // Initialize hyperslabs
    hsize_t dataCount[1];
    hsize_t dataOffset[1];
    hsize_t memCount[1];
    hsize_t memOffset[1];

    // Select hyperslabs
    dataCount[0] = 1;
    dataOffset[0] = index;
    memCount[0] = 1;
    memOffset[0] = 0;
    dataspace.selectHyperslab(H5S_SELECT_SET, dataCount, dataOffset);
    memspace.selectHyperslab(H5S_SELECT_SET, memCount, memOffset);

    // Read out the data as std::vector<double>
    std::vector<hvl_t> varlenSpecs(1);
    dataset.read(varlenSpecs.data(), memType, memspace, dataspace);
    auto dataPtr = static_cast<double*>(varlenSpecs[0].p);
    std::vector<double> readOut(dataPtr, dataPtr + varlenSpecs[0].len);
    H5free_memory(varlenSpecs[0].p);
    
    // Convert std::vector<double> to Eigen::VectorXd
    double* ptr = &readOut[0];
    Eigen::Map<Eigen::VectorXd> dataOut(ptr, readOut.size());    

    return dataOut;
}

然后,我可以将1d Eigen::VectorXd转换为nx 3 Eigen::MatrixXd,如下所示,并进行任何其他处理:

Eigen::VectorXd points = readH5Data(datasetPts, memTypeDbl, dataspacePts, memspacePts, 0);

Eigen::MatrixXd ptsMat = Eigen::Map<Eigen::MatrixXd>(points.data(), 3, nPoints).transpose();

但是,我现在需要使用这些数据点构造一个Open3D PointCloud,其构造如下:

PointCloud (const std::vector< Eigen::Vector3d > &points)

因此,我需要将数据设置为std::vector<Eigen::Vector3d>。因此,理想情况下,我想修改readH 5Data函数以返回std::vector<Eigen::Vector3d>。我相信我可以从Eigen::MatrixXdlike this进行转换:

std::vector<Eigen::Vector3d> vec(ptsMat.colwise().begin(), ptsMat.colwise().end());

但是我想知道我是否可以避免中间步骤,直接从std::vector<double>std::vector<Eigen::Vector3d>

4nkexdtk

4nkexdtk1#

好的,让我们把这个分解一下,主要是为了我对你的代码的理解。请仔细检查一下。这也意味着我的解决方案只有在这些条件成立的情况下才有效。

auto dataPtr = static_cast<double*>(varlenSpecs[0].p);
std::vector<double> readOut(dataPtr, dataPtr + varlenSpecs[0].len);

这告诉我,无论你的内存数据类型是什么,它都不是类似struct的类型或任何花哨的类型,只是一个基本的双精度数组。

Eigen::MatrixXd ptsMat =
    Eigen::Map<Eigen::MatrixXd>(points.data(), 3, nPoints).transpose();

因此,3行,N列以列为主的顺序,这意味着std::vector<Eigen::Vector3d>将在内存中具有相同的布局。这里有两个重要的方面需要注意:

  1. std::vector的内存布局不是不透明的,它必须实现为动态数组,您可以自由地将其视为动态数组
  2. Eigen::Vector3d没有填充
    这意味着你可以简单地使用memcpy。它并不花哨,但它可以以最小的开销完成工作。有人可能会反对缺乏内存安全性,但坦率地说,大多数HDF5的API缺乏内存安全性,而Eigen::Map也没有。
static_assert(sizeof(Eigen::Vector3d) == 3 * sizeof(double));
std::vector<Eigen::Vector3d> rtrn(varlenSpecs.len / 3);
std::memcpy(rtrn.data(), varlenSpecs.p, varlenSpecs.len * sizeof(double));

整个阅读功能也可以清理。

std::vector<Eigen::Vector3d> readH5Data(
        H5::DataSet &dataset, const H5::VarLenType& memType, int index)
{
    hsize_t dataCount = 1;
    hsize_t dataOffset = index;
    H5::DataSpace dataspace = dataset.getSpace();
    dataspace.selectHyperslab(H5S_SELECT_SET, &dataCount, &dataOffset);
    H5::DataSpace mspace(1, &dataCount);
    std::vector<Eigen::Vector3d> rtrn;
    hvl_t varlenSpecs;
    dataset.read(&varlenSpecs, memType, mspace, dataspace);
    try {
        rtrn.resize(varlenSpecs.len / 3);
    } catch(std::bad_alloc&) {
        H5free_memory(varlenSpecs.p);
        throw;
    }
    static_assert(sizeof(Eigen::Vector3d) == 3 * sizeof(double));
    std::memcpy(rtrn.data(), varlenSpecs.p, varlenSpecs.len * sizeof(double));
    H5free_memory(varlenSpecs.p);
    return rtrn;
}

注意使用1大小的数组是没有意义的。只使用标量。我也不认为有理由传入DataSpace对象。如果有的话,它会使它更容易出错。

相关问题