blob: 438f31b30c609b30aafad79e79b2e164a5f39da8 [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.orgcc337372012-01-04 08:09:32 +000017#include "common_video/libyuv/include/libyuv.h"
18
19namespace webrtc {
20namespace test {
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000021
22// Used for calculating min and max values
kjellander@webrtc.org82d91ae2011-12-05 13:03:38 +000023static bool LessForFrameResultValue (const FrameResult& s1,
24 const FrameResult& s2) {
kjellander@webrtc.orgf0a84642011-09-12 13:45:39 +000025 return s1.value < s2.value;
26}
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000027
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000028enum VideoMetricsType { kPSNR, kSSIM, kBoth };
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000029
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000030// Calculates metrics for a frame and adds statistics to the result for it.
31void 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) {
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000038 FrameResult frame_result = {0, 0};
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000039 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);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000051}
52
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000053// Calculates average, min and max values for the supplied struct, if non-NULL.
54void 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();
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000065
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000066 // Calculate min/max statistics
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000067 iter = std::min_element(result->frames.begin(), result->frames.end(),
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000068 LessForFrameResultValue);
69 result->min = iter->value;
70 result->min_frame_number = iter->frame_number;
leozwang@webrtc.org29fafef2012-02-21 19:46:33 +000071 iter = std::max_element(result->frames.begin(), result->frames.end(),
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000072 LessForFrameResultValue);
73 result->max = iter->value;
74 result->max_frame_number = iter->frame_number;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000075}
76
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000077// 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.
80int 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);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +000091
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +000092 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;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000130 }
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000131 frame_number++;
132 ref_bytes = fread(ref, 1, frame_length, ref_fp);
133 test_bytes = fread(test, 1, frame_length, test_fp);
134 }
135 int return_code = 0;
136 if (frame_number == 0) {
137 fprintf(stderr, "Tried to measure video metrics from empty files "
138 "(reference file: %s test file: %s)\n", ref_filename,
139 test_filename);
140 return_code = -3;
141 } else {
142 CalculateStats(psnr_result);
143 CalculateStats(ssim_result);
144 }
145 delete [] ref;
146 delete [] test;
147 fclose(ref_fp);
148 fclose(test_fp);
149 return return_code;
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000150}
151
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000152int I420MetricsFromFiles(const char* ref_filename,
153 const char* test_filename,
154 int width,
155 int height,
156 QualityMetricsResult* psnr_result,
157 QualityMetricsResult* ssim_result) {
158 assert(psnr_result != NULL);
159 assert(ssim_result != NULL);
160 return CalculateMetrics(kBoth, ref_filename, test_filename, width, height,
161 psnr_result, ssim_result);
mikhal@webrtc.org552f1732011-08-26 17:38:09 +0000162}
kjellander@webrtc.orgcc337372012-01-04 08:09:32 +0000163
164int I420PSNRFromFiles(const char* ref_filename,
165 const char* test_filename,
166 int width,
167 int height,
168 QualityMetricsResult* result) {
169 assert(result != NULL);
170 return CalculateMetrics(kPSNR, ref_filename, test_filename, width, height,
171 result, NULL);
172}
173
174int I420SSIMFromFiles(const char* ref_filename,
175 const char* test_filename,
176 int width,
177 int height,
178 QualityMetricsResult* result) {
179 assert(result != NULL);
180 return CalculateMetrics(kSSIM, ref_filename, test_filename, width, height,
181 NULL, result);
182}
183
184} // namespace test
185} // namespace webrtc