A small tool to run PSNR and SSIM between two I420 videos.
The tool runs the analysis between every two corresponding frames until either of the videos runs out of frames. The results are written in a file (results_file provided from the command line) in the format: Frame: <frame_number>, PSNR: <psnr_value>, SSIM: <ssim_value> TEST= psnr_ssim_analyzer --reference_file=<name_of_file> --test_file=<name_of_file> --results_file=<name_of_file> --width=<width_of_frames> --height=<height_of_frames> Review URL: https://webrtc-codereview.appspot.com/748007 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2665 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
4368c26b1a
commit
ac410e26a5
2
src/tools/OWNERS
Normal file
2
src/tools/OWNERS
Normal file
@ -0,0 +1,2 @@
|
||||
phoglund@webrtc.org
|
||||
kjellander@webrtc.org
|
@ -8,10 +8,8 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@ -19,15 +17,38 @@
|
||||
#include "tools/frame_analyzer/video_quality_analysis.h"
|
||||
#include "tools/simple_command_line_parser.h"
|
||||
|
||||
#define STATS_LINE_LENGTH 25
|
||||
|
||||
/*
|
||||
* A command line tool running PSNR and SSIM on a reference video and a test
|
||||
* video. The test video is a record of the reference video which can start at
|
||||
* an arbitrary point. It is possible that there will be repeated frames or
|
||||
* skipped frames as well. In order to have a way to compare corresponding
|
||||
* frames from the two videos, a stats file should be provided. The stats file
|
||||
* is a text file assumed to be in the format:
|
||||
* frame_xxxx yyyy
|
||||
* where xxxx is the frame number in the test video and yyyy is the
|
||||
* corresponding frame number in the original video.
|
||||
* The video files should be 1420 YUV videos.
|
||||
* The tool prints the result to the standard output in the following format:
|
||||
* BSTATS
|
||||
* <psnr_value> <ssim_value>; <psnr_value> <ssim_value>; ....
|
||||
* ESTATS
|
||||
* Unique_frames_count:<value>
|
||||
* Max_repeated:<value>
|
||||
* Max_skipped<value>
|
||||
*
|
||||
* The max value for PSNR is 48.0 (between equal frames), as for SSIM it is 1.0.
|
||||
*
|
||||
* Usage:
|
||||
* frame_analyzer --reference_file=<name_of_file> --test_file=<name_of_file>
|
||||
* --stats_file=<name_of_file> --width=<frame_width> --height=<frame_height>
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
std::string program_name = argv[0];
|
||||
std::string usage = "Compares the output video with the initially sent video."
|
||||
"\nExample usage:\n" + program_name + " --stats_file=stats.txt "
|
||||
"--reference_file=ref.yuv --test_file=test.yuv --width=320 --height=240\n"
|
||||
"Command line flags:\n"
|
||||
" - width(int): The width of the refence and test files. Default: -1\n"
|
||||
" - width(int): The width of the reference and test files. Default: -1\n"
|
||||
" - height(int): The height of the reference and test files. "
|
||||
" Default: -1\n"
|
||||
" - stats_file(string): The full name of the file containing the stats"
|
||||
|
@ -20,13 +20,13 @@
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
unsigned int GetI420FrameSize(unsigned int width, unsigned int height) {
|
||||
unsigned int half_width = (width + 1) >> 1;
|
||||
unsigned int half_height = (height + 1) >> 1;
|
||||
int GetI420FrameSize(int width, int height) {
|
||||
int half_width = (width + 1) >> 1;
|
||||
int half_height = (height + 1) >> 1;
|
||||
|
||||
unsigned int y_plane = width * height; // I420 Y plane.
|
||||
unsigned int u_plane = half_width * half_height; // I420 U plane.
|
||||
unsigned int v_plane = half_width * half_height; // I420 V plane.
|
||||
int y_plane = width * height; // I420 Y plane.
|
||||
int u_plane = half_width * half_height; // I420 U plane.
|
||||
int v_plane = half_width * half_height; // I420 V plane.
|
||||
|
||||
return y_plane + u_plane + v_plane;
|
||||
}
|
||||
@ -83,11 +83,15 @@ bool GetNextStatsLine(FILE* stats_file, char* line) {
|
||||
|
||||
bool GetNextI420Frame(FILE* input_file, int width, int height,
|
||||
uint8* result_frame) {
|
||||
unsigned int frame_size = GetI420FrameSize(width, height);
|
||||
int frame_size = GetI420FrameSize(width, height);
|
||||
bool errors = false;
|
||||
|
||||
size_t bytes_read = fread(result_frame, 1, frame_size, input_file);
|
||||
if (bytes_read != frame_size) {
|
||||
if (bytes_read != static_cast<size_t>(frame_size)) {
|
||||
// If end-of-file is reached, don't print an error.
|
||||
if (feof(input_file)) {
|
||||
return false;
|
||||
}
|
||||
fprintf(stdout, "Error while reading frame from file\n");
|
||||
errors = true;
|
||||
}
|
||||
@ -96,7 +100,7 @@ bool GetNextI420Frame(FILE* input_file, int width, int height,
|
||||
|
||||
bool ExtractFrameFromI420(const char* i420_file_name, int width, int height,
|
||||
int frame_number, uint8* result_frame) {
|
||||
unsigned int frame_size = GetI420FrameSize(width, height);
|
||||
int frame_size = GetI420FrameSize(width, height);
|
||||
int offset = frame_number * frame_size; // Calculate offset for the frame.
|
||||
bool errors = false;
|
||||
|
||||
@ -111,7 +115,7 @@ bool ExtractFrameFromI420(const char* i420_file_name, int width, int height,
|
||||
fseek(input_file, offset, SEEK_SET);
|
||||
|
||||
size_t bytes_read = fread(result_frame, 1, frame_size, input_file);
|
||||
if (bytes_read != frame_size &&
|
||||
if (bytes_read != static_cast<size_t>(frame_size) &&
|
||||
ferror(input_file)) {
|
||||
fprintf(stdout, "Error while reading frame no %d from file %s\n",
|
||||
frame_number, i420_file_name);
|
||||
|
@ -67,7 +67,7 @@ void PrintMaxRepeatedAndSkippedFrames(const char* stats_file_name);
|
||||
bool GetNextStatsLine(FILE* stats_file, char* line);
|
||||
|
||||
// Calculates the size of a I420 frame if given the width and height.
|
||||
unsigned int GetI420FrameSize(unsigned int width, unsigned int height);
|
||||
int GetI420FrameSize(int width, int height);
|
||||
|
||||
// Extract the sequence of the frame in the video. I.e. if line is
|
||||
// frame_0023 0284, we will get 23.
|
||||
|
118
src/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc
Normal file
118
src/tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "tools/frame_analyzer/video_quality_analysis.h"
|
||||
#include "tools/simple_command_line_parser.h"
|
||||
|
||||
void CompareFiles(const char* reference_file_name, const char* test_file_name,
|
||||
const char* results_file_name, int width, int height) {
|
||||
FILE* ref_file = fopen(reference_file_name, "rb");
|
||||
FILE* test_file = fopen(test_file_name, "rb");
|
||||
FILE* results_file = fopen(results_file_name, "w");
|
||||
|
||||
int size = webrtc::test::GetI420FrameSize(width, height);
|
||||
|
||||
// Allocate buffers for test and reference frames.
|
||||
uint8* test_frame = new uint8[size];
|
||||
uint8* ref_frame = new uint8[size];
|
||||
|
||||
int frame_counter = 0;
|
||||
|
||||
while (webrtc::test::GetNextI420Frame(ref_file, width, height, ref_frame) &&
|
||||
webrtc::test::GetNextI420Frame(test_file, width, height, test_frame)) {
|
||||
// Calculate the PSNR and SSIM.
|
||||
double result_psnr = webrtc::test::CalculateMetrics(
|
||||
webrtc::test::kPSNR, ref_frame, test_frame, width, height);
|
||||
double result_ssim = webrtc::test::CalculateMetrics(
|
||||
webrtc::test::kSSIM, ref_frame, test_frame, width, height);
|
||||
fprintf(results_file, "Frame: %d, PSNR: %f, SSIM: %f\n", frame_counter,
|
||||
result_psnr, result_ssim);
|
||||
++frame_counter;
|
||||
}
|
||||
delete[] test_frame;
|
||||
delete[] ref_frame;
|
||||
|
||||
fclose(ref_file);
|
||||
fclose(test_file);
|
||||
fclose(results_file);
|
||||
}
|
||||
|
||||
/*
|
||||
* A tool running PSNR and SSIM analysis on two videos - a reference video and a
|
||||
* test video. The two videos should be I420 YUV videos.
|
||||
* The tool just runs PSNR and SSIM on the corresponding frames in the test and
|
||||
* the reference videos until either the first or the second video runs out of
|
||||
* frames. The result is written in a results text file in the format:
|
||||
* Frame: <frame_number>, PSNR: <psnr_value>, SSIM: <ssim_value>
|
||||
* Frame: <frame_number>, ........
|
||||
*
|
||||
* The max value for PSNR is 48.0 (between equal frames), as for SSIM it is 1.0.
|
||||
*
|
||||
* Usage:
|
||||
* psnr_ssim_analyzer --reference_file=<name_of_file> --test_file=<name_of_file>
|
||||
* --results_file=<name_of_file> --width=<width_of_frames>
|
||||
* --height=<height_of_frames>
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
std::string program_name = argv[0];
|
||||
std::string usage = "Runs PSNR and SSIM on two I420 videos and write the"
|
||||
"results in a file.\n"
|
||||
"Example usage:\n" + program_name + " --reference_file=ref.yuv "
|
||||
"--test_file=test.yuv --results_file=results.txt --width=320 "
|
||||
"--height=240\n"
|
||||
"Command line flags:\n"
|
||||
" - width(int): The width of the reference and test files. Default: -1\n"
|
||||
" - height(int): The height of the reference and test files. "
|
||||
" Default: -1\n"
|
||||
" - reference_file(string): The reference YUV file to compare against."
|
||||
" Default: ref.yuv\n"
|
||||
" - test_file(string): The test YUV file to run the analysis for."
|
||||
" Default: test_file.yuv\n"
|
||||
" - results_file(string): The full name of the file where the results "
|
||||
"will be written. Default: results.txt\n";
|
||||
|
||||
webrtc::test::CommandLineParser parser;
|
||||
|
||||
// Init the parser and set the usage message
|
||||
parser.Init(argc, argv);
|
||||
parser.SetUsageMessage(usage);
|
||||
|
||||
parser.SetFlag("width", "-1");
|
||||
parser.SetFlag("height", "-1");
|
||||
parser.SetFlag("results_file", "results.txt");
|
||||
parser.SetFlag("reference_file", "ref.yuv");
|
||||
parser.SetFlag("test_file", "test.yuv");
|
||||
parser.SetFlag("results_file", "results.txt");
|
||||
parser.SetFlag("help", "false");
|
||||
|
||||
parser.ProcessFlags();
|
||||
if (parser.GetFlag("help") == "true") {
|
||||
parser.PrintUsageMessage();
|
||||
}
|
||||
parser.PrintEnteredFlags();
|
||||
|
||||
int width = strtol((parser.GetFlag("width")).c_str(), NULL, 10);
|
||||
int height = strtol((parser.GetFlag("height")).c_str(), NULL, 10);
|
||||
|
||||
if (width <= 0 || height <= 0) {
|
||||
fprintf(stderr, "Error: width or height cannot be <= 0!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
CompareFiles(parser.GetFlag("reference_file").c_str(),
|
||||
parser.GetFlag("test_file").c_str(),
|
||||
parser.GetFlag("results_file").c_str(), width, height);
|
||||
}
|
@ -11,33 +11,78 @@
|
||||
'../build/common.gypi',
|
||||
],
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'frame_analyzer',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv',
|
||||
{
|
||||
'target_name': 'command_line_parser',
|
||||
'type': '<(library)',
|
||||
'include_dirs': [
|
||||
'.',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'.',
|
||||
],
|
||||
},
|
||||
'sources': [
|
||||
'frame_analyzer/frame_analyzer.cc',
|
||||
'frame_analyzer/video_quality_analysis.h',
|
||||
'frame_analyzer/video_quality_analysis.cc',
|
||||
'simple_command_line_parser.h',
|
||||
'simple_command_line_parser.cc',
|
||||
],
|
||||
},
|
||||
}, # command_line_parser
|
||||
{
|
||||
'target_name': 'video_quality_analysis',
|
||||
'type': '<(library)',
|
||||
'dependencies': [
|
||||
'<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv',
|
||||
],
|
||||
'include_dirs': [
|
||||
'frame_analyzer',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'frame_analyzer',
|
||||
],
|
||||
},
|
||||
'export_dependent_settings': [
|
||||
'<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv',
|
||||
],
|
||||
'sources': [
|
||||
'frame_analyzer/video_quality_analysis.h',
|
||||
'frame_analyzer/video_quality_analysis.cc',
|
||||
],
|
||||
}, # video_quality_analysis
|
||||
{
|
||||
'target_name': 'frame_analyzer',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'command_line_parser',
|
||||
'video_quality_analysis',
|
||||
],
|
||||
'sources': [
|
||||
'frame_analyzer/frame_analyzer.cc',
|
||||
],
|
||||
}, # frame_analyzer
|
||||
{
|
||||
'target_name': 'psnr_ssim_analyzer',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'command_line_parser',
|
||||
'video_quality_analysis',
|
||||
],
|
||||
'sources': [
|
||||
'psnr_ssim_analyzer/psnr_ssim_analyzer.cc',
|
||||
],
|
||||
}, # psnr_ssim_analyzer
|
||||
{
|
||||
'target_name': 'rgba_to_i420_converter',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'command_line_parser',
|
||||
'<(DEPTH)/third_party/libyuv/libyuv.gyp:libyuv',
|
||||
],
|
||||
'sources': [
|
||||
'converter/converter.h',
|
||||
'converter/converter.cc',
|
||||
'converter/rgba_to_i420_converter.cc',
|
||||
'simple_command_line_parser.h',
|
||||
'simple_command_line_parser.cc',
|
||||
],
|
||||
},
|
||||
}, # rgba_to_i420_converter
|
||||
],
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user