首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Matlab中的视频无参考图像质量评估

Matlab中的视频无参考图像质量评估
EN

Code Review用户
提问于 2022-05-28 19:36:17
回答 2查看 155关注 0票数 2

我试图在视频上进行无参考图像质量评估(NR-IQA)的计算.采用自然图像质量评价器基于感知的图像质量评价器(PIQE)盲/无参考图像空间质量评价器(布里斯库)等多种方法,将计算结果显示在视频帧上,并导出到一个Excel文件中。采用BigBuckBunny文件作为实验视频的输入。

实验实现

代码语言:javascript
复制
%% Read and process a video into MATLAB
%   Reference: https://www.mathworks.com/solutions/image-video-processing/video-processing.html

tic
% Setup: create Video Reader and Writer
videoFilename = ['BigBuckBunny_320x180'];
videoPath = ['.'];

videoFullFilename = fullfile(videoPath, [videoFilename '.mp4']);
videoFileReader = VideoReader(videoFullFilename);
% Reference: https://www.mathworks.com/matlabcentral/answers/123189-get-the-number-of-frame-of-a-video-using-vision-videofilereader-and-videoreader
TotalFrameCount = floor(videoFileReader.Duration * videoFileReader.FrameRate);

% Setup: create output folder
OutputFolderRoot = 'NIQE_PIQE_BRISQUE';
if ~exist(OutputFolderRoot, 'dir')
    mkdir(OutputFolderRoot);
end
outputFilename = fullfile(OutputFolderRoot, [videoFilename '.avi']);
outputVideo = VideoWriter(outputFilename);

% Setup: create data record variables
% Reference: https://www.mathworks.com/help/matlab/ref/round.html
NIQE_results = zeros(1, round(TotalFrameCount, 0));
PIQE_results = zeros(1, round(TotalFrameCount, 0));
BRISQUE_results = zeros(1, round(TotalFrameCount, 0));

FrameIndex = 1;

% Setup: create deployable video player
depVideoPlayer = vision.DeployableVideoPlayer;

open(outputVideo);

%% Detect faces in each frame
while hasFrame(videoFileReader)
    % read video frame
    videoFrame = readFrame(videoFileReader);

    [videoFrame, NIQE_results, PIQE_results, BRISQUE_results] = processEachFrame(videoFrame, FrameIndex, NIQE_results, PIQE_results, BRISQUE_results);

    % Display video frame to screen
    depVideoPlayer(videoFrame);

    % Write frame to final video file
    writeVideo(outputVideo, videoFrame);
    
    % Show progress
    fprintf('The calculation of frame %d is finished!\n', FrameIndex);
    
    FrameIndex = FrameIndex + 1;
end
close(outputVideo);
depVideoPlayer.delete();
ElapsedTime = toc;

% Write calculated results
% Reference: https://www.mathworks.com/help/matlab/ref/fprintf.html
fileID = fopen(fullfile(OutputFolderRoot, [videoFilename '_ElapsedTime.txt']),'w');
nbytes = fprintf(fileID,'Elapsed time is %f seconds.\n', ElapsedTime);

output_filename = fullfile(OutputFolderRoot, [videoFilename '_Results.xlsx']);
% Write title
output_title = [{'Frame Index'} {'NIQE'} {'PIQE'} {'BRISQUE'}];
writecell(output_title, output_filename, 'Sheet', 1, 'Range', 'A1');
index_column = 1:TotalFrameCount;
output_data = [index_column' NIQE_results' PIQE_results' BRISQUE_results'];
writematrix(output_data, output_filename, 'Sheet', 1, 'Range', 'A2');

function [output, NIQE_results, PIQE_results, BRISQUE_results] = processEachFrame(input, FrameIndex, NIQE_results, PIQE_results, BRISQUE_results)
    output = input;
    NIQE_score = niqe(input);
    PIQE_score = piqe(input);
    BRISQUE_score = brisque(input);
    NIQE_position = [10 10];
    PIQE_position = [10 40];
    BRISQUE_position = [10 70];
    output = insertText(output, NIQE_position, ['NIQE_score: ' num2str(NIQE_score)], 'BoxOpacity', 0.6);
    output = insertText(output, PIQE_position, ['PIQE_score: ' num2str(PIQE_score)], 'BoxOpacity', 0.6);
    output = insertText(output, BRISQUE_position, ['BRISQUE_score: ' num2str(BRISQUE_score)], 'BoxOpacity', 0.6);
    NIQE_results(FrameIndex) = NIQE_score;
    PIQE_results(FrameIndex) = PIQE_score;
    BRISQUE_results(FrameIndex) = BRISQUE_score;
end

欢迎所有建议。

参考资料:

  1. 大巴克兔:https://peach.blender.org/
EN

回答 2

Code Review用户

回答已采纳

发布于 2022-05-30 07:12:28

MATLAB编辑器警告

在这两行中,MATLAB编辑器发出警告:“使用括号[]是不必要的”。这是因为他们没有连接任何东西,每对只有一件事。

代码语言:javascript
复制
videoFilename = ['BigBuckBunny_320x180'];
videoPath = ['.'];

代写

代码语言:javascript
复制
videoFilename = 'BigBuckBunny_320x180';
videoPath = '.';

冗余舍入

TotalFrameCount被计算为floor(...),然后在使用之前通过round,这显然不起任何作用:

代码语言:javascript
复制
TotalFrameCount = floor(videoFileReader.Duration * videoFileReader.FrameRate);
% ...
NIQE_results = zeros(1, round(TotalFrameCount, 0));
PIQE_results = zeros(1, round(TotalFrameCount, 0));
BRISQUE_results = zeros(1, round(TotalFrameCount, 0));

考虑使用现代字符串数组

如果您不需要在Octave上运行代码,则应该考虑使用字符串而不是char向量。有些事情会变得容易一些。例如,您将编写[videoFilename '_ElapsedTime.txt']而不是videoFilename + "_ElapsedTime.txt",而不是['NIQE_score: ' num2str(NIQE_score)]您将编写"NIQE_score: " + NIQE_score

细胞阵列构建

而不是[{'Frame Index'} {'NIQE'} {'PIQE'} {'BRISQUE'}],写{'Frame Index', 'NIQE', 'PIQE', 'BRISQUE'} (逗号不是必需的,但我总是喜欢将它们显式化)。或者,如果使用字符串数组,则编写["Frame Index", "NIQE", "PIQE", "BRISQUE"]

转置与复共轭转置

'算子应用复共轭转置.如果应用到的数组是实值的,那么它只是一个转置,但是最好显式地使用transpose操作符.'。习惯于选择正确的错误最终会防止一些很难发现的错误(在堆栈溢出中可以找到其中的一些)。

硬编码分数名

您有包含您计算的三个分数的名称的变量名。我会考虑这些名字数据,而不是代码。要改变这一点,您可以简化一些代码。例如,而不是

代码语言:javascript
复制
NIQE_results = zeros(1, round(TotalFrameCount, 0));
PIQE_results = zeros(1, round(TotalFrameCount, 0));
BRISQUE_results = zeros(1, round(TotalFrameCount, 0));

你可以

代码语言:javascript
复制
results = zeros(TotalFrameCount, ScoreCount);

这也简化了processEachFrame(),您不需要将这三个数组作为输入和输出传递,它只是一个数组。

稍后,您将它们连接在一起:

代码语言:javascript
复制
output_data = [index_column' NIQE_results' PIQE_results' BRISQUE_results'];

这会变成

代码语言:javascript
复制
output_data = [index_column' results];

(注意,我以正确的方向创建了results数组,以避免转置)。

您还将FrameIndex传递到processEachFrame()中,这样就可以将结果写入三个结果数组的正确位置。相反,只让函数返回值,并让调用代码将这些值写入正确的位置:

代码语言:javascript
复制
[videoFrame, results(FrameIndex,:)] = processEachFrame(videoFrame);

避免重复代码,使用循环代替

如果将分数计算为数据而不是代码,则可以更容易地避免重复的代码,而使用循环。您的函数processEachFrame()可以是这样的:

代码语言:javascript
复制
function [frame, results] = processEachFrame(frame)
    scores = struct('name', {'NIQE', 'PIQE', 'BRISQUE'}, ...
                    'function', {@niqe, @piqe, @brisque}, ...
                    'position', {[10 10], [10 40], [10 70]});
    n_scores = numel(scores);
    results = zeros(1, n_scores);
    for ii = 1:n_scores
        results(ii) = scores(ii).function(frame);
        frame = insertText(frame, scores(ii).position, [scores(ii).name, '_score: ', num2str(results(ii))], 'BoxOpacity', 0.6);
    end
end

这样不仅更短、更简单,而且以后添加分数也很容易,根本不需要修改代码。scores可能应该定义在脚本的顶部,函数之外。

请注意,如果我们将输入和输出变量命名为相同的(此处为frame),并且在调用函数时,我们使用与输入和输出相同的变量(代码中的videoFrame),那么MATLAB将优化而不是重复数组。见洛伦的博客文章

进度

显示

我可以想象,为处理的每一帧打印一行文本将很快淹没命令窗口中几乎相同的文本行。您可以尝试在文件交换中张贴的许多进度条中的一个。例如,我刚刚在命令窗口中找到了可以工作的这一个 (我自己还没有试过)。但是,即使只是打印帧号(没有换行符)也是一种简单方便的方法。

注释

在大多数情况下,您有一些有用的注释,指示代码的每个部分都做了什么。这使得在无需阅读整个脚本的情况下很容易找到相关代码。然而,有一条评论似乎是从不同的代码中复制粘贴的,并且描述了一些您不做的事情:

代码语言:javascript
复制
%% Detect faces in each frame

错误的评论比没有评论更有害。

变量名

您的变量名是明确的和描述性的,并且您不害怕长名称。这很好。

我建议你选择一种风格(snake_case,CamelCase或lowerCamelCase)。混合所有的样式使它看起来像代码是由来自多个来源的复制粘贴组成的。

票数 1
EN

Code Review用户

发布于 2022-06-14 08:35:51

帧率

问题

在本实验实现中,outputVideo的帧速率部分可能不同于输入视频。在检查.文件VideoWriter之后,有一个参数FrameRate,它的默认值是30。设置此参数使outputVideo的帧速率与输入视频匹配可能更好,方法如下。

代码语言:javascript
复制
outputVideo.FrameRate = videoFileReader.FrameRate;

注意:此设置应放在open操作之前。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/276918

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档