OpenCV联合C++/Qt 学习笔记(二十五)----加载深度神经网络模型及深度神经网络模型的使用

一、加载深度神经网络模型

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();
相关推荐
计算机安禾8 小时前
【c++面向对象编程】第49篇:面向对象的单元测试:用GoogleTest测试类
开发语言·c++·单元测试
坚定学代码8 小时前
如何在c++中使用MySQL
开发语言·c++·mysql
xuhaoyu_cpp_java8 小时前
Git学习(三)
经验分享·笔记·git·学习
小牛蛋8 小时前
vcpkg 管理 PCL + VTK + Qt 开发三维点云可视化软件
开发语言·qt
爱吃龙利鱼8 小时前
ubuntu2026.04部署k8s1.36版本的傻瓜式教程(注:运行时为docker,网络插件为calico)
运维·网络·笔记·docker·云原生·kubernetes
一只大袋鼠8 小时前
SpringBoot 入门学习笔记(二)Web 开发基础
spring boot·笔记·学习
纽扣6678 小时前
【C++通关之路】C++ 继承深度全景指南:从语法陷阱到内存底层的终极复习
开发语言·c++
楼田莉子8 小时前
C++17特性:强制省略拷贝优化/折叠表达式/非类型模板参数/嵌套命名空间
开发语言·c++
xifangge20258 小时前
Steam/Epic 游戏启动报错 0xc000007b / msvcp140.dll 缺失?VC++ 运行库底层修复指南
开发语言·c++·游戏