一、加载深度神经网络模型
1、读取深度学习模型函数
cpp
复制代码
/* 返回值:Net类型,返回加载完成的深度学习网络对象
用途:用于加载深度学习模型,创建OpenCV DNN模块中的神经网络对象 */
Net cv::dnn::readNet(const String& model,
const String& config = "",
const String& framework = "");
/*
model:模型文件名称,通常为训练好的权重文件
config:配置文件名称,某些模型需要单独的网络结构描述文件
framework:框架种类
*/
| 框架种类 |
模型文件格式 |
框架种类 |
| Caffe |
*.caffemodel |
*.prototxt |
| TensorFlow |
*.pb |
*.pbtxt |
| Torch |
*.t7 | *.net |
-- |
| Darknet |
*.weights |
*.efg |
| DLDT |
*.bin |
*.xml |
2、深度学习模型参数
| 函数名称 |
含义及使用方式 |
| empty() |
判断模型是否为空,不需要输入参数,模型为空返回true,否则返回false |
| getLayerNames() |
得到每次网络的名称,不需要输入参数,返回值为vector<String>类型变量 |
| getLayerId() |
得到某层网络的ID,输入参数为网络的名称,返回值为int类型变量 |
| getLayer() |
得到指向指定ID或名称的网络层的指针,输入参数为网络层ID,返回值为Ptr<Layer>类型变量 |
| forward() |
执行前向传输,输入参数为需要输出的网络层的名称,返回值为Mat类型数据 |
| setInput() |
设置网络新的输入数据 |
3、向网络层中添加数据
cpp
复制代码
/* 用途:用于将预处理后的输入数据送入OpenCV DNN网络模型。 */
void cv::dnn::Net::setInput(InputArray blob, const String& name = "",
double scalefactor = 1.0, const Scalar& mean = Scalar());
/*
blob:新的输入数据,数据类信息为CV_32F或CV_8U,
通常为blobFromImage()或blobFromImages()生成的Blob数据
name:网络输入层名称,多输入网络时用于指定输入层,默认情况下为空字符串
scalefactor:输入数据缩放系数,用于对输入像素值进行归一化处理
mean:输入图像均值,用于减均值预处理,常见于深度学习模型标准化操作
*/
4、示例代码
cpp
复制代码
QString path = QApplication::applicationDirPath() + "/Nets";
cv::String s_path = path.toLocal8Bit().data();
string model = "/bvlc_googlenet.caffemodel";
string config = "bvlc_googlenet.prototxt";
dnn::Net net = dnn::readNet(s_path + model, s_path + config);
if (net.empty())
{
qDebug() << "请确认是否输入空的模型文件";
return;
}
/*获取各层信息*/
vector<String> layerNames = net.getLayerNames();
for (int i = 0; i < layerNames.size(); i++)
{
/*读取每层网络的ID*/
int ID = net.getLayerId(layerNames[i]);
/*读取每层网络的信息*/
Ptr<dnn::Layer> layer = net.getLayer(ID);
cout << "ID: " << ID << " Name: " << layerNames[i] << endl
<< " type: " << layer->type.c_str() << endl;
}
二、深度神经网络模型的使用
1、输入数据尺寸转换函数
cpp
复制代码
/* 返回值:Mat类型,返回符合DNN输入要求的四维Blob数据
用途:用于将多张图像转换为OpenCV DNN模块所需的Blob格式。
该函数会自动完成:
1、图像缩放
2、数据归一化
3、减均值
4、通道排列
5、数据类型转换
最终生成:NCHW格式的四维张量数据 */
Mat cv::dnn::blobFromImages(InputArrayOfArrays images, double scalefactor=1.0,
Size size = Size(), const Scalar& mean = Scalar(),
bool swapRB=false, bool crop=false,int ddepth=CV_32F);
/*
images:输入图像数组,可以同时输入多张图像,图像可以是单通道、三通道或者四通道
scalefactor:图像像素缩放系数,用于归一化输入数据
size:神经网络输入尺寸,所有图像都会被缩放到该尺寸
mean:均值参数,用于对输入图像进行减均值处理
swapRB:是否交换R与B通道,true表示:BGR → RGB
crop:是否裁剪图像,true表示缩放后进行中心裁剪,false表示直接缩放
ddepth:输出图像的数据类型,可选参数为CV_32F或CV_8U
*/
2、示例介绍
cpp
复制代码
/************加载 TensorFlow 的 Inception 图像分类模型,
对一张图片进行"图像分类识别"**********************/
QString path = QApplication::applicationDirPath();
cv::String s_path = path.toLocal8Bit().data();
Mat img = imread(s_path + "/Images/airplane.jpg");
if (img.empty())
{
qDebug() << "图片加载失败, 请确认图像文件名称是否正确";
return;
}
/*读取分类种类名称*/
String typeListFile = s_path + "/Nets/image_recognition/imagenet_comp_graph_label_strings.txt";
vector<String> typeList;
ifstream file(typeListFile);
if (!file.is_open())
{
qDebug() << "请确认分类种类名称是否正确";
return;
}
std::string type;
while (!file.eof())
{
/*读取名称*/
getline(file, type);
if (type.length())
{
typeList.push_back(type);
}
}
file.close();
/*加载网络*/
String tf_pb_file = s_path + "/Nets/image_recognition/tensorflow_inception_graph.pb";
dnn::Net net = dnn::readNet(tf_pb_file);
if (net.empty())
{
qDebug() << "请确认是否输入空的模型文件";
return;
}
/*对输入图像数据进行处理*/
Mat blob = dnn::blobFromImage(img, 1.0f, Size(224, 224), Scalar(), true, false);
/*进行图像种类预测*/
Mat prob;
net.setInput(blob, "input");
prob = net.forward("softmax2");
/*得到最可能分类输出*/
Mat probMat = prob.reshape(1, 1);
Point classNumber;
double classProb;/*最大可能性*/
minMaxLoc(probMat, NULL, &classProb, NULL, &classNumber);
string typeName = typeList.at(classNumber.x).c_str();
cout << "typeName: " << typeName << " classProb: " << classProb;
/*检测内容*/
string str = typeName + " possiblity: " + to_string(classProb);
putText(img, str, Point(50, 50), FONT_HERSHEY_SIMPLEX, 1.0, Scalar(0, 0, 255), 2, 8);
imshow("img", img);
waitKey(0);
destroyAllWindows();
cpp
复制代码
/*********加载 5 个"快速风格迁移"模型,
把普通图片 lena.jpg 转换成不同艺术风格的图像************/
QString path = QApplication::applicationDirPath();
cv::String s_path = path.toLocal8Bit().data();
Mat image = imread(s_path + "/Images/lena.jpg");
if (image.empty())
{
qDebug() << "图片加载失败, 请确认图像文件名称是否正确";
return;
}
String models[5] = { "the_wave.t7","mosaic.t7" ,"feathers.t7" ,"candy.t7" ,"udnie.t7" };
for (int i = 0; i < std::size(models); i++)
{
dnn::Net net = dnn::readNet(s_path + "/Nets/fast_style/" + models[i]);
imshow("img", image);
/*计算图像每个通道的均值*/
Scalar imageMean = mean(image);
/*调整图像尺寸和格式*/
Mat blobImage = dnn::blobFromImage(image, 1.0, Size(256, 256), imageMean, false, false);
/*计算网络对原图像处理结果*/
net.setInput(blobImage);
Mat output = net.forward();
/*输出结果的尺寸和通道数*/
int outputChannels = output.size[1];
int outputRows = output.size[2];
int outputCols = output.size[3];
/*将输出结果存放到图像中*/
Mat result = Mat::zeros(Size(outputRows, outputCols), CV_32FC3);
float* data = output.ptr<float>();
for (int channel = 0; channel < outputChannels; channel++)
{
for (int row = 0; row < outputRows; row++)
{
for (int col = 0; col < outputCols; col++)
{
result.at<Vec3f>(row, col)[channel] = *data++;
}
}
}
/*对迁移结果进行进一步操作处理*/
/*恢复图像建交的均值*/
result = result + imageMean;
/*对图像进行归一化,便于图像展示*/
result = result / 255.0;
/*调整图像尺寸,使得与原图像尺寸相同*/
cv::resize(result, result, image.size());
/*显示结果*/
imshow(to_string(i) + "result", result);
}
waitKey(0);
destroyAllWindows();
cpp
复制代码
/*********************人脸识别+性别识别******************************/
QString path = QApplication::applicationDirPath();
cv::String s_path = path.toLocal8Bit().data();
Mat image = imread(s_path + "/Images/face.jpg");
if (image.empty())
{
qDebug() << "图片加载失败, 请确认图像文件名称是否正确";
return;
}
/*读取人脸识别模型*/
String model_bin = s_path + "/Nets/face_age/opencv_face_detector_uint8.pb";
String config_text = s_path + "/Nets/face_age/opencv_face_detector.pbtxt";
dnn::Net faceNet = dnn::readNet(model_bin, config_text);
/*读取性别检测模型*/
String genderProto = s_path + "/Nets/face_age/gender_deploy.prototxt";
String genderModel = s_path + "/Nets/face_age/gender_net.caffemodel";
String genderList[] = { "Male","Female" };
dnn::Net genderNet = dnn::readNet(genderModel, genderProto);
if (faceNet.empty() && genderNet.empty())
{
qDebug() << "请确定是否输入正确的模型文件";
return;
}
/*对整幅图像进行人脸检测*/
Mat blobImage = dnn::blobFromImage(image, 1.0, Size(300, 300), Scalar(), false, false);
faceNet.setInput(blobImage, "data");
Mat detect = faceNet.forward("detection_out");
/*人脸概率、人脸矩形区域的位置*/
Mat detectionMat(detect.size[2], detect.size[3], CV_32F, detect.ptr<float>());
/*对每个人脸区域进行性别检测*/
int exBoundray = 25;/*每个人脸区域四个方向扩充的尺寸*/
float confidenceThreshold = 0.5;/*判定为人脸的概率阈值,阈值越大准确性越高*/
for (int i = 0; i < detectionMat.rows; i++)
{
float confidence = detectionMat.at<float>(i, 2);/*检测为人脸的概率*/
/*只检测概率大于阈值区域的性别*/
if (confidence > confidenceThreshold)
{
/*网络检测人脸区域大小*/
int topLx = detectionMat.at<float>(i, 3) * image.cols;
int topLy = detectionMat.at<float>(i, 4) * image.rows;
int bottomRx = detectionMat.at<float>(i, 5) * image.cols;
int bottomRy = detectionMat.at<float>(i, 6) * image.rows;
Rect faceRect(topLx, topLy, bottomRx - topLx, bottomRy - topLy);
/*将网络检测出的区域尺寸进行扩充,要注意防止尺寸在图像真实尺寸之外*/
Rect faceTextRect;
faceTextRect.x = max(0, faceRect.x - exBoundray);
faceTextRect.y = max(0, faceRect.y - exBoundray);
faceTextRect.width = min(faceRect.width + exBoundray, image.cols - 1);
faceTextRect.height = min(faceRect.height + exBoundray, image.rows - 1);
Mat face = image(faceTextRect);/*扩充后的人脸图像*/
/*调整面部图像尺寸*/
Mat faceblob = dnn::blobFromImage(face, 1.0, Size(227, 227), Scalar(), false, false);
/*将调整后的面部图像输入到性别检测网络*/
genderNet.setInput(faceblob);
/*检测结果*/
Mat genderPreds = genderNet.forward();/*两个性别的可能性*/
/*性别检测结果*/
float male, female;
male = genderPreds.at<float>(0, 0);
female = genderPreds.at<float>(0, 1);
int classID = male > female ? 0 : 1;
String gender = genderList[classID];
/*在原图像中绘制面部轮廓和性别*/
rectangle(image, faceRect, Scalar(0, 0, 255), 2, 8, 0);
putText(image, gender.c_str(), faceRect.tl(), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 0, 255), 2, 8);
}
}
imshow("image", image);
waitKey(0);
destroyAllWindows();