无需复制即可将 OpenCV Mat 导入 C++ Tensorflow

Import OpenCV Mat into C++ Tensorflow without copying

我的目标是 运行 一个实时的 TensorFlow 模型,以根据学习的模型控制车辆。我们的车辆系统使用与 OpenCV 紧密相关的 ROS(机器人操作系统)。因此,我收到了一个包含来自 ROS 的感兴趣图像的 OpenCV Mat。

    cv::Mat cameraImg;

我想直接从这个 OpenCV 矩阵中的数据创建一个 Tensorflow Tensor,以避免逐行复制矩阵的费用。使用 的答案,我已经设法让网络的前向传播与以下代码一起工作:

cameraImg.convertTo(cameraImg, CV_32FC3);

Tensor inputImg(DT_FLOAT, TensorShape({1,inputheight,inputwidth,3}));
auto inputImageMapped = inputImg.tensor<float, 4>();
auto start = std::chrono::system_clock::now();
//Copy all the data over
for (int y = 0; y < inputheight; ++y) {
    const float* source_row = ((float*)cameraImg.data) + (y * inputwidth * 3);
    for (int x = 0; x < inputwidth; ++x) {
        const float* source_pixel = source_row + (x * 3);
        inputImageMapped(0, y, x, 0) = source_pixel[2];
        inputImageMapped(0, y, x, 1) = source_pixel[1];
        inputImageMapped(0, y, x, 2) = source_pixel[0];
    }
}
auto end = std::chrono::system_clock::now();

然而,使用这种方法复制到张量需要 80 毫秒到 130 毫秒,而整个正向传递(对于 10 层卷积网络)只需要 25 毫秒。

查看与 Tensors 相关的 the tensorflow documentation, it appears there is a Tensor constructor that takes an allocator. However, I have not been able to find any Tensorflow or Eigen documentation relating to this functionality or the Eigen Map class

有没有人知道如何加速这段代码,最好是通过重新使用我的 OpenCV 内存?

编辑: 我已经成功地实现了@mrry 的建议,并且可以重新使用 OpenCV 分配的内存。我已经打开 github issue 8033 请求将其添加到 tensorflow 源代码树中。我的方法不是很漂亮,但是很管用。

编译一个外部库并link到libtensorflow.so库还是很困难的。 tensorflow cmake library 可能会对此有所帮助,我还没有尝试过。

TensorFlow C API(相对于 C++ API)导出 TF_NewTensor() function, which allows you to create a tensor from a pointer and a length, and you can pass the resulting object to the TF_Run() 函数。

目前这是唯一的 public API 用于从预分配缓冲区创建 TensorFlow 张量的方法。没有支持将 TF_Tensor* 转换为 tensorflow::Tensor 的方法,但如果您查看实现,有一个具有 friend 访问权限的私有 API 可以执行此操作。如果您对此进行试验,并且可以显示出明显的加速,我们会考虑 feature request 将其添加到 public API.

我知道这是旧线程,但是使用现有的 C++ API 可以针对您的问题提供零复制解决方案:我用我的解决方案更新了您的 github 问题。 tensorflow/issues/8033

作为记录,我将我的解决方案复制到这里:

// allocate a Tensor
Tensor inputImg(DT_FLOAT, TensorShape({1,inputHeight,inputWidth,3}));

// get pointer to memory for that Tensor
float *p = inputImg.flat<float>().data();
// create a "fake" cv::Mat from it 
cv::Mat cameraImg(inputHeight, inputWidth, CV_32FC3, p);

// use it here as a destination
cv::Mat imagePixels = ...; // get data from your video pipeline
imagePixels.convertTo(cameraImg, CV_32FC3);

希望对您有所帮助