1 /*
2 * Copyright (c) 2011 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
11 #include "testsupport/metrics/video_metrics.h"
12
13 #include <algorithm> // min_element, max_element
14 #include <cassert>
15 #include <cstdio>
16
17 #include "common_video/libyuv/include/libyuv.h"
18
19 namespace webrtc {
20 namespace test {
21
22 // Used for calculating min and max values
LessForFrameResultValue(const FrameResult & s1,const FrameResult & s2)23 static bool LessForFrameResultValue (const FrameResult& s1,
24 const FrameResult& s2) {
25 return s1.value < s2.value;
26 }
27
28 enum VideoMetricsType { kPSNR, kSSIM, kBoth };
29
30 // Calculates metrics for a frame and adds statistics to the result for it.
CalculateFrame(VideoMetricsType video_metrics_type,uint8_t * ref,uint8_t * test,int width,int height,int frame_number,QualityMetricsResult * result)31 void CalculateFrame(VideoMetricsType video_metrics_type,
32 uint8_t* ref,
33 uint8_t* test,
34 int width,
35 int height,
36 int frame_number,
37 QualityMetricsResult* result) {
38 FrameResult frame_result;
39 frame_result.frame_number = frame_number;
40 switch (video_metrics_type) {
41 case kPSNR:
42 frame_result.value = I420PSNR(ref, test, width, height);
43 break;
44 case kSSIM:
45 frame_result.value = I420SSIM(ref, test, width, height);
46 break;
47 default:
48 assert(false);
49 }
50 result->frames.push_back(frame_result);
51 }
52
53 // Calculates average, min and max values for the supplied struct, if non-NULL.
CalculateStats(QualityMetricsResult * result)54 void CalculateStats(QualityMetricsResult* result) {
55 if (result == NULL || result->frames.size() == 0) {
56 return;
57 }
58 // Calculate average
59 std::vector<FrameResult>::iterator iter;
60 double metrics_values_sum = 0.0;
61 for (iter = result->frames.begin(); iter != result->frames.end(); ++iter) {
62 metrics_values_sum += iter->value;
63 }
64 result->average = metrics_values_sum / result->frames.size();
65
66 // Calculate min/max statistics
67 iter = min_element(result->frames.begin(), result->frames.end(),
68 LessForFrameResultValue);
69 result->min = iter->value;
70 result->min_frame_number = iter->frame_number;
71 iter = max_element(result->frames.begin(), result->frames.end(),
72 LessForFrameResultValue);
73 result->max = iter->value;
74 result->max_frame_number = iter->frame_number;
75 }
76
77 // Single method that handles all combinations of video metrics calculation, to
78 // minimize code duplication. Either psnr_result or ssim_result may be NULL,
79 // depending on which VideoMetricsType is targeted.
CalculateMetrics(VideoMetricsType video_metrics_type,const char * ref_filename,const char * test_filename,int width,int height,QualityMetricsResult * psnr_result,QualityMetricsResult * ssim_result)80 int CalculateMetrics(VideoMetricsType video_metrics_type,
81 const char* ref_filename,
82 const char* test_filename,
83 int width,
84 int height,
85 QualityMetricsResult* psnr_result,
86 QualityMetricsResult* ssim_result) {
87 assert(ref_filename != NULL);
88 assert(test_filename != NULL);
89 assert(width > 0);
90 assert(height > 0);
91
92 FILE* ref_fp = fopen(ref_filename, "rb");
93 if (ref_fp == NULL) {
94 // cannot open reference file
95 fprintf(stderr, "Cannot open file %s\n", ref_filename);
96 return -1;
97 }
98 FILE* test_fp = fopen(test_filename, "rb");
99 if (test_fp == NULL) {
100 // cannot open test file
101 fprintf(stderr, "Cannot open file %s\n", test_filename);
102 fclose(ref_fp);
103 return -2;
104 }
105 int frame_number = 0;
106
107 // Allocating size for one I420 frame.
108 const int frame_length = 3 * width * height >> 1;
109 uint8_t* ref = new uint8_t[frame_length];
110 uint8_t* test = new uint8_t[frame_length];
111
112 int ref_bytes = fread(ref, 1, frame_length, ref_fp);
113 int test_bytes = fread(test, 1, frame_length, test_fp);
114 while (ref_bytes == frame_length && test_bytes == frame_length) {
115 switch (video_metrics_type) {
116 case kPSNR:
117 CalculateFrame(kPSNR, ref, test, width, height, frame_number,
118 psnr_result);
119 break;
120 case kSSIM:
121 CalculateFrame(kSSIM, ref, test, width, height, frame_number,
122 ssim_result);
123 break;
124 case kBoth:
125 CalculateFrame(kPSNR, ref, test, width, height, frame_number,
126 psnr_result);
127 CalculateFrame(kSSIM, ref, test, width, height, frame_number,
128 ssim_result);
129 break;
130 default:
131 assert(false);
132 }
133 frame_number++;
134 ref_bytes = fread(ref, 1, frame_length, ref_fp);
135 test_bytes = fread(test, 1, frame_length, test_fp);
136 }
137 int return_code = 0;
138 if (frame_number == 0) {
139 fprintf(stderr, "Tried to measure video metrics from empty files "
140 "(reference file: %s test file: %s)\n", ref_filename,
141 test_filename);
142 return_code = -3;
143 } else {
144 CalculateStats(psnr_result);
145 CalculateStats(ssim_result);
146 }
147 delete [] ref;
148 delete [] test;
149 fclose(ref_fp);
150 fclose(test_fp);
151 return return_code;
152 }
153
I420MetricsFromFiles(const char * ref_filename,const char * test_filename,int width,int height,QualityMetricsResult * psnr_result,QualityMetricsResult * ssim_result)154 int I420MetricsFromFiles(const char* ref_filename,
155 const char* test_filename,
156 int width,
157 int height,
158 QualityMetricsResult* psnr_result,
159 QualityMetricsResult* ssim_result) {
160 assert(psnr_result != NULL);
161 assert(ssim_result != NULL);
162 return CalculateMetrics(kBoth, ref_filename, test_filename, width, height,
163 psnr_result, ssim_result);
164 }
165
I420PSNRFromFiles(const char * ref_filename,const char * test_filename,int width,int height,QualityMetricsResult * result)166 int I420PSNRFromFiles(const char* ref_filename,
167 const char* test_filename,
168 int width,
169 int height,
170 QualityMetricsResult* result) {
171 assert(result != NULL);
172 return CalculateMetrics(kPSNR, ref_filename, test_filename, width, height,
173 result, NULL);
174 }
175
I420SSIMFromFiles(const char * ref_filename,const char * test_filename,int width,int height,QualityMetricsResult * result)176 int I420SSIMFromFiles(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(kSSIM, ref_filename, test_filename, width, height,
183 NULL, result);
184 }
185
186 } // namespace test
187 } // namespace webrtc
188