自定义数据集、模型,用QT、c++调用tensorflow编译好的pb模型的图像分类项目
第一步:制作自己的TFRecord数据集,读取并显示(详细图解)
第二步:读取自定义的TFRecord数据集,训练卷积网络并保存pb模型
第三步:本文
流程图:
前面已经保存了训练好的pb模型,下面就来验证模型是否可以使用以及效果。
代码如下
#include<opencv2/opencv.hpp>
#include<opencv2/dnn.hpp>
#include<iostream>
#include<string.h>
using namespace cv;
using namespace std;
using namespace dnn;
//计算一列数中最大值的索引
int argmax(cv::Mat mat, int n) {
int index = 0;
float max = mat.at<float>(0, 0);
float cur = mat.at<float>(0, 0);
for (int i = 0; i < n; i++) {
cur = mat.at<float>(0, i);
if (cur > max) {
max = cur;
index = i;
}
}
return index;
}
// C++/Opencv实现softmax函数。
int softmax(const Mat& src, Mat& dst, int length) {
float max = 0.0;
float sum = 0.0;
for (int i = 0; i < length; i++) {
if (max < src.at<float>(0, i))
max = src.at<float>(0, i);
}
for (int i = 0; i < length; i++) {
dst.at<float>(0, i) = exp(src.at<float>(0, i) - max);
sum += dst.at<float>(0, i);
}
for (int i = 0; i < length; i++) {
dst.at<float>(0, i) /= sum;
}
return 0;
}
int main() {
int width = 100;
int height = 100;
cv::Mat src = cv::imread("flower_photos/daisy/517054463_036db655a1_m.jpg");
resize(src, src, cv::Size(width, height));
cv::namedWindow("input", CV_WINDOW_AUTOSIZE);
cv::imshow("input", src);
try
{
cv::String weights = "flower_cnn.pb";
dnn::Net net = cv::dnn::readNetFromTensorflow(weights);
//将图像转为神经网络输入的对象,由描述文件可知,图像尺寸统一为100*100
cv::Mat inputBlob = cv::dnn::blobFromImage(src, 0.00390625f, Size(width, height), Scalar(), true, false);
net.setInput(inputBlob, "input"); //设置网络输入,“数据”是输入层的名称
// 执行图像分类
Mat output = net.forward("output");
cout << output << endl;
Mat dst = output.clone();//深复制,用copyTo或者clone
softmax(output, dst, output.cols);
cout << dst << endl;
cout << argmax(dst, output.cols) << endl;
}
catch (const std::exception&)
{
std::system("pause");
}
cv::waitKey(0);
return 0;
}
注意事项:
1.可以看到output的结果为一维数组,因为在前面模型保存output节点时,output是卷积层的输出结果乘以1。输出节点不能是softmax后的节点,cv::dnn::readNetFromTensorflow函数无法识别softmax层。只能自己得到一维数组然后用C++处理。
2.关于blobFromImage函数有两个需要注意的地方。我们看前面的Tensorflow模型会发现,模型的x节点是读取tfrecords模型后的数据经过归一化的,所以这里不能直接输入原数据,也要归一化,即1/255=0.00390625f。可以看到第5个参数bool swapRB为true,这是因为opencv读取图像时默认是BGR色彩空间,而通常的色彩空间是RGB,所以输入的数据色彩空间也要转换下。要么这里参数选true,要么用cvtcolor(src, src, BRG2RGB)转换后,参数选false。
结果测试
5种类别各输入一张图片,测试结果。
输入一张雏菊图片,结果如下0,正确
输入一张蒲公英图片,结果如下1,正确
输入一张玫瑰图片,结果如下2,正确
输入一张向日葵图片,结果如下3,正确
输入一张郁金香图片,结果如下4,正确