Home | History | Annotate | Download | only in metrics
      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 
     19 namespace webrtc {
     20 namespace test {
     21 
     22 // Used for calculating min and max values
     23 static bool LessForFrameResultValue (const FrameResult& s1,
     24                                      const FrameResult& s2) {
     25     return s1.value < s2.value;
     26 }
     27 
     28 enum VideoMetricsType { kPSNR, kSSIM, kBoth };
     29 
     30 // Calculates metrics for a frame and adds statistics to the result for it.
     31 void 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.
     54 void 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.
     80 int 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 
    154 int 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 
    166 int 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 
    176 int 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