blob: 5b06823ba9b3345551fe7c33bf226dc4df0dbb28 [file] [log] [blame]
/*
============================================================================
Name : psnr.c
Copyright : Copyright Google Inc, 2012.
Description : Computes the overall/global PSNR of two input yuv clips.
============================================================================
*/
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <sys/stat.h>
#define MAX_PSNR 100
typedef enum {
STATUS_OK = 0,
STATUS_USAGE_ERROR = -1,
STATUS_FILE_SIZE_ERROR = -2,
STATUS_FILE_OPEN_ERROR = -3,
STATUS_ARGS_ERROR = -4,
STATUS_ALLOC_ERROR = -5,
} STATUS_CODE;
/* Note: Handles Maximum file size of 2GB! */
int get_file_size(const char *filename) {
struct stat st;
stat(filename, &st);
return st.st_size;
}
double mse2psnr(double samples, double peak, double mse) {
double psnr;
if ((double)mse > 0.0)
psnr = 10.0 * log10(peak * peak * samples / mse);
else
psnr = MAX_PSNR; // Limit to prevent / 0
if (psnr > MAX_PSNR)
psnr = MAX_PSNR;
return psnr;
}
int main(int argc, char *argv[]) {
int i;
int width, height;
int frame_size;
int max_frames;
int number_of_frames = 0;
double total_sq_error = 0.0;
unsigned char *frame0 = NULL, *frame1 = NULL;
FILE *file0_ptr = NULL, *file1_ptr = NULL;
STATUS_CODE return_status = STATUS_OK;
if (argc < 6) {
fprintf (stderr, "Usage: %s <yuv_file1> <yuv_file2> "
"<width> <height> <max_frames>\n", argv[0]);
return_status = STATUS_USAGE_ERROR;
goto end;
}
width = strtol(argv[3], NULL, 10);
height = strtol(argv[4], NULL, 10);
if (width < 1 || height < 1) {
fprintf (stderr, "ERROR: invalid frame size %dx%d.\n", width, height);
return_status = STATUS_ARGS_ERROR;
goto end;
}
frame_size = width * height * 3 / 2;
{
int size0 = get_file_size(argv[1]);
int size1 = get_file_size(argv[2]);
if ((size0 <= 0) || (size1 <= 0)) {
fprintf(stderr, "ERROR: input file size exceeds 2GB limit.\n");
return_status = STATUS_FILE_SIZE_ERROR;
goto end;
}
if ((size0 != size1) || (size0 % frame_size)) {
fprintf(stderr, "ERROR: input files must be same size and have only "
"full frames (file sizes:%d, %d).\n", size0, size1);
return_status = STATUS_FILE_SIZE_ERROR;
goto end;
}
}
if ((file0_ptr = fopen(argv[1], "rb")) == NULL) {
fprintf (stderr, "ERROR: unable to open input file %s.\n", argv[1]);
return_status = STATUS_FILE_OPEN_ERROR;
goto end;
}
if ((file1_ptr = fopen(argv[2], "rb")) == NULL) {
fprintf (stderr, "ERROR: unable to open input file %s.\n", argv[2]);
return_status = STATUS_FILE_OPEN_ERROR;
goto end;
}
if ((frame0 = calloc(frame_size, sizeof(unsigned char))) == NULL) {
fprintf (stderr, "ERROR: unable to allocate memory.\n");
return_status = STATUS_ALLOC_ERROR;
goto end;
}
if ((frame1 = calloc(frame_size, sizeof(unsigned char))) == NULL) {
fprintf (stderr, "ERROR: unable to allocate memory.\n");
return_status = STATUS_ALLOC_ERROR;
goto end;
}
max_frames = strtol(argv[5], NULL, 10);
while ((number_of_frames < max_frames)
&& (fread(frame0, 1, frame_size, file0_ptr) == frame_size)
&& (fread(frame1, 1, frame_size, file1_ptr) == frame_size)) {
unsigned char *ptr0 = frame0;
unsigned char *ptr1 = frame1;
for (i = 0; i < frame_size; ++i) {
double diff = (*ptr1) - (*ptr0);
total_sq_error += diff * diff;
++ptr0; ++ptr1;
}
++number_of_frames;
}
if (number_of_frames > 0) {
double samples = number_of_frames * frame_size;
double total_psnr = mse2psnr(samples, 255.0, total_sq_error);
fprintf(stdout, "%.3lf\n", total_psnr);
}
end:
if (frame0) free(frame0);
if (frame1) free(frame1);
if (file0_ptr) fclose(file0_ptr);
if (file1_ptr) fclose(file1_ptr);
return return_status;
}