Update video_quality_analysis to align videos instead of using barcodes
This CL is a follow-up to the previous CL https://webrtc-review.googlesource.com/c/src/+/94773 that added generic logic for aligning videos. This will allow us to easily extend video_quality_analysis with new sophisticated video quality metrics. Also, we can use any kind of video that does not necessarily need to contain bar codes. Removing the need to decode barcodes also leads to a big speedup for the tests. Bug: webrtc:9642 Change-Id: I74b0d630b3e1ed44781ad024115ded3143e28f50 Reviewed-on: https://webrtc-review.googlesource.com/94845 Reviewed-by: Paulina Hensman <phensman@webrtc.org> Reviewed-by: Patrik Höglund <phoglund@webrtc.org> Commit-Queue: Magnus Jedvert <magjed@webrtc.org> Cr-Commit-Position: refs/heads/master@{#24423}
This commit is contained in:
committed by
Commit Bot
parent
523b4c4330
commit
d65e143801
@ -1,372 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# 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.
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
import helper_functions
|
||||
|
||||
_DEFAULT_BARCODE_WIDTH = 352
|
||||
_DEFAULT_BARCODES_FILE = 'barcodes.yuv'
|
||||
|
||||
|
||||
def GenerateUpcaBarcodes(number_of_barcodes, barcode_width, barcode_height,
|
||||
output_directory='.',
|
||||
path_to_zxing='zxing-read-only'):
|
||||
"""Generates UPC-A barcodes.
|
||||
|
||||
This function generates a number_of_barcodes UPC-A barcodes. The function
|
||||
calls an example Java encoder from the Zxing library. The barcodes are
|
||||
generated as PNG images. The width of the barcodes shouldn't be less than 102
|
||||
pixels because otherwise Zxing can't properly generate the barcodes.
|
||||
|
||||
Args:
|
||||
number_of_barcodes(int): The number of barcodes to generate.
|
||||
barcode_width(int): Width of barcode in pixels.
|
||||
barcode_height(int): Height of barcode in pixels.
|
||||
output_directory(string): Output directory where to store generated
|
||||
barcodes.
|
||||
path_to_zxing(string): The path to Zxing.
|
||||
|
||||
Return:
|
||||
(bool): True if the conversion is successful.
|
||||
"""
|
||||
base_file_name = os.path.join(output_directory, "barcode_")
|
||||
jars = _FormJarsString(path_to_zxing)
|
||||
command_line_encoder = 'com.google.zxing.client.j2se.CommandLineEncoder'
|
||||
barcode_width = str(barcode_width)
|
||||
barcode_height = str(barcode_height)
|
||||
|
||||
errors = False
|
||||
for i in range(number_of_barcodes):
|
||||
suffix = helper_functions.ZeroPad(i)
|
||||
# Barcodes starting from 0
|
||||
content = helper_functions.ZeroPad(i, 11)
|
||||
output_file_name = base_file_name + suffix + ".png"
|
||||
|
||||
command = ["java", "-cp", jars, command_line_encoder,
|
||||
"--barcode_format=UPC_A", "--height=%s" % barcode_height,
|
||||
"--width=%s" % barcode_width,
|
||||
"--output=%s" % (output_file_name), "%s" % (content)]
|
||||
try:
|
||||
helper_functions.RunShellCommand(
|
||||
command, fail_msg=('Error during barcode %s generation' % content))
|
||||
except helper_functions.HelperError as err:
|
||||
print >> sys.stderr, err
|
||||
errors = True
|
||||
return not errors
|
||||
|
||||
|
||||
def ConvertPngToYuvBarcodes(input_directory='.', output_directory='.'):
|
||||
"""Converts PNG barcodes to YUV barcode images.
|
||||
|
||||
This function reads all the PNG files from the input directory which are in
|
||||
the format frame_xxxx.png, where xxxx is the number of the frame, starting
|
||||
from 0000. The frames should be consecutive numbers. The output YUV file is
|
||||
named frame_xxxx.yuv. The function uses ffmpeg to do the conversion.
|
||||
|
||||
Args:
|
||||
input_directory(string): The input direcotry to read the PNG barcodes from.
|
||||
output_directory(string): The putput directory to write the YUV files to.
|
||||
Return:
|
||||
(bool): True if the conversion was without errors.
|
||||
"""
|
||||
return helper_functions.PerformActionOnAllFiles(
|
||||
input_directory, 'barcode_', 'png', 0, _ConvertToYuvAndDelete,
|
||||
output_directory=output_directory, pattern='barcode_')
|
||||
|
||||
|
||||
def _ConvertToYuvAndDelete(output_directory, file_name, pattern):
|
||||
"""Converts a PNG file to a YUV file and deletes the PNG file.
|
||||
|
||||
Args:
|
||||
output_directory(string): The output directory for the YUV file.
|
||||
file_name(string): The PNG file name.
|
||||
pattern(string): The file pattern of the PNG/YUV file. The PNG/YUV files are
|
||||
named patternxx..x.png/yuv, where xx..x are digits starting from 00..0.
|
||||
Return:
|
||||
(bool): True upon successful conversion, false otherwise.
|
||||
"""
|
||||
# Pattern should be in file name
|
||||
if not pattern in file_name:
|
||||
return False
|
||||
pattern_position = file_name.rfind(pattern)
|
||||
|
||||
# Strip the path to the PNG file and replace the png extension with yuv
|
||||
yuv_file_name = file_name[pattern_position:-3] + 'yuv'
|
||||
yuv_file_name = os.path.join(output_directory, yuv_file_name)
|
||||
|
||||
command = ['ffmpeg', '-i', '%s' % (file_name), '-pix_fmt', 'yuv420p',
|
||||
'%s' % (yuv_file_name)]
|
||||
try:
|
||||
helper_functions.RunShellCommand(
|
||||
command, fail_msg=('Error during PNG to YUV conversion of %s' %
|
||||
file_name))
|
||||
os.remove(file_name)
|
||||
except helper_functions.HelperError as err:
|
||||
print >> sys.stderr, err
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def CombineYuvFramesIntoOneFile(output_file_name, input_directory='.'):
|
||||
"""Combines several YUV frames into one YUV video file.
|
||||
|
||||
The function combines the YUV frames from input_directory into one YUV video
|
||||
file. The frames should be named in the format frame_xxxx.yuv where xxxx
|
||||
stands for the frame number. The numbers have to be consecutive and start from
|
||||
0000. The YUV frames are removed after they have been added to the video.
|
||||
|
||||
Args:
|
||||
output_file_name(string): The name of the file to produce.
|
||||
input_directory(string): The directory from which the YUV frames are read.
|
||||
Return:
|
||||
(bool): True if the frame stitching went OK.
|
||||
"""
|
||||
output_file = open(output_file_name, "wb")
|
||||
success = helper_functions.PerformActionOnAllFiles(
|
||||
input_directory, 'barcode_', 'yuv', 0, _AddToFileAndDelete,
|
||||
output_file=output_file)
|
||||
output_file.close()
|
||||
return success
|
||||
|
||||
def _AddToFileAndDelete(output_file, file_name):
|
||||
"""Adds the contents of a file to a previously opened file.
|
||||
|
||||
Args:
|
||||
output_file(file): The ouput file, previously opened.
|
||||
file_name(string): The file name of the file to add to the output file.
|
||||
|
||||
Return:
|
||||
(bool): True if successful, False otherwise.
|
||||
"""
|
||||
input_file = open(file_name, "rb")
|
||||
input_file_contents = input_file.read()
|
||||
output_file.write(input_file_contents)
|
||||
input_file.close()
|
||||
try:
|
||||
os.remove(file_name)
|
||||
except OSError as e:
|
||||
print >> sys.stderr, 'Error deleting file %s.\nError: %s' % (file_name, e)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _OverlayBarcodeAndBaseFrames(barcodes_file, base_file, output_file,
|
||||
barcodes_component_sizes,
|
||||
base_component_sizes):
|
||||
"""Overlays the next YUV frame from a file with a barcode.
|
||||
|
||||
Args:
|
||||
barcodes_file(FileObject): The YUV file containing the barcodes (opened).
|
||||
base_file(FileObject): The base YUV file (opened).
|
||||
output_file(FileObject): The output overlaid file (opened).
|
||||
barcodes_component_sizes(list of tuples): The width and height of each Y, U
|
||||
and V plane of the barcodes YUV file.
|
||||
base_component_sizes(list of tuples): The width and height of each Y, U and
|
||||
V plane of the base YUV file.
|
||||
Return:
|
||||
(bool): True if there are more planes (i.e. frames) in the base file, false
|
||||
otherwise.
|
||||
"""
|
||||
# We will loop three times - once for the Y, U and V planes
|
||||
for ((barcode_comp_width, barcode_comp_height),
|
||||
(base_comp_width, base_comp_height)) in zip(barcodes_component_sizes,
|
||||
base_component_sizes):
|
||||
for base_row in range(base_comp_height):
|
||||
barcode_plane_traversed = False
|
||||
if (base_row < barcode_comp_height) and not barcode_plane_traversed:
|
||||
barcode_plane = barcodes_file.read(barcode_comp_width)
|
||||
if barcode_plane == "":
|
||||
barcode_plane_traversed = True
|
||||
else:
|
||||
barcode_plane_traversed = True
|
||||
base_plane = base_file.read(base_comp_width)
|
||||
|
||||
if base_plane == "":
|
||||
return False
|
||||
|
||||
if not barcode_plane_traversed:
|
||||
# Substitute part of the base component with the top component
|
||||
output_file.write(barcode_plane)
|
||||
base_plane = base_plane[barcode_comp_width:]
|
||||
output_file.write(base_plane)
|
||||
return True
|
||||
|
||||
|
||||
def OverlayYuvFiles(barcode_width, barcode_height, base_width, base_height,
|
||||
barcodes_file_name, base_file_name, output_file_name):
|
||||
"""Overlays two YUV files starting from the upper left corner of both.
|
||||
|
||||
Args:
|
||||
barcode_width(int): The width of the barcode (to be overlaid).
|
||||
barcode_height(int): The height of the barcode (to be overlaid).
|
||||
base_width(int): The width of a frame of the base file.
|
||||
base_height(int): The height of a frame of the base file.
|
||||
barcodes_file_name(string): The name of the YUV file containing the YUV
|
||||
barcodes.
|
||||
base_file_name(string): The name of the base YUV file.
|
||||
output_file_name(string): The name of the output file where the overlaid
|
||||
video will be written.
|
||||
"""
|
||||
# Component sizes = [Y_sizes, U_sizes, V_sizes]
|
||||
barcodes_component_sizes = [(barcode_width, barcode_height),
|
||||
(barcode_width/2, barcode_height/2),
|
||||
(barcode_width/2, barcode_height/2)]
|
||||
base_component_sizes = [(base_width, base_height),
|
||||
(base_width/2, base_height/2),
|
||||
(base_width/2, base_height/2)]
|
||||
|
||||
barcodes_file = open(barcodes_file_name, 'rb')
|
||||
base_file = open(base_file_name, 'rb')
|
||||
output_file = open(output_file_name, 'wb')
|
||||
|
||||
data_left = True
|
||||
while data_left:
|
||||
data_left = _OverlayBarcodeAndBaseFrames(barcodes_file, base_file,
|
||||
output_file,
|
||||
barcodes_component_sizes,
|
||||
base_component_sizes)
|
||||
|
||||
barcodes_file.close()
|
||||
base_file.close()
|
||||
output_file.close()
|
||||
|
||||
|
||||
def CalculateFramesNumberFromYuv(yuv_width, yuv_height, file_name):
|
||||
"""Calculates the number of frames of a YUV video.
|
||||
|
||||
Args:
|
||||
yuv_width(int): Width of a frame of the yuv file.
|
||||
yuv_height(int): Height of a frame of the YUV file.
|
||||
file_name(string): The name of the YUV file.
|
||||
Return:
|
||||
(int): The number of frames in the YUV file.
|
||||
"""
|
||||
file_size = os.path.getsize(file_name)
|
||||
|
||||
y_plane_size = yuv_width * yuv_height
|
||||
u_plane_size = (yuv_width/2) * (yuv_height/2) # Equals to V plane size too
|
||||
frame_size = y_plane_size + (2 * u_plane_size)
|
||||
return int(file_size/frame_size) # Should be int anyway
|
||||
|
||||
|
||||
def _FormJarsString(path_to_zxing):
|
||||
"""Forms the the Zxing core and javase jars argument.
|
||||
|
||||
Args:
|
||||
path_to_zxing(string): The path to the Zxing checkout folder.
|
||||
Return:
|
||||
(string): The newly formed jars argument.
|
||||
"""
|
||||
javase_jar = os.path.join(path_to_zxing, "javase", "javase.jar")
|
||||
core_jar = os.path.join(path_to_zxing, "core", "core.jar")
|
||||
delimiter = ':'
|
||||
if os.name != 'posix':
|
||||
delimiter = ';'
|
||||
return javase_jar + delimiter + core_jar
|
||||
|
||||
def _ParseArgs():
|
||||
"""Registers the command-line options."""
|
||||
usage = "usage: %prog [options]"
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
|
||||
parser.add_option('--barcode_width', type='int',
|
||||
default=_DEFAULT_BARCODE_WIDTH,
|
||||
help=('Width of the barcodes to be overlaid on top of the'
|
||||
' base file. Default: %default'))
|
||||
parser.add_option('--barcode_height', type='int', default=32,
|
||||
help=('Height of the barcodes to be overlaid on top of the'
|
||||
' base file. Default: %default'))
|
||||
parser.add_option('--base_frame_width', type='int', default=352,
|
||||
help=('Width of the base YUV file\'s frames. '
|
||||
'Default: %default'))
|
||||
parser.add_option('--base_frame_height', type='int', default=288,
|
||||
help=('Height of the top YUV file\'s frames. '
|
||||
'Default: %default'))
|
||||
parser.add_option('--barcodes_yuv', type='string',
|
||||
default=_DEFAULT_BARCODES_FILE,
|
||||
help=('The YUV file with the barcodes in YUV. '
|
||||
'Default: %default'))
|
||||
parser.add_option('--base_yuv', type='string', default='base.yuv',
|
||||
help=('The base YUV file to be overlaid. '
|
||||
'Default: %default'))
|
||||
parser.add_option('--output_yuv', type='string', default='output.yuv',
|
||||
help=('The output YUV file containing the base overlaid'
|
||||
' with the barcodes. Default: %default'))
|
||||
parser.add_option('--png_barcodes_output_dir', type='string', default='.',
|
||||
help=('Output directory where the PNG barcodes will be '
|
||||
'generated. Default: %default'))
|
||||
parser.add_option('--png_barcodes_input_dir', type='string', default='.',
|
||||
help=('Input directory from where the PNG barcodes will be '
|
||||
'read. Default: %default'))
|
||||
parser.add_option('--yuv_barcodes_output_dir', type='string', default='.',
|
||||
help=('Output directory where the YUV barcodes will be '
|
||||
'generated. Default: %default'))
|
||||
parser.add_option('--yuv_frames_input_dir', type='string', default='.',
|
||||
help=('Input directory from where the YUV will be '
|
||||
'read before combination. Default: %default'))
|
||||
parser.add_option('--zxing_dir', type='string', default='zxing',
|
||||
help=('Path to the Zxing barcodes library. '
|
||||
'Default: %default'))
|
||||
options = parser.parse_args()[0]
|
||||
return options
|
||||
|
||||
|
||||
def main():
|
||||
"""The main function.
|
||||
|
||||
A simple invocation will be:
|
||||
./webrtc/rtc_tools/barcode_tools/barcode_encoder.py --barcode_height=32
|
||||
--base_frame_width=352 --base_frame_height=288
|
||||
--base_yuv=<path_and_name_of_base_file>
|
||||
--output_yuv=<path and name_of_output_file>
|
||||
"""
|
||||
options = _ParseArgs()
|
||||
# The barcodes with will be different than the base frame width only if
|
||||
# explicitly specified at the command line.
|
||||
if options.barcode_width == _DEFAULT_BARCODE_WIDTH:
|
||||
options.barcode_width = options.base_frame_width
|
||||
# If the user provides a value for the barcodes YUV video file, we will keep
|
||||
# it. Otherwise we create a temp file which is removed after it has been used.
|
||||
keep_barcodes_yuv_file = False
|
||||
if options.barcodes_yuv != _DEFAULT_BARCODES_FILE:
|
||||
keep_barcodes_yuv_file = True
|
||||
|
||||
# Calculate the number of barcodes - it is equal to the number of frames in
|
||||
# the base file.
|
||||
number_of_barcodes = CalculateFramesNumberFromYuv(
|
||||
options.base_frame_width, options.base_frame_height, options.base_yuv)
|
||||
|
||||
script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||
zxing_dir = os.path.join(script_dir, 'third_party', 'zxing')
|
||||
# Generate barcodes - will generate them in PNG.
|
||||
GenerateUpcaBarcodes(number_of_barcodes, options.barcode_width,
|
||||
options.barcode_height,
|
||||
output_directory=options.png_barcodes_output_dir,
|
||||
path_to_zxing=zxing_dir)
|
||||
# Convert the PNG barcodes to to YUV format.
|
||||
ConvertPngToYuvBarcodes(options.png_barcodes_input_dir,
|
||||
options.yuv_barcodes_output_dir)
|
||||
# Combine the YUV barcodes into one YUV file.
|
||||
CombineYuvFramesIntoOneFile(options.barcodes_yuv,
|
||||
input_directory=options.yuv_frames_input_dir)
|
||||
# Overlay the barcodes over the base file.
|
||||
OverlayYuvFiles(options.barcode_width, options.barcode_height,
|
||||
options.base_frame_width, options.base_frame_height,
|
||||
options.barcodes_yuv, options.base_yuv, options.output_yuv)
|
||||
|
||||
if not keep_barcodes_yuv_file:
|
||||
# Remove the temporary barcodes YUV file
|
||||
os.remove(options.barcodes_yuv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user