blob: ddd31c46d041577eb4cf3cc3623a4517f2f23886 [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
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000023// Used for calculating min and max values.
kjellander@webrtc.org82d91ae2011-12-05 13:03:38 +000024static bool LessForFrameResultValue (const FrameResult& s1,
25 const FrameResult& s2) {
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000026 return s1.value < s2.value;
27}
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000028
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000029enum VideoMetricsType { kPSNR, kSSIM, kBoth };
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000030
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000031// Calculates metrics for a frame and adds statistics to the result for it.
32void CalculateFrame(VideoMetricsType video_metrics_type,
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000033 const I420VideoFrame* ref,
34 const I420VideoFrame* test,
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000035 int frame_number,
36 QualityMetricsResult* result) {
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000037 FrameResult frame_result = {0, 0};
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000038 frame_result.frame_number = frame_number;
39 switch (video_metrics_type) {
40 case kPSNR:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000041 frame_result.value = I420PSNR(ref, test);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000042 break;
43 case kSSIM:
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000044 frame_result.value = I420SSIM(ref, test);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000045 break;
46 default:
47 assert(false);
48 }
49 result->frames.push_back(frame_result);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000050}
51
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000052// Calculates average, min and max values for the supplied struct, if non-NULL.
53void CalculateStats(QualityMetricsResult* result) {
54 if (result == NULL || result->frames.size() == 0) {
55 return;
56 }
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000057 // Calculate average.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000058 std::vector<FrameResult>::iterator iter;
59 double metrics_values_sum = 0.0;
60 for (iter = result->frames.begin(); iter != result->frames.end(); ++iter) {
61 metrics_values_sum += iter->value;
62 }
63 result->average = metrics_values_sum / result->frames.size();
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000064
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000065 // Calculate min/max statistics.
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000066 iter = std::min_element(result->frames.begin(), result->frames.end(),
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000067 LessForFrameResultValue);
68 result->min = iter->value;
69 result->min_frame_number = iter->frame_number;
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000070 iter = std::max_element(result->frames.begin(), result->frames.end(),
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000071 LessForFrameResultValue);
72 result->max = iter->value;
73 result->max_frame_number = iter->frame_number;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000074}
75
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000076// Single method that handles all combinations of video metrics calculation, to
77// minimize code duplication. Either psnr_result or ssim_result may be NULL,
78// depending on which VideoMetricsType is targeted.
79int CalculateMetrics(VideoMetricsType video_metrics_type,
80 const char* ref_filename,
81 const char* test_filename,
82 int width,
83 int height,
84 QualityMetricsResult* psnr_result,
85 QualityMetricsResult* ssim_result) {
86 assert(ref_filename != NULL);
87 assert(test_filename != NULL);
88 assert(width > 0);
89 assert(height > 0);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000090
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000091 FILE* ref_fp = fopen(ref_filename, "rb");
92 if (ref_fp == NULL) {
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000093 // Cannot open reference file.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000094 fprintf(stderr, "Cannot open file %s\n", ref_filename);
95 return -1;
96 }
97 FILE* test_fp = fopen(test_filename, "rb");
98 if (test_fp == NULL) {
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +000099 // Cannot open test file.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000100 fprintf(stderr, "Cannot open file %s\n", test_filename);
101 fclose(ref_fp);
102 return -2;
103 }
104 int frame_number = 0;
105
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000106 // Read reference and test frames.
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000107 const int frame_length = 3 * width * height >> 1;
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000108 I420VideoFrame ref_frame;
109 I420VideoFrame test_frame;
110 scoped_array<uint8_t> ref_buffer(new uint8_t[frame_length]);
111 scoped_array<uint8_t> test_buffer(new uint8_t[frame_length]);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000112
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000113 int size_y = width * height;
114 int size_uv = ((width + 1 ) / 2) * ((height + 1) / 2);
115
116 int ref_bytes = fread(ref_buffer.get(), 1, frame_length, ref_fp);
117 int test_bytes = fread(test_buffer.get(), 1, frame_length, test_fp);
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000118 while (ref_bytes == frame_length && test_bytes == frame_length) {
kjellander@webrtc.orga56d7592012-11-02 22:36:14 +0000119 ref_frame.CreateFrame(size_y, ref_buffer.get(),
120 size_uv, ref_buffer.get() + size_y,
121 size_uv, ref_buffer.get() + size_y + size_uv,
122 width, height,
123 width, (width + 1) / 2, (width + 1) / 2);
124 test_frame.CreateFrame(size_y, test_buffer.get(),
125 size_uv, test_buffer.get() + size_y,
126 size_uv, test_buffer.get() + size_y + size_uv,
127 width, height,
128 width, (width + 1) / 2, (width + 1) / 2);
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