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