此篇介绍如何使用C++加载OpenCV DNN模块库来调用深度神经网络模型以及一些应用

目前,深度神经网络广泛应用于用于图像处理的各个领域,OpenCV4丰富了DNN模块,增加了与深度神经网络相关的函数及比较前沿的算法,以下就此展开描述:

>注意:此篇不对如何训练深度神经网络模型做出概述!后续会单独出文说明。

以下,围绕对深度神经网络模型的应用。

软硬件环境:


  • Ubuntu20.04
  • OpenCV4.5.1
  • Cmake3.18.5

01

加载深度神经网络模型

1、函数说明

读取深度学习模型函数:


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

*.cfg

DLDT

*.bin

*.xml

深度学习模型参数:


empty():判断模型是否为空,不需要输入参数,模型为空返回true,否则false;

getLayerNames():得到每层网络的名称,不需要输入参数,返回值为vector类型变量;

getLayerId():得到每层网络的ID,输入参数为网络名称,返回值为int类型变量;

getLayer():得到指向指定ID或名称的网络层的指针,输入参数为网络层ID,返回值为Ptr类型变量;

forward():执行前向传输,输入参数为需要输出的网络层的名称,返回值为Mat类型数据;

setInput():设置网络新的输入数据;

向网络层中添加数据:

void cv::dnn::Net::setInput(InputArray blob,const String & name="",double scalefactor=1.0,const Scalar & mean=Scalar())

blob:新的输入数据,类型为CV_32F或CV_8U;

name:输入网络层的名称;

scalefactor:可选的标准化比例;

mean:可选的减数数值;

2、编写代码

1、编写main.cpp:

#include #include using namespace cv;using namespace std;using namespace cv::dnn;int main(){    string model="/home/zja/Downloads/bvlc_googlenet.caffemodel";    string config="/home/zja/Downloads/bvlc_googlenet.prototxt";    Net net=dnn::readNet(model,config);    if(net.empty())    {        cout<<"请确认输入了模型文件"<<endl;        return -1;    }    //获取各层信息    vector layerNames=net.getLayerNames();    for(unsigned long i=0;i    {        //读取每层网络的ID        int ID=net.getLayerId(layerNames[i]);        //读取每层网络信息        Ptr layer=net.getLayer(ID);        //输出网络信息        cout<<"网络层数:"<"网络名称:"<endl<<"网络层数类型:"<type.c_str()<<endl;    }return 0;}
#include 
#include 

using namespace cv;
using namespace std;
using namespace cv::dnn;

int main()
{
    string model="/home/zja/Downloads/bvlc_googlenet.caffemodel";
    string config="/home/zja/Downloads/bvlc_googlenet.prototxt";
    Net net=dnn::readNet(model,config);
    if(net.empty())
    {
        cout<<"请确认输入了模型文件"<<endl;
        return -1;
    }
    //获取各层信息
    vector layerNames=net.getLayerNames();
    for(unsigned long i=0;i
    {
        //读取每层网络的ID
        int ID=net.getLayerId(layerNames[i]);
        //读取每层网络信息
        Ptr layer=net.getLayer(ID);
        //输出网络信息
        cout<<"网络层数:"<"网络名称:"<endl<<"网络层数类型:"<type.c_str()<<endl;
    }

return 0;
}




2、编写CMakeLists.txt:

# 声明要求的 cmake 最低版本cmake_minimum_required(VERSION 2.8)# 声明一个 myCV 工程project( myCV )# 设置编译模式 (Debug/Release/...)set(CMAKE_BUILD_TYPE "Debug")# 寻找OpenCV库find_package( OpenCV REQUIRED )# 添加头文件include_directories( ${OpenCV_INCLUDE_DIRS} )# 生成可执行二进制文件add_executable( MyCV main.cpp )# 链接OpenCV库target_link_libraries( MyCV ${OpenCV_LIBS} )

3、文件目录结构:

(base) zja@zja:~$ tree -L 2
.
├── bvlc_googlenet.caffemodel
├── bvlc_googlenet.prototxt
├── CMakeLists.txt
└── main.cpp

3、运行

Cmake编译以上代码:

(base) zja@zja:~$ mkdir build && cd build
(base) zja@zja:~/build$ cmake ..
(base) zja@zja:~/build$ make
(base) zja@zja:~/build$ ./Mycv

4、运行结果

以下为加载的深度神经网络模型网络信息,见百度云盘:

链接: https://pan.baidu.com/s/1ogk7qLsV2-sVmFT9RRZ-_A 提取码: 99qf

网络层数:1网络名称:conv1/7x7_s2网络层数类型:Convolution网络层数:2网络名称:conv1/relu_7x7网络层数类型:ReLU网络层数:3网络名称:pool1/3x3_s2网络层数类型:Pooling......网络层数:141网络名称:loss3/classifier网络层数类型:InnerProduct网络层数:142网络名称:prob网络层数类型:Softmax

02

深度神经网络模型使用

1、函数说明

输入数据尺寸转换函数:


Mat cv::dnn::blobFromImages(InputArrayOfArrays images,double scalefactor=  1.0,Size size=Size(),const Scalar & Scalar(),bool swapRB=false,bool crop=false,int ddepth=CV_32F)

images:输入图像,图像可以是单通道、三通道或者四通道;

scalefactor:图像像素缩放系数;

size:输出图像的尺寸;

mean:像素值去均值化的数值;

swapRB:是否交换三通道图像的第一个通道和最后一个通道的标志;

size:调整大小后是否对图像进行剪切的标志;

size:输出图像的数据类型,可选参数为CV_32F或CV_8U;

>注意:训练模型时规定了输入数据尺寸!

2、编写代码

以下用到的四种深度神经网络模型见百度云盘:

链接: https://pan.baidu.com/s/1BdplyQDfqwRBwXsSFb9aOg 提取码: fbti 

1、编写main1.cpp:

使用目标识别深度神经网络模型:

tensorflow_inception_graph.pb && imagenet_comp_graph_label_strings.txt     

功能:目标识别。

#include #include #include using namespace cv;using namespace std;using namespace cv::dnn;int main(){    Mat AirLine=imread("/home/zja/Pictures/TianTianDnn/TianTianDnn1.jpg");    if(AirLine.empty())    {        cout<<"Please Input your Image Correctly"<<endl;        return -1;    }    //读取分类种类名称    String typeListFile="/home/zja/Downloads/imagenet_comp_graph_label_strings.txt";    vector typeList;//字符串向量    ifstream file(typeListFile);    if(!file.is_open())    {        printf("请确认分类种类名称是否正确");        return -1;    }    std::string type;    while(!file.eof())    {        //读取名称        getline(file,type);        if(type.length())            typeList.push_back(type);    }    file.close();    //加载网络    String tf_pb_file="/home/zja/Downloads/tensorflow_inception_graph.pb";    Net net=readNet(tf_pb_file);    if(net.empty())    {        printf("请确认模型文件是否正确");        return -1;    }    //对输入图像数据进行处理    Mat blob=blobFromImage(AirLine,1.0f,Size(244,244),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<<"图像中物体最有可能为:"<"可能性为:"<    //检测内容    string str=typeName+"  possibility is:  "+to_string(classProb);    putText(AirLine,str,Point(50,50),FONT_HERSHEY_SIMPLEX,1.0,Scalar(0,0,255),2,8);    imshow("图像判断结果",AirLine);waitKey(0);return 0;}
#include 
#include 
#include 

using namespace cv;
using namespace std;
using namespace cv::dnn;

int main()
{
    Mat AirLine=imread("/home/zja/Pictures/TianTianDnn/TianTianDnn1.jpg");
    if(AirLine.empty())
    {
        cout<<"Please Input your Image Correctly"<<endl;
        return -1;
    }

    //读取分类种类名称
    String typeListFile="/home/zja/Downloads/imagenet_comp_graph_label_strings.txt";
    vector typeList;//字符串向量
    ifstream file(typeListFile);
    if(!file.is_open())
    {
        printf("请确认分类种类名称是否正确");
        return -1;
    }

    std::string type;
    while(!file.eof())
    {
        //读取名称
        getline(file,type);
        if(type.length())
            typeList.push_back(type);
    }
    file.close();

    //加载网络
    String tf_pb_file="/home/zja/Downloads/tensorflow_inception_graph.pb";
    Net net=readNet(tf_pb_file);
    if(net.empty())
    {
        printf("请确认模型文件是否正确");
        return -1;
    }

    //对输入图像数据进行处理
    Mat blob=blobFromImage(AirLine,1.0f,Size(244,244),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<<"图像中物体最有可能为:"<"可能性为:"<

    //检测内容
    string str=typeName+"  possibility is:  "+to_string(classProb);
    putText(AirLine,str,Point(50,50),FONT_HERSHEY_SIMPLEX,1.0,Scalar(0,0,255),2,8);
    imshow("图像判断结果",AirLine);

waitKey(0);
return 0;
}




2、编写main2.cpp:

使用风格迁移深度神经网络模型:

"the_wave.t7","mosaic.t7","feathers.t7","candy.t7","udnie.t7","composition_vii.t7","la_muse.t7","starry_night.t7","the_scream.t7"

功能:风格迁移变换。

#include #include using namespace cv;using namespace std;using namespace cv::dnn;int main(){    Mat image=imread("/home/zja/Pictures/TianTian.jpg");    //下载地址:https://cs.stanford.edu/people/jcjohns/fast-neural-style/models//instance_norm/candy.t7    String models[9]={"the_wave.t7","mosaic.t7","feathers.t7","candy.t7","udnie.t7","composition_vii.t7","la_muse.t7","starry_night.t7","the_scream.t7"};    for(int i=0;i<9;i++)    {        Net net=readNet("/home/zja/Downloads/fast_style/"+models[i]);        imshow("原始图像",image);        //计算图像每个通道的均值        Scalar imageMean=mean(image);        //调整图像尺寸和格式        Mat blobImage=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(outputCols,outputRows),CV_32FC3);        float* data=output.ptr<float>();        for(int channel=0;channel        {            for(int row=0;row            {                for(int col=0;col                {                    result.at(row,col)[channel]=*data++;                }            }        }        //对迁移结果进行进一步操作处理        //恢复图像减掉的均值        result=result+imageMean;        //对图像进行归一化,便于图像显示        result=result/255.0;        //调整图像尺寸,使得与原图像尺寸相同        resize(result,result,image.size());        //显示结果        imshow("第"+to_string(i)+"风格迁移结果",result);    }waitKey(0);return 0;}

3、编写main3.cpp:

使用人脸识别深度神经网络模型:

opencv_face_detector_uint8.pb && opencv_face_detector.pbtxt

使用性别识别深度神经网络模型:

gender_net.caffemodel && gender_deploy.prototxt

功能:人脸识别及性别判断。

注意:第一个网络输出结果=第二个网络的输入

#include #include using namespace cv;using namespace std;using namespace cv::dnn;int main(){    Mat image=imread("/home/zja/Pictures/face_age/1.jpg");    if(image.empty())    {        cout<<"请确认是否输入正确的图像文件"<<endl;        return -1;    }    //读取人脸识别模型    String model_bin="/home/zja/Downloads/face_age/opencv_face_detector_uint8.pb";    String config_text="/home/zja/Downloads/face_age/opencv_face_detector.pbtxt";    Net faceNet=readNet(model_bin,config_text);    //读取性别检测模型    String genderProto="/home/zja/Downloads/face_age/gender_deploy.prototxt";    String genderModel="/home/zja/Downloads/face_age/gender_net.caffemodel";    String genderList[]={"Male","Female"};    Net genderNet=readNet(genderModel,genderProto);    if(faceNet.empty() && genderNet.empty())    {        cout<<"请正确输入模型文件"<<endl;        return -1;    }    //对整副图像进行人脸检测    Mat blobImage=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    {        float confidence=detectionMat.at<float>(i,2);//检测为人脸的概率        //只检测概率大于阈值区域的性别        if(confidence>confidenceThreshold)        {            //网络检测人脸区域大小            int topLx=detectionMat.at<float>(i,3)*image.cols;//398            int topLy=detectionMat.at<float>(i,4)*image.rows;//195            int bottomRx=detectionMat.at<float>(i,5)*image.cols;//573            int bottomRy=detectionMat.at<float>(i,6)*image.rows;//421            Rect faceRect(topLx,topLy,bottomRx-topLx,bottomRy-topLy);            //将网络检测出的区域尺寸进行扩充,要注意防止尺寸在图像真实尺寸之外            Rect faceTextRect;            faceTextRect.x=max(0,faceRect.x-exBoundray);//373            faceTextRect.y=max(0,faceRect.y-exBoundray);//170            faceTextRect.width=min(faceRect.width+exBoundray,image.cols-1);//200            faceTextRect.height=min(faceRect.height+exBoundray,image.rows-1);//251            Mat face=image(faceTextRect);//扩充后的人脸图像            //调整面部图像尺寸            Mat faceblob=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);waitKey(0);return 0;}

4、编写CMakeLists.txt:

# 声明要求的 cmake 最低版本cmake_minimum_required(VERSION 2.8)# 声明一个 myCV 工程project( myCV )# 设置编译模式 (Debug/Release/...)set(CMAKE_BUILD_TYPE "Release")# 寻找OpenCV库find_package( OpenCV REQUIRED )# 添加头文件include_directories( ${OpenCV_INCLUDE_DIRS} )# 生成可执行二进制文件add_executable( MyCV main1.cpp main2.cpp main3.cpp )# 链接OpenCV库target_link_libraries( MyCV ${OpenCV_LIBS} )

5、文件目录结构:

(base) zja@zja:~$ tree -L 2
.
├── main1.cpp
├── main2.cpp
├── main3.cpp
└── CMakeLists.txt

3、运行

Cmake编译以上代码:

(base) zja@zja:~$ mkdir build && cd build
(base) zja@zja:~/build$ cmake ..
(base) zja@zja:~/build$ make
(base) zja@zja:~/build$ ./Mycv

4、运行结果

4.1、运行main1.cpp的结果:

深度神经网络 绘图 深度神经网络应用实例_深度神经网络与脉冲神经网络

终端输出:

(base) zja@zja:~/build$
图像中物体最有可能为:Bernese mountain dog可能性为:0.967678

4.2、运行main2.cpp的结果:

深度神经网络 绘图 深度神经网络应用实例_#include_02

深度神经网络 绘图 深度神经网络应用实例_深度神经网络_03

4.3、运行main3.cpp的结果:

深度神经网络 绘图 深度神经网络应用实例_深度神经网络_04

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~.....END