栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

视频质量评价工具vmaf分析

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

视频质量评价工具vmaf分析

vmaf介绍
  1. 网址:https://github.com/Netflix/vmaf

  2. VMAF,即Video Muitimethod Assessment Fusion(视频多方法评价融合);由 Netflix 推出的视频质量评价工具,用来解决传统指标不能反映多种场景、多种特征的视频情况。该指标是目前互联网视频最主流的客观视频评价指标,适用于衡量大规模环境中流播视频质量的观感。

  3. vmaf是一种视频质量指标,将人类视觉建模与机器学习相结合,由Netflix和南加州大学C.-C Jay Kuo教授之间的研究合作;之后德克萨斯大学奥斯汀分校Alan Bovik教授和南特大学Patrick Le Callet教授之间展开合作,用来提高与人类主观感知有关的vmaf准确性,并扩大其范围涵盖更多用例;2016年6月完成开源。

  4. vmaf可以用作优化准则,以获得更好的编码决策;VMAF被用于我们的整个生产流程中,不仅可以测量编码过程的结果,还可以指导编码达到最佳质量。在我们的动态优化器中,有一个关于如何在编码中使用VMAF的重要示例 Dynamic Optimizer,其中,每镜头的编码决策由每个编码器选项的比特率和质量测量确定。在优化过程中,VMAF 分数对于获取准确的质量测量以及选择 凸壳上的最终分辨率/比特率点至关重要。

  5. VMAF分数范围从 0 到 100,其中 0 表示最低质量,100表示最高。思考VMAF分数的一种好方法是将其线性映射到人的意见量表,在此条件下可获得训练分数。例如,默认模型 v0.6.1 使用由绝对类别评分(ACR)方法收集的分数进行训练,该分数使用 1080p 显示屏,观看距离为3H。观看者对视频质量的评分为“很差”,“差”,“一般”,“好”和“优秀”,并且粗略地讲,“差”被映射为VMAF级别20,“优秀”为100。

  6. 视频数据源的内容特征:
    ○ 动漫、室内、室外、镜头摇移、面部拉近、人物、水面、显著的物体、多个物体;
    ○ 不同亮度、不同对比度、不同材质、不同活动、颜色变化、色泽浓郁度、锐利度;
    ○ 块效应、振铃效应、胶片颗粒度、蚊式噪声;

编译

https://blog.csdn.net/yanceyxin/article/details/108075460

原理
  • 三种指标:视觉信息保真度(VIF:visual quality fidelity)、细节损失指标(DLM:detail loss measure)、时域运动指标/平均相关位置像素差(TI:temporal information)。其中VIF和DLM是空间域的,一帧画面之内的特征。TI 是时间域的,多帧画面之间相关性的特征。这些特性之间融合计算总分的过程使用了训练好的SVM来预测。
  • VIF:视觉信息保真度指标来源于论文《image information and visual quality》https://ieeexplore.ieee.org/document/1576816
  • DLM:细节损失指标来源于论文《Image Quality Assessment by Separately Evaluating Detail Losses and Additive Impairments》https://ieeexplore.ieee.org/document/5765502
  • TI :时域运动指标/平均相关位置像素差是一种衡量相邻帧之间时域差分的算法;这个仅仅计算像素亮度分量的均值作差即可得到该值。
compute_vmaf demo代码逻辑分析

command line tool源码分析

[vmaf.c]

int main(int argc, char *arg[])
{
    int err = 0;
    
    //文件流相关操作
    const int istty = isatty(fileno(stderr));
    
    //解析命令行,传入CLISettings结构体中
    CLISettings c;
    cli_parse(argc, argv, &c);
    
    //输出VMAF版本号
    if (istty && !c.quiet) {
        fprintf(stderr, "VMAF version %sn", vmaf_version());
    }
    
    //从路径读入视频数据
    FILE *file_ref = fopen(c.path_ref, "rb");
    if (!file_ref) {
        fprintf(stderr, "could not open file: %sn", c.path_ref);
        return -1;
    }

    FILE *file_dist = fopen(c.path_dist, "rb");
    if (!file_dist) {
        fprintf(stderr, "could not open file: %sn", c.path_dist);
        return -1;
    }
    
    //从FILE中传入video_input结构体里
    video_input vid_ref;
    if (c.use_yuv) {
        err = raw_input_open(&vid_ref, file_ref,
                             c.width, c.height, c.pix_fmt, c.bitdepth);
    } else {
        err = video_input_open(&vid_ref, file_ref);
    }
    if (err) {
        fprintf(stderr, "problem with reference file: %sn", c.path_ref);
        return -1;
    }

    video_input vid_dist;
    if (c.use_yuv) {
        err = raw_input_open(&vid_dist, file_dist,
                             c.width, c.height, c.pix_fmt, c.bitdepth);
    } else {
        err = video_input_open(&vid_dist, file_dist);
    }
    if (err) {
        fprintf(stderr, "problem with distorted file: %sn", c.path_dist);
        return -1;
    }
    
    //验证ref和dist视频的相容性
    err = validate_videos(&vid_ref, &vid_dist);
    if (err) {
        fprintf(stderr, "videos are incompatible, %d %s.n",
                err, err == 1 ? "problem" : "problems");
        return -1;
    }
    
    //configure信息
    VmafConfiguration cfg = {
        .log_level = VMAF_LOG_LEVEL_INFO,
        .n_threads = c.thread_cnt,
        .n_subsample = c.subsample,
        .cpumask = c.cpumask,
    };
    
    //1. 初始化,vmaf是上下文,cfg是选择结构体用来初始化上下文
    VmafContext *vmaf;
    err = vmaf_init(&vmaf, cfg);
    if (err) {
        fprintf(stderr, "problem initializing VMAF contextn");
        return -1;
    }
    //内存申请
    VmafModel **model;
    const size_t model_sz = sizeof(*model) * c.model_cnt;
    model = malloc(model_sz);
    memset(model, 0, model_sz);

    VmafModelCollection **model_collection;
    const size_t model_collection_sz =
        sizeof(*model_collection) * c.model_cnt;
    model_collection = malloc(model_sz);
    memset(model_collection, 0, model_collection_sz);

    const char *model_collection_label[c.model_cnt];
    unsigned model_collection_cnt = 0;
    
    //对model次数进行循环操作
    for (unsigned i = 0; i < c.model_cnt; i++) {
        
        //根据判断version是否使用来调用不同的方法load模型
        if (c.model_config[i].version) {
            //加载默认内置模型之一
            err = vmaf_model_load(&model[i], &c.model_config[i].cfg,
                                  c.model_config[i].version);
        } else {
            //从系统文件读取模型文件
            err = vmaf_model_load_from_path(&model[i], &c.model_config[i].cfg,
                                            c.model_config[i].path);
        }

        //加载模型错误的情况下
        if (err) {
            // check for model_collection before failing
            // this is implicit because the `--model` option could take either
            // a model or model_collection
            if (c.model_config[i].version) {
                err = vmaf_model_collection_load(&model[i],
                                        &model_collection[model_collection_cnt],
                                        &c.model_config[i].cfg,
                                        c.model_config[i].version);
            } else {
                err = vmaf_model_collection_load_from_path(&model[i],
                                        &model_collection[model_collection_cnt],
                                        &c.model_config[i].cfg,
                                        c.model_config[i].path);
            }

            if (err) {
                fprintf(stderr, "problem loading model: %sn",
                        c.model_config[i].version ?
                            c.model_config[i].version : c.model_config[i].path);
                return -1;
            }

            model_collection_label[model_collection_cnt] =
                c.model_config[i].version ?
                    c.model_config[i].version : c.model_config[i].path;

            for (unsigned j = 0; j < c.model_config[i].overload_cnt; j++) {
                err = vmaf_model_collection_feature_overload(
                               model[i],
                               &model_collection[model_collection_cnt],
                               c.model_config[i].feature_overload[j].name,
                               c.model_config[i].feature_overload[j].opts_dict);
                if (err) {
                    fprintf(stderr,
                            "problem overloading feature extractors from "
                            "model collection: %sn",
                            c.model_config[i].version ?
                            c.model_config[i].version : c.model_config[i].path);
                    return -1;
                }
            }

            err = vmaf_use_features_from_model_collection(vmaf,
                                        model_collection[model_collection_cnt]);
            if (err) {
                fprintf(stderr,
                        "problem loading feature extractors from "
                        "model collection: %sn",
                        c.model_config[i].version ?
                            c.model_config[i].version : c.model_config[i].path);
                return -1;
            }

            model_collection_cnt++;
            continue;
        }

        //
        for (unsigned j = 0; j < c.model_config[i].overload_cnt; j++) {
            err = vmaf_model_feature_overload(model[i],
                               c.model_config[i].feature_overload[j].name,
                               c.model_config[i].feature_overload[j].opts_dict);
            if (err) {
                fprintf(stderr,
                        "problem overloading feature extractors from "
                        "model: %sn",
                        c.model_config[i].version ?
                            c.model_config[i].version : c.model_config[i].path);
                return -1;

            }
        }

        //使用' vmaf_use_features_from_model() '注册模型所需的所有特征提取器
        err = vmaf_use_features_from_model(vmaf, model[i]);
        if (err) {
            fprintf(stderr,
                    "problem loading feature extractors from model: %sn",
                     c.model_config[i].version ?
                         c.model_config[i].version : c.model_config[i].path);
            return -1;
        }
    }
    

    //如果有辅助指标,使用‘vmaf_use_feature()’直接注册
    for (unsigned i = 0; i < c.feature_cnt; i++) {
        err = vmaf_use_feature(vmaf, c.feature_cfg[i].name,
                               c.feature_cfg[i].opts_dict);
        if (err) {
            fprintf(stderr, "problem loading feature extractor: %sn",
                    c.feature_cfg[i].name);
            return -1;
        }
    }

    //循环读取所有图像
    float fps = 0.;
    const time_t t0 = clock();
    unsigned picture_index;
    for (picture_index = 0 ;; picture_index++) {

        if (c.frame_cnt && picture_index >= c.frame_cnt)
            break;

        //核心就是封装了vmaf_picture_alloc,要分配“VmafPicture”,请使用“vmaf_picture_alloc”。分配之后,可以用像素数据填充缓冲区。
        VmafPicture pic_ref, pic_dist;
        int ret1 = fetch_picture(&vid_ref, &pic_ref);
        int ret2 = fetch_picture(&vid_dist, &pic_dist);

        if (ret1 && ret2) {
            break;
        } else if (ret1 < 0 || ret2 < 0) {
            fprintf(stderr, "nproblem while reading picturesn");
            break;
        } else if (ret1) {
            fprintf(stderr, "n"%s" ended before "%s".n",
                    c.path_ref, c.path_dist);
            break;
        } else if (ret2) {
            fprintf(stderr, "n"%s" ended before "%s".n",
                    c.path_dist, c.path_ref);
            break;
        }

        if (istty && !c.quiet) {
            if (picture_index > 0 && !(picture_index % 10)) {
                fps = (picture_index + 1) /
                      (((float)clock() - t0) / CLOCKS_PER_SEC);
            }

            fprintf(stderr, "r%d frame%s %s %.2f FPS33[K",
                    picture_index + 1, picture_index ? "s" : " ",
                    spinner[picture_index % spinner_length], fps);
            fflush(stderr);
        }

        //使用' vmaf_read_pictures() '循环读取所有输入的图片。当您完成图片读取时,一些特征提取器可能仍然需要刷新内部缓冲区
        err = vmaf_read_pictures(vmaf, &pic_ref, &pic_dist, picture_index);
        if (err) {
            fprintf(stderr, "nproblem reading picturesn");
            break;
        }
    }
    if (istty && !c.quiet)
        fprintf(stderr, "n");

    //一旦缓冲区被刷新,所有对' vmaf_read_pictures() '的进一步调用都无效。
    err |= vmaf_read_pictures(vmaf, NULL, NULL, 0);
    if (err) {
        fprintf(stderr, "problem flushing contextn");
        return err;
    }
    

    //在图片被读取后,您可以检索一个vmaf分数。使用' vmaf_score_at_index '来获取单个索引的分数,并使用' vmaf_score_pooled() '来获得跨多个帧的分数
    if (!c.no_prediction) {
        for (unsigned i = 0; i < c.model_cnt; i++) {
            double vmaf_score;
            err = vmaf_score_pooled(vmaf, model[i], VMAF_POOL_METHOD_MEAN,
                                    &vmaf_score, 0, picture_index - 1);
            if (err) {
                fprintf(stderr, "problem generating pooled VMAF scoren");
                return -1;
            }

            if (istty && (!c.quiet || !c.output_path)) {
                fprintf(stderr, "%s: %fn",
                        c.model_config[i].version ?
                            c.model_config[i].version : c.model_config[i].path,
                        vmaf_score);
            }
        }

        //使用模型集合预测特定指数的VMAF得分
        for (unsigned i = 0; i < model_collection_cnt; i++) {
            VmafModelCollectionScore score = { 0 };
            err = vmaf_score_pooled_model_collection(vmaf, model_collection[i],
                                                     VMAF_POOL_METHOD_MEAN, &score,
                                                     0, picture_index - 1);
            if (err) {
                fprintf(stderr, "problem generating pooled VMAF scoren");
                return -1;
            }

            switch (score.type) {
            case VMAF_MODEL_COLLECTION_SCORE_BOOTSTRAP:
                if (istty && (!c.quiet || !c.output_path)) {
                    fprintf(stderr, "%s: %f, ci.p95: [%f, %f], stddev: %fn",
                            model_collection_label[i],
                            score.bootstrap.bagging_score, score.bootstrap.ci.p95.lo,
                            score.bootstrap.ci.p95.hi,
                            score.bootstrap.stddev);
                }
                break;
            default:
                break;
            }
        }
    }

    //输出结果到指定路径
    if (c.output_path)
        vmaf_write_output(vmaf, c.output_path, c.output_fmt);

    //销毁、释放实例与内存
    for (unsigned i = 0; i < c.model_cnt; i++)
        vmaf_model_destroy(model[i]);
    free(model);

    for (unsigned i = 0; i < model_collection_cnt; i++)
        vmaf_model_collection_destroy(model_collection[i]);
    free(model_collection);

    video_input_close(&vid_ref);
    video_input_close(&vid_dist);
    //清除vmaf
    vmaf_close(vmaf);
    cli_free(&c);
    
    
    return err;
}
使用案例
  1. FFmpeg
  2. Elecard StreamEye
  3. MSU Video Quality Measurement Tool
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1023111.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号