blob: edf898cbca19b163921f21eadd8c3d5a69ced5c1 [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
kjellander@webrtc.org5b97b122011-12-08 07:42:18 +000011#include "testsupport/metrics/video_metrics.h"
kjellander@webrtc.org82d91ae2011-12-05 13:03:38 +000012
kjellander@webrtc.orgb2d74972013-01-26 16:36:40 +000013#include <algorithm> // min_element, max_element
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000014#include <cassert>
15#include <cstdio>
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000016
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000017#include "common_video/interface/i420_video_frame.h"
andrew@webrtc.orgc1354bd2012-07-27 18:21:16 +000018#include "common_video/libyuv/include/webrtc_libyuv.h"
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000019
20namespace webrtc {
21namespace test {
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000022
phoglund@webrtc.org273ccad2012-11-29 10:08:16 +000023// Copy here so our callers won't need to include libyuv for this constant.
phoglund@webrtc.org5b689ef2012-12-13 10:15:06 +000024double kMetricsPerfectPSNR = kPerfectPSNR;
phoglund@webrtc.org273ccad2012-11-29 10:08:16 +000025
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000026// Used for calculating min and max values.
kjellander@webrtc.orgb2d74972013-01-26 16:36:40 +000027static bool LessForFrameResultValue(const FrameResult& s1,
28 const FrameResult& s2) {
29 return s1.value < s2.value;
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000030}
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000031
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000032enum VideoMetricsType { kPSNR, kSSIM, kBoth };
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000033
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000034// Calculates metrics for a frame and adds statistics to the result for it.
35void CalculateFrame(VideoMetricsType video_metrics_type,
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000036 const I420VideoFrame* ref,
37 const I420VideoFrame* test,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000038 int frame_number,
39 QualityMetricsResult* result) {
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000040 FrameResult frame_result = {0, 0};
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000041 frame_result.frame_number = frame_number;
42 switch (video_metrics_type) {
43 case kPSNR:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000044 frame_result.value = I420PSNR(ref, test);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000045 break;
46 case kSSIM:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000047 frame_result.value = I420SSIM(ref, test);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000048 break;
49 default:
50 assert(false);
51 }
52 result->frames.push_back(frame_result);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000053}
54
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000055// Calculates average, min and max values for the supplied struct, if non-NULL.
56void CalculateStats(QualityMetricsResult* result) {
57 if (result == NULL || result->frames.size() == 0) {
58 return;
59 }
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000060 // Calculate average.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000061 std::vector<FrameResult>::iterator iter;
62 double metrics_values_sum = 0.0;
63 for (iter = result->frames.begin(); iter != result->frames.end(); ++iter) {
64 metrics_values_sum += iter->value;
65 }
66 result->average = metrics_values_sum / result->frames.size();
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000067
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000068 // Calculate min/max statistics.
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000069 iter = std::min_element(result->frames.begin(), result->frames.end(),
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000070 LessForFrameResultValue);
71 result->min = iter->value;
72 result->min_frame_number = iter->frame_number;
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000073 iter = std::max_element(result->frames.begin(), result->frames.end(),
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000074 LessForFrameResultValue);
75 result->max = iter->value;
76 result->max_frame_number = iter->frame_number;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000077}
78
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000079// Single method that handles all combinations of video metrics calculation, to
80// minimize code duplication. Either psnr_result or ssim_result may be NULL,
81// depending on which VideoMetricsType is targeted.
82int CalculateMetrics(VideoMetricsType video_metrics_type,
83 const char* ref_filename,
84 const char* test_filename,
85 int width,
86 int height,
87 QualityMetricsResult* psnr_result,
88 QualityMetricsResult* ssim_result) {
89 assert(ref_filename != NULL);
90 assert(test_filename != NULL);
91 assert(width > 0);
92 assert(height > 0);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000093
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000094 FILE* ref_fp = fopen(ref_filename, "rb");
95 if (ref_fp == NULL) {
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000096 // Cannot open reference file.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000097 fprintf(stderr, "Cannot open file %s\n", ref_filename);
98 return -1;
99 }
100 FILE* test_fp = fopen(test_filename, "rb");
101 if (test_fp == NULL) {
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000102 // Cannot open test file.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000103 fprintf(stderr, "Cannot open file %s\n", test_filename);
104 fclose(ref_fp);
105 return -2;
106 }
107 int frame_number = 0;
108
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000109 // Read reference and test frames.
kjellander@webrtc.orgb2d74972013-01-26 16:36:40 +0000110 const size_t frame_length = 3 * width * height >> 1;
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000111 I420VideoFrame ref_frame;
112 I420VideoFrame test_frame;
113 scoped_array<uint8_t> ref_buffer(new uint8_t[frame_length]);
114 scoped_array<uint8_t> test_buffer(new uint8_t[frame_length]);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000115
mikhal@webrtc.org658d4232013-01-08 19:19:59 +0000116 // Set decoded image parameters.
117 int half_width = (width + 1) / 2;
118 ref_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
119 test_frame.CreateEmptyFrame(width, height, width, half_width, half_width);
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000120
kjellander@webrtc.orgb2d74972013-01-26 16:36:40 +0000121 size_t ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
122 size_t test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000123 while (ref_bytes == frame_length && test_bytes == frame_length) {
mikhal@webrtc.org658d4232013-01-08 19:19:59 +0000124 // Converting from buffer to plane representation.
125 ConvertToI420(kI420, ref_buffer.get(), 0, 0, width, height, 0,
126 kRotateNone, &ref_frame);
127 ConvertToI420(kI420, test_buffer.get(), 0, 0, width, height, 0,
128 kRotateNone, &test_frame);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000129 switch (video_metrics_type) {
130 case kPSNR:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000131 CalculateFrame(kPSNR, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000132 psnr_result);
133 break;
134 case kSSIM:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000135 CalculateFrame(kSSIM, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000136 ssim_result);
137 break;
138 case kBoth:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000139 CalculateFrame(kPSNR, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000140 psnr_result);
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000141 CalculateFrame(kSSIM, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000142 ssim_result);
143 break;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000144 }
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000145 frame_number++;
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000146 ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
147 test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000148 }
149 int return_code = 0;
150 if (frame_number == 0) {
151 fprintf(stderr, "Tried to measure video metrics from empty files "
152 "(reference file: %s test file: %s)\n", ref_filename,
153 test_filename);
154 return_code = -3;
155 } else {
156 CalculateStats(psnr_result);
157 CalculateStats(ssim_result);
158 }
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000159 fclose(ref_fp);
160 fclose(test_fp);
161 return return_code;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000162}
163
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000164int I420MetricsFromFiles(const char* ref_filename,
165 const char* test_filename,
166 int width,
167 int height,
168 QualityMetricsResult* psnr_result,
169 QualityMetricsResult* ssim_result) {
170 assert(psnr_result != NULL);
171 assert(ssim_result != NULL);
172 return CalculateMetrics(kBoth, ref_filename, test_filename, width, height,
173 psnr_result, ssim_result);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000174}
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000175
176int I420PSNRFromFiles(const char* ref_filename,
177 const char* test_filename,
178 int width,
179 int height,
180 QualityMetricsResult* result) {
181 assert(result != NULL);
182 return CalculateMetrics(kPSNR, ref_filename, test_filename, width, height,
183 result, NULL);
184}
185
186int I420SSIMFromFiles(const char* ref_filename,
187 const char* test_filename,
188 int width,
189 int height,
190 QualityMetricsResult* result) {
191 assert(result != NULL);
192 return CalculateMetrics(kSSIM, ref_filename, test_filename, width, height,
193 NULL, result);
194}
195
196} // namespace test
197} // namespace webrtc