blob: f790604b16e25b4492cc36828e66be26e1d5d779 [file] [log] [blame]
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "rtc_tools/frame_analyzer/video_quality_analysis.h"
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +000012
mandermo74568172017-01-17 03:24:57 -080013#include <algorithm>
Magnus Jedvertb468ace2018-09-05 16:11:48 +020014#include <numeric>
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +000015
Magnus Jedvert3100fc12018-10-16 15:09:22 +020016#include "rtc_base/logging.h"
17#include "rtc_base/strings/string_builder.h"
18#include "rtc_tools/frame_analyzer/video_color_aligner.h"
19#include "rtc_tools/frame_analyzer/video_temporal_aligner.h"
Edward Lemur2e5966b2018-01-30 15:33:02 +010020#include "test/testsupport/perf_test.h"
Magnus Jedvert10e829a2018-09-05 10:46:18 +020021#include "third_party/libyuv/include/libyuv/compare.h"
22#include "third_party/libyuv/include/libyuv/convert.h"
Sami Kalliomäki0673bc92018-08-27 17:58:13 +020023
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +000024namespace webrtc {
25namespace test {
26
Henrik Kjellander67bcb602015-10-07 08:42:52 +020027ResultsContainer::ResultsContainer() {}
28ResultsContainer::~ResultsContainer() {}
29
Magnus Jedvert10e829a2018-09-05 10:46:18 +020030template <typename FrameMetricFunction>
31static double CalculateMetric(
32 const FrameMetricFunction& frame_metric_function,
33 const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
34 const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
35 RTC_CHECK_EQ(ref_buffer->width(), test_buffer->width());
36 RTC_CHECK_EQ(ref_buffer->height(), test_buffer->height());
37 return frame_metric_function(
38 ref_buffer->DataY(), ref_buffer->StrideY(), ref_buffer->DataU(),
39 ref_buffer->StrideU(), ref_buffer->DataV(), ref_buffer->StrideV(),
40 test_buffer->DataY(), test_buffer->StrideY(), test_buffer->DataU(),
41 test_buffer->StrideU(), test_buffer->DataV(), test_buffer->StrideV(),
42 test_buffer->width(), test_buffer->height());
Sami Kalliomäki0673bc92018-08-27 17:58:13 +020043}
44
Magnus Jedvert10e829a2018-09-05 10:46:18 +020045double Psnr(const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
46 const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
47 // LibYuv sets the max psnr value to 128, we restrict it to 48.
48 // In case of 0 mse in one frame, 128 can skew the results significantly.
49 return std::min(48.0,
50 CalculateMetric(&libyuv::I420Psnr, ref_buffer, test_buffer));
Sami Kalliomäki0673bc92018-08-27 17:58:13 +020051}
52
Magnus Jedvert10e829a2018-09-05 10:46:18 +020053double Ssim(const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
54 const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
55 return CalculateMetric(&libyuv::I420Ssim, ref_buffer, test_buffer);
Sami Kalliomäki0673bc92018-08-27 17:58:13 +020056}
57
Magnus Jedvertb468ace2018-09-05 16:11:48 +020058std::vector<AnalysisResult> RunAnalysis(
59 const rtc::scoped_refptr<webrtc::test::Video>& reference_video,
60 const rtc::scoped_refptr<webrtc::test::Video>& test_video,
61 const std::vector<size_t>& test_frame_indices) {
Magnus Jedvert3100fc12018-10-16 15:09:22 +020062 const rtc::scoped_refptr<Video> temporally_aligned_reference_video =
63 ReorderVideo(reference_video, test_frame_indices);
Magnus Jedvert3e169ac2018-08-24 12:44:59 +000064
Magnus Jedvert3100fc12018-10-16 15:09:22 +020065 const ColorTransformationMatrix color_transformation =
66 CalculateColorTransformationMatrix(temporally_aligned_reference_video,
67 test_video);
68
69 char buf[256];
70 rtc::SimpleStringBuilder string_builder(buf);
71 for (int i = 0; i < 3; ++i) {
72 string_builder << "\n";
73 for (int j = 0; j < 4; ++j)
74 string_builder.AppendFormat("%6.2f ", color_transformation[i][j]);
75 }
76 RTC_LOG(LS_INFO) << "Adjusting test video with color transformation: "
77 << string_builder.str();
78
79 const rtc::scoped_refptr<Video> color_adjusted_test_video =
80 AdjustColors(color_transformation, test_video);
81
82 std::vector<AnalysisResult> results;
83 for (size_t i = 0; i < color_adjusted_test_video->number_of_frames(); ++i) {
Magnus Jedvertb468ace2018-09-05 16:11:48 +020084 const rtc::scoped_refptr<I420BufferInterface>& test_frame =
Magnus Jedvert3100fc12018-10-16 15:09:22 +020085 color_adjusted_test_video->GetFrame(i);
Magnus Jedvertb468ace2018-09-05 16:11:48 +020086 const rtc::scoped_refptr<I420BufferInterface>& reference_frame =
Magnus Jedvert3100fc12018-10-16 15:09:22 +020087 temporally_aligned_reference_video->GetFrame(i);
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +000088
89 // Fill in the result struct.
90 AnalysisResult result;
Magnus Jedvertb468ace2018-09-05 16:11:48 +020091 result.frame_number = test_frame_indices[i];
92 result.psnr_value = Psnr(reference_frame, test_frame);
93 result.ssim_value = Ssim(reference_frame, test_frame);
94 results.push_back(result);
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +000095 }
96
Magnus Jedvertb468ace2018-09-05 16:11:48 +020097 return results;
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +000098}
99
Magnus Jedvertb468ace2018-09-05 16:11:48 +0200100std::vector<Cluster> CalculateFrameClusters(
101 const std::vector<size_t>& indices) {
102 std::vector<Cluster> clusters;
103
104 for (size_t index : indices) {
105 if (!clusters.empty() && clusters.back().index == index) {
106 // This frame belongs to the previous cluster.
107 ++clusters.back().number_of_repeated_frames;
Sami Kalliomäki0673bc92018-08-27 17:58:13 +0200108 } else {
Magnus Jedvertb468ace2018-09-05 16:11:48 +0200109 // Start a new cluster.
110 clusters.push_back({index, /* number_of_repeated_frames= */ 1});
Sami Kalliomäki0673bc92018-08-27 17:58:13 +0200111 }
mandermo74568172017-01-17 03:24:57 -0800112 }
Magnus Jedvertb468ace2018-09-05 16:11:48 +0200113
114 return clusters;
Magnus Jedvert9bb55fc2018-08-24 14:56:03 +0200115}
116
Magnus Jedvertb468ace2018-09-05 16:11:48 +0200117int GetMaxRepeatedFrames(const std::vector<Cluster>& clusters) {
118 int max_number_of_repeated_frames = 0;
119 for (const Cluster& cluster : clusters) {
120 max_number_of_repeated_frames = std::max(max_number_of_repeated_frames,
121 cluster.number_of_repeated_frames);
Magnus Jedvert3e169ac2018-08-24 12:44:59 +0000122 }
Magnus Jedvertb468ace2018-09-05 16:11:48 +0200123 return max_number_of_repeated_frames;
124}
125
126int GetMaxSkippedFrames(const std::vector<Cluster>& clusters) {
127 size_t max_skipped_frames = 0;
128 for (size_t i = 1; i < clusters.size(); ++i) {
129 const size_t skipped_frames = clusters[i].index - clusters[i - 1].index - 1;
130 max_skipped_frames = std::max(max_skipped_frames, skipped_frames);
Sami Kalliomäki0673bc92018-08-27 17:58:13 +0200131 }
Magnus Jedvertb468ace2018-09-05 16:11:48 +0200132 return static_cast<int>(max_skipped_frames);
133}
mandermo74568172017-01-17 03:24:57 -0800134
Magnus Jedvertb468ace2018-09-05 16:11:48 +0200135int GetTotalNumberOfSkippedFrames(const std::vector<Cluster>& clusters) {
136 // The number of reference frames the test video spans.
137 const size_t number_ref_frames =
138 clusters.empty() ? 0 : 1 + clusters.back().index - clusters.front().index;
139 return static_cast<int>(number_ref_frames - clusters.size());
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +0000140}
141
kjellander@webrtc.orgf880f862013-09-10 12:10:01 +0000142void PrintAnalysisResults(const std::string& label, ResultsContainer* results) {
kjellander@webrtc.orge2df8b72013-11-03 18:34:51 +0000143 PrintAnalysisResults(stdout, label, results);
144}
145
Yves Gerey665174f2018-06-19 15:03:05 +0200146void PrintAnalysisResults(FILE* output,
147 const std::string& label,
kjellander@webrtc.orge2df8b72013-11-03 18:34:51 +0000148 ResultsContainer* results) {
Edward Lemur2e5966b2018-01-30 15:33:02 +0100149 SetPerfResultsOutput(output);
kjellander@webrtc.orgf880f862013-09-10 12:10:01 +0000150
151 if (results->frames.size() > 0u) {
Edward Lemur2e5966b2018-01-30 15:33:02 +0100152 PrintResult("Unique_frames_count", "", label, results->frames.size(),
153 "score", false);
kjellander@webrtc.orgf880f862013-09-10 12:10:01 +0000154
Edward Lemur2e5966b2018-01-30 15:33:02 +0100155 std::vector<double> psnr_values;
156 std::vector<double> ssim_values;
157 for (const auto& frame : results->frames) {
158 psnr_values.push_back(frame.psnr_value);
159 ssim_values.push_back(frame.ssim_value);
kjellander@webrtc.orgf880f862013-09-10 12:10:01 +0000160 }
Edward Lemur2e5966b2018-01-30 15:33:02 +0100161
162 PrintResultList("PSNR", "", label, psnr_values, "dB", false);
163 PrintResultList("SSIM", "", label, ssim_values, "score", false);
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +0000164 }
Edward Lemur2e5966b2018-01-30 15:33:02 +0100165
166 PrintResult("Max_repeated", "", label, results->max_repeated_frames, "",
167 false);
168 PrintResult("Max_skipped", "", label, results->max_skipped_frames, "", false);
169 PrintResult("Total_skipped", "", label, results->total_skipped_frames, "",
170 false);
171 PrintResult("Decode_errors_reference", "", label, results->decode_errors_ref,
172 "", false);
173 PrintResult("Decode_errors_test", "", label, results->decode_errors_test, "",
174 false);
vspasova@webrtc.orgfd800702012-08-16 14:07:02 +0000175}
176
177} // namespace test
178} // namespace webrtc