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