OpenCV的视频输入和相似度测量

允我心安 提交于 2019-11-26 14:22:23
#include <iostream>
#include <string>
#include <iomanip>                  // 控制浮动类型的打印精度
#include <sstream>                  // 字符串和数值的转换

#include <opencv2/core.hpp>         // CV::Mat,Scalar
#include <opencv2/imgproc.hpp>      // 高斯平滑
#include <opencv2/videoio.hpp>      // 视频
#include <opencv2/highgui.hpp>      

using namespace std;
using namespace cv;

double getPSNR(const Mat& I1, const Mat& I2);
Scalar getMSSIM(const Mat& I1, const Mat& I2);

int main(int argc, char *argv[])
{
    const string sourceReference = "E:\\VS2015Opencv\\vs2015\\project\\video\\01.avi";
    const string sourceCompareWith = "E:\\VS2015Opencv\\vs2015\\project\\video\\011.avi";

    int frameNum = -1;                                                          // 计算帧数
    int psnrTriggerValue = 35;

    VideoCapture captRefrnc(sourceReference), captUndTst(sourceCompareWith);    // 获取视频
    if (!captRefrnc.isOpened() || !captUndTst.isOpened()) { return -1; }

    Size refS = Size((int)captRefrnc.get(CAP_PROP_FRAME_WIDTH),                 // 视频帧的大小
        (int)captRefrnc.get(CAP_PROP_FRAME_HEIGHT));
    Size uTSi = Size((int)captUndTst.get(CAP_PROP_FRAME_WIDTH),
        (int)captUndTst.get(CAP_PROP_FRAME_HEIGHT));
    if (refS != uTSi) { return -1; }                                            // 视频帧大小应相同

    const char* WIN_UT = "Under Test";                                          // 显示窗口                         
    const char* WIN_RF = "Reference";
    namedWindow(WIN_RF, WINDOW_AUTOSIZE);
    namedWindow(WIN_UT, WINDOW_AUTOSIZE);
    moveWindow(WIN_RF, 0, 0);
    moveWindow(WIN_UT, refS.width, 0);

    cout << "Reference frame resolution: Width=" << refS.width << "  Height=" << refS.height
        << " of nr#: " << captRefrnc.get(CAP_PROP_FRAME_COUNT) << endl;
    cout << "PSNR trigger value " << psnrTriggerValue << endl;

    Mat frameReference, frameUnderTest;
    double psnrV;                                                               // PSNR方法
    Scalar mssimV;                                                              // SSIM方法

    for (;;)
    {
        captRefrnc >> frameReference;
        captUndTst >> frameUnderTest;
        if (frameReference.empty() || frameUnderTest.empty())
        {
            cout << "The End" << endl;
            break;
        }

        ++frameNum;
        cout << "Frame:" << frameNum << "#";                                    // 当前帧数,0开始

        psnrV = getPSNR(frameReference, frameUnderTest);                        // 定义的PSNR函数

                                                                                // setiosflags(ios::fixed)用定点方式显示实数,setprecision(n)可控制输出流显示浮点数的数字个数
        cout << setiosflags(ios::fixed) << setprecision(3) << psnrV << "dB";

        if (psnrV < psnrTriggerValue && psnrV)                                  // PSNR结果不为零且小于输入值
        {
            mssimV = getMSSIM(frameReference, frameUnderTest);

            cout << "\tMSSIM:" << " R " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[2] * 100 << "%"
                << " G " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[1] * 100 << "%"
                << " B " << setiosflags(ios::fixed) << setprecision(2) << mssimV.val[0] * 100 << "%";
        }

        cout << endl;

        imshow(WIN_RF, frameReference);
        imshow(WIN_UT, frameUnderTest);

        char c = (char)waitKey(30);
        if (c == 27) break;
    }

    return 0;
}

double getPSNR(const Mat& I1, const Mat& I2)                        // PSNR方法
{
    Mat s1;
    absdiff(I1, I2, s1);                                            // |I1 - I2|
    s1.convertTo(s1, CV_32F);                                       // 转换为32位进行运算
    s1 = s1.mul(s1);                                                // |I1 - I2|^2

    Scalar s = sum(s1);                                             // 各个通道求和

    double sse = s.val[0] + s.val[1] + s.val[2];                    // 所有通道的值相加在一起
    if (sse <= 1e-10)       // 当值太小时近似于0,由公式可知分母为0时需另外对待,使用SSIM方法
        return 0;
    else
    {
        double mse = sse / (double)(I1.channels() * I1.total());    // 公式
        double psnr = 10.0 * log10((255 * 255) / mse);
        return psnr;
    }
}
Scalar getMSSIM(const Mat& i1, const Mat& i2)                       // SSIM方法
{
    const double C1 = 6.5025, C2 = 58.5225;

    Mat I1, I2;
    i1.convertTo(I1, CV_32F);                                       // 转换为32位进行运算
    i2.convertTo(I2, CV_32F);

    Mat I1_2 = I1.mul(I1);                                          // I1^2
    Mat I2_2 = I2.mul(I2);                                          // I2^2
    Mat I1_I2 = I1.mul(I2);                                         // I1 * I2

    Mat sigma1_2, sigma2_2, sigma12;                                // 先平方再高斯滤波
    GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);
    GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);
    GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);

    Mat mu1, mu2;
    GaussianBlur(I1, mu1, Size(11, 11), 1.5);
    GaussianBlur(I2, mu2, Size(11, 11), 1.5);

    Mat mu1_2 = mu1.mul(mu1);                                       // 先高斯滤波再平方
    Mat mu2_2 = mu2.mul(mu2);
    Mat mu1_mu2 = mu1.mul(mu2);

    sigma1_2 -= mu1_2;                                              // 两种方式的差值
    sigma2_2 -= mu2_2;
    sigma12 -= mu1_mu2;

    Mat t1, t2, t3, t4;
    t1 = 2 * mu1_mu2 + C1;
    t2 = 2 * sigma12 + C2;
    t3 = t1.mul(t2);                                                // t3 = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))

    t1 = mu1_2 + mu2_2 + C1;
    t2 = sigma1_2 + sigma2_2 + C2;
    t4 = t1.mul(t2);                                                // t4 =((mu1_2 + mu2_2 + C1).*(sigma1_2 + sigma2_2 + C2))

    Mat ssim_map;
    divide(t3, t4, ssim_map);                                       // ssim_map =  t3./t4;
    Scalar mssim = mean(ssim_map);                                  // ssim map矩阵的平均值

    return mssim;
}

代码解析:

 

 

 

参考:【OpenCV】视频输入与相似度测量

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!