psnr_main.cc revision b83bb38f0a92bedeb52baa31e515220927ef53bb
1/*
2 *  Copyright 2013 The LibYuv 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// Get PSNR or SSIM for video sequence. Assuming RAW 4:2:0 Y:Cb:Cr format
12// To build: g++ -O3 -o psnr psnr.cc ssim.cc psnr_main.cc
13// or VisualC: cl /Ox psnr.cc ssim.cc psnr_main.cc
14//
15// To enable OpenMP and SSE2
16// gcc: g++ -msse2 -O3 -fopenmp -o psnr psnr.cc ssim.cc psnr_main.cc
17// vc:  cl /arch:SSE2 /Ox /openmp psnr.cc ssim.cc psnr_main.cc
18//
19// Usage: psnr org_seq rec_seq -s width height [-skip skip_org skip_rec]
20
21#ifndef _CRT_SECURE_NO_WARNINGS
22#define _CRT_SECURE_NO_WARNINGS
23#endif
24
25#include <stddef.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#ifdef _OPENMP
30#include <omp.h>
31#endif
32
33#include "./psnr.h"
34#include "./ssim.h"
35#ifdef HAVE_JPEG
36#include "libyuv/compare.h"
37#include "libyuv/convert.h"
38#endif
39
40struct metric {
41  double y, u, v, all;
42  double min_y, min_u, min_v, min_all;
43  double global_y, global_u, global_v, global_all;
44  int min_frame;
45};
46
47// options
48bool verbose = false;
49bool quiet = false;
50bool show_name = false;
51bool do_swap_uv = false;
52bool do_psnr = false;
53bool do_ssim = false;
54bool do_mse = false;
55bool do_lssim = false;
56int image_width = 0, image_height = 0;
57int fileindex_org = 0;  // argv argument contains the source file name.
58int fileindex_rec = 0;  // argv argument contains the destination file name.
59int num_rec = 0;
60int num_skip_org = 0;
61int num_skip_rec = 0;
62int num_frames = 0;
63#ifdef _OPENMP
64int num_threads = 0;
65#endif
66
67// Parse PYUV format. ie name.1920x800_24Hz_P420.yuv
68bool ExtractResolutionFromFilename(const char* name,
69                                   int* width_ptr,
70                                   int* height_ptr) {
71  // Isolate the .width_height. section of the filename by searching for a
72  // dot or underscore followed by a digit.
73  for (int i = 0; name[i]; ++i) {
74    if ((name[i] == '.' || name[i] == '_') && name[i + 1] >= '0' &&
75        name[i + 1] <= '9') {
76      int n = sscanf(name + i + 1, "%dx%d", width_ptr, height_ptr);  // NOLINT
77      if (2 == n) {
78        return true;
79      }
80    }
81  }
82
83#ifdef HAVE_JPEG
84  // Try parsing file as a jpeg.
85  FILE* const file_org = fopen(name, "rb");
86  if (file_org == NULL) {
87    fprintf(stderr, "Cannot open %s\n", name);
88    return false;
89  }
90  fseek(file_org, 0, SEEK_END);
91  size_t total_size = ftell(file_org);
92  fseek(file_org, 0, SEEK_SET);
93  uint8* const ch_org = new uint8[total_size];
94  memset(ch_org, 0, total_size);
95  size_t bytes_org = fread(ch_org, sizeof(uint8), total_size, file_org);
96  fclose(file_org);
97  if (bytes_org == total_size) {
98    if (0 == libyuv::MJPGSize(ch_org, total_size, width_ptr, height_ptr)) {
99      delete[] ch_org;
100      return true;
101    }
102  }
103  delete[] ch_org;
104#endif  // HAVE_JPEG
105  return false;
106}
107
108// Scale Y channel from 16..240 to 0..255.
109// This can be useful when comparing codecs that are inconsistant about Y
110uint8 ScaleY(uint8 y) {
111  int ny = (y - 16) * 256 / 224;
112  if (ny < 0)
113    ny = 0;
114  if (ny > 255)
115    ny = 255;
116  return static_cast<uint8>(ny);
117}
118
119// MSE = Mean Square Error
120double GetMSE(double sse, double size) {
121  return sse / size;
122}
123
124void PrintHelp(const char* program) {
125  printf("%s [-options] org_seq rec_seq [rec_seq2.. etc]\n", program);
126#ifdef HAVE_JPEG
127  printf("jpeg or raw YUV 420 supported.\n");
128#endif
129  printf("options:\n");
130  printf(
131      " -s <width> <height> .... specify YUV size, mandatory if none of the "
132      "sequences have the\n");
133  printf(
134      "                          resolution embedded in their filename (ie. "
135      "name.1920x800_24Hz_P420.yuv)\n");
136  printf(" -psnr .................. compute PSNR (default)\n");
137  printf(" -ssim .................. compute SSIM\n");
138  printf(" -mse ................... compute MSE\n");
139  printf(" -swap .................. Swap U and V plane\n");
140  printf(" -skip <org> <rec> ...... Number of frame to skip of org and rec\n");
141  printf(" -frames <num> .......... Number of frames to compare\n");
142#ifdef _OPENMP
143  printf(" -t <num> ............... Number of threads\n");
144#endif
145  printf(" -n ..................... Show file name\n");
146  printf(" -v ..................... verbose++\n");
147  printf(" -q ..................... quiet\n");
148  printf(" -h ..................... this help\n");
149  exit(0);
150}
151
152void ParseOptions(int argc, const char* argv[]) {
153  if (argc <= 1)
154    PrintHelp(argv[0]);
155  for (int c = 1; c < argc; ++c) {
156    if (!strcmp(argv[c], "-v")) {
157      verbose = true;
158    } else if (!strcmp(argv[c], "-q")) {
159      quiet = true;
160    } else if (!strcmp(argv[c], "-n")) {
161      show_name = true;
162    } else if (!strcmp(argv[c], "-psnr")) {
163      do_psnr = true;
164    } else if (!strcmp(argv[c], "-mse")) {
165      do_mse = true;
166    } else if (!strcmp(argv[c], "-ssim")) {
167      do_ssim = true;
168    } else if (!strcmp(argv[c], "-lssim")) {
169      do_ssim = true;
170      do_lssim = true;
171    } else if (!strcmp(argv[c], "-swap")) {
172      do_swap_uv = true;
173    } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) {
174      PrintHelp(argv[0]);
175    } else if (!strcmp(argv[c], "-s") && c + 2 < argc) {
176      image_width = atoi(argv[++c]);   // NOLINT
177      image_height = atoi(argv[++c]);  // NOLINT
178    } else if (!strcmp(argv[c], "-skip") && c + 2 < argc) {
179      num_skip_org = atoi(argv[++c]);  // NOLINT
180      num_skip_rec = atoi(argv[++c]);  // NOLINT
181    } else if (!strcmp(argv[c], "-frames") && c + 1 < argc) {
182      num_frames = atoi(argv[++c]);  // NOLINT
183#ifdef _OPENMP
184    } else if (!strcmp(argv[c], "-t") && c + 1 < argc) {
185      num_threads = atoi(argv[++c]);  // NOLINT
186#endif
187    } else if (argv[c][0] == '-') {
188      fprintf(stderr, "Unknown option. %s\n", argv[c]);
189    } else if (fileindex_org == 0) {
190      fileindex_org = c;
191    } else if (fileindex_rec == 0) {
192      fileindex_rec = c;
193      num_rec = 1;
194    } else {
195      ++num_rec;
196    }
197  }
198  if (fileindex_org == 0 || fileindex_rec == 0) {
199    fprintf(stderr, "Missing filenames\n");
200    PrintHelp(argv[0]);
201  }
202  if (num_skip_org < 0 || num_skip_rec < 0) {
203    fprintf(stderr, "Skipped frames incorrect\n");
204    PrintHelp(argv[0]);
205  }
206  if (num_frames < 0) {
207    fprintf(stderr, "Number of frames incorrect\n");
208    PrintHelp(argv[0]);
209  }
210  if (image_width == 0 || image_height == 0) {
211    int org_width, org_height;
212    int rec_width, rec_height;
213    bool org_res_avail = ExtractResolutionFromFilename(argv[fileindex_org],
214                                                       &org_width, &org_height);
215    bool rec_res_avail = ExtractResolutionFromFilename(argv[fileindex_rec],
216                                                       &rec_width, &rec_height);
217    if (org_res_avail) {
218      if (rec_res_avail) {
219        if ((org_width == rec_width) && (org_height == rec_height)) {
220          image_width = org_width;
221          image_height = org_height;
222        } else {
223          fprintf(stderr, "Sequences have different resolutions.\n");
224          PrintHelp(argv[0]);
225        }
226      } else {
227        image_width = org_width;
228        image_height = org_height;
229      }
230    } else if (rec_res_avail) {
231      image_width = rec_width;
232      image_height = rec_height;
233    } else {
234      fprintf(stderr, "Missing dimensions.\n");
235      PrintHelp(argv[0]);
236    }
237  }
238}
239
240bool UpdateMetrics(uint8* ch_org,
241                   uint8* ch_rec,
242                   const int y_size,
243                   const int uv_size,
244                   const size_t total_size,
245                   int number_of_frames,
246                   metric* cur_distortion_psnr,
247                   metric* distorted_frame,
248                   bool do_psnr) {
249  const int uv_offset = (do_swap_uv ? uv_size : 0);
250  const uint8* const u_org = ch_org + y_size + uv_offset;
251  const uint8* const u_rec = ch_rec + y_size;
252  const uint8* const v_org = ch_org + y_size + (uv_size - uv_offset);
253  const uint8* const v_rec = ch_rec + y_size + uv_size;
254  if (do_psnr) {
255#ifdef HAVE_JPEG
256    double y_err = static_cast<double>(
257        libyuv::ComputeSumSquareError(ch_org, ch_rec, y_size));
258    double u_err = static_cast<double>(
259        libyuv::ComputeSumSquareError(u_org, u_rec, uv_size));
260    double v_err = static_cast<double>(
261        libyuv::ComputeSumSquareError(v_org, v_rec, uv_size));
262#else
263    double y_err = ComputeSumSquareError(ch_org, ch_rec, y_size);
264    double u_err = ComputeSumSquareError(u_org, u_rec, uv_size);
265    double v_err = ComputeSumSquareError(v_org, v_rec, uv_size);
266#endif
267    const double total_err = y_err + u_err + v_err;
268    cur_distortion_psnr->global_y += y_err;
269    cur_distortion_psnr->global_u += u_err;
270    cur_distortion_psnr->global_v += v_err;
271    cur_distortion_psnr->global_all += total_err;
272    distorted_frame->y = ComputePSNR(y_err, static_cast<double>(y_size));
273    distorted_frame->u = ComputePSNR(u_err, static_cast<double>(uv_size));
274    distorted_frame->v = ComputePSNR(v_err, static_cast<double>(uv_size));
275    distorted_frame->all =
276        ComputePSNR(total_err, static_cast<double>(total_size));
277  } else {
278    distorted_frame->y = CalcSSIM(ch_org, ch_rec, image_width, image_height);
279    distorted_frame->u =
280        CalcSSIM(u_org, u_rec, (image_width + 1) / 2, (image_height + 1) / 2);
281    distorted_frame->v =
282        CalcSSIM(v_org, v_rec, (image_width + 1) / 2, (image_height + 1) / 2);
283    distorted_frame->all =
284        (distorted_frame->y + distorted_frame->u + distorted_frame->v) /
285        total_size;
286    distorted_frame->y /= y_size;
287    distorted_frame->u /= uv_size;
288    distorted_frame->v /= uv_size;
289
290    if (do_lssim) {
291      distorted_frame->all = CalcLSSIM(distorted_frame->all);
292      distorted_frame->y = CalcLSSIM(distorted_frame->y);
293      distorted_frame->u = CalcLSSIM(distorted_frame->u);
294      distorted_frame->v = CalcLSSIM(distorted_frame->v);
295    }
296  }
297
298  cur_distortion_psnr->y += distorted_frame->y;
299  cur_distortion_psnr->u += distorted_frame->u;
300  cur_distortion_psnr->v += distorted_frame->v;
301  cur_distortion_psnr->all += distorted_frame->all;
302
303  bool ismin = false;
304  if (distorted_frame->y < cur_distortion_psnr->min_y)
305    cur_distortion_psnr->min_y = distorted_frame->y;
306  if (distorted_frame->u < cur_distortion_psnr->min_u)
307    cur_distortion_psnr->min_u = distorted_frame->u;
308  if (distorted_frame->v < cur_distortion_psnr->min_v)
309    cur_distortion_psnr->min_v = distorted_frame->v;
310  if (distorted_frame->all < cur_distortion_psnr->min_all) {
311    cur_distortion_psnr->min_all = distorted_frame->all;
312    cur_distortion_psnr->min_frame = number_of_frames;
313    ismin = true;
314  }
315  return ismin;
316}
317
318int main(int argc, const char* argv[]) {
319  ParseOptions(argc, argv);
320  if (!do_psnr && !do_ssim) {
321    do_psnr = true;
322  }
323
324#ifdef _OPENMP
325  if (num_threads) {
326    omp_set_num_threads(num_threads);
327  }
328  if (verbose) {
329    printf("OpenMP %d procs\n", omp_get_num_procs());
330  }
331#endif
332  // Open original file (first file argument)
333  FILE* const file_org = fopen(argv[fileindex_org], "rb");
334  if (file_org == NULL) {
335    fprintf(stderr, "Cannot open %s\n", argv[fileindex_org]);
336    exit(1);
337  }
338
339  // Open all files to compare to
340  FILE** file_rec = new FILE*[num_rec];
341  memset(file_rec, 0, num_rec * sizeof(FILE*));  // NOLINT
342  for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
343    file_rec[cur_rec] = fopen(argv[fileindex_rec + cur_rec], "rb");
344    if (file_rec[cur_rec] == NULL) {
345      fprintf(stderr, "Cannot open %s\n", argv[fileindex_rec + cur_rec]);
346      fclose(file_org);
347      for (int i = 0; i < cur_rec; ++i) {
348        fclose(file_rec[i]);
349      }
350      delete[] file_rec;
351      exit(1);
352    }
353  }
354
355  const int y_size = image_width * image_height;
356  const int uv_size = ((image_width + 1) / 2) * ((image_height + 1) / 2);
357  const size_t total_size = y_size + 2 * uv_size;  // NOLINT
358#if defined(_MSC_VER)
359  _fseeki64(
360      file_org,
361      static_cast<__int64>(num_skip_org) * static_cast<__int64>(total_size),
362      SEEK_SET);
363#else
364  fseek(file_org, num_skip_org * total_size, SEEK_SET);
365#endif
366  for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
367#if defined(_MSC_VER)
368    _fseeki64(
369        file_rec[cur_rec],
370        static_cast<__int64>(num_skip_rec) * static_cast<__int64>(total_size),
371        SEEK_SET);
372#else
373    fseek(file_rec[cur_rec], num_skip_rec * total_size, SEEK_SET);
374#endif
375  }
376
377  uint8* const ch_org = new uint8[total_size];
378  uint8* const ch_rec = new uint8[total_size];
379  if (ch_org == NULL || ch_rec == NULL) {
380    fprintf(stderr, "No memory available\n");
381    fclose(file_org);
382    for (int i = 0; i < num_rec; ++i) {
383      fclose(file_rec[i]);
384    }
385    delete[] ch_org;
386    delete[] ch_rec;
387    delete[] file_rec;
388    exit(1);
389  }
390
391  metric* const distortion_psnr = new metric[num_rec];
392  metric* const distortion_ssim = new metric[num_rec];
393  for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
394    metric* cur_distortion_psnr = &distortion_psnr[cur_rec];
395    cur_distortion_psnr->y = 0.0;
396    cur_distortion_psnr->u = 0.0;
397    cur_distortion_psnr->v = 0.0;
398    cur_distortion_psnr->all = 0.0;
399    cur_distortion_psnr->min_y = kMaxPSNR;
400    cur_distortion_psnr->min_u = kMaxPSNR;
401    cur_distortion_psnr->min_v = kMaxPSNR;
402    cur_distortion_psnr->min_all = kMaxPSNR;
403    cur_distortion_psnr->min_frame = 0;
404    cur_distortion_psnr->global_y = 0.0;
405    cur_distortion_psnr->global_u = 0.0;
406    cur_distortion_psnr->global_v = 0.0;
407    cur_distortion_psnr->global_all = 0.0;
408    distortion_ssim[cur_rec] = cur_distortion_psnr[cur_rec];
409  }
410
411  if (verbose) {
412    printf("Size: %dx%d\n", image_width, image_height);
413  }
414
415  if (!quiet) {
416    printf("Frame");
417    if (do_psnr) {
418      printf("\t PSNR-Y \t PSNR-U \t PSNR-V \t PSNR-All \t Frame");
419    }
420    if (do_ssim) {
421      printf("\t  SSIM-Y\t  SSIM-U\t  SSIM-V\t  SSIM-All\t Frame");
422    }
423    if (show_name) {
424      printf("\tName\n");
425    } else {
426      printf("\n");
427    }
428  }
429
430  int number_of_frames;
431  for (number_of_frames = 0;; ++number_of_frames) {
432    if (num_frames && number_of_frames >= num_frames)
433      break;
434
435    size_t bytes_org = fread(ch_org, sizeof(uint8), total_size, file_org);
436    if (bytes_org < total_size) {
437#ifdef HAVE_JPEG
438      // Try parsing file as a jpeg.
439      uint8* const ch_jpeg = new uint8[bytes_org];
440      memcpy(ch_jpeg, ch_org, bytes_org);
441      memset(ch_org, 0, total_size);
442
443      if (0 != libyuv::MJPGToI420(ch_jpeg, bytes_org, ch_org, image_width,
444                                  ch_org + y_size, (image_width + 1) / 2,
445                                  ch_org + y_size + uv_size,
446                                  (image_width + 1) / 2, image_width,
447                                  image_height, image_width, image_height)) {
448        delete[] ch_jpeg;
449        break;
450      }
451      delete[] ch_jpeg;
452#else
453      break;
454#endif  // HAVE_JPEG
455    }
456
457    for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
458      size_t bytes_rec =
459          fread(ch_rec, sizeof(uint8), total_size, file_rec[cur_rec]);
460      if (bytes_rec < total_size) {
461#ifdef HAVE_JPEG
462        // Try parsing file as a jpeg.
463        uint8* const ch_jpeg = new uint8[bytes_rec];
464        memcpy(ch_jpeg, ch_rec, bytes_rec);
465        memset(ch_rec, 0, total_size);
466
467        if (0 != libyuv::MJPGToI420(ch_jpeg, bytes_rec, ch_rec, image_width,
468                                    ch_rec + y_size, (image_width + 1) / 2,
469                                    ch_rec + y_size + uv_size,
470                                    (image_width + 1) / 2, image_width,
471                                    image_height, image_width, image_height)) {
472          delete[] ch_jpeg;
473          break;
474        }
475        delete[] ch_jpeg;
476#else
477        break;
478#endif  // HAVE_JPEG
479      }
480
481      if (verbose) {
482        printf("%5d", number_of_frames);
483      }
484      if (do_psnr) {
485        metric distorted_frame;
486        metric* cur_distortion_psnr = &distortion_psnr[cur_rec];
487        bool ismin = UpdateMetrics(ch_org, ch_rec, y_size, uv_size, total_size,
488                                   number_of_frames, cur_distortion_psnr,
489                                   &distorted_frame, true);
490        if (verbose) {
491          printf("\t%10.6f", distorted_frame.y);
492          printf("\t%10.6f", distorted_frame.u);
493          printf("\t%10.6f", distorted_frame.v);
494          printf("\t%10.6f", distorted_frame.all);
495          printf("\t%5s", ismin ? "min" : "");
496        }
497      }
498      if (do_ssim) {
499        metric distorted_frame;
500        metric* cur_distortion_ssim = &distortion_ssim[cur_rec];
501        bool ismin = UpdateMetrics(ch_org, ch_rec, y_size, uv_size, total_size,
502                                   number_of_frames, cur_distortion_ssim,
503                                   &distorted_frame, false);
504        if (verbose) {
505          printf("\t%10.6f", distorted_frame.y);
506          printf("\t%10.6f", distorted_frame.u);
507          printf("\t%10.6f", distorted_frame.v);
508          printf("\t%10.6f", distorted_frame.all);
509          printf("\t%5s", ismin ? "min" : "");
510        }
511      }
512      if (verbose) {
513        if (show_name) {
514          printf("\t%s", argv[fileindex_rec + cur_rec]);
515        }
516        printf("\n");
517      }
518    }
519  }
520
521  // Final PSNR computation.
522  for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
523    metric* cur_distortion_psnr = &distortion_psnr[cur_rec];
524    metric* cur_distortion_ssim = &distortion_ssim[cur_rec];
525    if (number_of_frames > 0) {
526      const double norm = 1. / static_cast<double>(number_of_frames);
527      cur_distortion_psnr->y *= norm;
528      cur_distortion_psnr->u *= norm;
529      cur_distortion_psnr->v *= norm;
530      cur_distortion_psnr->all *= norm;
531      cur_distortion_ssim->y *= norm;
532      cur_distortion_ssim->u *= norm;
533      cur_distortion_ssim->v *= norm;
534      cur_distortion_ssim->all *= norm;
535    }
536
537    if (do_psnr) {
538      const double global_psnr_y =
539          ComputePSNR(cur_distortion_psnr->global_y,
540                      static_cast<double>(y_size) * number_of_frames);
541      const double global_psnr_u =
542          ComputePSNR(cur_distortion_psnr->global_u,
543                      static_cast<double>(uv_size) * number_of_frames);
544      const double global_psnr_v =
545          ComputePSNR(cur_distortion_psnr->global_v,
546                      static_cast<double>(uv_size) * number_of_frames);
547      const double global_psnr_all =
548          ComputePSNR(cur_distortion_psnr->global_all,
549                      static_cast<double>(total_size) * number_of_frames);
550      printf("Global:\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d", global_psnr_y,
551             global_psnr_u, global_psnr_v, global_psnr_all, number_of_frames);
552      if (show_name) {
553        printf("\t%s", argv[fileindex_rec + cur_rec]);
554      }
555      printf("\n");
556    }
557
558    if (!quiet) {
559      printf("Avg:");
560      if (do_psnr) {
561        printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d", cur_distortion_psnr->y,
562               cur_distortion_psnr->u, cur_distortion_psnr->v,
563               cur_distortion_psnr->all, number_of_frames);
564      }
565      if (do_ssim) {
566        printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d", cur_distortion_ssim->y,
567               cur_distortion_ssim->u, cur_distortion_ssim->v,
568               cur_distortion_ssim->all, number_of_frames);
569      }
570      if (show_name) {
571        printf("\t%s", argv[fileindex_rec + cur_rec]);
572      }
573      printf("\n");
574    }
575    if (!quiet) {
576      printf("Min:");
577      if (do_psnr) {
578        printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d",
579               cur_distortion_psnr->min_y, cur_distortion_psnr->min_u,
580               cur_distortion_psnr->min_v, cur_distortion_psnr->min_all,
581               cur_distortion_psnr->min_frame);
582      }
583      if (do_ssim) {
584        printf("\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d",
585               cur_distortion_ssim->min_y, cur_distortion_ssim->min_u,
586               cur_distortion_ssim->min_v, cur_distortion_ssim->min_all,
587               cur_distortion_ssim->min_frame);
588      }
589      if (show_name) {
590        printf("\t%s", argv[fileindex_rec + cur_rec]);
591      }
592      printf("\n");
593    }
594
595    if (do_mse) {
596      double global_mse_y =
597          GetMSE(cur_distortion_psnr->global_y,
598                 static_cast<double>(y_size) * number_of_frames);
599      double global_mse_u =
600          GetMSE(cur_distortion_psnr->global_u,
601                 static_cast<double>(uv_size) * number_of_frames);
602      double global_mse_v =
603          GetMSE(cur_distortion_psnr->global_v,
604                 static_cast<double>(uv_size) * number_of_frames);
605      double global_mse_all =
606          GetMSE(cur_distortion_psnr->global_all,
607                 static_cast<double>(total_size) * number_of_frames);
608      printf("MSE:\t%10.6f\t%10.6f\t%10.6f\t%10.6f\t%5d", global_mse_y,
609             global_mse_u, global_mse_v, global_mse_all, number_of_frames);
610      if (show_name) {
611        printf("\t%s", argv[fileindex_rec + cur_rec]);
612      }
613      printf("\n");
614    }
615  }
616  fclose(file_org);
617  for (int cur_rec = 0; cur_rec < num_rec; ++cur_rec) {
618    fclose(file_rec[cur_rec]);
619  }
620  delete[] distortion_psnr;
621  delete[] distortion_ssim;
622  delete[] ch_org;
623  delete[] ch_rec;
624  delete[] file_rec;
625  return 0;
626}
627