如何将 OpenCV Mat 传递到 C++ Tensorflow 图中?

How do I pass an OpenCV Mat into a C++ Tensorflow graph?

在 Tensorflow C++ 中,我可以使用

将图像文件加载到图中
tensorflow::Node* file_reader =  tensorflow::ops::ReadFile(tensorflow::ops::Const(IMAGE_FILE_NAME, b.opts()),b.opts().WithName(input_name));
tensorflow::Node* image_reader = tensorflow::ops::DecodePng(file_reader, b.opts().WithAttr("channels", 3).WithName("png_reader"));
tensorflow::Node* float_caster = tensorflow::ops::Cast(image_reader, tensorflow::DT_FLOAT, b.opts().WithName("float_caster"));
tensorflow::Node* dims_expander = tensorflow::ops::ExpandDims(float_caster, tensorflow::ops::Const(0, b.opts()), b.opts());
tensorflow::Node* resized = tensorflow::ops::ResizeBilinear(dims_expander, tensorflow::ops::Const({input_height, input_width},b.opts().WithName("size")),b.opts());

对于嵌入式应用程序,我想将 OpenCV Mat 传递到该图中。

我如何将 Mat 转换为可用作 tensorflow::ops::Cast 或 tensorflow::ops::ExpandDims 输入的张量?

它不是直接来自 CvMat,但您可以在 TensorFlow Android 示例中看到如何从内存数组初始化张量的示例: https://github.com/tensorflow/tensorflow/blob/0.6.0/tensorflow/examples/android/jni/tensorflow_jni.cc#L173

您将从创建一个新的 tensorflow::Tensor 对象开始,使用如下内容(所有代码均未经测试):

tensorflow::Tensor input_tensor(tensorflow::DT_FLOAT, tensorflow::TensorShape({1, height, width, depth}));

这将创建一个具有浮点值、批量大小为 1、大小为 widthxheightdepth 通道的 Tensor 对象。例如,具有 3 个通道的 128 宽 x 64 高图像将以 {1, 64, 128, 3} 的形状传递。 batch size 仅在需要在一次调用中传入多个图像时使用,为了简单使用,您可以将其保留为 1。

然后你会得到张量后面的底层数组,使用如下一行:

auto input_tensor_mapped = input_tensor.tensor<float, 4>();

input_tensor_mapped 对象是您新创建的张量中数据的接口,然后您可以将自己的数据复制到其中。在这里,我假设您已将 source_data 设置为指向源数据的指针,例如:

const float* source_data = some_structure.imageData;

然后您可以遍历数据并将其复制过来:

for (int y = 0; y < height; ++y) {
    const float* source_row = source_data + (y * width * depth);
    for (int x = 0; x < width; ++x) {
        const float* source_pixel = source_row + (x * depth);
        for (int c = 0; c < depth; ++c) {
           const float* source_value = source_pixel + c;
           input_tensor_mapped(0, y, x, c) = *source_value;
        }
    }
}

显然有机会优化这种幼稚的方法,我手头没有示例代码来展示如何处理获取源数据的 OpenCV 端,但希望这对您入门有所帮助。

我曾尝试 运行 opencv Mat 文件上的初始模型,以下代码对我有效 https://gist.github.com/kyrs/9adf86366e9e4f04addb. Although there are some issue with integration of opencv and tensorflow. Code worked without any issue for .png files but failed to load .jpg and .jpeg. You can follow this for more info https://github.com/tensorflow/tensorflow/issues/1924

这里是完整的阅读示例:

Mat image;
image = imread("flowers.jpg", CV_LOAD_IMAGE_COLOR);
cv::resize(image, image, cv::Size(input_height, input_width), 0, 0, CV_INTER_CUBIC);

int depth = 3;
tensorflow::Tensor input_tensor(tensorflow::DT_FLOAT,
                                tensorflow::TensorShape({1, image.rows, image.cols, depth}));

for (int y = 0; y < image.rows; y++) {
    for (int x = 0; x < image.cols; x++) {
        Vec3b pixel = image.at<Vec3b>(y, x);

        input_tensor_mapped(0, y, x, 0) = pixel.val[2]; //R
        input_tensor_mapped(0, y, x, 1) = pixel.val[1]; //G
        input_tensor_mapped(0, y, x, 2) = pixel.val[0]; //B
    }
}

auto result = Sub(root.WithOpName("subtract_mean"), input_tensor, {input_mean});
ClientSession session(root);
TF_CHECK_OK(session.Run({result}, out_tensors));
Tensor convertMatToTensor(Mat &input)
{
    int height = input.rows;
    int width = input.cols;
    int depth = input.channels();

    Tensor imgTensor(tensorflow::DT_FLOAT, tensorflow::TensorShape({height, width, depth}));

    float* p = imgTensor.flat<float>().data();
    Mat outputImg(height, width, CV_32FC3, p);
    input.convertTo(outputImg, CV_32FC3);

    return imgTensor;
}