人脸识别 注释

发布时间 2023-07-16 21:16:22作者: blackstrom
#include <QCoreApplication>
#include<opencv2\opencv.hpp>
#include<opencv2\dnn.hpp>
#include <iostream>
#include<map>
#include<string>
#include<time.h>
using namespace std;
using namespace cv;
const size_t inWidth = 300;//size_t是C++中的一种无符号整数类型.通常用于表示数组索引、循环计数器和内存分配等场景。const size_t表示定义了一个常量变量,并且该变量的类型为size_t。通过使用const关键字,这个变量被声明为常量,意味着它的值不能被修改。
const size_t inHeight = 300;
const float WHRatio = inWidth / (float)inHeight;//这行代码计算了一个宽高比(WHRatio)的值。具体来说,代码中使用了以下变量和操作:计算宽高比的方法是将输入图像的宽度除以输入图像的高度,将结果强制转换为float类型。这样做是为了确保计算结果为浮点数,而不是整数。计算得到的宽高比(WHRatio)可以在后续的图像处理或计算中使用,例如用于调整图像的宽高比、比较宽高比等操作。

const char* classNames[]= {"background", "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
"fire hydrant", "background", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "background", "backpack",
"umbrella", "background", "background", "handbag", "tie", "suitcase", "frisbee","skis", "snowboard", "sports ball", "kite", "baseball bat","baseball glove", "skateboard", "surfboard", "tennis racket",
"bottle", "background", "wine glass", "cup", "fork", "knife", "spoon","bowl", "banana",  "apple", "sandwich", "orange","broccoli", "carrot", "hot dog",  "pizza", "donut",
"cake", "chair", "couch", "potted plant", "bed", "background", "dining table", "background", "background", "toilet", "background","tv", "laptop", "mouse", "remote", "keyboard",
"cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "background","book", "clock", "vase", "scissors","teddy bear", "hair drier", "toothbrush"};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    clock_t start, finish;//clock_t是C/C++中的一个计时器类型,用于度量程序的运行时间。具体步骤如下:
    //1在程序执行前,使用start = clock()记录开始时间。2在程序执行完毕后,使用finish = clock()记录结束时间。3通过对finish和start进行相减,可以计算出程序的运行时间。

    double totaltime;
    Mat frame;
    String weights = "C:/Users/Administrator/Downloads/Simple_Qt_opencv_dnn-master/Simple_Qt_opencv_dnn-master/ssd_mobilenet_v1_coco_11_06_2017/frozen_inference_graph.pb";
    String prototxt = "C:/Users/Administrator/Downloads/Simple_Qt_opencv_dnn-master/Simple_Qt_opencv_dnn-master/ssd_mobilenet_v1_coco_11_06_2017/ssd_mobilenet_v1_coco.pbtxt";
    //String weights = "C:/Users/Administrator/Desktop/face/opencv_face_detector_uint8.pb";//00读入权重模型
   // String prototxt ="C:/Users/Administrator/Desktop/face/opencv_face_detector.pbtxt";//00读入网络结构模型

    dnn::Net net = cv::dnn::readNetFromTensorflow(weights, prototxt);//01初始化网络,加载tensorflow权重和网络结构

    frame = imread("E:/00.jpg");//读取图片

    start = clock();//记录程序开始时间
    Size frame_size = frame.size();//定义了一个名为frame_size的变量,其类型为Size。frame.size()表示获取了一个名为frame的对象(通常是图像帧)的尺寸信息。通常情况下,frame.size()函数会返回一个Size对象,该对象包含了frame的宽度和高度信息。将frame.size()的返回值赋值给frame_size变量,意味着我们可以通过frame_size变量来访问帧的尺寸信息。
    Size cropSize;
    if (frame_size.width / (float)frame_size.height > WHRatio)//如果图片宽高比大于1.0
    {
        cropSize = Size(static_cast<int>(frame_size.height * WHRatio),
                        frame_size.height);//static_cast<int>(a)静态类型转换,将a的类型转换为int  //frame_size.height * WHRatio这部分其实是将图片按照WHRatio的比例转化为新width
    }
    else
    {
        cropSize = Size(frame_size.width,
                        static_cast<int>(frame_size.width / WHRatio));//frame_size.width / WHRatio这部分其实是将图片按照WHRatio的比例转化为新width
    }
    Rect crop(Point((frame_size.width - cropSize.width) / 2,
                    (frame_size.height - cropSize.height) / 2),
              cropSize);//创建了一个名为crop的矩形对象(Rect)。该矩形表示了裁剪区域的位置和大小。
    //crop的位置是由两个Point对象定义的。第一个Point对象的x坐标是(frame_size.width - cropSize.width) / 2,y坐标是(frame_size.height - cropSize.height) / 2。这意味着裁剪区域的左上角在x和y方向上距离图像边界的距离是相等的。
    //crop的大小是cropSize,它是在之前的代码行中计算得到的裁剪区域的大小。
    //通过定义crop矩形对象,你可以在后续的处理中使用该矩形来指定图像的裁剪区域。例如,可以使用crop作为参数来提取图像中的裁剪区域或者在图像上绘制裁剪区域。

    cv::Mat blob = cv::dnn::blobFromImage(frame, 1. / 255, Size(300, 300));//02 blobFromImage主要是对图片进行预处理,整体像素值减去平均值(menu),通过缩放系数(scalefactor)对图片像素值进行缩放,将图像转为4维
    // 1*3*300*300
    cout << "blob size: " << blob.size << endl;//blob是深度学习常用的数据结构,用于在网络中传递和处理图像数据。1./ 255: 归一化参数,将像素值缩放到0到1之间。Size(300, 300): 输出blob的大小,该函数会将图像调整为指定的大小。在这个例子中,输出blob的大小为300x300。该函数返回一个cv::Mat对象,表示转换后的blob。这个blob可以传入深度学习模型进行预测或其他处理。

    net.setInput(blob);//03设置网络的输入数据为blob
    Mat output = net.forward();//04执行网络推断,运行向前传播,获取人脸检测结果
    // 1*1*100*7
    cout << "output size: " << output.size << endl;

    Mat detectionMat(output.size[2], output.size[3], CV_32F, output.ptr<float>());//在OpenCV中,这段代码创建了一个名为"detectionMat"的CV_32F类型的矩阵,并使用output.ptr<float>()提供的指针来初始化矩阵的数据。"output.size[2]"和"output.size[3]"是矩阵的大小参数,通常用于确定矩阵的行数和列数。output.ptr<float>()是一个指针,用于获取指向output矩阵数据数组的起始位置的地址。在这种情况下,数据类型为float。这个指针可以用于初始化其他变量或进行进一步的操作,例如将数据复制到另一个矩阵中。

    frame = frame(crop);//这行代码将使用之前定义的crop矩形对象来对名为frame的图像进行裁剪。以frame(crop)的形式调用frame对象,将会返回裁剪后的图像,该图像的内容将仅限于crop所指定的区域。通过将裁剪后的图像赋值给frame变量,你可以在后续的处理中使用这个裁剪后的图像。例如,可以对裁剪后的图像进行进一步的分析、处理或显示。请确保crop矩形对象的边界没有超出frame图像的范围,以避免错误或异常情况。

    float confidenceThreshold = 0.50;

    for (int i = 0; i < detectionMat.rows; i++)//遍历检测结果
    {
        float confidence = detectionMat.at<float>(i, 2);//获取置信度
        if (confidence > confidenceThreshold)//设置置信度阈值
        {
            size_t objectClass = (size_t)(detectionMat.at<float>(i, 1));
            //计算人脸框的位置?
            int xLeftBottom = static_cast<int>(detectionMat.at<float>(i, 3) * frame.cols);
            int yLeftBottom = static_cast<int>(detectionMat.at<float>(i, 4) * frame.rows);
            int xRightTop = static_cast<int>(detectionMat.at<float>(i, 5) * frame.cols);
            int yRightTop = static_cast<int>(detectionMat.at<float>(i, 6) * frame.rows);
            ostringstream ss;
            ss << confidence;
            String conf(ss.str());
            //绘制人脸框和置信度
            Rect object((int)xLeftBottom, (int)yLeftBottom,
                        (int)(xRightTop - xLeftBottom),
                        (int)(yRightTop - yLeftBottom));
            rectangle(frame, object, Scalar(0, 255, 0), 1);
            //cout << "objectClass:" << objectClass << endl;
            String label = String(classNames[objectClass]) + ": " + conf;
            //cout << "label"<<label << endl;
            int baseLine = 0;
            Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);//获取文本的大小信息。getTextSize函数将返回一个Size对象,其中包含了文本的宽度和高度的信息。该代码行将这个结果保存在名为labelSize的变量中。
            rectangle(frame, Rect(Point(xLeftBottom, yLeftBottom - labelSize.height),
                                  Size(labelSize.width, labelSize.height + baseLine)),
                      Scalar(0, 255, 0), CV_FILLED);
            putText(frame, label, Point(xLeftBottom, yLeftBottom),
                    FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0));
        }
    }
    finish = clock();
    totaltime = finish - start;
    cout << "识别图像所用的时间为:" << totaltime <<"ms"<< endl;
    namedWindow("result", 0);
    imshow("result", frame);
    waitKey(0);
    return a.exec();
}