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 19namespace webrtc { 20namespace test { 21 22// Used for calculating min and max values 23static bool LessForFrameResultValue (const FrameResult& s1, 24 const FrameResult& s2) { 25 return s1.value < s2.value; 26} 27 28enum VideoMetricsType { kPSNR, kSSIM, kBoth }; 29 30// 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) { 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. 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(); 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. 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); 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 154int 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 166int 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 176int 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