blob: f537e03925aad80ff4bae7c0578ba75cd8d743cd [file] [log] [blame]
mikhal@webrtc.org552f1732011-08-26 17:38:09 +00001/*
mflodman@webrtc.orgc80d9d92012-02-06 10:11:25 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
mikhal@webrtc.org552f1732011-08-26 17:38:09 +00003 *
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
pbos@webrtc.org34741c82013-05-27 08:02:22 +000011#include "webrtc/test/testsupport/metrics/video_metrics.h"
kjellander@webrtc.org82d91ae2011-12-05 13:03:38 +000012
pbos@webrtc.org12dc1a32013-08-05 16:22:53 +000013#include <assert.h>
14#include <stdio.h>
15
kjellander@webrtc.orgb2d74972013-01-26 16:36:40 +000016#include <algorithm> // min_element, max_element
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000017
pbos@webrtc.org34741c82013-05-27 08:02:22 +000018#include "webrtc/common_video/interface/i420_video_frame.h"
19#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000020
21namespace webrtc {
22namespace test {
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000023
phoglund@webrtc.org273ccad2012-11-29 10:08:16 +000024// Copy here so our callers won't need to include libyuv for this constant.
phoglund@webrtc.org5b689ef2012-12-13 10:15:06 +000025double kMetricsPerfectPSNR = kPerfectPSNR;
phoglund@webrtc.org273ccad2012-11-29 10:08:16 +000026
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000027// Used for calculating min and max values.
kjellander@webrtc.orgb2d74972013-01-26 16:36:40 +000028static bool LessForFrameResultValue(const FrameResult& s1,
29 const FrameResult& s2) {
30 return s1.value < s2.value;
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000031}
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000032
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000033enum VideoMetricsType { kPSNR, kSSIM, kBoth };
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000034
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000035// Calculates metrics for a frame and adds statistics to the result for it.
36void CalculateFrame(VideoMetricsType video_metrics_type,
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000037 const I420VideoFrame* ref,
38 const I420VideoFrame* test,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000039 int frame_number,
40 QualityMetricsResult* result) {
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000041 FrameResult frame_result = {0, 0};
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000042 frame_result.frame_number = frame_number;
43 switch (video_metrics_type) {
44 case kPSNR:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000045 frame_result.value = I420PSNR(ref, test);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000046 break;
47 case kSSIM:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000048 frame_result.value = I420SSIM(ref, test);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000049 break;
50 default:
51 assert(false);
52 }
53 result->frames.push_back(frame_result);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000054}
55
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000056// Calculates average, min and max values for the supplied struct, if non-NULL.
57void CalculateStats(QualityMetricsResult* result) {
58 if (result == NULL || result->frames.size() == 0) {
59 return;
60 }
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000061 // Calculate average.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000062 std::vector<FrameResult>::iterator iter;
63 double metrics_values_sum = 0.0;
64 for (iter = result->frames.begin(); iter != result->frames.end(); ++iter) {
65 metrics_values_sum += iter->value;
66 }
67 result->average = metrics_values_sum / result->frames.size();
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000068
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000069 // Calculate min/max statistics.
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000070 iter = std::min_element(result->frames.begin(), result->frames.end(),
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000071 LessForFrameResultValue);
72 result->min = iter->value;
73 result->min_frame_number = iter->frame_number;
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000074 iter = std::max_element(result->frames.begin(), result->frames.end(),
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000075 LessForFrameResultValue);
76 result->max = iter->value;
77 result->max_frame_number = iter->frame_number;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000078}
79
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000080// Single method that handles all combinations of video metrics calculation, to
81// minimize code duplication. Either psnr_result or ssim_result may be NULL,
82// depending on which VideoMetricsType is targeted.
83int CalculateMetrics(VideoMetricsType video_metrics_type,
84 const char* ref_filename,
85 const char* test_filename,
86 int width,
87 int height,
88 QualityMetricsResult* psnr_result,
89 QualityMetricsResult* ssim_result) {
90 assert(ref_filename != NULL);
91 assert(test_filename != NULL);
92 assert(width > 0);
93 assert(height > 0);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000094
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000095 FILE* ref_fp = fopen(ref_filename, "rb");
96 if (ref_fp == NULL) {
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000097 // Cannot open reference file.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000098 fprintf(stderr, "Cannot open file %s\n", ref_filename);
99 return -1;
100 }
101 FILE* test_fp = fopen(test_filename, "rb");
102 if (test_fp == NULL) {
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000103 // Cannot open test file.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000104 fprintf(stderr, "Cannot open file %s\n", test_filename);
105 fclose(ref_fp);
106 return -2;
107 }
108 int frame_number = 0;
109
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000110 // Read reference and test frames.
kjellander@webrtc.orgb2d74972013-01-26 16:36:40 +0000111 const size_t frame_length = 3 * width * height >> 1;
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000112 I420VideoFrame ref_frame;
113 I420VideoFrame test_frame;
114 scoped_array<uint8_t> ref_buffer(new uint8_t[frame_length]);
115 scoped_array<uint8_t> test_buffer(new uint8_t[frame_length]);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000116
mikhal@webrtc.org658d4232013-01-08 19:19:59 +0000117 // Set decoded image parameters.
118 int half_width = (width + 1) / 2;
119 ref_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
120 test_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000121
kjellander@webrtc.orgb2d74972013-01-26 16:36:40 +0000122 size_t ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
123 size_t test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000124 while (ref_bytes == frame_length && test_bytes == frame_length) {
mikhal@webrtc.org658d4232013-01-08 19:19:59 +0000125 // Converting from buffer to plane representation.
126 ConvertToI420(kI420, ref_buffer.get(), 0, 0, width, height, 0,
127 kRotateNone, &ref_frame);
128 ConvertToI420(kI420, test_buffer.get(), 0, 0, width, height, 0,
129 kRotateNone, &test_frame);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000130 switch (video_metrics_type) {
131 case kPSNR:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000132 CalculateFrame(kPSNR, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000133 psnr_result);
134 break;
135 case kSSIM:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000136 CalculateFrame(kSSIM, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000137 ssim_result);
138 break;
139 case kBoth:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000140 CalculateFrame(kPSNR, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000141 psnr_result);
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000142 CalculateFrame(kSSIM, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000143 ssim_result);
144 break;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000145 }
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000146 frame_number++;
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000147 ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
148 test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000149 }
150 int return_code = 0;
151 if (frame_number == 0) {
152 fprintf(stderr, "Tried to measure video metrics from empty files "
153 "(reference file: %s test file: %s)\n", ref_filename,
154 test_filename);
155 return_code = -3;
156 } else {
157 CalculateStats(psnr_result);
158 CalculateStats(ssim_result);
159 }
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000160 fclose(ref_fp);
161 fclose(test_fp);
162 return return_code;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000163}
164
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000165int I420MetricsFromFiles(const char* ref_filename,
166 const char* test_filename,
167 int width,
168 int height,
169 QualityMetricsResult* psnr_result,
170 QualityMetricsResult* ssim_result) {
171 assert(psnr_result != NULL);
172 assert(ssim_result != NULL);
173 return CalculateMetrics(kBoth, ref_filename, test_filename, width, height,
174 psnr_result, ssim_result);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000175}
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000176
177int I420PSNRFromFiles(const char* ref_filename,
178 const char* test_filename,
179 int width,
180 int height,
181 QualityMetricsResult* result) {
182 assert(result != NULL);
183 return CalculateMetrics(kPSNR, ref_filename, test_filename, width, height,
184 result, NULL);
185}
186
187int I420SSIMFromFiles(const char* ref_filename,
188 const char* test_filename,
189 int width,
190 int height,
191 QualityMetricsResult* result) {
192 assert(result != NULL);
193 return CalculateMetrics(kSSIM, ref_filename, test_filename, width, height,
194 NULL, result);
195}
196
197} // namespace test
198} // namespace webrtc