blob: c9d6d9dcc589870b458326a8e951b866b99e9c41 [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.orgf0a84642011-09-12 13:45:39 +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.
andrew@webrtc.orgd8aeb302012-12-12 20:58:32 +000024double kMetricsInfinitePSNR = kInfinitePSNR;
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.org82d91ae2011-12-05 13:03:38 +000027static bool LessForFrameResultValue (const FrameResult& s1,
28 const FrameResult& s2) {
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000029 return s1.value < s2.value;
30}
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.orgcc337372012-01-04 08:09:32 +0000110 const int 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
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000116 int size_y = width * height;
117 int size_uv = ((width + 1 ) / 2) * ((height + 1) / 2);
118
119 int ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
120 int test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000121 while (ref_bytes == frame_length && test_bytes == frame_length) {
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000122 ref_frame.CreateFrame(size_y, ref_buffer.get(),
123 size_uv, ref_buffer.get() + size_y,
124 size_uv, ref_buffer.get() + size_y + size_uv,
125 width, height,
126 width, (width + 1) / 2, (width + 1) / 2);
127 test_frame.CreateFrame(size_y, test_buffer.get(),
128 size_uv, test_buffer.get() + size_y,
129 size_uv, test_buffer.get() + size_y + size_uv,
130 width, height,
131 width, (width + 1) / 2, (width + 1) / 2);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000132 switch (video_metrics_type) {
133 case kPSNR:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000134 CalculateFrame(kPSNR, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000135 psnr_result);
136 break;
137 case kSSIM:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000138 CalculateFrame(kSSIM, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000139 ssim_result);
140 break;
141 case kBoth:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000142 CalculateFrame(kPSNR, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000143 psnr_result);
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000144 CalculateFrame(kSSIM, &ref_frame, &test_frame, frame_number,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000145 ssim_result);
146 break;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000147 }
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000148 frame_number++;
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000149 ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
150 test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000151 }
152 int return_code = 0;
153 if (frame_number == 0) {
154 fprintf(stderr, "Tried to measure video metrics from empty files "
155 "(reference file: %s test file: %s)\n", ref_filename,
156 test_filename);
157 return_code = -3;
158 } else {
159 CalculateStats(psnr_result);
160 CalculateStats(ssim_result);
161 }
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000162 fclose(ref_fp);
163 fclose(test_fp);
164 return return_code;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000165}
166
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000167int I420MetricsFromFiles(const char* ref_filename,
168 const char* test_filename,
169 int width,
170 int height,
171 QualityMetricsResult* psnr_result,
172 QualityMetricsResult* ssim_result) {
173 assert(psnr_result != NULL);
174 assert(ssim_result != NULL);
175 return CalculateMetrics(kBoth, ref_filename, test_filename, width, height,
176 psnr_result, ssim_result);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000177}
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000178
179int I420PSNRFromFiles(const char* ref_filename,
180 const char* test_filename,
181 int width,
182 int height,
183 QualityMetricsResult* result) {
184 assert(result != NULL);
185 return CalculateMetrics(kPSNR, ref_filename, test_filename, width, height,
186 result, NULL);
187}
188
189int I420SSIMFromFiles(const char* ref_filename,
190 const char* test_filename,
191 int width,
192 int height,
193 QualityMetricsResult* result) {
194 assert(result != NULL);
195 return CalculateMetrics(kSSIM, ref_filename, test_filename, width, height,
196 NULL, result);
197}
198
199} // namespace test
200} // namespace webrtc