opencv_contrib自测程序(4.6.0)

#include "ContribDemo.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <opencv2\opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/tracking.hpp>
#include <opencv2/img_hash.hpp>
#include <opencv2/intensity_transform.hpp>
#include <opencv2/saliency.hpp>
#include <opencv2/quality.hpp>

using namespace cv;
using namespace std;
using namespace aruco;
using namespace cv::img_hash;
using namespace cv::intensity_transform;
using namespace saliency;

ContribDemo::ContribDemo()
{

}

ContribDemo::~ContribDemo()
{

}

/* Marker检测 */
void ContribDemo::test1()
{
    Mat image = imread("../x64/Debug/picture/48.jpg", 1);

    Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);

    Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
    params->cornerRefinementMethod = aruco::CORNER_REFINE_NONE;

    Mat imageCopy;
    image.copyTo(imageCopy);

    vector<int> ids;
    vector<vector<Point2f>> corners, rejected;
    aruco::detectMarkers(image, dictionary, corners, ids, params);

    //如果检测到至少一个标记
    if (ids.size() > 0) {
        aruco::drawDetectedMarkers(imageCopy, corners, ids);
        vector< vector<Point2f>> rejectedCandidates;
        imshow("test", imageCopy);
        
    }
}

/* 姿态估计 */
void ContribDemo::test2()
{
    Ptr<aruco::DetectorParameters> parameters = aruco::DetectorParameters::create();

    VideoCapture captrue("../x64/Debug/video/14.mp4");
    if (!captrue.isOpened()) {
        return;
    }
    Mat img;
    Mat cameraMatrix = (Mat_<float>(3, 3) << 6.0097346876749680e+02, 0., 3.2031703441387049e+02, 0.,
                        6.0225171288478975e+02, 2.3947860507800820e+02, 0., 0., 1.);

    Mat distCoeffs = (Mat_<float>(5, 1) << -1.3711862233491057e-01, 2.7024113413198220e-01,
                      9.1669430855309032e-04, -3.3899628828653657e-03, 0.);

    while (captrue.read(img)) {
        vector<int> markerIds;
        vector< vector<Point2f> > markerCorners, rejectedCandidates;
        Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
        aruco::detectMarkers(img, dictionary, markerCorners, markerIds, parameters, rejectedCandidates);
        if (markerIds.size() > 0) {
            aruco::drawDetectedMarkers(img, markerCorners, markerIds);
            vector<Vec3d> rvecs, tvecs;
            aruco::estimatePoseSingleMarkers(markerCorners, 0.05, cameraMatrix, distCoeffs, rvecs, tvecs);
            for (int i = 0; i < markerIds.size(); i++) {
                cout << tvecs[i] << endl;
            }
        }
        imshow("ss", img);

        char c = waitKey(100);
        if (c == 27) {
            break;
        }
    }
}

/**
* 人脸定位
* @param net 人脸检测网络
* @param frame 检测图像
* @param conf_threshold 阈值
* @return tuple<Mat, vector<vector<int>>> 元组容器,可返回多个值
*/
tuple<Mat, vector<vector<int>>> getFaceBox(dnn::Net net, Mat &frame, double conf_threshold)
{
    Mat frameOpenCVDNN = frame.clone();
    int frameHeight = frameOpenCVDNN.rows;
    int frameWidth = frameOpenCVDNN.cols;

    double inScaleFactor = 1.0; //缩放尺寸

    Size size = Size(300, 300);
    Scalar meanVal = Scalar(104, 117, 123);

    Mat inputBlob;
    inputBlob = dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, size, meanVal, true, false);
    net.setInput(inputBlob, "data");

    Mat detection = net.forward("detection_out"); //四维矩阵输出
    Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>()); //提取结果信息
    vector<vector<int>> bboxes;

    for (int i = 0; i < detectionMat.rows; i++) {
        float confidence = detectionMat.at<float>(i, 2); //预测概率
        if (confidence > conf_threshold) {
            //左上角点,坐标被归一化
            int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frameWidth);
            int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frameHeight);

            //右下角角点,坐标被归一化
            int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frameWidth);
            int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frameHeight);
            vector<int> box = { x1, y1, x2, y2 };

            bboxes.push_back(box); //人脸坐标
            rectangle(frameOpenCVDNN, Point(x1, y1), Point(x2, y2), Scalar(0, 255, 0), 2, 4); //图像框选
        }
    }
    return make_tuple(frameOpenCVDNN, bboxes);
}

/* dnn年纪性别识别 */
void ContribDemo::test3()
{
    //人脸模型
    string faceProto = "E:/opencv/sources/samples/dnn/face_detector/opencv_face_detector.pbtxt";
    string faceModel = "E:/opencv/sources/samples/dnn/face_detector/opencv_face_detector_uint8.pb";

    //年龄模型
    string ageProto = "E:/opencv/sources/samples/dnn/face_detector/age_deploy.prototxt";
    string ageModel = "E:/opencv/sources/samples/dnn/face_detector/age_net.caffemodel";

    //性别模型
    string genderProto = "E:/opencv/sources/samples/dnn/face_detector/gender_deploy.prototxt";
    string genderModel = "E:/opencv/sources/samples/dnn/face_detector/gender_net.caffemodel";

    //均值
    Scalar MODEL_MEAN_VALUES = Scalar(78.4263377603, 87.7689143744, 114.895847746);

    //年龄段标签
    vector<string> ageList = {"(0-2)", "(4-6)", "(8-12)", "(15-20)", "(25-32)", "(38-43)", "(48-53)", "(60-100)"};

    //性别标签
    vector<string> genderList = {"Man", "Waman"};

    //导入网络
    dnn::Net ageNet = dnn::readNet(ageProto, ageModel);
    dnn::Net genderNet = dnn::readNet(genderProto, genderModel);
    dnn::Net faceNet = dnn::readNetFromTensorflow(faceModel, faceProto);

    VideoCapture cap;
    cap.open("../x64/Debug/video/11.mp4");

    int padding = 20;
    while (waitKey(1) < 0) {
        Mat frame;
        cap.read(frame);
        if (frame.empty()) {
            waitKey();
            break;
        }

        vector<vector<int>> bboxes; //人脸坐标
        Mat frameFace; //人脸检测结果图
        tie(frameFace, bboxes) = getFaceBox(faceNet, frame, 0.7); //人脸定位,tie()函数解包frameFace和bboxes

        if (bboxes.size() == 0) { //人脸判断
            cout << "No face detected, checking next frame." << endl;
            continue;
        }

        //逐个提取人脸检测
        for (auto it = begin(bboxes); it != end(bboxes); ++it) {
            Rect rec(it->at(0) - padding, it->at(1) - padding, it->at(2) - it->at(0) + 2 * padding, it->at(3) - it->at(1) + 2 * padding); //框选人脸

            rec.width = ((rec.x + rec.width) > frame.cols) ? (frame.cols - rec.x - 1) : rec.width; //避免人脸框选超过图像边缘
            rec.height = ((rec.y + rec.height) > frame.rows) ? (frame.rows - rec.y - 1) : rec.height;
            Mat face = frame(rec); //原图中提取人脸

                                   //性别检测
            Mat blob;
            blob = dnn::blobFromImage(face, 1, Size(227, 227), MODEL_MEAN_VALUES, false);
            genderNet.setInput(blob);

            vector<float> genderPreds = genderNet.forward(); //获取前向传播softmax结果
            int iMaxGender = std::distance(genderPreds.begin(), max_element(genderPreds.begin(), genderPreds.end())); //返回最大值和第一个值下标的距离
            string gender = genderList[iMaxGender]; //获得检测结果

            cout << "Gender: " << gender << endl;

            //年龄识别
            ageNet.setInput(blob);
            vector<float> agePreds = ageNet.forward();

            int iMaxAge = std::distance(agePreds.begin(), max_element(agePreds.begin(), agePreds.end())); //找到年龄预测最大下表
            string age = ageList[iMaxAge];
            cout << "Age: " << age << endl;

            string label = gender + ", " + age; //label输出标签
            putText(frameFace, label, Point(it->at(0), it->at(1) - 15), cv::FONT_HERSHEY_SIMPLEX, 0.9, Scalar(0, 255, 255), 2, cv::LINE_AA); //在人脸定位图上显示结果
        }
        imshow("Frame", frameFace);
    }
}

/*人脸检测haar级联
* @param frame 原图
* @param faceCascadePath 模型文件 
*/
Mat detectFaceHaar(Mat frame, string faceCascadePath)
{
    //图像缩放
    auto inHeight = 300;
    auto inWidth = 0;
    if (!inWidth) {
        inWidth = (int)(((float)frame.cols / (float)frame.rows) * inHeight);
    }
    resize(frame, frame, Size(inWidth, inHeight));

    //转换为灰度图
    Mat frameGray = frame.clone();
    cvtColor(frame, frameGray, COLOR_BGR2GRAY);

    //级联分类器
    CascadeClassifier faceCascade;
    faceCascade.load(faceCascadePath);
    std::vector<Rect> faces;
    faceCascade.detectMultiScale(frameGray, faces);

    for (size_t i = 0; i < faces.size(); i++) {
        int x1 = faces[i].x;
        int y1 = faces[i].y;
        int x2 = faces[i].x + faces[i].width;
        int y2 = faces[i].y + faces[i].height;
        Rect face_rect(Point2i(x1, y1), Point2i(x2, y2));
        rectangle(frame, face_rect, Scalar(0, 255, 0), 2, 4);
    }
    return frame;
}

/* 人脸检测
* @param frame 原图
* @param configFile 模型结构定义文件
* @param weightFile 模型文件 */
Mat detectFaceOpenCVDNN(Mat frame, string configFile, string weightFile)
{
    const size_t inWidth = 300;
    const size_t inHeight = 300;
    const double inScaleFactor = 1.0; //缩放比例
    const double confidenceThreshold = 0.7; //阈值
    const Scalar meanVal(104.0, 177.0, 123.0); //均值

    Mat frameOpenCVDNN = frame.clone();
    dnn::Net net;
    Mat inputBlob;
    int frameHeight = frameOpenCVDNN.rows;
    int frameWidth = frameOpenCVDNN.cols;
    
    string suffixStr = configFile.substr(configFile.find_last_of('.') + 1); //获取文件后缀
    if (suffixStr == "prototxt") { //判断是caffe模型还是tensorflow模型
        net = dnn::readNetFromCaffe(configFile, weightFile);
        inputBlob = dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, Size(inWidth, inHeight), meanVal, false, false);
    }
    else {
        net = dnn::readNet(configFile, weightFile);
        inputBlob = dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, Size(inWidth, inHeight), meanVal, true, false);
    }

    //读图检测
    net.setInput(inputBlob, "data");
    Mat detection = net.forward("detection_out");
    Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());

    for (int i = 0; i < detectionMat.rows; i++) { //分类精度
        float confidence = detectionMat.at<float>(i, 2);
        if (confidence > confidenceThreshold) { 
            //左上角坐标
            int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frameWidth);
            int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frameHeight);
            //右下角坐标
            int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frameWidth);
            int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frameHeight);
           
            rectangle(frameOpenCVDNN, Point(x1, y1), Point(x2, y2), Scalar(0, 255, 0), 2, 4);
        }
    }
    return frameOpenCVDNN;
}

/* 三种找脸方法对比 */
void ContribDemo::test4()
{
    string strFace = "E:/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml";

    string strCaffe = "E:/opencv/sources/samples/dnn/face_detector/deploy.prototxt";
    string strWeight = "E:/opencv/sources/samples/dnn/face_detector/res10_300x300_ssd_iter_140000_fp16.caffemodel";
    string strConfig = "E:/opencv/sources/samples/dnn/face_detector/opencv_face_detector.pbtxt";
    string strTensorWeight = "E:/opencv/sources/samples/dnn/face_detector/opencv_face_detector_uint8.pb";

    Mat frame = imread("../x64/Debug/picture/test1/1404.png");

    Mat maHarr = detectFaceHaar(frame, strFace);
    imshow("111", maHarr);
    Mat maOpenCVCaffe = detectFaceOpenCVDNN(frame, strCaffe, strWeight);
    imshow("222", maOpenCVCaffe);
    Mat maOpenCVTf = detectFaceOpenCVDNN(frame, strConfig, strTensorWeight);
    imshow("333", maOpenCVTf);
}

/* dnn文本检测 */
void ContribDemo::test5()
{
    auto inpWidth = 320;
    auto inpHeight = 320;
    auto confThreshold = 0.8; //置信度阈值
    auto nmsThreshold = 0.2; //非极大值抑制算法阈值

    string strModel = "E:/opencv/sources/samples/dnn/face_detector/frozen_east_text_detection.pb";
    dnn::Net net = dnn::readNet(strModel); //读取模型

    Mat srcImg = imread("../x64/Debug/picture/52.png");
    vector<Mat> output;
    vector<String> outputLayers(2);
    outputLayers[0] = "feature_fusion/Conv_7/Sigmoid";
    outputLayers[1] = "feature_fusion/concat_3";

    //检测图像
    Mat frame, blob;
    frame = srcImg.clone();
    dnn::blobFromImage(frame, blob, 1.0, Size(inpWidth, inpHeight), Scalar(123.68, 116.78, 103.94), true, false); //获取深度学习模型的输入
    net.setInput(blob);
    net.forward(output, outputLayers); //输出结果

    Mat scores = output[0]; //置信度
    Mat geometry = output[1]; //位置参数

    //对检测框进行解码,获取文本框位置方向
    vector<RotatedRect> boxes; //文本框位置参数
    vector<float> confidences; //文本框置信度
    boxes.clear();

    //判断是不是符合提取要求
    CV_Assert(scores.dims == 4);
    CV_Assert(geometry.dims == 4);
    CV_Assert(scores.size[0] == 1);
    CV_Assert(geometry.size[0] == 1);
    CV_Assert(scores.size[1] == 1);
    CV_Assert(geometry.size[1] == 5);
    CV_Assert(scores.size[2] == geometry.size[2]);
    CV_Assert(scores.size[3] == geometry.size[3]);

    const int height = scores.size[2];
    const int width = scores.size[3];
    for (int y = 0; y < height; ++y) {
        const float *scoresData = scores.ptr<float>(0, 0, y); //识别概率
        
        const float *x0_data = geometry.ptr<float>(0, 0, y); //文本框坐标
        const float *x1_data = geometry.ptr<float>(0, 1, y);
        const float *x2_data = geometry.ptr<float>(0, 2, y);
        const float *x3_data = geometry.ptr<float>(0, 3, y);
        
        const float *anglesData = geometry.ptr<float>(0, 4, y); //文本框角度
        for (int x = 0; x < width; ++x) { //遍历所有检测到的检测框
            float score = scoresData[x];
            if (score < confThreshold) { //低于阈值忽略该检测框
                continue;
            }

            //解码预测。乘以4,因为特征图比输入图像少4倍
            float offsetX = x * 4.0f, offsetY = y * 4.0f;

            //角度及相关正余弦计算
            float angle = anglesData[x];
            float cosA = cos(angle);
            float sinA = sin(angle);
            float h = x0_data[x] + x2_data[x];
            float w = x1_data[x] + x3_data[x];

            Point2f offset(offsetX + cosA * x1_data[x] + sinA * x2_data[x], offsetY - sinA * x1_data[x] + cosA * x2_data[x]);
            Point2f p1 = Point2f(-sinA * h, -cosA * h) + offset;
            Point2f p3 = Point2f(-cosA * w, sinA * w) + offset;

            RotatedRect r(0.5f * (p1 + p3), Size2f(w, h), -angle * 180.0f / (float)CV_PI); //旋转矩形,分别输入中心点坐标,图像宽高,角度
            
            boxes.push_back(r); //保存检测框
            confidences.push_back(score); //保存检测框的置信度
        }
    }

    vector<int> indices; //符合要求的文本框
    dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices); //应用非极大性抑制算法

    //输出预测
    Point2f ratio((float)frame.cols / inpWidth, (float)frame.rows / inpHeight); //缩放比例
    for (size_t i = 0; i < indices.size(); ++i) {
        RotatedRect &box = boxes[indices[i]];
        Point2f vertices[4];
        box.points(vertices);

        for (int j = 0; j < 4; ++j) { //还原坐标点
            vertices[j].x *= ratio.x;
            vertices[j].y *= ratio.y;
        }

        for (int j = 0; j < 4; ++j) { //画框
            line(frame, vertices[j], vertices[(j + 1) % 4], Scalar(0, 255, 0), 2, LINE_AA);
        }
    }

    //投放效率信息,时间
    vector<double> layersTimes;
    double freq = getTickFrequency() / 1000;
    double t = net.getPerfProfile(layersTimes) / freq;
    string label = format("Inference time: %.2f ms", t);
    putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
    imshow("result", frame);
}

vector<string> classes1; //类别参数
/** 画框,classId 类别,conf 置信度 */
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame)
{
    rectangle(frame, Point(left, top), Point(right, bottom), Scalar(255, 178, 50), 3);

    //获取类名及其置信度的标签
    string label = format("%.2f", conf);
    if (!classes1.empty()) {
        CV_Assert(classId < (int)classes1.size());
        label = classes1[classId] + ":" + label;
    }

    //在每个框左上角标上标签
    int baseLine;
    Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
    top = max(top, labelSize.height);
    rectangle(frame, Point(left, top - round(1.5*labelSize.height)), Point(left + round(1.5*labelSize.width), top + baseLine), Scalar(255,255,255), FILLED);
    putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 0), 1);
}

/**基于非极大性抑制去除边框,frame 视频图像,outs 输出层结果 */
void postprocess(Mat& frame, const vector<Mat>& outs)
{
    float confThreshold = 0.5; //Confidence threshold 置信度阈值
    float nmsThreshold = 0.4; //非极大性抑制阈值
    vector<int> classIds; //输出类
    vector<float> confidences; //置信度
    vector<Rect> boxes;

    for (size_t i = 0; i < outs.size(); ++i) { //遍历所有的输出层
        //扫描所有来自网络的边界框输出,只保留具有高置信度分数的边界框。将框的类标签指定为框得分最高的类。
        float* data = (float*)outs[i].data; //读取框
        for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols) {
            Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
            Point classIdPoint;
            double confidence;
            minMaxLoc(scores, 0, &confidence, 0, &classIdPoint); //获取置信度和位置参数

            if (confidence > confThreshold) { //如果大于置信度阈值
                //获取坐标
                int centerX = (int)(data[0] * frame.cols);
                int centerY = (int)(data[1] * frame.rows);
                int width = (int)(data[2] * frame.cols);
                int height = (int)(data[3] * frame.rows);
                int left = centerX - width / 2;
                int top = centerY - height / 2;

                classIds.push_back(classIdPoint.x);
                confidences.push_back((float)confidence);
                boxes.push_back(Rect(left, top, width, height));
            }
        }
    }

    vector<int> indices; //输出非极大性抑制结果,按置信度从大到小输出
    dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices); //非极大性抑制
    for (size_t i = 0; i < indices.size(); ++i) { //绘图
        int idx = indices[i];
        Rect box = boxes[idx];
        drawPred(classIds[idx], confidences[idx], box.x, box.y, box.x + box.width, box.y + box.height, frame);
    } 
}

/* 获取输出层 */
vector<String> getOutputsNames(const dnn::Net& net)
{
    vector<String> names;
    if (names.empty()) {
        vector<int> outLayers = net.getUnconnectedOutLayers(); //获取输出层的索引,即具有未连接输出的层
        vector<String> layersNames = net.getLayerNames(); //获取网络中所有层的名称
        names.resize(outLayers.size()); // 在名称中获取输出层的名称
        for (size_t i = 0; i < outLayers.size(); ++i) {
            names[i] = layersNames[outLayers[i] - 1];
        }
    }
    return names;
}

/* 使用YOLOv3和OpenCV进行基于深度学习的目标检测(识别物体) */
void ContribDemo::test6()
{
    int inpWidth = 416; //检测图像宽高
    int inpHeight = 416;
    //模型参数文件
    String modelConfiguration = "E:/opencv/sources/data/haarcascades/yolov3.cfg";
    String modelWeights = "E:/opencv/sources/data/haarcascades/yolov3.weights";

    //读取分类类名
    string classesFile = "E:/opencv/sources/data/haarcascades/coco.names";
    ifstream ifs(classesFile.c_str());
    string line;
    while (getline(ifs, line)) {
        classes1.push_back(line);
    }

    //导入网络
    dnn::Net net = dnn::readNetFromDarknet(modelConfiguration, modelWeights);
    net.setPreferableBackend(dnn::DNN_BACKEND_OPENCV);
    net.setPreferableTarget(dnn::DNN_TARGET_CPU); //仅仅使用CPU

    Mat blob;
    clock_t start, finish;
    Mat frame = imread("../x64/Debug/picture/53.jpg");

    start = clock();
    dnn::blobFromImage(frame, blob, 1 / 255.0, Size(inpWidth, inpHeight), Scalar(0, 0, 0), true, false); //创建神经网络输入图像

    net.setInput(blob); //设置输出

    vector<Mat> outs;
    net.forward(outs, getOutputsNames(net)); //获取输出层结果

    postprocess(frame, outs); //删除置信度低的边界框
    finish = clock();
    cout << "time is " << double(finish - start) / CLOCKS_PER_SEC << endl;

    //输出前向传播的时间
    vector<double> layersTimes;
    double freq = getTickFrequency() / 1000;
    double t = net.getPerfProfile(layersTimes) / freq;
    string label = format("Inference time for a frame : %.2f ms", t);
    putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));

    imshow("result", frame);
}

/* 进行手部关键点检测 */
void ContribDemo::test7()
{
    //各个部位连接线坐标,比如(0,1)表示第0特征点和第1特征点连接线为拇指
    const int POSE_PAIRS[20][2] = {
        {0,1}, {1,2}, {2,3}, {3,4},         //拇指
        {0,5}, {5,6}, {6,7}, {7,8},         //索引
        {0,9}, {9,10}, {10,11}, {11,12},    //中间
        {0,13}, {13,14}, {14,15}, {15,16},  //环
        {0,17}, {17,18}, {18,19}, {19,20}   //小型
    };

    int nPoints = 21;
    string protoFile = "E:/opencv/sources/samples/dnn/face_detector/pose_deploy.prototxt"; //模型文件位置
    string weightsFile = "E:/opencv/sources/samples/dnn/face_detector/pose_iter_102000.caffemodel";

    string imageFile = "../x64/Debug/picture/56.jpg";
    Mat frame = imread(imageFile);

    Mat frameCopy = frame.clone();
    int frameWidth = frame.cols;
    int frameHeight = frame.rows;
    float thresh = 0.01;

    float aspect_ratio = frameWidth / (float)frameHeight; //原图宽高比
    int inHeight = 368;
    int inWidth = (int(aspect_ratio*inHeight) * 8) / 8; //缩放图像

    double t = (double)getTickCount();

    //调用caffe模型
    dnn::Net net = dnn::readNetFromCaffe(protoFile, weightsFile);
    Mat inpBlob = dnn::blobFromImage(frame, 1.0 / 255, Size(inWidth, inHeight), Scalar(0, 0, 0), false, false);
    net.setInput(inpBlob);
    Mat output = net.forward();

    int H = output.size[2];
    int W = output.size[3];

    //找到各点的位置
    vector<Point> points(nPoints);
    for (int n = 0; n < nPoints; n++) {
        Mat probMap(H, W, CV_32F, output.ptr(0, n)); //第一个特征点的预测矩阵
        resize(probMap, probMap, Size(frameWidth, frameHeight)); //放大预测矩阵

        Point maxLoc;
        double prob;

        minMaxLoc(probMap, 0, &prob, 0, &maxLoc); //寻找预测矩阵,最大值概率以及最大值的坐标位置
        if (prob > thresh) {
            circle(frameCopy, Point((int)maxLoc.x, (int)maxLoc.y), 8, Scalar(0, 255, 255), -1); //画图
            putText(frameCopy, format("%d", n), Point((int)maxLoc.x, (int)maxLoc.y), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2);
        }
        points[n] = maxLoc; //保存特征点的坐标
    }

    int nPairs = sizeof(POSE_PAIRS) / sizeof(POSE_PAIRS[0]); //获取要画的骨架线个数
    for (int n = 0; n < nPairs; n++) { //连接点,画骨架
        //查找2个连接的身体/手部
        Point2f partA = points[POSE_PAIRS[n][0]];
        Point2f partB = points[POSE_PAIRS[n][1]];

        if (partA.x <= 0 || partA.y <= 0 || partB.x <= 0 || partB.y <= 0) {
            continue;
        }

        //画骨条线
        line(frame, partA, partB, Scalar(0, 255, 255), 8);
        circle(frame, partA, 8, Scalar(0, 0, 255), -1);
        circle(frame, partB, 8, Scalar(0, 0, 255), -1);
    }

    //计算运行时间
    t = ((double)getTickCount() - t) / getTickFrequency();
    cout << "Time Taken = " << t << endl;
    imshow("Output-Keypoints", frameCopy);
    imshow("Output-Skeleton", frame);
}

vector<string> classes;
vector<Scalar> colors;

/* 绘制预测的边界框,着色并在图像上显示遮罩 */
void drawBox(Mat &frame, int classId, float conf, Rect box, Mat &objectMask)
{
    float maskThreshold = 0.3; //掩模阈值
    rectangle(frame, Point(box.x, box.y), Point(box.x + box.width, box.y + box.height), Scalar(255, 178, 50), 3); //画预测框

    string label = format("%.2f", conf); //置信度获取
    if (!classes.empty()) { //获取标签
        CV_Assert(classId < (int)classes.size());
        label = classes[classId] + ":" + label;
    }

    int baseLine; //在边界框顶部显示标签
    Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); //标签,字体,文本大小的倍数,文本粗细,文本最低点对应的纵坐标
    box.y = max(box.y, labelSize.height);

    //画框打标签
    rectangle(frame, Point(box.x, box.y - round(1.5 * labelSize.height)), Point(box.x + round(1.5 * labelSize.width), box.y + baseLine), Scalar(255, 255, 255), FILLED);
    putText(frame, label, Point(box.x, box.y), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 0), 1);
    Scalar color = colors[classId % colors.size()]; //填充颜色

    resize(objectMask, objectMask, Size(box.width, box.height)); //重置大小
    Mat mask = (objectMask > maskThreshold);
    Mat coloredRoi = (0.3 * color + 0.7 * frame(box)); //叠加获得颜色掩模
    coloredRoi.convertTo(coloredRoi, CV_8UC3);

    //画轮廓
    vector<Mat> contours;
    Mat hierarchy;
    mask.convertTo(mask, CV_8U);
    findContours(mask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
    drawContours(coloredRoi, contours, -1, color, 5, LINE_8, hierarchy, 100);
    coloredRoi.copyTo(frame(box), mask);
}

/* 提取每张图像的预测框和掩模 */
void postprocess1(Mat &frame, const vector<Mat> &outs)
{
    float confThreshold = 0.5; //置信度阈值
    Mat outDetections = outs[0]; //预测框结果
    Mat outMasks = outs[1]; //掩模结果

    const int numDetections = outDetections.size[2]; //预测的框个数
    const int numClasses = outMasks.size[1]; //类别数
    outDetections = outDetections.reshape(1, outDetections.total() / 7);
    
    for (int i = 0; i < numDetections; ++i) { //筛选预测框数
        float score = outDetections.at<float>(i, 2); //提取预测框置信度
        if (score > confThreshold) { //超过阈值
            //类别
            int classId = static_cast<int>(outDetections.at<float>(i, 1));
            int left = static_cast<int>(frame.cols * outDetections.at<float>(i, 3));
            int top = static_cast<int>(frame.rows * outDetections.at<float>(i, 4));
            int right = static_cast<int>(frame.cols * outDetections.at<float>(i, 5));
            int bottom = static_cast<int>(frame.rows * outDetections.at<float>(i, 6));

            //防止框画在外面
            left = max(0, min(left, frame.cols - 1));
            top = max(0, min(top, frame.rows - 1));
            right = max(0, min(right, frame.cols - 1));
            bottom = max(0, min(bottom, frame.rows - 1));
            Rect box = Rect(left, top, right - left + 1, bottom - top + 1);

            Mat objectMask(outMasks.size[2], outMasks.size[3], CV_32F, outMasks.ptr<float>(i, classId)); //提取掩模

            drawBox(frame, classId, score, box, objectMask); //绘制边界框,着色并在图像上显示遮罩
        } 
    }
}

/* 使用Mask R-CNN进行对象检测和实例分割 */
void ContribDemo::test8()
{
    //0-image,1-video,2-camera
    int read_file = 1;

    //导入分类名文件
    string classesFile = "../x64/Debug/xml/mscoco_labels.names";
    ifstream ifs(classesFile.c_str());
    string line;
    while (getline(ifs, line)) {
        classes.push_back(line);
    }

    //导入颜色类文件
    string colorsFile = "../x64/Debug/xml/colors.txt";
    ifstream colorFptr(colorsFile.c_str());
    while (getline(colorFptr, line)) {
        char *pEnd;
        double r, g, b;

        //字符串转换成浮点数
        r = strtod(line.c_str(), &pEnd);
        g = strtod(pEnd, NULL);
        b = strtod(pEnd, NULL);
        Scalar color = Scalar(r, g, b, 255.0);
        colors.push_back(Scalar(r, g, b, 255.0));
    }

    //给出模型的配置和重量文件
    String textGraph = "E:/opencv/sources/samples/dnn/face_detector/mask_rcnn_inception_v2_coco.pbtxt";
    String modelWeights = "E:/opencv/sources/samples/dnn/face_detector/frozen_inference_graph.pb";

    //导入网络
    dnn::Net net = dnn::readNetFromTensorflow(modelWeights, textGraph);
    net.setPreferableBackend(dnn::DNN_BACKEND_OPENCV);
    net.setPreferableTarget(dnn::DNN_TARGET_CPU); //只使用CPU

    //打开视频文件、图像文件或相机流
    string str;
    VideoCapture cap;
    VideoWriter video;
    Mat frame, blob;

    //输出文件,默认是视频
    if (read_file == 0) {
        str = "../x64/Debug/picture/57.jpg";
        ifstream ifile(str);
        if (!ifile) {
            throw("error");
        }
        frame = imread(str);
    }
    else if (read_file == 1) {
        str = "../x64/Debug/video/17.mp4";
        ifstream ifile(str);
        if (!ifile) {
            throw("error");
        }
        cap.open(str);
    }
    else { //打开摄像头
        cap.open(0);
    }

    //显示窗口
    static const string kWinName = "object detection";
    while (waitKey(1) < 0) {
        //如果是视频
        if (read_file != 0) {
            cap >> frame;
        }

        //如果图像不存在
        if (frame.empty()) {
            cout << "Done processing !!!" << endl;
            waitKey(0);
            break;
        }

        //获得深度学习的输入图像
        dnn::blobFromImage(frame, blob, 1.0, Size(frame.cols, frame.rows), Scalar(), true, false);
        net.setInput(blob); //设置输入

        //获得输出层
        std::vector<String> outNames(2);
        outNames[0] = "detection_out_final";
        outNames[1] = "detection_masks";
        vector<Mat> outs;
        net.forward(outs, outNames);

        //提取预测框和掩模
        postprocess1(frame, outs);

        //输入效率信息。函数getPerfProfile返回推理的总时间(t)和每个层的时间(以layersTimes为单位)
        vector<double> layersTimes;
        double freq = getTickFrequency() / 1000;
        double t = net.getPerfProfile(layersTimes) / freq;
        string label = format("Mask-RCNN Inference time for a frame : %0.0f ms", t);
        putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 0));

        //保存结果
        Mat detectedFrame;
        frame.convertTo(detectedFrame, CV_8U);
        imshow(kWinName, frame);

        if (waitKey(10) == 27) {
            break;
        }
    }

    cap.release();
    //释放生成的视频
    if (read_file != 0) {
        video.release();
    }
}

/* 实现选择性搜索算法 */
void ContribDemo::test9()
{
    setUseOptimized(true); //使用多线程,开启CPU的硬件指令优化功能
    setNumThreads(4);

    Mat im = imread("../x64/Debug/picture/dogs.jpg");
    int newHeight = 200;
    int newWidth = im.cols*newHeight / im.rows;
    resize(im, im, Size(newWidth, newHeight));

    Ptr<ximgproc::segmentation::SelectiveSearchSegmentation> ss = ximgproc::segmentation::createSelectiveSearchSegmentation(); //默认参数生成选择性搜索类
    ss->setBaseImage(im); //要进行分割的图像

    //ss->switchToSelectiveSearchFast(); //快速搜索(速度快,召回率低)
    ss->switchToSelectiveSearchQuality(); //精准搜索(速度慢,召回率高)

    std::vector<Rect> rects;
    ss->process(rects); //保存搜索到的框,按可能性从高到低排名
    std::cout << "Total Number of Region Proposals: " << rects.size() << std::endl;

    int numShowRects = 10; //在图像中保存多少框
    Mat imOut = im.clone(); //做一份图像图像拷贝
    for (int i = 0; i < numShowRects; i++) { //画框前numShowRects个
        rectangle(imOut, rects[i], Scalar(0, 255, 0));
    }
    imshow("Output", imOut);
}

/* 实现红眼去除 */
void ContribDemo::test10()
{
    Mat img = imread("../x64/Debug/picture/red_eyes.jpg", IMREAD_COLOR);
    Mat imgOut = img.clone();
    imshow("Red Eyes", img);

    vector<Rect> eyes;
    CascadeClassifier eyesCascade("E:/opencv/sources/data/haarcascades/haarcascade_eye.xml");
    eyesCascade.detectMultiScale(img, eyes, 1.3, 4, 0 | CASCADE_SCALE_IMAGE, Size(100, 100));

    for (size_t i = 0; i < eyes.size(); i++) { //每只眼睛都进行处理
        Mat eye = img(eyes[i]); //提取眼睛图像
        imshow("Eyes0", eye);

        vector<Mat>bgr(3);
        split(eye, bgr); //颜色分离

        Mat mask = (bgr[2] > 150) & (bgr[2] > (bgr[1] + bgr[0])); //红眼检测器,获得结果掩模
        Mat maskFloodfill = mask.clone();
        imshow("Eyes1", mask);

        floodFill(maskFloodfill, Point(0, 0), Scalar(255)); //漫水填充
        imshow("Eyes2", maskFloodfill);

        Mat mask2;
        bitwise_not(maskFloodfill, mask2); //反色
        imshow("Eyes3", mask2);

        mask = (mask2 | mask); //或运算
        imshow("Eyes4", mask);

        dilate(mask, mask, Mat(), Point(-1, -1), 3, 1, 1); //扩充孔洞
        imshow("Eyes5", mask);

        Mat mean = (bgr[0] + bgr[1]) / 2; //计算b通道和g通道的均值
        imshow("Eyes6", mean);

        mean.copyTo(bgr[2], mask); //用该均值图像覆盖原图掩模部分图像
        mean.copyTo(bgr[0], mask);
        mean.copyTo(bgr[1], mask);

        Mat eyeOut;
        merge(bgr, eyeOut); //图像合并
        imshow("Eyes7", eyeOut);

        eyeOut.copyTo(imgOut(eyes[i])); //眼部图像替换
    }
    imshow("Red Eyes Removed", imgOut);
}

/* 图像孔洞填充 */
void ContribDemo::test11()
{
    Mat im_in = imread("../x64/Debug/picture/nickel.jpg", IMREAD_GRAYSCALE);
    Mat im_th;
    threshold(im_in, im_th, 220, 255, THRESH_BINARY_INV); //阈值分割

    Mat im_floodfill = im_th.clone();
    floodFill(im_floodfill, cv::Point(0,0), Scalar(255)); //(0, 0) 以点(0,0)为种子点,进行漫水填充

    Mat im_floodfill_inv;
    bitwise_not(im_floodfill, im_floodfill_inv); //反转图像

    Mat im_out = (im_th | im_floodfill_inv); //获得前景

    imshow("Thresholded Image", im_th);
    imshow("Floodfilled Image", im_floodfill);
    imshow("Inverted Floodfilled Image", im_floodfill_inv);
    imshow("Foreground", im_out);
}

/* 生成字典图像 */
void ContribDemo::test12()
{
    Mat markerImage;
	
	Ptr<cv::aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_100); // 生成字典
	
	aruco::drawMarker(dictionary, 33, 200, markerImage, 1); //生成图像,参数分别为字典,第几个标识,图像输出大小为200X200,输出图像,标记边框的宽度

	imshow("cow1.jpg", markerImage);
}

/* ArUco标记的增强现实 */
void ContribDemo::test13()
{
    //取边框放在新图或者视频上
	string str;
	VideoCapture cap;
	VideoWriter video;
	Mat frame, blob;
	
	Mat im_src = imread("../x64/Debug/picture/new_scenery.jpg"); // 新场景图像
	String detectPath = "../x64/Debug/video/test13.mp4";

	try {
		str = detectPath;
		ifstream ifile(str);
		if (!ifile) {
			throw("error");
		}
		cap.open(str);
	}
	catch (...) {
		cout << "Could not open the input image/video stream" << endl;
		return;
	}

	namedWindow("Augmented", WINDOW_NORMAL);
	while (waitKey(100) < 0) {
		cap >> frame;
		if (frame.empty()) { // 如果到了视频的结尾
			cout << "Done processing !!!" << endl;
			waitKey(3000);
			break;
		}

		vector<int> markerIds;
		Ptr<Dictionary> dictionary = getPredefinedDictionary(DICT_6X6_250); //加载用于标记的词典

		vector<vector<Point2f>> markerCorners, rejectedCandidates; //声明标记到的角点和没有被标记到的角点
		Ptr<DetectorParameters> parameters = DetectorParameters::create(); //使用默认值初始化检测器参数
		detectMarkers(frame, dictionary, markerCorners, markerIds, parameters, rejectedCandidates); //检测标记
        if (markerCorners.empty()) {
            break;
        }

		vector<Point> pts_dst; //使用检测到的标记,在目标帧上定位要显示新场景的四边形
		float scalingFac = 0.02; //计算缩减距离
		Point refPt1, refPt2, refPt3, refPt4;

		auto it = std::find(markerIds.begin(), markerIds.end(), 25); //寻找目标四边形的左上角点,查找字典中id为25的标志,返回一个vector
		int index = std::distance(markerIds.begin(), it); //返回markerIds中25的下标
		refPt1 = markerCorners.at(index).at(1); //返回markerIds中25的左上角坐标

		it = std::find(markerIds.begin(), markerIds.end(), 33); //求目标四边形的右上角点,查找字典中id为33的标志,返回一个vector
		index = std::distance(markerIds.begin(), it); //返回markerIds中33的下标
		refPt2 = markerCorners.at(index).at(2); //返回markerIds中33的右上角坐标

		float distance = norm(refPt1 - refPt2); //返回欧式距离
		pts_dst.push_back(Point(refPt1.x - round(scalingFac * distance), refPt1.y - round(scalingFac * distance))); //将缩减后的坐标放入标记点容器
		pts_dst.push_back(Point(refPt2.x + round(scalingFac * distance), refPt2.y - round(scalingFac * distance)));

		// 求目标四边形的右下角点
		it = std::find(markerIds.begin(), markerIds.end(), 30);
		index = std::distance(markerIds.begin(), it);
		refPt3 = markerCorners.at(index).at(0);
		pts_dst.push_back(Point(refPt3.x + round(scalingFac * distance), refPt3.y + round(scalingFac * distance)));

		// 寻找目标四边形的左下角点
		it = std::find(markerIds.begin(), markerIds.end(), 23);
		index = std::distance(markerIds.begin(), it);
		refPt4 = markerCorners.at(index).at(0);
		pts_dst.push_back(Point(refPt4.x - round(scalingFac * distance), refPt4.y + round(scalingFac * distance)));

		// 全新图像的角点,从左上角开始顺时针存入pts_src中
		vector<Point> pts_src;
		pts_src.push_back(Point(0, 0));
		pts_src.push_back(Point(im_src.cols, 0));
		pts_src.push_back(Point(im_src.cols, im_src.rows));
		pts_src.push_back(Point(0, im_src.rows));

		Mat h = cv::findHomography(pts_src, pts_dst); //计算单应性矩阵

		Mat warpedImage; //仿射变换后的图像
		warpPerspective(im_src, warpedImage, h, frame.size(), INTER_CUBIC); //基于单应性矩阵映射图像

		Mat mask = Mat::zeros(frame.rows, frame.cols, CV_8UC1); //准备一个表示要从仿射图像图像复制到原始帧中的区域的遮罩
		fillConvexPoly(mask, pts_dst, Scalar(255, 255, 255), LINE_AA); //计算单应性矩阵

		Mat element = getStructuringElement(MORPH_RECT, Size(5, 5)); //侵蚀mask以不复制仿射图像的边界效果
		erode(mask, mask, element);

		//将仿射的图像复制到遮罩区域中的原始帧中
		Mat imOut = frame.clone();
		warpedImage.copyTo(imOut, mask);
		Mat concatenatedOutput;
		hconcat(frame, imOut, concatenatedOutput); //并排显示原始图像和新输出图像

        imshow("result", concatenatedOutput);
        imshow("111", frame);
	}

	cap.release();
	video.release();

}

template <typename T>
inline void test_one(const std::string &title, const Mat &a, const Mat &b)
{
	cout << "=== " << title << " ===" << endl;
	TickMeter tick;
	Mat hashA, hashB;
	
	Ptr<ImgHashBase> func;
	func = T::create();

	tick.reset();
	tick.start();
	func->compute(a, hashA); //计算图a的哈希值
	tick.stop();
	cout << "compute1: " << tick.getTimeMilli() << " ms" << endl;

	tick.reset();
	tick.start();
	func->compute(b, hashB); //计算图b的哈希值
	tick.stop();
	cout << "compute2: " << tick.getTimeMilli() << " ms" << endl;
	
	cout << "compare: " << func->compare(hashA, hashB) << endl << endl; //比较两张图像哈希值的距离
}

/* 实现图像哈希算法 */
void ContribDemo::test14()
{
    Mat input = imread("../x64/Debug/picture/11.jpg");
	Mat target = imread("../x64/Debug/picture/15.jpg");

	// 通过不同方法比较图像相似性
	test_one<AverageHash>("AverageHash", input, target);
	test_one<PHash>("PHash", input, target);
	test_one<MarrHildrethHash>("MarrHildrethHash", input, target);
	test_one<RadialVarianceHash>("RadialVarianceHash", input, target);
	test_one<BlockMeanHash>("BlockMeanHash", input, target);
	test_one<ColorMomentHash>("ColorMomentHash", input, target);
}

// 计算对比度
double rmsContrast(Mat srcImg)
{
	Mat dstImg, dstImg_mean, dstImg_std; //灰度化
	cvtColor(srcImg, dstImg, COLOR_BGR2GRAY); //计算图像均值和方差
	meanStdDev(dstImg, dstImg_mean, dstImg_std); //获得图像对比度
	
	double contrast = dstImg_std.at<double>(0, 0);
	return contrast;
}

namespace
{
	Mat g_image; //gamma变换变量
	int g_gamma = 40;
	const int g_gammaMax = 500;
	Mat g_imgGamma;
	const std::string g_gammaWinName = "Gamma Correction";

	//对比度拉伸
	Mat g_contrastStretch;
	int g_r1 = 70;
	int g_s1 = 15;
	int g_r2 = 120;
	int g_s2 = 240;
	const std::string g_contrastWinName = "Contrast Stretching";

	//创建gamma变换滑动条
	static void onTrackbarGamma(int, void*)
	{
		float gamma = g_gamma / 100.0f;
		gammaCorrection(g_image, g_imgGamma, gamma);
		imshow(g_gammaWinName, g_imgGamma);
		cout << g_gammaWinName << ": " << rmsContrast(g_imgGamma) << endl;
	}

	//创建对数变换滑动条
	static void onTrackbarContrastR1(int, void*)
	{
		contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
		imshow("对比度拉伸", g_contrastStretch);
		cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
	}

	static void onTrackbarContrastS1(int, void*)
	{
		contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
		imshow("对比度拉伸", g_contrastStretch);
		cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
	}

	static void onTrackbarContrastR2(int, void*)
	{
		contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
		imshow("对比度拉伸", g_contrastStretch);
		cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
	}

	static void onTrackbarContrastS2(int, void*)
	{
		contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
		imshow("对比度拉伸", g_contrastStretch);
		cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
	}
}

/* 图像强度变换实现图像对比度均衡 */
void ContribDemo::test15()
{
	//g_image = imread("../x64/Debug/picture/car.png");
    //g_image = imread("../x64/Debug/picture/indicator.jpg");
    g_image = imread("../x64/Debug/picture/tree.jpg");
    //g_image = imread("../x64/Debug/picture/xray.jpg");

	namedWindow(g_gammaWinName);
	createTrackbar("Gamma value", g_gammaWinName, &g_gamma, g_gammaMax, onTrackbarGamma); //创建gamma变换筛选方法

	//对比度拉伸 Contrast Stretching
	namedWindow(g_contrastWinName);
	createTrackbar("Contrast R1", g_contrastWinName, &g_r1, 256, onTrackbarContrastR1);
	createTrackbar("Contrast S1", g_contrastWinName, &g_s1, 256, onTrackbarContrastS1);
	createTrackbar("Contrast R2", g_contrastWinName, &g_r2, 256, onTrackbarContrastR2);
	createTrackbar("Contrast S2", g_contrastWinName, &g_s2, 256, onTrackbarContrastS2);

	//应用强度转换
	Mat imgAutoscaled, imgLog;
	autoscaling(g_image, imgAutoscaled); //自动伸缩功能
	gammaCorrection(g_image, g_imgGamma, g_gamma / 100.0f); //gamma变换
	logTransform(g_image, imgLog); //对数变换
	contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2); //对比度拉伸

	imshow("Original Image", g_image);
	cout << "Original Image: " << rmsContrast(g_image) << endl;

	imshow("Autoscale", imgAutoscaled);
	cout << "Autoscale: " << rmsContrast(imgAutoscaled) << endl;

	imshow(g_gammaWinName, g_imgGamma);
	cout << g_gammaWinName << ": " << rmsContrast(g_imgGamma) << endl;

	imshow("Log Transformation", imgLog);
	cout << "Log Transformation: " << rmsContrast(imgLog) << endl;

	imshow(g_contrastWinName, g_contrastStretch);
	cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}

/* 视觉显著性检测 */
void ContribDemo::test16()
{
	//显著性检测算法,可选:SPECTRAL_RESIDUAL,FINE_GRAINED,BING,BinWangApr2014
	String saliency_algorithm = "SPECTRAL_RESIDUAL";
	String training_path = "D:/Opencv4.6.0/opencv/opencv_contrib-4.6.0/modules/saliency/samples/ObjectnessTrainedModel"; //模型路径

	VideoCapture cap;
	cap.open("../x64/Debug/video/vtest.avi");
	cap.set(CAP_PROP_POS_FRAMES, 0);

	Mat binaryMap; // 二值化检测结果
	Mat image; // 检测图像

    Mat frame;
	cap >> frame;
	if (frame.empty()) {
		return;
	}

	frame.copyTo(image);

	//根据输入的方法确定检测类型
    Ptr<Saliency> saliencyAlgorithm;
	if (saliency_algorithm.find("SPECTRAL_RESIDUAL") == 0) { //静态显著性谱残差,检测结果,白色区域表示显著区域
		Mat saliencyMap;
		saliencyAlgorithm = StaticSaliencySpectralResidual::create();
		//计算显著性
		double start = static_cast<double>(getTickCount());
		bool success = saliencyAlgorithm->computeSaliency(image, saliencyMap);
		double duration = ((double)getTickCount() - start) / getTickFrequency();
		cout << "computeSaliency cost time is: " << duration * 1000 << "ms" << endl;

		if (success) {
			StaticSaliencySpectralResidual spec;
			//二值化图像
			double start = static_cast<double>(getTickCount());
			spec.computeBinaryMap(saliencyMap, binaryMap);
			double duration = ((double)getTickCount() - start) / getTickFrequency();
			cout << "computeBinaryMap cost time is: " << duration * 1000 << "ms" << endl;

			imshow("Original Image", image);
			imshow("Saliency Map", saliencyMap);
			imshow("Binary Map", binaryMap);
			waitKey(0);
		}
	}
	else if (saliency_algorithm.find("FINE_GRAINED") == 0) { //静态显著性细粒度
		Mat saliencyMap;
		saliencyAlgorithm = StaticSaliencyFineGrained::create();
		// 计算显著性
		double start = static_cast<double>(getTickCount());
		bool success = saliencyAlgorithm->computeSaliency(image, saliencyMap);
		double duration = ((double)getTickCount() - start) / getTickFrequency();
		cout << "computeSaliency cost time is: " << duration * 1000 << "ms" << endl;

		if (success) {
			StaticSaliencyFineGrained spec;
			// 二值化图像
			double start = static_cast<double>(getTickCount());
			spec.computeBinaryMap(saliencyMap, binaryMap);
			double duration = ((double)getTickCount() - start) / getTickFrequency();
			cout << "computeBinaryMap cost time is: " << duration * 1000 << "ms" << endl;

			imshow("Saliency Map", saliencyMap);
			imshow("Original Image", image);
			imshow("Binary Map", binaryMap);
			waitKey(0);
		}
	}
	else if (saliency_algorithm.find("BING") == 0) { //ObjectnessBING
		// 判断模型是否存在
		if (training_path.empty()) {
			cout << "Path of trained files missing! " << endl;
			return;
		}
		else {
			saliencyAlgorithm = ObjectnessBING::create();
			vector<Vec4i> saliencyMap;
			
			saliencyAlgorithm.dynamicCast<ObjectnessBING>()->setTrainingPath(training_path); //提取模型文件参数
			saliencyAlgorithm.dynamicCast<ObjectnessBING>()->setBBResDir("Results"); //将算法检测结果保存在Results文件夹内
			saliencyAlgorithm.dynamicCast<ObjectnessBING>()->setNSS(50); //设置非极大值抑制,值越大检测到的目标越少,检测速度越快

			// 计算显著性
			double start = static_cast<double>(getTickCount());
			//基于三个颜色空间进行检测,可以只检测一个空间,把training_path下其他空间模型删除即可,如只保留ObjNessB2W8MAXBGR前缀的文件,算法耗时只有原来的一半
			bool success = saliencyAlgorithm->computeSaliency(image, saliencyMap);
			double duration = ((double)getTickCount() - start) / getTickFrequency();
			cout << "computeSaliency cost time is: " << duration * 1000 << "ms" << endl;

			if (success) {
				//saliencyMap获取检测到的目标个数
				int ndet = int(saliencyMap.size());
				std::cout << "Objectness done " << ndet << std::endl;
				//目标按可能性从大到小排列,maxd为显示前5个目标,step设置颜色,jitter设置矩形框微调
				int maxd = 5, step = 255 / maxd, jitter = 9;
				Mat draw = image.clone();
				for (int i = 0; i < std::min(maxd, ndet); i++) {
					Vec4i bb = saliencyMap[i]; //获得矩形框坐标点
					Scalar col = Scalar(((i*step) % 255), 50, 255 - ((i*step) % 255)); //设定颜色
					Point off(theRNG().uniform(-jitter, jitter), theRNG().uniform(-jitter, jitter)); //矩形框微调
					
					rectangle(draw, Point(bb[0] + off.x, bb[1] + off.y), Point(bb[2] + off.x, bb[3] + off.y), col, 2);
					rectangle(draw, Rect(20, 20 + i * 10, 10, 10), col, -1);
				}
				imshow("BING", draw);
				waitKey();
			}
		}
	}
	else if (saliency_algorithm.find("BinWangApr2014") == 0) { //BinWangApr2014
		saliencyAlgorithm = MotionSaliencyBinWangApr2014::create();
		saliencyAlgorithm.dynamicCast<MotionSaliencyBinWangApr2014>()->setImagesize(image.cols, image.rows);
		saliencyAlgorithm.dynamicCast<MotionSaliencyBinWangApr2014>()->init();

		bool paused = false;
		int i = 0;
		for (;;) {
			if (!paused) {
				cap >> frame;
				if (frame.empty()) {
					return;
				}

				Mat srcImg = frame.clone();
				cvtColor(frame, frame, COLOR_BGR2GRAY);

				Mat saliencyMap;
				double start = static_cast<double>(getTickCount());
				saliencyAlgorithm->computeSaliency(frame, saliencyMap);
				double duration = ((double)getTickCount() - start) / getTickFrequency();
				cout << "computeSaliency cost time is: " << duration * 1000 << "ms" << endl;

				imshow("image", frame);
				imshow("saliencyMap", saliencyMap * 255);

				i++;
			}

			char c = (char)waitKey(2);
		}
	}

	destroyAllWindows();
}

// 计算结果均值
double calMEAN(Scalar result)
{
	int i = 0;
	double sum = 0;
	// 计算总和
	for (auto val : result.val) {
		if (0 == val || isinf(val)) {
			break;
		}
		sum += val;
		i++;
	}
	return sum / i;
}

/* 图像质量评价 */
void ContribDemo::test17()
{
    // img1为基准图像,img2为检测图像
	Mat img1, img2;
	img1 = imread("../x64/Debug/picture/61.jpg");
	img2 = imread("../x64/Debug/picture/61_1.jpg");

	//MSE结果越小,检测图像和基准图像的差距越小
	Mat quality_map;
    Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_map);
	cout << "*************MSE:" << calMEAN(result_static) << endl;

	//PSNR结果越小,检测图像和基准图像的差距越大
	Mat quality_map2;
    result_static = quality::QualityPSNR::compute(img1, img2, quality_map2, 255.0);
	cout << "*************PSNR:" << calMEAN(result_static) << endl;

	//GMSD结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越大
	Mat quality_map3;
    result_static = quality::QualityGMSD::compute(img1, img2, quality_map3);
	cout << "*************GMSD:" << calMEAN(result_static) << endl;

	//SSIM结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越小
	Mat quality_map4;
    result_static = quality::QualitySSIM::compute(img1, img2, quality_map4);
	cout << "*************SSIM:" << calMEAN(result_static) << endl;

	//BRISQUE不需要基准图像,结果为一个0到100之间的数,越小表示检测图像质量越好
	Mat quality_map5;
    String model_path = "D:/Opencv4.6.0/opencv/opencv_contrib-4.6.0/modules/quality/samples/brisque_model_live.yml";
	String range_path = "D:/Opencv4.6.0/opencv/opencv_contrib-4.6.0/modules/quality/samples/brisque_range_live.yml";
    result_static = quality::QualityBRISQUE::compute(img2, model_path, range_path);
	cout << "*************BRISQUE:" << calMEAN(result_static) << endl;
}
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值