前一章我讲到了导入模型,现在自然是该用一张图片去做预测了,自然涉及到导入图像。Tensorflow的C++API有导入图片的函数,不过我这里介绍使用opencv的imread,再把Mat型变量转化为Tensorflow支持的Tensor型变量。
Mat转换为Tensor
Tensor t_in(DT_FLOAT, TensorShape({ 1,512,512,3 }));
cvmat_to_tensor(resized_img, &t_in);
首先需要定义一个Tensor类型,DT_FLOAT是数据类型,代表浮点型,后面的TensorShape是图像的大小,注意是四维的,格式为NHWC。
之后使用自定义的函数cvmat_to_tensor把Mat型的resized_img转化为Tensor类型的数据。该函数的作用看函数名称也能猜出意思。
下面就是cvmat_to_tensor的定义。大家可以直接复制粘贴使用。
void cvmat_to_tensor(const cv::Mat &img, Tensor* tensor) {
float *p = tensor->flat<float>().data();
int rows = img.rows;
int cols = img.cols;
cv::Mat imagePixels = Mat(rows, cols, CV_32FC3, p);
img.convertTo(imagePixels, CV_32FC3);
}
定义一些必要的变量
在预测之前,我们需要两个很重要的变量,一个是指向Session类型的指针,还有一个是GraphDef类型。
Session * session = 0;
Status status = NewSession(SessionOptions(), &session);
GraphDef gdef;
其中gdef会送到自定义的readPb函数中去,让模型导入到我们的gdef图中,这是上一章的内容。
我们还需要定义保存输出的变量和需要送到feed dict中的变量。
vector<Tensor> outputs;
vector<std::pair<string, Tensor> > inputs = {
{ "input/input_data", t_in }
};
这里简单解释一下,outputs用来存输出节点的输出。inputs是一个vector类型,注意vector是pair结构。pair中的string是输入节点的名称,注意这里不需要像Python一样加“:0”。
开始预测
status = session->Run(inputs, { "post_processing/result" }, {}, &outputs);
if (!status.ok()) {
cout << status.ToString() << "\n";
}
Tensor t = outputs[0];
注意,最后一行的[0]是必不可少的。至于原因,看最下面。
这样就完成预测了。至于这个输出tensor t是什么,作为模型的使用者,你应该知道啦。
我们还可以通过TensorShape查看输出的shape。
TensorShape shape = t.shape();
cout << shape.dims() << std::endl; // 2 6
cout << shape.dim_size(0) << " " << shape.dim_size(1) << std::endl;
笔者这里使用的是yolo,输出了两个框,每个框携带四个坐标值,一个score,一个类别,共6个值。所以输出是 [2,6]。
下一章我们将讲述怎么看输出tensor t 中的值。
update on 2019.7.21
当在sess->run的时候,有一个参数是输出参数,我们设置了几个节点输出,那么vector<Tensor> 的内容就有几个Tensor。我之前以为outputs[0]的含义是node的第0号输出,原来是理解错误的。
比如我们看下面的例子。
model.session->Run(inputs, { “prob”,“label” }, {}, &outputs);
我们想得到一张图的概率(prob)和标签(label),输出就不是一个了,而是两个。只要用相应的位置去取出来就行了,例如:
Tensor prob = outputs[0]; Tensor label = outputs[1];