video_metrics.cc revision c55a96383497a772a307b346368133960b02ad03
1c55a96383497a772a307b346368133960b02ad03Eric Laurent/* 2c55a96383497a772a307b346368133960b02ad03Eric Laurent * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. 3c55a96383497a772a307b346368133960b02ad03Eric Laurent * 4c55a96383497a772a307b346368133960b02ad03Eric Laurent * Use of this source code is governed by a BSD-style license 5c55a96383497a772a307b346368133960b02ad03Eric Laurent * that can be found in the LICENSE file in the root of the source 6c55a96383497a772a307b346368133960b02ad03Eric Laurent * tree. An additional intellectual property rights grant can be found 7c55a96383497a772a307b346368133960b02ad03Eric Laurent * in the file PATENTS. All contributing project authors may 8c55a96383497a772a307b346368133960b02ad03Eric Laurent * be found in the AUTHORS file in the root of the source tree. 9c55a96383497a772a307b346368133960b02ad03Eric Laurent */ 10c55a96383497a772a307b346368133960b02ad03Eric Laurent 11c55a96383497a772a307b346368133960b02ad03Eric Laurent#include "testsupport/metrics/video_metrics.h" 12c55a96383497a772a307b346368133960b02ad03Eric Laurent 13c55a96383497a772a307b346368133960b02ad03Eric Laurent#include <algorithm> // min_element, max_element 14c55a96383497a772a307b346368133960b02ad03Eric Laurent#include <cassert> 15c55a96383497a772a307b346368133960b02ad03Eric Laurent#include <cstdio> 16c55a96383497a772a307b346368133960b02ad03Eric Laurent 17c55a96383497a772a307b346368133960b02ad03Eric Laurent#include "common_video/libyuv/include/libyuv.h" 18c55a96383497a772a307b346368133960b02ad03Eric Laurent 19c55a96383497a772a307b346368133960b02ad03Eric Laurentnamespace webrtc { 20c55a96383497a772a307b346368133960b02ad03Eric Laurentnamespace test { 21c55a96383497a772a307b346368133960b02ad03Eric Laurent 22c55a96383497a772a307b346368133960b02ad03Eric Laurent// Used for calculating min and max values 23c55a96383497a772a307b346368133960b02ad03Eric Laurentstatic bool LessForFrameResultValue (const FrameResult& s1, 24c55a96383497a772a307b346368133960b02ad03Eric Laurent const FrameResult& s2) { 25c55a96383497a772a307b346368133960b02ad03Eric Laurent return s1.value < s2.value; 26c55a96383497a772a307b346368133960b02ad03Eric Laurent} 27c55a96383497a772a307b346368133960b02ad03Eric Laurent 28c55a96383497a772a307b346368133960b02ad03Eric Laurentenum VideoMetricsType { kPSNR, kSSIM, kBoth }; 29c55a96383497a772a307b346368133960b02ad03Eric Laurent 30c55a96383497a772a307b346368133960b02ad03Eric Laurent// Calculates metrics for a frame and adds statistics to the result for it. 31c55a96383497a772a307b346368133960b02ad03Eric Laurentvoid CalculateFrame(VideoMetricsType video_metrics_type, 32c55a96383497a772a307b346368133960b02ad03Eric Laurent uint8_t* ref, 33c55a96383497a772a307b346368133960b02ad03Eric Laurent uint8_t* test, 34c55a96383497a772a307b346368133960b02ad03Eric Laurent int width, 35c55a96383497a772a307b346368133960b02ad03Eric Laurent int height, 36c55a96383497a772a307b346368133960b02ad03Eric Laurent int frame_number, 37c55a96383497a772a307b346368133960b02ad03Eric Laurent QualityMetricsResult* result) { 38c55a96383497a772a307b346368133960b02ad03Eric Laurent FrameResult frame_result; 39c55a96383497a772a307b346368133960b02ad03Eric Laurent frame_result.frame_number = frame_number; 40c55a96383497a772a307b346368133960b02ad03Eric Laurent switch (video_metrics_type) { 41c55a96383497a772a307b346368133960b02ad03Eric Laurent case kPSNR: 42c55a96383497a772a307b346368133960b02ad03Eric Laurent frame_result.value = I420PSNR(ref, test, width, height); 43c55a96383497a772a307b346368133960b02ad03Eric Laurent break; 44c55a96383497a772a307b346368133960b02ad03Eric Laurent case kSSIM: 45c55a96383497a772a307b346368133960b02ad03Eric Laurent frame_result.value = I420SSIM(ref, test, width, height); 46c55a96383497a772a307b346368133960b02ad03Eric Laurent break; 47c55a96383497a772a307b346368133960b02ad03Eric Laurent default: 48c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(false); 49c55a96383497a772a307b346368133960b02ad03Eric Laurent } 50c55a96383497a772a307b346368133960b02ad03Eric Laurent result->frames.push_back(frame_result); 51c55a96383497a772a307b346368133960b02ad03Eric Laurent} 52c55a96383497a772a307b346368133960b02ad03Eric Laurent 53c55a96383497a772a307b346368133960b02ad03Eric Laurent// Calculates average, min and max values for the supplied struct, if non-NULL. 54c55a96383497a772a307b346368133960b02ad03Eric Laurentvoid CalculateStats(QualityMetricsResult* result) { 55c55a96383497a772a307b346368133960b02ad03Eric Laurent if (result == NULL || result->frames.size() == 0) { 56c55a96383497a772a307b346368133960b02ad03Eric Laurent return; 57c55a96383497a772a307b346368133960b02ad03Eric Laurent } 58c55a96383497a772a307b346368133960b02ad03Eric Laurent // Calculate average 59c55a96383497a772a307b346368133960b02ad03Eric Laurent std::vector<FrameResult>::iterator iter; 60c55a96383497a772a307b346368133960b02ad03Eric Laurent double metrics_values_sum = 0.0; 61c55a96383497a772a307b346368133960b02ad03Eric Laurent for (iter = result->frames.begin(); iter != result->frames.end(); ++iter) { 62c55a96383497a772a307b346368133960b02ad03Eric Laurent metrics_values_sum += iter->value; 63c55a96383497a772a307b346368133960b02ad03Eric Laurent } 64c55a96383497a772a307b346368133960b02ad03Eric Laurent result->average = metrics_values_sum / result->frames.size(); 65c55a96383497a772a307b346368133960b02ad03Eric Laurent 66c55a96383497a772a307b346368133960b02ad03Eric Laurent // Calculate min/max statistics 67c55a96383497a772a307b346368133960b02ad03Eric Laurent iter = min_element(result->frames.begin(), result->frames.end(), 68c55a96383497a772a307b346368133960b02ad03Eric Laurent LessForFrameResultValue); 69c55a96383497a772a307b346368133960b02ad03Eric Laurent result->min = iter->value; 70c55a96383497a772a307b346368133960b02ad03Eric Laurent result->min_frame_number = iter->frame_number; 71c55a96383497a772a307b346368133960b02ad03Eric Laurent iter = max_element(result->frames.begin(), result->frames.end(), 72c55a96383497a772a307b346368133960b02ad03Eric Laurent LessForFrameResultValue); 73c55a96383497a772a307b346368133960b02ad03Eric Laurent result->max = iter->value; 74c55a96383497a772a307b346368133960b02ad03Eric Laurent result->max_frame_number = iter->frame_number; 75c55a96383497a772a307b346368133960b02ad03Eric Laurent} 76c55a96383497a772a307b346368133960b02ad03Eric Laurent 77c55a96383497a772a307b346368133960b02ad03Eric Laurent// Single method that handles all combinations of video metrics calculation, to 78c55a96383497a772a307b346368133960b02ad03Eric Laurent// minimize code duplication. Either psnr_result or ssim_result may be NULL, 79c55a96383497a772a307b346368133960b02ad03Eric Laurent// depending on which VideoMetricsType is targeted. 80c55a96383497a772a307b346368133960b02ad03Eric Laurentint CalculateMetrics(VideoMetricsType video_metrics_type, 81c55a96383497a772a307b346368133960b02ad03Eric Laurent const char* ref_filename, 82c55a96383497a772a307b346368133960b02ad03Eric Laurent const char* test_filename, 83c55a96383497a772a307b346368133960b02ad03Eric Laurent int width, 84c55a96383497a772a307b346368133960b02ad03Eric Laurent int height, 85c55a96383497a772a307b346368133960b02ad03Eric Laurent QualityMetricsResult* psnr_result, 86c55a96383497a772a307b346368133960b02ad03Eric Laurent QualityMetricsResult* ssim_result) { 87c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(ref_filename != NULL); 88c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(test_filename != NULL); 89c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(width > 0); 90c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(height > 0); 91c55a96383497a772a307b346368133960b02ad03Eric Laurent 92c55a96383497a772a307b346368133960b02ad03Eric Laurent FILE* ref_fp = fopen(ref_filename, "rb"); 93c55a96383497a772a307b346368133960b02ad03Eric Laurent if (ref_fp == NULL) { 94c55a96383497a772a307b346368133960b02ad03Eric Laurent // cannot open reference file 95c55a96383497a772a307b346368133960b02ad03Eric Laurent fprintf(stderr, "Cannot open file %s\n", ref_filename); 96c55a96383497a772a307b346368133960b02ad03Eric Laurent return -1; 97c55a96383497a772a307b346368133960b02ad03Eric Laurent } 98c55a96383497a772a307b346368133960b02ad03Eric Laurent FILE* test_fp = fopen(test_filename, "rb"); 99c55a96383497a772a307b346368133960b02ad03Eric Laurent if (test_fp == NULL) { 100c55a96383497a772a307b346368133960b02ad03Eric Laurent // cannot open test file 101c55a96383497a772a307b346368133960b02ad03Eric Laurent fprintf(stderr, "Cannot open file %s\n", test_filename); 102c55a96383497a772a307b346368133960b02ad03Eric Laurent fclose(ref_fp); 103c55a96383497a772a307b346368133960b02ad03Eric Laurent return -2; 104c55a96383497a772a307b346368133960b02ad03Eric Laurent } 105c55a96383497a772a307b346368133960b02ad03Eric Laurent int frame_number = 0; 106c55a96383497a772a307b346368133960b02ad03Eric Laurent 107c55a96383497a772a307b346368133960b02ad03Eric Laurent // Allocating size for one I420 frame. 108c55a96383497a772a307b346368133960b02ad03Eric Laurent const int frame_length = 3 * width * height >> 1; 109c55a96383497a772a307b346368133960b02ad03Eric Laurent uint8_t* ref = new uint8_t[frame_length]; 110c55a96383497a772a307b346368133960b02ad03Eric Laurent uint8_t* test = new uint8_t[frame_length]; 111c55a96383497a772a307b346368133960b02ad03Eric Laurent 112c55a96383497a772a307b346368133960b02ad03Eric Laurent int ref_bytes = fread(ref, 1, frame_length, ref_fp); 113c55a96383497a772a307b346368133960b02ad03Eric Laurent int test_bytes = fread(test, 1, frame_length, test_fp); 114c55a96383497a772a307b346368133960b02ad03Eric Laurent while (ref_bytes == frame_length && test_bytes == frame_length) { 115c55a96383497a772a307b346368133960b02ad03Eric Laurent switch (video_metrics_type) { 116c55a96383497a772a307b346368133960b02ad03Eric Laurent case kPSNR: 117c55a96383497a772a307b346368133960b02ad03Eric Laurent CalculateFrame(kPSNR, ref, test, width, height, frame_number, 118c55a96383497a772a307b346368133960b02ad03Eric Laurent psnr_result); 119c55a96383497a772a307b346368133960b02ad03Eric Laurent break; 120c55a96383497a772a307b346368133960b02ad03Eric Laurent case kSSIM: 121c55a96383497a772a307b346368133960b02ad03Eric Laurent CalculateFrame(kSSIM, ref, test, width, height, frame_number, 122c55a96383497a772a307b346368133960b02ad03Eric Laurent ssim_result); 123c55a96383497a772a307b346368133960b02ad03Eric Laurent break; 124c55a96383497a772a307b346368133960b02ad03Eric Laurent case kBoth: 125c55a96383497a772a307b346368133960b02ad03Eric Laurent CalculateFrame(kPSNR, ref, test, width, height, frame_number, 126c55a96383497a772a307b346368133960b02ad03Eric Laurent psnr_result); 127c55a96383497a772a307b346368133960b02ad03Eric Laurent CalculateFrame(kSSIM, ref, test, width, height, frame_number, 128c55a96383497a772a307b346368133960b02ad03Eric Laurent ssim_result); 129c55a96383497a772a307b346368133960b02ad03Eric Laurent break; 130c55a96383497a772a307b346368133960b02ad03Eric Laurent default: 131c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(false); 132c55a96383497a772a307b346368133960b02ad03Eric Laurent } 133c55a96383497a772a307b346368133960b02ad03Eric Laurent frame_number++; 134c55a96383497a772a307b346368133960b02ad03Eric Laurent ref_bytes = fread(ref, 1, frame_length, ref_fp); 135c55a96383497a772a307b346368133960b02ad03Eric Laurent test_bytes = fread(test, 1, frame_length, test_fp); 136c55a96383497a772a307b346368133960b02ad03Eric Laurent } 137c55a96383497a772a307b346368133960b02ad03Eric Laurent int return_code = 0; 138c55a96383497a772a307b346368133960b02ad03Eric Laurent if (frame_number == 0) { 139c55a96383497a772a307b346368133960b02ad03Eric Laurent fprintf(stderr, "Tried to measure video metrics from empty files " 140c55a96383497a772a307b346368133960b02ad03Eric Laurent "(reference file: %s test file: %s)\n", ref_filename, 141c55a96383497a772a307b346368133960b02ad03Eric Laurent test_filename); 142c55a96383497a772a307b346368133960b02ad03Eric Laurent return_code = -3; 143c55a96383497a772a307b346368133960b02ad03Eric Laurent } else { 144c55a96383497a772a307b346368133960b02ad03Eric Laurent CalculateStats(psnr_result); 145c55a96383497a772a307b346368133960b02ad03Eric Laurent CalculateStats(ssim_result); 146c55a96383497a772a307b346368133960b02ad03Eric Laurent } 147c55a96383497a772a307b346368133960b02ad03Eric Laurent delete [] ref; 148c55a96383497a772a307b346368133960b02ad03Eric Laurent delete [] test; 149c55a96383497a772a307b346368133960b02ad03Eric Laurent fclose(ref_fp); 150c55a96383497a772a307b346368133960b02ad03Eric Laurent fclose(test_fp); 151c55a96383497a772a307b346368133960b02ad03Eric Laurent return return_code; 152c55a96383497a772a307b346368133960b02ad03Eric Laurent} 153c55a96383497a772a307b346368133960b02ad03Eric Laurent 154c55a96383497a772a307b346368133960b02ad03Eric Laurentint I420MetricsFromFiles(const char* ref_filename, 155c55a96383497a772a307b346368133960b02ad03Eric Laurent const char* test_filename, 156c55a96383497a772a307b346368133960b02ad03Eric Laurent int width, 157c55a96383497a772a307b346368133960b02ad03Eric Laurent int height, 158c55a96383497a772a307b346368133960b02ad03Eric Laurent QualityMetricsResult* psnr_result, 159c55a96383497a772a307b346368133960b02ad03Eric Laurent QualityMetricsResult* ssim_result) { 160c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(psnr_result != NULL); 161c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(ssim_result != NULL); 162c55a96383497a772a307b346368133960b02ad03Eric Laurent return CalculateMetrics(kBoth, ref_filename, test_filename, width, height, 163c55a96383497a772a307b346368133960b02ad03Eric Laurent psnr_result, ssim_result); 164c55a96383497a772a307b346368133960b02ad03Eric Laurent} 165c55a96383497a772a307b346368133960b02ad03Eric Laurent 166c55a96383497a772a307b346368133960b02ad03Eric Laurentint I420PSNRFromFiles(const char* ref_filename, 167c55a96383497a772a307b346368133960b02ad03Eric Laurent const char* test_filename, 168c55a96383497a772a307b346368133960b02ad03Eric Laurent int width, 169c55a96383497a772a307b346368133960b02ad03Eric Laurent int height, 170c55a96383497a772a307b346368133960b02ad03Eric Laurent QualityMetricsResult* result) { 171c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(result != NULL); 172c55a96383497a772a307b346368133960b02ad03Eric Laurent return CalculateMetrics(kPSNR, ref_filename, test_filename, width, height, 173c55a96383497a772a307b346368133960b02ad03Eric Laurent result, NULL); 174c55a96383497a772a307b346368133960b02ad03Eric Laurent} 175c55a96383497a772a307b346368133960b02ad03Eric Laurent 176c55a96383497a772a307b346368133960b02ad03Eric Laurentint I420SSIMFromFiles(const char* ref_filename, 177c55a96383497a772a307b346368133960b02ad03Eric Laurent const char* test_filename, 178c55a96383497a772a307b346368133960b02ad03Eric Laurent int width, 179c55a96383497a772a307b346368133960b02ad03Eric Laurent int height, 180c55a96383497a772a307b346368133960b02ad03Eric Laurent QualityMetricsResult* result) { 181c55a96383497a772a307b346368133960b02ad03Eric Laurent assert(result != NULL); 182c55a96383497a772a307b346368133960b02ad03Eric Laurent return CalculateMetrics(kSSIM, ref_filename, test_filename, width, height, 183c55a96383497a772a307b346368133960b02ad03Eric Laurent NULL, result); 184c55a96383497a772a307b346368133960b02ad03Eric Laurent} 185c55a96383497a772a307b346368133960b02ad03Eric Laurent 186c55a96383497a772a307b346368133960b02ad03Eric Laurent} // namespace test 187c55a96383497a772a307b346368133960b02ad03Eric Laurent} // namespace webrtc 188