diff --git a/setup.py b/setup.py index ff76ba9621da17adb041922d5be3f30f1b33f6d1..9daf8a8622298318dbfb6a1819519fff63407406 100644 --- a/setup.py +++ b/setup.py @@ -15,15 +15,6 @@ include_dirs = [package_dir] packages = ['bob-io >= 2.0.0a2'] version = '2.0.0a0' -# Check if python-imaging means pil or pillow -pil_or_pillow = [] -try: - import pkg_resources - pkg_resources.require('PIL') - pil_or_pillow.append('pil') -except pkg_resources.DistributionNotFound as e: - pil_or_pillow.append('pillow') - setup( name='xbob.io.base', @@ -42,7 +33,7 @@ setup( install_requires=[ 'setuptools', 'xbob.blitz', - ] + pil_or_pillow, + ], namespace_packages=[ "xbob", @@ -57,31 +48,21 @@ setup( packages = packages, include_dirs = include_dirs, version = version, - define_macros = [('__STDC_CONSTANT_MACROS', None)], ), Extension("xbob.io.base._library", [ "xbob/io/base/bobskin.cpp", "xbob/io/base/codec.cpp", "xbob/io/base/file.cpp", - "xbob/io/base/videoreader.cpp", - "xbob/io/base/videowriter.cpp", "xbob/io/base/hdf5.cpp", "xbob/io/base/main.cpp", ], packages = packages, include_dirs = include_dirs, version = version, - define_macros = [('__STDC_CONSTANT_MACROS', None)], ), ], - entry_points={ - 'console_scripts': [ - 'xbob_video_test.py = xbob.io.script.video_test:main', - ], - }, - classifiers = [ 'Development Status :: 3 - Alpha', 'Intended Audience :: Developers', diff --git a/xbob/io/base/__init__.py b/xbob/io/base/__init__.py index 62541ea13993e7a737d5a101894c5df065b2c3c3..436943a423aeada364f7558392b7dda0ad95219e 100644 --- a/xbob/io/base/__init__.py +++ b/xbob/io/base/__init__.py @@ -1,10 +1,22 @@ -from ._library import File, VideoReader, VideoWriter, HDF5File +from ._library import File, HDF5File from . import version from .version import module as __version__ from .version import api as __api_version__ import os +def __is_string__(s): + """Returns ``True`` if the given object is a string + + This method can be used with Python-2.x or 3.x and returns a string + respecting each environment's constraints. + """ + + from sys import version_info + + return (version_info[0] < 3 and isinstance(s, (str, unicode))) or \ + isinstance(s, (bytes, str)) + def create_directories_save(directory, dryrun=False): """Creates a directory if it does not exists, with concurrent access support. This function will also create any parent directories that might be required. @@ -59,13 +71,12 @@ def load(inputs): from collections import Iterable import numpy - from .utils import is_string - if is_string(inputs): + if __is_string__(inputs): return File(inputs, 'r').read() elif isinstance(inputs, Iterable): retval = [] for obj in inputs: - if is_string(obj): + if __is_string__(obj): retval.append(load(obj)) elif isinstance(obj, File): retval.append(obj.read()) diff --git a/xbob/io/base/data/img_indexed_color.png b/xbob/io/base/data/img_indexed_color.png deleted file mode 100644 index c21427b328350a26155841013e461fa46e406be5..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/data/img_indexed_color.png and /dev/null differ diff --git a/xbob/io/base/data/test.jpg b/xbob/io/base/data/test.jpg deleted file mode 100644 index 0bf2deb6a8f29793c1ae15a880aef370c43d01fc..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/data/test.jpg and /dev/null differ diff --git a/xbob/io/base/data/test.mov b/xbob/io/base/data/test.mov deleted file mode 100644 index 29288925be1751b12c8c4fb10079a6b49e64fa7e..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/data/test.mov and /dev/null differ diff --git a/xbob/io/base/data/test.pbm b/xbob/io/base/data/test.pbm deleted file mode 100644 index 0902b226f358f3b4703342c59f93200c3263d880..0000000000000000000000000000000000000000 --- a/xbob/io/base/data/test.pbm +++ /dev/null @@ -1,3 +0,0 @@ -P4 -4 6 -€@ @ \ No newline at end of file diff --git a/xbob/io/base/data/test.pgm b/xbob/io/base/data/test.pgm deleted file mode 100644 index 32cd7fae38dc8850cb7ce244908ba749eb364327..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/data/test.pgm and /dev/null differ diff --git a/xbob/io/base/data/test.ppm b/xbob/io/base/data/test.ppm deleted file mode 100644 index a5baf843114a68f76cdc36e96658bfbcd782b3eb..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/data/test.ppm and /dev/null differ diff --git a/xbob/io/base/data/test_h264.mov b/xbob/io/base/data/test_h264.mov deleted file mode 100644 index a7de9a3ca48dad544104d14ec162811c1a81c23e..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/data/test_h264.mov and /dev/null differ diff --git a/xbob/io/base/fonts/bold.ttf b/xbob/io/base/fonts/bold.ttf deleted file mode 100644 index 8f5a1d3f1edd49cf7a630887f2f61dfe6ebd9fc2..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/fonts/bold.ttf and /dev/null differ diff --git a/xbob/io/base/fonts/bold_italic.ttf b/xbob/io/base/fonts/bold_italic.ttf deleted file mode 100644 index 916e9b351d985581d728574bb8f4766e64f7ffde..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/fonts/bold_italic.ttf and /dev/null differ diff --git a/xbob/io/base/fonts/font_license.txt b/xbob/io/base/fonts/font_license.txt deleted file mode 100644 index 69399804ca6c71fd61ed0ae47535baad3bb55e79..0000000000000000000000000000000000000000 --- a/xbob/io/base/fonts/font_license.txt +++ /dev/null @@ -1,97 +0,0 @@ -Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. -Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) - -Bitstream Vera Fonts Copyright ------------------------------- - -Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is -a trademark of Bitstream, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of the fonts accompanying this license ("Fonts") and associated -documentation files (the "Font Software"), to reproduce and distribute the -Font Software, including without limitation the rights to use, copy, merge, -publish, distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to the -following conditions: - -The above copyright and trademark notices and this permission notice shall -be included in all copies of one or more of the Font Software typefaces. - -The Font Software may be modified, altered, or added to, and in particular -the designs of glyphs or characters in the Fonts may be modified and -additional glyphs or characters may be added to the Fonts, only if the fonts -are renamed to names not containing either the words "Bitstream" or the word -"Vera". - -This License becomes null and void to the extent applicable to Fonts or Font -Software that has been modified and is distributed under the "Bitstream -Vera" names. - -The Font Software may be sold as part of a larger software package but no -copy of one or more of the Font Software typefaces may be sold by itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, -TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME -FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING -ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF -THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE -FONT SOFTWARE. - -Except as contained in this notice, the names of Gnome, the Gnome -Foundation, and Bitstream Inc., shall not be used in advertising or -otherwise to promote the sale, use or other dealings in this Font Software -without prior written authorization from the Gnome Foundation or Bitstream -Inc., respectively. For further information, contact: fonts at gnome dot -org. - -Arev Fonts Copyright ------------------------------- - -Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the fonts accompanying this license ("Fonts") and -associated documentation files (the "Font Software"), to reproduce -and distribute the modifications to the Bitstream Vera Font Software, -including without limitation the rights to use, copy, merge, publish, -distribute, and/or sell copies of the Font Software, and to permit -persons to whom the Font Software is furnished to do so, subject to -the following conditions: - -The above copyright and trademark notices and this permission notice -shall be included in all copies of one or more of the Font Software -typefaces. - -The Font Software may be modified, altered, or added to, and in -particular the designs of glyphs or characters in the Fonts may be -modified and additional glyphs or characters may be added to the -Fonts, only if the fonts are renamed to names not containing either -the words "Tavmjong Bah" or the word "Arev". - -This License becomes null and void to the extent applicable to Fonts -or Font Software that has been modified and is distributed under the -"Tavmjong Bah Arev" names. - -The Font Software may be sold as part of a larger software package but -no copy of one or more of the Font Software typefaces may be sold by -itself. - -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL -TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. - -Except as contained in this notice, the name of Tavmjong Bah shall not -be used in advertising or otherwise to promote the sale, use or other -dealings in this Font Software without prior written authorization -from Tavmjong Bah. For further information, contact: tavmjong @ free -. fr. \ No newline at end of file diff --git a/xbob/io/base/fonts/italic.ttf b/xbob/io/base/fonts/italic.ttf deleted file mode 100644 index 7b8b787525dd555b0c0144d0a8db84f9f317e268..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/fonts/italic.ttf and /dev/null differ diff --git a/xbob/io/base/fonts/regular.ttf b/xbob/io/base/fonts/regular.ttf deleted file mode 100644 index ea0bfd8c276fd20bc0f121bf28c9facbda430905..0000000000000000000000000000000000000000 Binary files a/xbob/io/base/fonts/regular.ttf and /dev/null differ diff --git a/xbob/io/base/main.cpp b/xbob/io/base/main.cpp index b00c088718efdab595ba2eff916019af8fb6cae2..6d3b5c1ae17cdab66a0f4ceea6a8c0a50038f6be 100644 --- a/xbob/io/base/main.cpp +++ b/xbob/io/base/main.cpp @@ -18,7 +18,7 @@ static PyMethodDef module_methods[] = { {0} /* Sentinel */ }; -PyDoc_STRVAR(module_docstr, "bob::io classes and methods"); +PyDoc_STRVAR(module_docstr, "Core bob::io classes and methods"); int PyXbobIo_APIVersion = XBOB_IO_BASE_API_VERSION; @@ -44,17 +44,6 @@ static PyObject* create_module (void) { PyBobIoHDF5File_Type.tp_new = PyType_GenericNew; if (PyType_Ready(&PyBobIoHDF5File_Type) < 0) return 0; -#if WITH_FFMPEG - PyBobIoVideoReader_Type.tp_new = PyType_GenericNew; - if (PyType_Ready(&PyBobIoVideoReader_Type) < 0) return 0; - - PyBobIoVideoReaderIterator_Type.tp_new = PyType_GenericNew; - if (PyType_Ready(&PyBobIoVideoReaderIterator_Type) < 0) return 0; - - PyBobIoVideoWriter_Type.tp_new = PyType_GenericNew; - if (PyType_Ready(&PyBobIoVideoWriter_Type) < 0) return 0; -#endif /* WITH_FFMPEG */ - # if PY_VERSION_HEX >= 0x03000000 PyObject* m = PyModule_Create(&module_definition); # else @@ -77,17 +66,6 @@ static PyObject* create_module (void) { Py_INCREF(&PyBobIoHDF5File_Type); if (PyModule_AddObject(m, "HDF5File", (PyObject *)&PyBobIoHDF5File_Type) < 0) return 0; -#if WITH_FFMPEG - Py_INCREF(&PyBobIoVideoReader_Type); - if (PyModule_AddObject(m, "VideoReader", (PyObject *)&PyBobIoVideoReader_Type) < 0) return 0; - - Py_INCREF(&PyBobIoVideoReaderIterator_Type); - if (PyModule_AddObject(m, "VideoReader.iter", (PyObject *)&PyBobIoVideoReaderIterator_Type) < 0) return 0; - - Py_INCREF(&PyBobIoVideoWriter_Type); - if (PyModule_AddObject(m, "VideoWriter", (PyObject *)&PyBobIoVideoWriter_Type) < 0) return 0; -#endif /* WITH_FFMPEG */ - static void* PyXbobIo_API[PyXbobIo_API_pointers]; /* exhaustive list of C APIs */ @@ -138,18 +116,6 @@ static PyObject* create_module (void) { PyXbobIo_API[PyBobIoCodec_GetDescription_NUM] = (void *)&PyBobIoCodec_GetDescription; -#if WITH_FFMPEG - /****************** - * Video bindings * - ******************/ - - PyXbobIo_API[PyBobIoVideoReader_Type_NUM] = (void *)&PyBobIoVideoReader_Type; - - PyXbobIo_API[PyBobIoVideoReaderIterator_Type_NUM] = (void *)&PyBobIoVideoReaderIterator_Type; - - PyXbobIo_API[PyBobIoVideoWriter_Type_NUM] = (void *)&PyBobIoVideoWriter_Type; -#endif /* WITH_FFMPEG */ - #if PY_VERSION_HEX >= 0x02070000 /* defines the PyCapsule */ diff --git a/xbob/io/base/script/__init__.py b/xbob/io/base/script/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/xbob/io/base/script/video_test.py b/xbob/io/base/script/video_test.py deleted file mode 100644 index 130d29f0d24e7497cb08a6499c8142a59147d452..0000000000000000000000000000000000000000 --- a/xbob/io/base/script/video_test.py +++ /dev/null @@ -1,520 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Andre Anjos <andre.dos.anjos@gmail.com> -# Thu 14 Mar 17:53:16 2013 - -"""This program can run manual tests using any video codec available in Bob. It -can report standard distortion figures and build video test sequences for -manual inspection. It tries to help identifying problems with: - - 1. Color distortion - 2. Frame skipping or delay - 3. Encoding or decoding quality - 4. User test (with a user provided video sample) - -You can parameterize the program with the type of file, (FFmpeg) codec and a -few other parameters. The program then generates artificial input signals to -test for each of the parameters above. -""" - -import os -import sys -import argparse -import numpy - -# internal -from .. import utils, create_directories_save -from .. import save as save_to_file -from .. import test_utils - -def list_codecs(*args, **kwargs): - - from ..version import supported_video_codecs, available_video_codecs - CODECS = supported_video_codecs() - ALL_CODECS = available_video_codecs() - - retval = """\ - Supported Codecs: - -----------------\n""" - - for k in sorted(CODECS.keys()): - retval += (" %-20s %s\n" % (k, CODECS[k]['long_name']))[:80] - - return retval[:-1] - -def list_all_codecs(*args, **kwargs): - - from ..version import supported_video_codecs, available_video_codecs - - CODECS = supported_video_codecs() - ALL_CODECS = available_video_codecs() - - retval = """\ - Available Codecs: - -----------------\n""" - - for k in sorted(ALL_CODECS.keys()): - retval += (" %-20s %s\n" % (k, ALL_CODECS[k]['long_name']))[:80] - - return retval[:-1] - -def list_formats(*args, **kwargs): - - from ..version import supported_videowriter_formats, available_videowriter_formats - FORMATS = supported_videowriter_formats() - ALL_FORMATS = available_videowriter_formats() - - retval = """\ - Supported Formats: - ------------------\n""" - - for k in sorted(FORMATS.keys()): - retval += (" %-20s %s\n" % (k, FORMATS[k]['long_name']))[:80] - - return retval[:-1] - -def list_all_formats(*args, **kwargs): - - from ..version import supported_videowriter_formats, available_videowriter_formats - FORMATS = supported_videowriter_formats() - ALL_FORMATS = available_videowriter_formats() - - retval = """\ - Available Formats: - ------------------\n""" - - for k in sorted(ALL_FORMATS.keys()): - retval += (" %-20s %s\n" % (k, ALL_FORMATS[k]['long_name']))[:80] - - return retval[:-1] - -__epilog__ = """Example usage: - -1. Check for color distortion using H.264 codec in a .mov video container: - - $ %(prog)s --format='mov' --codec='h264' color - -2. Check for frame skipping using MJPEG codec in an .avi video container: - - $ %(prog)s --format='avi' --codec='mjpeg' frameskip - -3. Check for encoding/decoding quality using a FFV1 codec in a '.flv' video -container (not supported - note the usage of the '--force' flag): - - $ %(prog)s --force --format='flv' --codec='ffv1' noise - -4. To run-only the user-video test and provide a test video: - - $ %(prog)s --format='mov' --user-video=test_sample.avi user - -5. To list all available codecs: - - $ %(prog)s --list-codecs - -6. To list all available formats: - - $ %(prog)s --list-formats - -7. Run all tests for all **supported** codecs and formats: - - $ %(prog)s -""" % { - 'prog': os.path.basename(sys.argv[0]), - } - -def user_video(original, max_frames, format, codec, filename): - """Returns distortion patterns for a set of frames with moving colors. - - Keyword parameters: - - original - The name (path) to the original user file that will be used for the test - - max_frames - The maximum number of frames to read from user input - - format - The string that identifies the format to be used for the output file - - codec - The codec to be used for the output file - - filename - The name (path) of the file to use for encoding the test - """ - - from .. import VideoReader, VideoWriter - vreader = VideoReader(original, check=True) - orig = vreader[:max_frames] - - # rounding frame rate - some older codecs do not accept random frame rates - framerate = vreader.frame_rate - if codec in ('mpegvideo', 'mpeg1video', 'mpeg2video'): - import math - framerate = math.ceil(vreader.frame_rate) - - vwriter = VideoWriter(filename, vreader.height, vreader.width, - framerate, codec=codec, format=format, check=False) - for k in orig: vwriter.append(k) - del vwriter - return orig, framerate, VideoReader(filename, check=False) - -def summarize(function, shape, framerate, format, codec, output=None): - """Summarizes distortion patterns for a given set of video settings and - for a given input function. - - Keyword parameters: - - function - The function that will be evaluated, summarized - - shape (int, int, int) - The length (number of frames), height and width for the generated sequence - - format - The string that identifies the format to be used for the output file - - codec - The codec to be used for the output file - - output - If set, the video is not created on the temporary directory, but it is - saved on the advised location. This must be a filename. - - Returns a single a single string summarizing the distortion results - """ - - length, height, width = shape - - if output: - fname = output - else: - fname = test_utils.temporary_filename(suffix='.%s' % format) - - retval = "did not run" - - try: - # Width and height should be powers of 2 as the encoded image is going - # to be approximated to the closest one, would not not be the case. - # In this case, the encoding is subject to more noise as the filtered, - # final image that is encoded will contain added noise on the extra - # borders. - orig, framerate, encoded = function(shape, framerate, format, codec, fname) - - tmp = [] - for k, of in enumerate(orig): - tmp.append(abs(of.astype('float64')-encoded[k].astype('float64')).sum()) - size = numpy.prod(orig[0].shape) - S = sum(tmp)/size - M = S/len(tmp) - Min = min(tmp)/size - Max = max(tmp)/size - ArgMin = tmp.index(min(tmp)) - ArgMax = tmp.index(max(tmp)) - retval = "%.3f min=%.3f@%d max=%.3f@%d" % (M, Min, ArgMin, Max, ArgMax) - if abs(encoded.frame_rate - framerate) > 0.01: - retval += " !FR(%g)" % abs(encoded.frame_rate - framerate) - if len(encoded) != len(orig): - retval += " !LEN(%d)" % len(encoded) - - finally: - - if os.path.exists(fname) and output is None: os.unlink(fname) - - if output: - return retval, orig, encoded - else: - return retval - -def detail(function, shape, framerate, format, codec, outdir): - """Summarizes distortion patterns for a given set of video settings and - for a given input function. - - Keyword parameters: - - shape (int, int, int) - The length (number of frames), height and width for the generated sequence - - format - The string that identifies the format to be used for the output file - - codec - The codec to be used for the output file - - outdir - We will save all analysis for this sequence on the given output directory. - - Returns a single a single string summarizing the distortion results. - """ - - length, height, width = shape - - text_format = "%%0%dd" % len(str(length-1)) - - output = os.path.join(outdir, "video." + format) - retval, orig, encoded = summarize(function, shape, framerate, - format, codec, output) - - length, _, height, width = orig.shape - - # save original, reloaded and difference images on output directories - for i, orig_frame in enumerate(orig): - out = numpy.ndarray((3, height, 3*width), dtype='uint8') - out[:,:,:width] = orig_frame - out[:,:,width:(2*width)] = encoded[i] - diff = abs(encoded[i].astype('int64')-orig_frame.astype('int64')) - diff[diff>0] = 255 #binary output - out[:,:,(2*width):] = diff.astype('uint8') - save_to_file(out, os.path.join(outdir, 'frame-' + (text_format%i) + '.png')) - - return retval - -def main(user_input=None): - - from .._library import __version__ - from ..version import supported_video_codecs, available_video_codecs, supported_videowriter_formats, available_videowriter_formats - from .. import test as io_test - - parser = argparse.ArgumentParser(description=__doc__, epilog=__epilog__, - formatter_class=argparse.RawDescriptionHelpFormatter) - - name = os.path.basename(os.path.splitext(sys.argv[0])[0]) - version_info = 'Video Encoding/Decoding Test Tool v%s (%s)' % (__version__, name) - parser.add_argument('-V', '--version', action='version', version=version_info) - - test_choices = [ - 'color', - 'frameskip', - 'noise', - 'user', - ] - - parser.add_argument("test", metavar='TEST', type=str, nargs='*', - default=test_choices, help="The name of the test or tests you want to run. Choose between `%s'. If none given, run through all." % ('|'.join(test_choices))) - - CODECS = supported_video_codecs() - ALL_CODECS = available_video_codecs() - - supported_codecs = sorted(CODECS.keys()) - available_codecs = sorted(ALL_CODECS.keys()) - - parser.add_argument("-c", "--codec", metavar='CODEC', type=str, nargs='*', - default=supported_codecs, choices=available_codecs, help="The name of the codec you want to test with. For a list of available codecs, look below. If none given, run through all.") - parser.add_argument("--list-codecs", action="store_true", default=False, - help="List all supported codecs and exits") - parser.add_argument("--list-all-codecs", action="store_true", default=False, - help="List all available codecs and exits") - - FORMATS = supported_videowriter_formats() - ALL_FORMATS = available_videowriter_formats() - - supported_formats = sorted(FORMATS.keys()) - available_formats = sorted(ALL_FORMATS.keys()) - parser.add_argument("-f", "--format", metavar='FORMAT', type=str, nargs='*', - default=supported_formats, choices=available_formats, help="The name of the format you want to test with. For a list of available formats, look below. If none given, run through all.") - parser.add_argument("--list-formats", action="store_true", default=False, - help="List all supported formats and exits") - parser.add_argument("--list-all-formats", action="store_true", default=False, - help="List all available formats and exits") - - parser.add_argument("-F", "--force", action="store_true", default=False, - help="Force command execution (possibly generating an error) even if the format or the combination of format+codec is not supported. This flag is needed in case you need to test new formats or combinations of formats and codecs which are unsupported by the build") - - parser.add_argument("-t", "--height", metavar="INT", type=int, - default=128, help="Height of the test video (defaults to %(default)s pixels). Note this number has to be even.") - - parser.add_argument("-w", "--width", metavar="INT", type=int, - default=128, help="Width of the test video (defaults to %(default)s pixels). Note this number has to be even.") - - parser.add_argument("-l", "--length", metavar="INT", type=int, - default=30, help="Length of the test sequence (defaults to %(default)s frames). The longer, the more accurate the test becomes.") - - parser.add_argument("-r", "--framerate", metavar="FLOAT", type=float, - default=30., help="Framerate to be used on the test videos (defaults to %(default)s Hz).") - - parser.add_argument("-o", "--output", type=str, - help="If set, then videos created for the tests are stored on the given directory. By default this option is empty and videos are created on a temporary directory and deleted after tests are done. If you set it, we also produced detailed output analysis for manual inspection.") - - parser.add_argument("-u", "--user-video", type=str, metavar="PATH", - help="Set the path to the user video that will be used for distortion tests (if not set use default test video)") - - parser.add_argument("-n", "--user-frames", type=int, default=10, metavar="INT", help="Set the number of maximum frames to read from the user video (reads %(default)s by default)") - - args = parser.parse_args(args=user_input) - - # manual check because of argparse limitation - for t in args.test: - if t not in test_choices: - parser.error("invalid test choice: '%s' (choose from %s)" % \ - (t, ", ".join(["'%s'" % k for k in test_choices]))) - - if not args.test: args.test = test_choices - - if args.list_codecs: - print(list_codecs()) - sys.exit(0) - - if args.list_all_codecs: - print(list_all_codecs()) - sys.exit(0) - - if args.list_formats: - print(list_formats()) - sys.exit(0) - - if args.list_all_formats: - print(list_all_formats()) - sys.exit(0) - - if 'user' in args.test and args.user_video is None: - # in this case, take our standard video test - args.user_video = test_utils.datafile('test.mov', io_test.__name__) - - def wrap_user_function(shape, framerate, format, codec, filename): - return user_video(args.user_video, args.user_frames, format, codec, filename) - - # mapping between test name and function - test_function = { - 'color': (utils.color_distortion, 'C'), - 'frameskip': (utils.frameskip_detection, 'S'), - 'noise': (utils.quality_degradation, 'N'), - 'user': (wrap_user_function, 'U'), - } - - # result table - table = {} - - # report results in a readable way - print(version_info) - print("Settings:") - print(" Width : %d pixels" % args.width) - print(" Height : %d pixels" % args.height) - print(" Length : %d frames" % args.length) - print(" Framerate: %f Hz" % args.framerate) - - print("Legend:") - for k, (f, code) in test_function.items(): - print(" %s: %s test" % (code, k.capitalize())) - - sys.stdout.write("Running %d test(s)..." % - (len(args.test)*len(args.format)*len(args.codec))) - sys.stdout.flush() - - # run tests - need_notes = False - for test in args.test: - test_table = table.setdefault(test, {}) - f, code = test_function[test] - - for format in args.format: - format_table = test_table.setdefault(format, {}) - - for codec in args.codec: - - # cautionary settings - notes = "" - if format not in FORMATS: - if args.force: - notes += "[!F] " - need_notes = True - - else: - sys.stdout.write(code) - sys.stdout.flush() - format_table[codec] = "unsupported format" - continue - - else: - if codec not in FORMATS[format]['supported_codecs']: - if args.force: - notes += "[!F+C] " - need_notes = True - - else: - sys.stdout.write(code) - sys.stdout.flush() - format_table[codec] = "format+codec unsupported" - continue - - if args.output: - - size = '%dx%dx%d@%gHz' % (args.length, args.height, args.width, - args.framerate) - outdir = os.path.join(args.output, test, codec, size, format) - create_directories_save(outdir) - - try: - result = detail(f, (args.length, args.height, args.width), - args.framerate, format, codec, outdir) - sys.stdout.write(code) - sys.stdout.flush() - except Exception as e: - result = str(e) - sys.stdout.write(code) - sys.stdout.flush() - finally: - format_table[codec] = notes + result - - else: - - try: - result = summarize(f, (args.length, args.height, args.width), - args.framerate, format, codec) - sys.stdout.write(code) - sys.stdout.flush() - except Exception as e: - result = str(e) - sys.stdout.write(code) - sys.stdout.flush() - finally: - format_table[codec] = notes + result - - sys.stdout.write("\n") - sys.stdout.flush() - - # builds a nicely organized dynamically sized table - test_size = max([len(k) for k in args.test] + [len('test')]) - fmt_size = max([len(k) for k in args.format] + [len('fmt')]) - codec_size = max([len(k) for k in args.codec] + [len('codec')]) - figure_size = 79 - (test_size + 3 + fmt_size + 3 + codec_size + 3 + 2) - if figure_size <= 0: figure_size = 40 - - test_cover = (test_size + 2) * '=' - fmt_cover = (fmt_size + 2) * '=' - codec_cover = (codec_size + 2) * '=' - figure_cover = (figure_size + 2) * '=' - - sep = test_cover + ' ' + fmt_cover + ' ' + codec_cover + ' ' + figure_cover - line = " %s %s %s %s" - - print("") - print(sep) - print(line % ( - 'test'.ljust(test_size), - 'fmt'.center(fmt_size), - 'codec'.center(codec_size), - 'figure (lower means better quality)'.ljust(figure_size), - )) - print(sep) - - for test in sorted(table.keys()): - test_table = table[test] - for format in sorted(test_table.keys()): - format_table = test_table[format] - for codec in sorted(format_table.keys()): - figure = format_table[codec] - print(line % ( - test.ljust(test_size), - format.center(fmt_size), - codec.ljust(codec_size), - figure.ljust(figure_size), - )) - - print(sep) - - # only printed if unsupported combinations of formats and codecs are used - if need_notes: - print("") - print("Notes:") - print(" [!F] Format is available, but not supported by this build") - print(" [!F+C] Format is supported, but not in combination with this codec") diff --git a/xbob/io/base/test_file.py b/xbob/io/base/test_file.py index 431e32430f4ce94c4706bd991cc8feb8bb6b7747..372fab6aef2e2af8a84bff291e48ad0975956a06 100644 --- a/xbob/io/base/test_file.py +++ b/xbob/io/base/test_file.py @@ -291,35 +291,6 @@ def test_tensorfile(): # complete transcoding test transcode(test_utils.datafile('torch.tensor', __name__)) -@test_utils.extension_available('.pgm') -@test_utils.extension_available('.pbm') -@test_utils.extension_available('.ppm') -def test_netpbm(): - - def image_transcode(filename): - - tmpname = test_utils.temporary_filename(suffix=os.path.splitext(filename)[1]) - - try: - # complete transcoding test - image = load(filename) - - # save with the same extension - write(image, tmpname) - - # reload the image from the file - image2 = load(tmpname) - - assert numpy.array_equal(image, image2) - - finally: - if os.path.exists(tmpname): os.unlink(tmpname) - - image_transcode(test_utils.datafile('test.pgm', __name__)) #indexed, works fine - image_transcode(test_utils.datafile('test.pbm', __name__)) #indexed, works fine - image_transcode(test_utils.datafile('test.ppm', __name__)) #indexed, works fine - #image_transcode(test_utils.datafile('test.jpg', __name__)) #does not work because of re-compression - @test_utils.extension_available('.csv') def test_csv(): diff --git a/xbob/io/base/test_image.py b/xbob/io/base/test_image.py deleted file mode 100644 index 84f6e981ec96de28f7f997d1c027be830bf3cdfd..0000000000000000000000000000000000000000 --- a/xbob/io/base/test_image.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Laurent El Shafey <laurent.el-shafey@idiap.ch> -# Wed Aug 14 12:27:57 CEST 2013 -# -# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland - -"""Runs some image tests -""" - -from . import load, test_utils - -# These are some global parameters for the test. -PNG_INDEXED_COLOR = test_utils.datafile('img_indexed_color.png', __name__) - -def test_png_indexed_color(): - - # Read an indexed color PNG image, and compared with hardcoded values - img = load(PNG_INDEXED_COLOR) - assert img.shape == (3,22,32) - assert img[0,0,0] == 255 - assert img[0,17,17] == 117 diff --git a/xbob/io/base/test_utils.py b/xbob/io/base/test_utils.py index 103b9e7700e541999bd29a4895cc192cfa3a8f46..2ba6d1908acf6653be2439453d9c16041a2eb7cd 100644 --- a/xbob/io/base/test_utils.py +++ b/xbob/io/base/test_utils.py @@ -49,74 +49,6 @@ def temporary_filename(prefix='bobtest_', suffix='.hdf5'): os.unlink(name) return name -# Here is a table of ffmpeg versions against libavcodec, libavformat and -# libavutil versions -ffmpeg_versions = { - '0.5': [ SV('52.20.0'), SV('52.31.0'), SV('49.15.0') ], - '0.6': [ SV('52.72.2'), SV('52.64.2'), SV('50.15.1') ], - '0.7': [ SV('52.122.0'), SV('52.110.0'), SV('50.43.0') ], - '0.8': [ SV('53.7.0'), SV('53.4.0'), SV('51.9.1') ], - '0.9': [ SV('53.42.0'), SV('53.24.0'), SV('51.32.0') ], - '0.10': [ SV('53.60.100'), SV('53.31.100'), SV('51.34.101') ], - '0.11': [ SV('54.23.100'), SV('54.6.100'), SV('51.54.100') ], - '1.0': [ SV('54.59.100'), SV('54.29.104'), SV('51.73.101') ], - '1.1': [ SV('54.86.100'), SV('54.59.106'), SV('52.13.100') ], - '1.2': [ SV('54.92.100'), SV('54.63.104'), SV('52.18.100') ], - '2.0': [ SV('55.18.102'), SV('55.12.100'), SV('52.38.100') ], - '2.1': [ SV('55.39.100'), SV('55.19.104'), SV('52.48.100') ], - } - -def ffmpeg_version_lessthan(v): - '''Returns true if the version of ffmpeg compiled-in is at least the version - indicated as a string parameter.''' - - from .version import externals - if externals['FFmpeg']['ffmpeg'] == 'unavailable': return False - avcodec_inst= SV(externals['FFmpeg']['avcodec']) - avcodec_req = ffmpeg_versions[v][0] - return avcodec_inst < avcodec_req - -def ffmpeg_found(version_geq=None): - '''Decorator to check if a codec is available before enabling a test - - To use this, decorate your test routine with something like: - - .. code-block:: python - - @ffmpeg_found() - - You can pass an optional string to require that the FFMpeg version installed - is greater or equal that version identifier. For example: - - .. code-block:: python - - @ffmpeg_found('0.10') #requires at least version 0.10 - - Versions you can test for are set in the ``ffmpeg_versions`` dictionary in - this module. - ''' - - def test_wrapper(test): - - @functools.wraps(test) - def wrapper(*args, **kwargs): - try: - from .version import externals - avcodec_inst = SV(externals['FFmpeg']['avcodec']) - avformat_inst = SV(externals['FFmpeg']['avformat']) - avutil_inst = SV(externals['FFmpeg']['avutil']) - if version_geq is not None: - avcodec_req,avformat_req,avutil_req = ffmpeg_versions[version_geq] - if avcodec_inst < avcodec_req: - raise nose.plugins.skip.SkipTest('FFMpeg/libav version installed (%s) is smaller than required for this test (%s)' % (externals['FFmpeg']['ffmpeg'], version_geq)) - return test(*args, **kwargs) - except KeyError: - raise nose.plugins.skip.SkipTest('FFMpeg was not available at compile time') - - return wrapper - - return test_wrapper - def bob_at_least(version_geq): '''Decorator to check if at least a certain version of Bob is installed @@ -142,24 +74,6 @@ def bob_at_least(version_geq): return test_wrapper -def codec_available(codec): - '''Decorator to check if a codec is available before enabling a test''' - - def test_wrapper(test): - - @functools.wraps(test) - def wrapper(*args, **kwargs): - from .version import supported_video_codecs - d = supported_video_codecs() - if codec in d and d[codec]['encode'] and d[codec]['decode']: - return test(*args, **kwargs) - else: - raise nose.plugins.skip.SkipTest('A functional codec for "%s" is not installed with FFmpeg' % codec) - - return wrapper - - return test_wrapper - def extension_available(extension): '''Decorator to check if a extension is available before enabling a test''' diff --git a/xbob/io/base/test_video.py b/xbob/io/base/test_video.py deleted file mode 100644 index cec068cd8ea735718f393f333e23db52c490e96b..0000000000000000000000000000000000000000 --- a/xbob/io/base/test_video.py +++ /dev/null @@ -1,324 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Andre Anjos <andre.anjos@idiap.ch> -# Wed Jun 22 17:50:08 2011 +0200 -# -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland - -"""Runs some video tests -""" - -import os -import numpy -import nose.tools -from . import test_utils - -# These are some global parameters for the test. -INPUT_VIDEO = test_utils.datafile('test.mov', __name__) - -def test_codec_support(): - - # Describes all encoders - from .version import describe_encoder, describe_decoder, supported_video_codecs - - supported = supported_video_codecs() - - for k,v in supported.items(): - # note: searching by name (using `k') will not always work - if v['decode']: assert describe_decoder(v['id']) - if v['encode']: assert describe_encoder(v['id']) - - # Assert we support, at least, some known codecs - for codec in ('ffv1', 'wmv2', 'mpeg4', 'mjpeg'): - assert codec in supported - assert supported[codec]['encode'] - assert supported[codec]['decode'] - -@test_utils.ffmpeg_found() -def test_input_format_support(): - - # Describes all encoders - from .version import supported_videoreader_formats - - supported = supported_videoreader_formats() - - # Assert we support, at least, some known codecs - for fmt in ('avi', 'mov', 'mp4'): - assert fmt in supported - -@test_utils.ffmpeg_found() -def test_output_format_support(): - - # Describes all encoders - from .version import supported_videowriter_formats - - supported = supported_videowriter_formats() - - # Assert we support, at least, some known codecs - for fmt in ('avi', 'mov', 'mp4'): - assert fmt in supported - -@test_utils.ffmpeg_found() -def test_video_reader_attributes(): - - from . import VideoReader - - iv = VideoReader(INPUT_VIDEO) - - assert isinstance(iv.filename, str) - assert isinstance(iv.height, int) - assert isinstance(iv.width, int) - assert iv.height != iv.width - assert isinstance(iv.duration, int) - assert isinstance(iv.format_name, str) - assert isinstance(iv.format_long_name, str) - assert isinstance(iv.codec_name, str) - assert isinstance(iv.codec_long_name, str) - assert isinstance(iv.frame_rate, float) - assert isinstance(iv.video_type, tuple) - assert len(iv.video_type) == 3 - assert isinstance(iv.video_type[0], numpy.dtype) - assert isinstance(iv.video_type[1], tuple) - assert isinstance(iv.video_type[2], tuple) - assert isinstance(iv.frame_type, tuple) - assert len(iv.frame_type) == 3 - assert iv.frame_type[0] == iv.video_type[0] - assert isinstance(iv.video_type[1], tuple) - nose.tools.eq_(len(iv.video_type[1]), len(iv.frame_type[1])+1) - nose.tools.eq_(len(iv.video_type[2]), len(iv.frame_type[2])+1) - assert isinstance(iv.info, str) - -@test_utils.ffmpeg_found() -def test_video_reader_str(): - - from . import VideoReader - - iv = VideoReader(INPUT_VIDEO) - assert repr(iv) - assert str(iv) - -@test_utils.ffmpeg_found() -def test_can_iterate(): - - from . import VideoReader - video = VideoReader(INPUT_VIDEO) - counter = 0 - for frame in video: - assert isinstance(frame, numpy.ndarray) - assert len(frame.shape) == 3 - assert frame.shape[0] == 3 #color-bands (RGB) - assert frame.shape[1] == 240 #height - assert frame.shape[2] == 320 #width - counter += 1 - - assert counter == len(video) #we have gone through all frames - -@test_utils.ffmpeg_found() -def test_iteration(): - - from . import load, VideoReader - f = VideoReader(INPUT_VIDEO) - objs = load(INPUT_VIDEO) - - nose.tools.eq_(len(f), len(objs)) - for l, i in zip(objs, f): - assert numpy.allclose(l, i) - -@test_utils.ffmpeg_found() -def test_indexing(): - - from . import VideoReader - f = VideoReader(INPUT_VIDEO) - - nose.tools.eq_(len(f), 375) - - objs = f[:10] - nose.tools.eq_(len(objs), 10) - obj0 = f[0] - obj1 = f[1] - - # simple indexing - assert numpy.allclose(objs[0], obj0) - assert numpy.allclose(objs[1], obj1) - assert numpy.allclose(f[len(f)-1], f[-1]) - assert numpy.allclose(f[len(f)-2], f[-2]) - -@test_utils.ffmpeg_found() -def test_slicing_empty(): - - from . import load, VideoReader - f = VideoReader(INPUT_VIDEO) - - objs = f[1:1] - assert objs.shape == tuple() - assert objs.dtype == numpy.uint8 - -@test_utils.ffmpeg_found() -def test_slicing_0(): - - from . import load, VideoReader - f = VideoReader(INPUT_VIDEO) - - objs = f[:] - for i, k in enumerate(load(INPUT_VIDEO)): - assert numpy.allclose(k, objs[i]) - -@test_utils.ffmpeg_found() -def test_slicing_1(): - - from . import VideoReader - f = VideoReader(INPUT_VIDEO) - - s = f[3:10:2] - nose.tools.eq_(len(s), 4) - assert numpy.allclose(s[0], f[3]) - assert numpy.allclose(s[1], f[5]) - assert numpy.allclose(s[2], f[7]) - assert numpy.allclose(s[3], f[9]) - -@test_utils.ffmpeg_found() -def test_slicing_2(): - - from . import VideoReader - f = VideoReader(INPUT_VIDEO) - - s = f[-10:-2:3] - nose.tools.eq_(len(s), 3) - assert numpy.allclose(s[0], f[len(f)-10]) - assert numpy.allclose(s[1], f[len(f)-7]) - assert numpy.allclose(s[2], f[len(f)-4]) - -@test_utils.ffmpeg_found() -def test_slicing_3(): - - from . import VideoReader - f = VideoReader(INPUT_VIDEO) - objs = f.load() - - # get negative stepping slice - s = f[20:10:-3] - nose.tools.eq_(len(s), 4) - assert numpy.allclose(s[0], f[20]) - assert numpy.allclose(s[1], f[17]) - assert numpy.allclose(s[2], f[14]) - assert numpy.allclose(s[3], f[11]) - -@test_utils.ffmpeg_found() -def test_slicing_4(): - - from . import VideoReader - f = VideoReader(INPUT_VIDEO) - objs = f[:21] - - # get all negative slice - s = f[-10:-20:-3] - nose.tools.eq_(len(s), 4) - assert numpy.allclose(s[0], f[len(f)-10]) - assert numpy.allclose(s[1], f[len(f)-13]) - assert numpy.allclose(s[2], f[len(f)-16]) - assert numpy.allclose(s[3], f[len(f)-19]) - - -@test_utils.ffmpeg_found() -def test_can_use_array_interface(): - - from . import load, VideoReader - array = load(INPUT_VIDEO) - iv = VideoReader(INPUT_VIDEO) - - for frame_id, frame in zip(range(array.shape[0]), iv.__iter__()): - assert numpy.array_equal(array[frame_id,:,:,:], frame) - -@test_utils.ffmpeg_found() -def test_video_reading_after_writing(): - - from . import test_utils - tmpname = test_utils.temporary_filename(suffix='.avi') - - from . import VideoWriter, VideoReader - - try: - - width = 20 - height = 20 - framerate = 24 - - outv = VideoWriter(tmpname, height, width, framerate) - for i in range(0, 3): - newframe = (numpy.random.random_integers(0,255,(3,height,width))) - outv.append(newframe.astype('uint8')) - outv.close() - - # this should not crash - i = VideoReader(tmpname) - nose.tools.eq_(i.number_of_frames, 3) - nose.tools.eq_(i.width, width) - nose.tools.eq_(i.height, height) - - finally: - # And we erase both files after this - if os.path.exists(tmpname): os.unlink(tmpname) - -@test_utils.ffmpeg_found() -def test_video_writer_close(): - - from . import test_utils - tmpname = test_utils.temporary_filename(suffix='.avi') - - from . import VideoWriter, VideoReader - - try: - - width = 20 - height = 20 - framerate = 24 - - outv = VideoWriter(tmpname, height, width, framerate) - for i in range(0, 3): - newframe = (numpy.random.random_integers(0,255,(3,height,width))) - outv.append(newframe.astype('uint8')) - outv.close() - - # this should not crash - nose.tools.eq_(outv.filename, tmpname) - nose.tools.eq_(outv.width, width) - nose.tools.eq_(outv.height, height) - nose.tools.eq_(len(outv), 3) - nose.tools.eq_(outv.number_of_frames, len(outv)) - nose.tools.eq_(outv.frame_rate, framerate) - assert outv.bit_rate - assert outv.gop - - finally: - # And we erase both files after this - if os.path.exists(tmpname): os.unlink(tmpname) - -@test_utils.ffmpeg_found() -def test_closed_video_writer_raises(): - - from . import test_utils - tmpname = test_utils.temporary_filename(suffix='.avi') - - from . import VideoWriter - - try: - - width = 20 - height = 20 - framerate = 24 - - outv = VideoWriter(tmpname, height, width, framerate) - for i in range(0, 3): - newframe = (numpy.random.random_integers(0,255,(3,height,width))) - outv.append(newframe.astype('uint8')) - outv.close() - - nose.tools.assert_raises(RuntimeError, outv.__str__) - nose.tools.assert_raises(RuntimeError, outv.__repr__) - nose.tools.assert_raises(RuntimeError, outv.append, newframe) - - del outv - - finally: - # And we erase both files after this - if os.path.exists(tmpname): os.unlink(tmpname) diff --git a/xbob/io/base/test_video_codec.py b/xbob/io/base/test_video_codec.py deleted file mode 100644 index 90a3e392eb6835e9b6dec6c28a1529ba03690f11..0000000000000000000000000000000000000000 --- a/xbob/io/base/test_video_codec.py +++ /dev/null @@ -1,192 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Andre Anjos <andre.anjos@idiap.ch> -# Tue 12 Nov 09:25:56 2013 - -"""Runs quality tests on video codecs supported -""" - -import os -import numpy -import nose.tools -from . import test_utils -from .utils import color_distortion, frameskip_detection, quality_degradation - -# These are some global parameters for the test. -INPUT_VIDEO = test_utils.datafile('test.mov', __name__) - -@test_utils.ffmpeg_found() -def check_format_codec(function, shape, framerate, format, codec, maxdist): - - length, height, width = shape - fname = test_utils.temporary_filename(suffix='.%s' % format) - - try: - orig, framerate, encoded = function(shape, framerate, format, codec, fname) - reloaded = encoded.load() - - # test number of frames is correct - assert len(orig) == len(encoded), "original length %d != %d encoded" % (len(orig), len(encoded)) - assert len(orig) == len(reloaded), "original length %d != %d reloaded" % (len(orig), len(reloaded)) - - # test distortion patterns (quick sequential check) - dist = [] - for k, of in enumerate(orig): - dist.append(abs(of.astype('float64')-reloaded[k].astype('float64')).mean()) - assert max(dist) <= maxdist, "max(distortion) %g > %g allowed" % (max(dist), maxdist) - - # assert we can randomly access any frame (choose 3 at random) - for k in numpy.random.randint(length, size=(3,)): - rdist = abs(orig[k].astype('float64')-encoded[k].astype('float64')).mean() - assert rdist <= maxdist, "distortion(frame[%d]) %g > %g allowed" % (k, rdist, maxdist) - - # make sure that the encoded frame rate is not off by a big amount - assert abs(framerate - encoded.frame_rate) <= (1.0/length), "reloaded framerate %g differs from original %g by more than %g" % (encoded.frame_rate, framerate, 1.0/length) - - finally: - - if os.path.exists(fname): os.unlink(fname) - -def test_format_codecs(): - - length = 30 - width = 128 - height = 128 - framerate = 30. - shape = (length, height, width) - methods = dict( - frameskip = frameskip_detection, - color = color_distortion, - noise = quality_degradation, - ) - - # distortion patterns for specific codecs - distortions = dict( - # we require high standards by default - default = dict(frameskip=0.1, color=9.0, noise=45.), - - # high-quality encoders - zlib = dict(frameskip=0.0, color=0.0, noise=0.0), - ffv1 = dict(frameskip=0.05, color=9., noise=46.), - vp8 = dict(frameskip=0.3, color=9.0, noise=65.), - libvpx = dict(frameskip=0.3, color=9.0, noise=65.), - h264 = dict(frameskip=0.4, color=9.0, noise=50.), - libx264 = dict(frameskip=0.4, color=9.0, noise=50.), - theora = dict(frameskip=0.5, color=9.0, noise=70.), - libtheora = dict(frameskip=0.5, color=9.0, noise=70.), - mpeg4 = dict(frameskip=1.0, color=9.0, noise=55.), - - # older, but still good quality encoders - mjpeg = dict(frameskip=1.2, color=9.0, noise=50.), - mpegvideo = dict(frameskip=1.3, color=9.0, noise=55.), - mpeg2video = dict(frameskip=1.3, color=9.0, noise=55.), - mpeg1video = dict(frameskip=1.4, color=9.0, noise=50.), - - # low quality encoders - avoid using - available for compatibility - wmv2 = dict(frameskip=3.0, color=10., noise=50.), - wmv1 = dict(frameskip=2.5, color=10., noise=50.), - msmpeg4 = dict(frameskip=5., color=10., noise=50.), - msmpeg4v2 = dict(frameskip=5., color=10., noise=50.), - ) - - # some exceptions - if test_utils.ffmpeg_version_lessthan('0.6'): - distortions['ffv1']['frameskip'] = 0.55 - distortions['mpeg1video']['frameskip'] = 1.5 - distortions['mpegvideo']['color'] = 9.0 - distortions['mpegvideo']['frameskip'] = 1.4 - distortions['mpeg2video']['color'] = 9.0 - distortions['mpeg2video']['frameskip'] = 1.4 - - from .version import externals - if externals['FFmpeg']['ffmpeg'] != 'unavailable': - from .version import supported_videowriter_formats - SUPPORTED = supported_videowriter_formats() - for format in SUPPORTED: - for codec in SUPPORTED[format]['supported_codecs']: - for method in methods: - check_format_codec.description = "%s.test_%s_format_%s_codec_%s" % (__name__, method, format, codec) - distortion = distortions.get(codec, distortions['default'])[method] - yield check_format_codec, methods[method], shape, framerate, format, codec, distortion - -@test_utils.ffmpeg_found() -def check_user_video(format, codec, maxdist): - - from . import VideoReader, VideoWriter - fname = test_utils.temporary_filename(suffix='.%s' % format) - MAXLENTH = 10 #use only the first 10 frames - - try: - - orig_vreader = VideoReader(INPUT_VIDEO) - orig = orig_vreader[:MAXLENTH] - (olength, _, oheight, owidth) = orig.shape - assert len(orig) == MAXLENTH, "original length %d != %d MAXLENTH" % (len(orig), MAXLENTH) - - # encode the input video using the format and codec provided by the user - outv = VideoWriter(fname, oheight, owidth, orig_vreader.frame_rate, - codec=codec, format=format) - for k in orig: outv.append(k) - outv.close() - - # reload from saved file - encoded = VideoReader(fname) - reloaded = encoded.load() - - # test number of frames is correct - assert len(orig) == len(encoded), "original length %d != %d encoded" % (len(orig), len(encoded)) - assert len(orig) == len(reloaded), "original length %d != %d reloaded" % (len(orig), len(reloaded)) - - # test distortion patterns (quick sequential check) - dist = [] - for k, of in enumerate(orig): - dist.append(abs(of.astype('float64')-reloaded[k].astype('float64')).mean()) - assert max(dist) <= maxdist, "max(distortion) %g > %g allowed" % (max(dist), maxdist) - - # make sure that the encoded frame rate is not off by a big amount - assert abs(orig_vreader.frame_rate - encoded.frame_rate) <= (1.0/MAXLENTH), "original video framerate %g differs from encoded %g by more than %g" % (encoded.frame_rate, framerate, 1.0/MAXLENTH) - - finally: - - if os.path.exists(fname): os.unlink(fname) - -def test_user_video(): - - # distortion patterns for specific codecs - distortions = dict( - # we require high standards by default - default = 1.5, - - # high-quality encoders - zlib = 0.0, - ffv1 = 1.7, - vp8 = 2.7, - libvpx = 2.7, - h264 = 2.5, - libx264 = 2.5, - theora = 2.0, - libtheora = 2.0, - mpeg4 = 2.3, - - # older, but still good quality encoders - mjpeg = 1.8, - mpegvideo = 2.3, - mpeg2video = 2.3, - mpeg1video = 2.3, - - # low quality encoders - avoid using - available for compatibility - wmv2 = 2.3, - wmv1 = 2.3, - msmpeg4 = 2.3, - msmpeg4v2 = 2.3, - ) - - from .version import externals - if externals['FFmpeg']['ffmpeg'] != 'unavailable': - from .version import supported_videowriter_formats - SUPPORTED = supported_videowriter_formats() - for format in SUPPORTED: - for codec in SUPPORTED[format]['supported_codecs']: - check_user_video.description = "%s.test_user_video_format_%s_codec_%s" % (__name__, format, codec) - distortion = distortions.get(codec, distortions['default']) - yield check_user_video, format, codec, distortion diff --git a/xbob/io/base/utils.py b/xbob/io/base/utils.py deleted file mode 100644 index 9e3d67b7f83120050dab2bf357cc72489bd4b5c7..0000000000000000000000000000000000000000 --- a/xbob/io/base/utils.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Andre Anjos <andre.dos.anjos@gmail.com> -# Thu 14 Mar 17:00:58 2013 -# -# Copyright (C) 2011-2013 Idiap Research Institute, Martigny, Switzerland - -"""Some utilities to generate fake patterns -""" - -import numpy - -DEFAULT_FONT = __import__('pkg_resources').resource_filename(__name__, - __import__('os').path.join("fonts", "regular.ttf")) - -def estimate_fontsize(height, width, format): - """Estimates the best fontsize to fit into a image that is (height, width)""" - - try: - # if PIL is installed this works: - import Image, ImageFont, ImageDraw - except ImportError: - # if Pillow is installed, this works better: - from PIL import Image, ImageFont, ImageDraw - - best_size = min(height, width) - fit = False - while best_size > 0: - font = ImageFont.truetype(DEFAULT_FONT, best_size) - (text_width, text_height) = font.getsize(format % 0) - if text_width < width and text_height < height: break - best_size -= 1 - - if best_size <= 0: - raise RuntimeError("Cannot find best size for font") - - return best_size - -def print_numbers(frame, counter, format, fontsize): - """Generates an image that serves as a test pattern for encoding/decoding and - accuracy tests.""" - - try: - # if PIL is installed this works: - import Image, ImageFont, ImageDraw - except ImportError: - # if Pillow is installed, this works better: - from PIL import Image, ImageFont, ImageDraw - - _, height, width = frame.shape - - # text at the center, indicating the frame number - text = format % counter - dim = min(width, height) - font = ImageFont.truetype(DEFAULT_FONT, fontsize) - (text_width, text_height) = font.getsize(text) - x_pos = int((width - text_width) / 2) - y_pos = int((height - text_height) / 2) - # this is buggy in Pillow-2.0.0, so we do it manually - #img = Image.fromarray(frame.transpose(1,2,0)) - img = Image.fromstring('RGB', (frame.shape[1], frame.shape[2]), frame.transpose(1,2,0).tostring()) - draw = ImageDraw.Draw(img) - draw.text((x_pos, y_pos), text, font=font, fill=(255,255,255)) - return numpy.asarray(img).transpose(2,0,1) - -def generate_colors(height, width, shift): - """Generates an image that serves as a test pattern for encoding/decoding and - accuracy tests.""" - - retval = numpy.ndarray((3, height, width), dtype='uint8') - - # standard color test pattern - w = width / 7; w2 = 2*w; w3 = 3*w; w4 = 4*w; w5 = 5*w; w6 = 6*w - retval[0,:,0:w] = 255; retval[1,:,0:w] = 255; retval[2,:,0:w] = 255; - retval[0,:,w:w2] = 255; retval[1,:,w:w2] = 255; retval[2,:,w:w2] = 0; - retval[0,:,w2:w3] = 0; retval[1,:,w2:w3] = 255; retval[2,:,w2:w3] = 255; - retval[0,:,w3:w4] = 0; retval[1,:,w3:w4] = 255; retval[2,:,w3:w4] = 0; - retval[0,:,w4:w5] = 255; retval[1,:,w4:w5] = 0; retval[2,:,w4:w5] = 255; - retval[0,:,w5:w6] = 255; retval[1,:,w5:w6] = 0; retval[2,:,w5:w6] = 0; - retval[0,:,w6:] = 0; retval[1,:,w6:] = 0; retval[2,:,w6:] = 255; - - # rotate by 'shift' - retval = numpy.roll(retval, shift, axis=2) - return retval - -def color_distortion(shape, framerate, format, codec, filename): - """Returns distortion patterns for a set of frames with moving colors. - - Keyword parameters: - - shape (int, int, int) - The length (number of frames), height and width for the generated sequence - - format - The string that identifies the format to be used for the output file - - codec - The codec to be used for the output file - - filename - The name of the file to use for encoding the test - """ - - length, height, width = shape - from . import VideoReader, VideoWriter - outv = VideoWriter(filename, height, width, framerate, codec=codec, - format=format, check=False) - orig = [] - text_format = "%%0%dd" % len(str(length-1)) - fontsize = estimate_fontsize(height, width, text_format) - fontsize = int(fontsize/4) - for i in range(0, length): - newframe = generate_colors(height, width, i%width) - newframe = print_numbers(newframe, i, text_format, fontsize) - outv.append(newframe) - orig.append(newframe) - outv.close() - orig = numpy.array(orig, dtype='uint8') - return orig, framerate, VideoReader(filename, check=False) - -def frameskip_detection(shape, framerate, format, codec, filename): - """Returns distortion patterns for a set of frames with big numbers. - - Keyword parameters: - - shape (int, int, int) - The length (number of frames), height and width for the generated sequence - - format - The string that identifies the format to be used for the output file - - codec - The codec to be used for the output file - - filename - The name of the file to use for encoding the test - """ - - length, height, width = shape - from . import VideoReader, VideoWriter - text_format = "%%0%dd" % len(str(length-1)) - fontsize = estimate_fontsize(height, width, text_format) - outv = VideoWriter(filename, height, width, framerate, codec=codec, - format=format, check=False) - orig = [] - for i in range(0, length): - newframe = numpy.zeros((3, height, width), dtype='uint8') - newframe = print_numbers(newframe, i, text_format, fontsize) - outv.append(newframe) - orig.append(newframe) - outv.close() - orig = numpy.array(orig, dtype='uint8') - return orig, framerate, VideoReader(filename, check=False) - -def quality_degradation(shape, framerate, format, codec, filename): - """Returns noise patterns for a set of frames. - - Keyword parameters: - - shape (int, int, int) - The length (number of frames), height and width for the generated sequence - - format - The string that identifies the format to be used for the output file - - codec - The codec to be used for the output file - - filename - The name of the file to use for encoding the test - """ - - length, height, width = shape - from . import VideoReader, VideoWriter - text_format = "%%0%dd" % len(str(length-1)) - fontsize = estimate_fontsize(height, width, text_format) - fontsize = int(fontsize/4) - outv = VideoWriter(filename, height, width, framerate, codec=codec, - format=format, check=False) - orig = [] - for i in range(0, length): - newframe = numpy.random.randint(0, 256, (3, height, width)).astype('uint8') - newframe = print_numbers(newframe, i, text_format, fontsize) - outv.append(newframe) - orig.append(newframe) - outv.close() - orig = numpy.array(orig, dtype='uint8') - return orig, framerate, VideoReader(filename, check=False) - -def is_string(s): - """Returns ``True`` if the given object is a string - - This method can be used with Python-2.x or 3.x and returns a string - respecting each environment's constraints. - """ - - from sys import version_info - - return (version_info[0] < 3 and isinstance(s, (str, unicode))) or \ - isinstance(s, (bytes, str)) diff --git a/xbob/io/base/version.cpp b/xbob/io/base/version.cpp index 7602399c2758f15617a95b86a8f4b8c2fe6e2075..e5458f98d6e14ad8fa0b32ff40ac846ec6637114 100644 --- a/xbob/io/base/version.cpp +++ b/xbob/io/base/version.cpp @@ -21,51 +21,10 @@ #include <bob/config.h> #include <bob/io/CodecRegistry.h> -#include <bob/io/VideoUtilities.h> #include <xbob.blitz/capi.h> #include <xbob.blitz/cleanup.h> - -extern "C" { - -#ifdef NO_IMPORT_ARRAY -#undef NO_IMPORT_ARRAY -#endif -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include <numpy/arrayobject.h> - #include <hdf5.h> -#include <jpeglib.h> - -#define PNG_SKIP_SETJMP_CHECK -// #define requires because of the problematic pngconf.h. -// Look at the thread here: -// https://bugs.launchpad.net/ubuntu/+source/libpng/+bug/218409 -#include <png.h> - -#if WITH_FFMPEG -# include <libavformat/avformat.h> -# include <libavcodec/avcodec.h> -# if LIBAVUTIL_VERSION_INT >= 0x320f01 //50.15.1 @ ffmpeg-0.6 -# include <libavutil/opt.h> -# include <libavutil/pixdesc.h> -# endif -# include <libavutil/avutil.h> -# include <libswscale/swscale.h> -# if !HAVE_FFMPEG_AVCODEC_AVCODECID -# define AVCodecID CodecID -# endif -#endif - -#include <gif_lib.h> - -#if WITH_MATIO -#include <matio.h> -#endif - -#include <tiffio.h> - -} static int dict_set(PyObject* d, const char* key, const char* value) { PyObject* v = Py_BuildValue("s", value); @@ -92,74 +51,6 @@ static PyObject* make_object(const char* s) { return Py_BuildValue("s", s); } -#if WITH_FFMPEG - -static PyObject* make_object(bool v) { - if (v) Py_RETURN_TRUE; - Py_RETURN_FALSE; -} - -static PyObject* make_object(unsigned int v) { - return Py_BuildValue("n", v); -} - -static PyObject* make_object(double v) { - return PyFloat_FromDouble(v); -} - -static PyObject* make_object(PyObject* v) { - Py_INCREF(v); - return v; -} - -/** - * Sets a dictionary entry using a string as key and another one as value. - * Returns 1 in case of success, 0 in case of failure. - */ -template <typename T> -int dict_set_string(boost::shared_ptr<PyObject> d, const char* key, T value) { - PyObject* pyvalue = make_object(value); - if (!pyvalue) return 0; - int retval = PyDict_SetItemString(d.get(), key, pyvalue); - Py_DECREF(pyvalue); - if (retval == 0) return 1; //all good - return 0; //a problem occurred -} - -/** - * Sets a dictionary entry using a string as key and another one as value. - * Returns 1 in case of success, 0 in case of faiulre. - */ -template <typename T> -int list_append(PyObject* l, T value) { - PyObject* pyvalue = make_object(value); - if (!pyvalue) return 0; - int retval = PyList_Append(l, pyvalue); - Py_DECREF(pyvalue); - if (retval == 0) return 1; //all good - return 0; //a problem occurred -} - -/** - * A deleter, for shared_ptr's - */ -void pyobject_deleter(PyObject* o) { - Py_XDECREF(o); -} - -/** - * Checks if it is a Python string for Python 2.x or 3.x - */ -int check_string(PyObject* o) { -# if PY_VERSION_HEX >= 0x03000000 - return PyUnicode_Check(o); -# else - return PyString_Check(o); -# endif -} - -#endif /* WITH_FFMPEG */ - /*********************************************************** * Version number generation ***********************************************************/ @@ -172,111 +63,6 @@ static PyObject* hdf5_version() { return Py_BuildValue("s", f.str().c_str()); } -/** - * FFmpeg version - */ -static PyObject* ffmpeg_version() { - PyObject* retval = PyDict_New(); - if (!retval) return 0; - auto retval_ = make_safe(retval); - -#if WITH_FFMPEG -# if defined(FFMPEG_VERSION) - if (std::strlen(FFMPEG_VERSION)) { - if (!dict_set(retval, "ffmpeg", FFMPEG_VERSION)) return 0; - } -# endif - if (!dict_set(retval, "avformat", BOOST_PP_STRINGIZE(LIBAVFORMAT_VERSION))) { - return 0; - } - if (!dict_set(retval, "avcodec", BOOST_PP_STRINGIZE(LIBAVCODEC_VERSION))) { - return 0; - } - if (!dict_set(retval, "avutil", BOOST_PP_STRINGIZE(LIBAVUTIL_VERSION))) { - return 0; - } - if (!dict_set(retval, "swscale", BOOST_PP_STRINGIZE(LIBSWSCALE_VERSION))) { - return 0; - } -#else - if (!dict_set(retval, "ffmpeg", "unavailable")) { - return 0; - } -#endif - Py_INCREF(retval); - return retval; -} - -/** - * LibJPEG version - */ -static PyObject* libjpeg_version() { - boost::format f("%d (compiled with %d bits depth)"); - f % JPEG_LIB_VERSION; - f % BITS_IN_JSAMPLE; - return Py_BuildValue("s", f.str().c_str()); -} - -/** - * Libpng version - */ -static PyObject* libpng_version() { - return Py_BuildValue("s", PNG_LIBPNG_VER_STRING); -} - -/** - * Libtiff version - */ -static PyObject* libtiff_version() { - - static const std::string beg_str("LIBTIFF, Version "); - static const size_t beg_len = beg_str.size(); - std::string vtiff(TIFFGetVersion()); - - // Remove first part if it starts with "LIBTIFF, Version " - if(vtiff.compare(0, beg_len, beg_str) == 0) - vtiff = vtiff.substr(beg_len); - - // Remove multiple (copyright) lines if any - size_t end_line = vtiff.find("\n"); - if(end_line != std::string::npos) - vtiff = vtiff.substr(0,end_line); - - return Py_BuildValue("s", vtiff.c_str()); - -} - -/** - * Version of giflib support - */ -static PyObject* giflib_version() { -#ifdef GIF_LIB_VERSION - return Py_BuildValue("s", GIF_LIB_VERSION); -#else - boost::format f("%s.%s.%s"); - f % BOOST_PP_STRINGIZE(GIFLIB_MAJOR); - f % BOOST_PP_STRINGIZE(GIFLIB_MINOR); - f % BOOST_PP_STRINGIZE(GIFLIB_RELEASE); - return Py_BuildValue("s", f.str().c_str()); -#endif -} - - -/** - * Matio, if compiled with such support - */ -static PyObject* matio_version() { -#if WITH_MATIO - boost::format f("%s.%s.%s"); - f % BOOST_PP_STRINGIZE(MATIO_MAJOR_VERSION); - f % BOOST_PP_STRINGIZE(MATIO_MINOR_VERSION); - f % BOOST_PP_STRINGIZE(MATIO_RELEASE_LEVEL); - return Py_BuildValue("s", f.str().c_str()); -#else - return Py_BuildValue("s", "unavailable"); -#endif -} - /** * Bob version, API version and platform */ @@ -348,13 +134,6 @@ static PyObject* build_version_dictionary() { if (!dict_steal(retval, "Bob", bob_version())) return 0; if (!dict_steal(retval, "HDF5", hdf5_version())) return 0; - if (!dict_steal(retval, "FFmpeg", ffmpeg_version())) return 0; - if (!dict_steal(retval, "libjpeg", libjpeg_version())) return 0; - if (!dict_set(retval, "libnetpbm", "Unknown version")) return 0; - if (!dict_steal(retval, "libpng", libpng_version())) return 0; - if (!dict_steal(retval, "libtiff", libtiff_version())) return 0; - if (!dict_steal(retval, "giflib", giflib_version())) return 0; - if (!dict_steal(retval, "MatIO", matio_version())) return 0; if (!dict_steal(retval, "Boost", boost_version())) return 0; if (!dict_steal(retval, "Compiler", compiler_version())) return 0; if (!dict_steal(retval, "Python", python_version())) return 0; @@ -367,453 +146,6 @@ static PyObject* build_version_dictionary() { return retval; } -/*********************************************************** - * FFmpeg information - ***********************************************************/ - -#if WITH_FFMPEG - -/** - * Describes a given codec. We return a **new reference** to a dictionary - * containing the codec properties. - */ -static PyObject* describe_codec(const AVCodec* codec) { - - /** - * We wrap the returned object into a smart pointer until we - * are absolutely sure all went good. At this point, we free - * the PyObject* from the boost encapsulation and return it. - */ - boost::shared_ptr<PyObject> retval(PyDict_New(), &pyobject_deleter); - if (!retval) return 0; - - /* Sets basic properties for the codec */ - if (!dict_set_string(retval, "name", codec->name)) return 0; - if (!dict_set_string(retval, "long_name", codec->long_name)) return 0; - if (!dict_set_string(retval, "id", (unsigned int)codec->id)) return 0; - - /** - * If pixel formats are available, creates and attaches a - * list with all their names - */ - - boost::shared_ptr<PyObject> pixfmt; - if (codec->pix_fmts) { - pixfmt.reset(PyList_New(0), &pyobject_deleter); - if (!pixfmt) return 0; - - unsigned int i=0; - while(codec->pix_fmts[i] != -1) { - if (!list_append(pixfmt.get(), -#if LIBAVUTIL_VERSION_INT >= 0x320f01 //50.15.1 @ ffmpeg-0.6 - av_get_pix_fmt_name -#else - avcodec_get_pix_fmt_name -#endif - (codec->pix_fmts[i++]))) return 0; - } - pixfmt.reset(PySequence_Tuple(pixfmt.get()), &pyobject_deleter); - } - else { - Py_INCREF(Py_None); - pixfmt.reset(Py_None, &pyobject_deleter); - } - - if (!dict_set_string(retval, "pixfmts", pixfmt.get())) return 0; - - /* Get specific framerates for the codec, if any */ - const AVRational* rate = codec->supported_framerates; - boost::shared_ptr<PyObject> rates(PyList_New(0), &pyobject_deleter); - if (!rates) return 0; - - while (rate && rate->num && rate->den) { - list_append(rates.get(), ((double)rate->num)/((double)rate->den)); - ++rate; - } - rates.reset(PySequence_Tuple(rates.get()), &pyobject_deleter); - if (!rates) return 0; - - if (!dict_set_string(retval, "specific_framerates_hz", rates.get())) return 0; - - /* Other codec capabilities */ -# ifdef CODEC_CAP_LOSSLESS - if (!dict_set_string(retval, "lossless", (bool)(codec->capabilities & CODEC_CAP_LOSSLESS))) return 0; -# endif -# ifdef CODEC_CAP_EXPERIMENTAL - if (!dict_set_string(retval, "experimental", (bool)(codec->capabilities & CODEC_CAP_EXPERIMENTAL))) return 0; -# endif -# ifdef CODEC_CAP_DELAY - if (!dict_set_string(retval, "delay", (bool)(codec->capabilities & CODEC_CAP_DELAY))) return 0; -# endif -# ifdef CODEC_CAP_HWACCEL - if (!dict_set_string(retval, "hardware_accelerated", (bool)(codec->capabilities & CODEC_CAP_HWACCEL))) return 0; -# endif - if (!dict_set_string(retval, "encode", (bool)(avcodec_find_encoder(codec->id)))) return 0; - if (!dict_set_string(retval, "decode", (bool)(avcodec_find_decoder(codec->id)))) return 0; - - /* If all went OK, detach the returned value from the smart pointer **/ - Py_INCREF(retval.get()); - return retval.get(); - -} - -/** - * Describes a given codec or raises, in case the codec cannot be accessed - */ -static PyObject* PyBobIo_DescribeEncoder(PyObject*, PyObject *args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - static const char* const_kwlist[] = {"key", 0}; - static char** kwlist = const_cast<char**>(const_kwlist); - - PyObject* key = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &key)) return 0; - - if (!PyNumber_Check(key) && !check_string(key)) { - PyErr_SetString(PyExc_TypeError, "input `key' must be a number identifier or a string with the codec name"); - return 0; - } - - if (PyNumber_Check(key)) { - - /* If you get to this point, the user passed a number - re-parse */ - int id = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &id)) return 0; - - AVCodec* codec = avcodec_find_encoder((AVCodecID)id); - if (!codec) { - PyErr_Format(PyExc_RuntimeError, "ffmpeg::avcodec_find_encoder(%d == 0x%x) did not return a valid codec", id, id); - return 0; - } - - return describe_codec(codec); - } - - /* If you get to this point, the user passed a string - re-parse */ - const char* name = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name)) return 0; - - AVCodec* codec = avcodec_find_encoder_by_name(name); - if (!codec) { - PyErr_Format(PyExc_RuntimeError, "ffmpeg::avcodec_find_encoder_by_name(`%s') did not return a valid codec", name); - return 0; - } - - return describe_codec(codec); -} - -PyDoc_STRVAR(s_describe_encoder_str, "describe_encoder"); -PyDoc_STRVAR(s_describe_encoder_doc, -"describe_encoder([key]) -> dict\n\ -\n\ -Parameters:\n\ -\n\ -key\n\ - [int|str, optional] A key which can be either the encoder\n\ - name or its numerical identifier.\n\ -\n\ -Returns a dictionary containing a description of properties in\n\ -the given encoder.\n\ -"); - -/** - * Describes a given codec or raises, in case the codec cannot be accessed - */ -static PyObject* PyBobIo_DescribeDecoder(PyObject*, PyObject *args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - static const char* const_kwlist[] = {"key", 0}; - static char** kwlist = const_cast<char**>(const_kwlist); - - PyObject* key = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &key)) return 0; - - if (!PyNumber_Check(key) && !check_string(key)) { - PyErr_SetString(PyExc_TypeError, "input `key' must be a number identifier or a string with the codec name"); - return 0; - } - - if (PyNumber_Check(key)) { - - /* If you get to this point, the user passed a number - re-parse */ - int id = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &id)) return 0; - - AVCodec* codec = avcodec_find_decoder((AVCodecID)id); - if (!codec) { - PyErr_Format(PyExc_RuntimeError, "ffmpeg::avcodec_find_decoder(%d == 0x%x) did not return a valid codec", id, id); - return 0; - } - - return describe_codec(codec); - } - - /* If you get to this point, the user passed a string - re-parse */ - const char* name = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", kwlist, &name)) return 0; - - AVCodec* codec = avcodec_find_decoder_by_name(name); - if (!codec) { - PyErr_Format(PyExc_RuntimeError, "ffmpeg::avcodec_find_decoder_by_name(`%s') did not return a valid codec", name); - return 0; - } - - return describe_codec(codec); -} - -PyDoc_STRVAR(s_describe_decoder_str, "describe_decoder"); -PyDoc_STRVAR(s_describe_decoder_doc, -"describe_decoder([key]) -> dict\n\ -\n\ -Parameters:\n\ -\n\ -key\n\ - [int|str, optional] A key which can be either the decoder\n\ - name or its numerical identifier.\n\ -\n\ -Returns a dictionary containing a description of properties in\n\ -the given decoder.\n\ -"); - -static PyObject* get_video_codecs(void (*f)(std::map<std::string, const AVCodec*>&)) { - - std::map<std::string, const AVCodec*> m; - f(m); - - PyObject* retval = PyDict_New(); - if (!retval) return 0; - auto retval_ = make_safe(retval); - - for (auto k=m.begin(); k!=m.end(); ++k) { - PyObject* pyvalue = describe_codec(k->second); - if (!pyvalue) return 0; - auto pyvalue_ = make_safe(pyvalue); - if (PyDict_SetItemString(retval, k->first.c_str(), pyvalue) != 0) return 0; - } - - Py_INCREF(retval); - return retval; - -} - -static PyObject* PyBobIo_SupportedCodecs(PyObject*) { - return get_video_codecs(&bob::io::detail::ffmpeg::codecs_supported); -} - -static PyObject* PyBobIo_AvailableCodecs(PyObject*) { - return get_video_codecs(&bob::io::detail::ffmpeg::codecs_installed); -} - -PyDoc_STRVAR(s_supported_codecs_str, "supported_video_codecs"); -PyDoc_STRVAR(s_supported_codecs_doc, -"supported_video_codecs() -> dict\n\ -\n\ -Returns a dictionary with currently supported video codec properties.\n\ -\n\ -Returns a dictionary containing a detailed description of the\n\ -built-in codecs for videos that are fully supported.\n\ -"); - -PyDoc_STRVAR(s_available_codecs_str, "available_video_codecs"); -PyDoc_STRVAR(s_available_codecs_doc, -"available_video_codecs() -> dict\n\ -\n\ -Returns a dictionary with all available video codec properties.\n\ -\n\ -Returns a dictionary containing a detailed description of the\n\ -built-in codecs for videos that are available but **not necessarily\n\ -supported**.\n\ -"); - -static PyObject* get_video_iformats(void (*f)(std::map<std::string, AVInputFormat*>&)) { - - std::map<std::string, AVInputFormat*> m; - f(m); - - PyObject* retval = PyDict_New(); - if (!retval) return 0; - auto retval_ = make_safe(retval); - - for (auto k=m.begin(); k!=m.end(); ++k) { - - PyObject* property = PyDict_New(); - if (!property) return 0; - auto property_ = make_safe(property); - - if (!dict_set(property, "name", k->second->name)) return 0; - - if (!dict_set(property, "long_name", k->second->long_name)) return 0; - - // get extensions - std::vector<std::string> exts; - bob::io::detail::ffmpeg::tokenize_csv(k->second->extensions, exts); - - PyObject* ext_list = PyList_New(0); - if (!ext_list) return 0; - auto ext_list_ = make_safe(ext_list); - - for (auto ext=exts.begin(); ext!=exts.end(); ++ext) { - if (!list_append(ext_list, ext->c_str())) return 0; - } - - Py_INCREF(ext_list); - if (!dict_steal(property, "extensions", ext_list)) return 0; - - Py_INCREF(property); - if (!dict_steal(retval, k->first.c_str(), property)) return 0; - - } - - Py_INCREF(retval); - return retval; - -} - -static PyObject* PyBobIo_SupportedInputFormats(PyObject*) { - return get_video_iformats(&bob::io::detail::ffmpeg::iformats_supported); -} - -static PyObject* PyBobIo_AvailableInputFormats(PyObject*) { - return get_video_iformats(&bob::io::detail::ffmpeg::iformats_installed); -} - -PyDoc_STRVAR(s_supported_iformats_str, "supported_videoreader_formats"); -PyDoc_STRVAR(s_supported_iformats_doc, -"supported_videoreader_formats() -> dict\n\ -\n\ -Returns a dictionary with currently supported video input formats.\n\ -\n\ -Returns a dictionary containing a detailed description of the\n\ -built-in input formats for videos that are fully supported.\n\ -"); - -PyDoc_STRVAR(s_available_iformats_str, "available_videoreader_formats"); -PyDoc_STRVAR(s_available_iformats_doc, -"available_videoreader_formats() -> dict\n\ -\n\ -Returns a dictionary with currently available video input formats.\n\ -\n\ -Returns a dictionary containing a detailed description of the\n\ -built-in input formats for videos that are available, but **not\n\ -necessarily supported** by this library.\n\ -"); - -static PyObject* get_video_oformats(bool supported) { - - std::map<std::string, AVOutputFormat*> m; - if (supported) bob::io::detail::ffmpeg::oformats_supported(m); - else bob::io::detail::ffmpeg::oformats_installed(m); - - PyObject* retval = PyDict_New(); - if (!retval) return 0; - auto retval_ = make_safe(retval); - - for (auto k=m.begin(); k!=m.end(); ++k) { - - PyObject* property = PyDict_New(); - if (!property) return 0; - auto property_ = make_safe(property); - - if (!dict_set(property, "name", k->second->name)) return 0; - - if (!dict_set(property, "long_name", k->second->long_name)) return 0; - - if (!dict_set(property, "mime_type", k->second->mime_type)) return 0; - - // get extensions - std::vector<std::string> exts; - bob::io::detail::ffmpeg::tokenize_csv(k->second->extensions, exts); - - PyObject* ext_list = PyList_New(0); - if (!ext_list) return 0; - auto ext_list_ = make_safe(ext_list); - - for (auto ext=exts.begin(); ext!=exts.end(); ++ext) { - if (!list_append(ext_list, ext->c_str())) return 0; - } - - Py_INCREF(ext_list); - if (!dict_steal(property, "extensions", ext_list)) return 0; - - /** get recommended codec **/ - PyObject* default_codec = 0; - if (k->second->video_codec) { - AVCodec* codec = avcodec_find_encoder(k->second->video_codec); - if (codec) { - default_codec = describe_codec(codec); - if (!default_codec) return 0; - } - } - - if (!default_codec) { - Py_INCREF(Py_None); - default_codec = Py_None; - } - - if (!dict_steal(property, "default_codec", default_codec)) return 0; - - /** get supported codec list **/ - if (supported) { - std::vector<const AVCodec*> codecs; - bob::io::detail::ffmpeg::oformat_supported_codecs(k->second->name, codecs); - - PyObject* supported_codecs = PyDict_New(); - if (!supported_codecs) return 0; - auto supported_codecs_ = make_safe(supported_codecs); - - for (auto c=codecs.begin(); c!=codecs.end(); ++c) { - PyObject* codec_descr = describe_codec(*c); - auto codec_descr_ = make_safe(codec_descr); - if (!codec_descr) return 0; - Py_INCREF(codec_descr); - if (!dict_steal(supported_codecs, (*c)->name, codec_descr)) return 0; - } - - Py_INCREF(supported_codecs); - if (!dict_steal(property, "supported_codecs", supported_codecs)) return 0; - } - - Py_INCREF(property); - if (!dict_steal(retval, k->first.c_str(), property)) return 0; - - } - - Py_INCREF(retval); - return retval; - -} - -static PyObject* PyBobIo_SupportedOutputFormats(PyObject*) { - return get_video_oformats(true); -} - -static PyObject* PyBobIo_AvailableOutputFormats(PyObject*) { - return get_video_oformats(false); -} - -PyDoc_STRVAR(s_supported_oformats_str, "supported_videowriter_formats"); -PyDoc_STRVAR(s_supported_oformats_doc, -"supported_videowriter_formats() -> dict\n\ -\n\ -Returns a dictionary with currently supported video output formats.\n\ -\n\ -Returns a dictionary containing a detailed description of the\n\ -built-in output formats for videos that are fully supported.\n\ -"); - -PyDoc_STRVAR(s_available_oformats_str, "available_videowriter_formats"); -PyDoc_STRVAR(s_available_oformats_doc, -"available_videowriter_formats() -> dict\n\ -\n\ -Returns a dictionary with currently available video output formats.\n\ -\n\ -Returns a dictionary containing a detailed description of the\n\ -built-in output formats for videos that are available, but **not\n\ -necessarily supported** by this library.\n\ -"); - -#endif /* WITH_FFMPEG */ - static PyObject* PyBobIo_Extensions(PyObject*) { typedef std::map<std::string, std::string> map_type; @@ -851,58 +183,6 @@ static PyMethodDef module_methods[] = { METH_NOARGS, s_extensions_doc, }, - -#if WITH_FFMPEG - { - s_describe_encoder_str, - (PyCFunction)PyBobIo_DescribeEncoder, - METH_VARARGS|METH_KEYWORDS, - s_describe_encoder_doc, - }, - { - s_describe_decoder_str, - (PyCFunction)PyBobIo_DescribeDecoder, - METH_VARARGS|METH_KEYWORDS, - s_describe_decoder_doc, - }, - { - s_supported_codecs_str, - (PyCFunction)PyBobIo_SupportedCodecs, - METH_NOARGS, - s_supported_codecs_doc, - }, - { - s_available_codecs_str, - (PyCFunction)PyBobIo_AvailableCodecs, - METH_NOARGS, - s_available_codecs_doc, - }, - { - s_supported_iformats_str, - (PyCFunction)PyBobIo_SupportedInputFormats, - METH_NOARGS, - s_supported_iformats_doc, - }, - { - s_available_iformats_str, - (PyCFunction)PyBobIo_AvailableInputFormats, - METH_NOARGS, - s_available_iformats_doc, - }, - { - s_supported_oformats_str, - (PyCFunction)PyBobIo_SupportedOutputFormats, - METH_NOARGS, - s_supported_oformats_doc, - }, - { - s_available_oformats_str, - (PyCFunction)PyBobIo_AvailableOutputFormats, - METH_NOARGS, - s_available_oformats_doc, - }, -#endif /* WITH_FFMPEG */ - {0} /* Sentinel */ }; diff --git a/xbob/io/base/videoreader.cpp b/xbob/io/base/videoreader.cpp deleted file mode 100644 index 837d0f62c51102a2f75889d57e14ff4c98be14b3..0000000000000000000000000000000000000000 --- a/xbob/io/base/videoreader.cpp +++ /dev/null @@ -1,703 +0,0 @@ -/** - * @author Andre Anjos <andre.anjos@idiap.ch> - * @date Wed 6 Nov 21:44:34 2013 - * - * @brief Bindings to bob::io::VideoReader - */ - -#define XBOB_IO_BASE_MODULE -#include "bobskin.h" -#include <xbob.io.base/api.h> - -#if WITH_FFMPEG -#include <boost/make_shared.hpp> -#include <numpy/arrayobject.h> -#include <xbob.blitz/capi.h> -#include <xbob.blitz/cleanup.h> -#include <stdexcept> - -#define VIDEOREADER_NAME "VideoReader" -PyDoc_STRVAR(s_videoreader_str, XBOB_EXT_MODULE_PREFIX "." VIDEOREADER_NAME); - -PyDoc_STRVAR(s_videoreader_doc, -"VideoReader(filename, [check=True]) -> new bob::io::VideoReader\n\ -\n\ -Use this object to read frames from video files.\n\ -\n\ -Constructor parameters:\n\ -\n\ -filename\n\ - [str] The file path to the file you want to read data from\n\ -\n\ -check\n\ - [bool] Format and codec will be extracted from the video metadata.\n\ - By default, if the format and/or the codec are not\n\ - supported by this version of Bob, an exception will be raised.\n\ - You can (at your own risk) set this flag to ``False`` to\n\ - avoid this check.\n\ -\n\ -VideoReader objects can read data from video files. The current\n\ -implementation uses `FFmpeg <http://ffmpeg.org>`_ (or\n\ -`libav <http://libav.org>`_ if FFmpeg is not available) which is\n\ -a stable freely available video encoding and decoding library,\n\ -designed specifically for these tasks. You can read an entire\n\ -video in memory by using the :py:meth:`xbob.io.video.Reader.load`\n\ -method or use iterators to read it frame by frame and avoid\n\ -overloading your machine\'s memory. The maximum precision data\n\ -`FFmpeg`_ will yield is a 24-bit (8-bit per band) representation\n\ -of each pixel (32-bit depths are also supported by `FFmpeg`_, but\n\ -not by this extension presently). So, the output of data is done\n\ -with ``uint8`` as data type. Output will be colored using the RGB\n\ -standard, with each band varying between 0 and 255, with zero\n\ -meaning pure black and 255, pure white (color).\n\ -\n\ -"); - -/* How to create a new PyBobIoVideoReaderObject */ -static PyObject* PyBobIoVideoReader_New(PyTypeObject* type, PyObject*, PyObject*) { - - /* Allocates the python object itself */ - PyBobIoVideoReaderObject* self = (PyBobIoVideoReaderObject*)type->tp_alloc(type, 0); - - self->v.reset(); - - return reinterpret_cast<PyObject*>(self); -} - -static void PyBobIoVideoReader_Delete (PyBobIoVideoReaderObject* o) { - - o->v.reset(); - Py_TYPE(o)->tp_free((PyObject*)o); - -} - -/* The __init__(self) method */ -static int PyBobIoVideoReader_Init(PyBobIoVideoReaderObject* self, - PyObject *args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - static const char* const_kwlist[] = {"filename", "check", 0}; - static char** kwlist = const_cast<char**>(const_kwlist); - - PyObject* filename = 0; - - PyObject* pycheck = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&|O", kwlist, - &PyBobIo_FilenameConverter, &filename, &pycheck)) return -1; - - auto filename_ = make_safe(filename); - - bool check = false; - if (pycheck && PyObject_IsTrue(pycheck)) check = true; - -#if PY_VERSION_HEX >= 0x03000000 - const char* c_filename = PyBytes_AS_STRING(filename); -#else - const char* c_filename = PyString_AS_STRING(filename); -#endif - - try { - self->v = boost::make_shared<bob::io::VideoReader>(c_filename, check); - } - catch (std::exception& e) { - PyErr_SetString(PyExc_RuntimeError, e.what()); - return -1; - } - catch (...) { - PyErr_Format(PyExc_RuntimeError, "cannot open video file `%s' for reading: unknown exception caught", c_filename); - return -1; - } - - return 0; ///< SUCCESS -} - -PyObject* PyBobIoVideoReader_Filename(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("s", self->v->filename().c_str()); -} - -PyDoc_STRVAR(s_filename_str, "filename"); -PyDoc_STRVAR(s_filename_doc, -"[str] The full path to the file that will be decoded by this object"); - -PyObject* PyBobIoVideoReader_Height(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("n", self->v->height()); -} - -PyDoc_STRVAR(s_height_str, "height"); -PyDoc_STRVAR(s_height_doc, -"[int] The height of each frame in the video (a multiple of 2)"); - -PyObject* PyBobIoVideoReader_Width(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("n", self->v->width()); -} - -PyDoc_STRVAR(s_width_str, "width"); -PyDoc_STRVAR(s_width_doc, -"[int] The width of each frame in the video (a multiple of 2)"); - -PyObject* PyBobIoVideoReader_NumberOfFrames(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("n", self->v->numberOfFrames()); -} - -PyDoc_STRVAR(s_number_of_frames_str, "number_of_frames"); -PyDoc_STRVAR(s_number_of_frames_doc, -"[int] The number of frames in this video file"); - -PyObject* PyBobIoVideoReader_Duration(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("n", self->v->duration()); -} - -PyDoc_STRVAR(s_duration_str, "duration"); -PyDoc_STRVAR(s_duration_doc, -"[int] Total duration of this video file in microseconds (long)"); - -PyObject* PyBobIoVideoReader_FormatName(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("s", self->v->formatName().c_str()); -} - -PyDoc_STRVAR(s_format_name_str, "format_name"); -PyDoc_STRVAR(s_format_name_doc, -"[str] Short name of the format in which this video file was recorded in"); - -PyObject* PyBobIoVideoReader_FormatLongName(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("s", self->v->formatLongName().c_str()); -} - -PyDoc_STRVAR(s_format_long_name_str, "format_long_name"); -PyDoc_STRVAR(s_format_long_name_doc, -"[str] Full name of the format in which this video file was recorded in"); - -PyObject* PyBobIoVideoReader_CodecName(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("s", self->v->codecName().c_str()); -} - -PyDoc_STRVAR(s_codec_name_str, "codec_name"); -PyDoc_STRVAR(s_codec_name_doc, -"[str] Short name of the codec in which this video file was recorded in"); - -PyObject* PyBobIoVideoReader_CodecLongName(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("s", self->v->codecLongName().c_str()); -} - -PyDoc_STRVAR(s_codec_long_name_str, "codec_long_name"); -PyDoc_STRVAR(s_codec_long_name_doc, -"[str] Full name of the codec in which this video file was recorded in"); - -PyObject* PyBobIoVideoReader_FrameRate(PyBobIoVideoReaderObject* self) { - return PyFloat_FromDouble(self->v->frameRate()); -} - -PyDoc_STRVAR(s_frame_rate_str, "frame_rate"); -PyDoc_STRVAR(s_frame_rate_doc, -"[float] Video's announced frame rate (note there are video formats with variable frame rates)"); - -PyObject* PyBobIoVideoReader_VideoType(PyBobIoVideoReaderObject* self) { - return PyBobIo_TypeInfoAsTuple(self->v->video_type()); -} - -PyDoc_STRVAR(s_video_type_str, "video_type"); -PyDoc_STRVAR(s_video_type_doc, -"[tuple] Typing information to load all of the file at once"); - -PyObject* PyBobIoVideoReader_FrameType(PyBobIoVideoReaderObject* self) { - return PyBobIo_TypeInfoAsTuple(self->v->frame_type()); -} - -PyDoc_STRVAR(s_frame_type_str, "frame_type"); -PyDoc_STRVAR(s_frame_type_doc, -"[tuple] Typing information to load each frame separatedly"); - -static PyObject* PyBobIoVideoReader_Print(PyBobIoVideoReaderObject* self) { - return Py_BuildValue("s", self->v->info().c_str()); -} - -PyDoc_STRVAR(s_info_str, "info"); -PyDoc_STRVAR(s_info_doc, -"[str] A string with lots of video information (same as ``str(x)``)"); - -static PyGetSetDef PyBobIoVideoReader_getseters[] = { - { - s_filename_str, - (getter)PyBobIoVideoReader_Filename, - 0, - s_filename_doc, - 0, - }, - { - s_height_str, - (getter)PyBobIoVideoReader_Height, - 0, - s_height_doc, - 0, - }, - { - s_width_str, - (getter)PyBobIoVideoReader_Width, - 0, - s_width_doc, - 0, - }, - { - s_number_of_frames_str, - (getter)PyBobIoVideoReader_NumberOfFrames, - 0, - s_number_of_frames_doc, - 0, - }, - { - s_duration_str, - (getter)PyBobIoVideoReader_Duration, - 0, - s_duration_doc, - 0, - }, - { - s_format_name_str, - (getter)PyBobIoVideoReader_FormatName, - 0, - s_format_name_doc, - 0, - }, - { - s_format_long_name_str, - (getter)PyBobIoVideoReader_FormatLongName, - 0, - s_format_long_name_doc, - 0, - }, - { - s_codec_name_str, - (getter)PyBobIoVideoReader_CodecName, - 0, - s_codec_name_doc, - 0, - }, - { - s_codec_long_name_str, - (getter)PyBobIoVideoReader_CodecLongName, - 0, - s_codec_long_name_doc, - 0, - }, - { - s_frame_rate_str, - (getter)PyBobIoVideoReader_FrameRate, - 0, - s_frame_rate_doc, - 0, - }, - { - s_video_type_str, - (getter)PyBobIoVideoReader_VideoType, - 0, - s_video_type_doc, - 0, - }, - { - s_frame_type_str, - (getter)PyBobIoVideoReader_FrameType, - 0, - s_frame_type_doc, - 0, - }, - { - s_info_str, - (getter)PyBobIoVideoReader_Print, - 0, - s_info_doc, - 0, - }, - {0} /* Sentinel */ -}; - -static PyObject* PyBobIoVideoReader_Repr(PyBobIoVideoReaderObject* self) { - return -# if PY_VERSION_HEX >= 0x03000000 - PyUnicode_FromFormat -# else - PyString_FromFormat -# endif - ("%s(filename='%s')", Py_TYPE(self)->tp_name, self->v->filename().c_str()); -} - -/** - * If a keyboard interruption occurs, then it is translated into a C++ - * exception that makes the loop stops. - */ -static void Check_Interrupt() { - if(PyErr_CheckSignals() == -1) { - if (!PyErr_Occurred()) PyErr_SetInterrupt(); - throw std::runtime_error("error is already set"); - } -} - -static PyObject* PyBobIoVideoReader_Load(PyBobIoVideoReaderObject* self, PyObject *args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - static const char* const_kwlist[] = {"raise_on_error", 0}; - static char** kwlist = const_cast<char**>(const_kwlist); - - PyObject* raise = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &raise)) return 0; - - bool raise_on_error = false; - if (raise && PyObject_IsTrue(raise)) raise_on_error = true; - - const bob::core::array::typeinfo& info = self->v->video_type(); - - npy_intp shape[NPY_MAXDIMS]; - for (size_t k=0; k<info.nd; ++k) shape[k] = info.shape[k]; - - int type_num = PyBobIo_AsTypenum(info.dtype); - if (type_num == NPY_NOTYPE) return 0; ///< failure - - PyObject* retval = PyArray_SimpleNew(info.nd, shape, type_num); - if (!retval) return 0; - auto retval_ = make_safe(retval); - - Py_ssize_t frames_read = 0; - - try { - bobskin skin((PyArrayObject*)retval, info.dtype); - frames_read = self->v->load(skin, raise_on_error, &Check_Interrupt); - } - catch (std::exception& e) { - if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, e.what()); - return 0; - } - catch (...) { - if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading video from file `%s'", self->v->filename().c_str()); - return 0; - } - - if (frames_read != shape[0]) { - //resize - shape[0] = frames_read; - PyArray_Dims newshape; - newshape.ptr = shape; - newshape.len = info.nd; - PyArray_Resize((PyArrayObject*)retval, &newshape, 1, NPY_ANYORDER); - } - - Py_INCREF(retval); - return retval; - -} - -PyDoc_STRVAR(s_load_str, "load"); -PyDoc_STRVAR(s_load_doc, -"x.load([raise_on_error=False] -> numpy.ndarray\n\ -\n\ -Loads all of the video stream in a numpy ndarray organized\n\ -in this way: (frames, color-bands, height, width). I'll dynamically\n\ -allocate the output array and return it to you.\n\ -\n\ -The flag ``raise_on_error``, which is set to ``False`` by default\n\ -influences the error reporting in case problems are found with the\n\ -video file. If you set it to ``True``, we will report problems\n\ -raising exceptions. If you either don't set it or set it to\n\ -``False``, we will truncate the file at the frame with problems\n\ -and will not report anything. It is your task to verify if the\n\ -number of frames returned matches the expected number of frames as\n\ -reported by the property ``number_of_frames`` (or ``len``) of this\n\ -object.\n\ -"); - -static PyMethodDef PyBobIoVideoReader_Methods[] = { - { - s_load_str, - (PyCFunction)PyBobIoVideoReader_Load, - METH_VARARGS|METH_KEYWORDS, - s_load_doc, - }, - {0} /* Sentinel */ -}; - -static PyObject* PyBobIoVideoReader_GetIndex (PyBobIoVideoReaderObject* self, Py_ssize_t i) { - - if (i < 0) i += self->v->numberOfFrames(); ///< adjust for negative indexing - - if (i < 0 || (size_t)i >= self->v->numberOfFrames()) { - PyErr_Format(PyExc_IndexError, "video frame index out of range - `%s' only contains %" PY_FORMAT_SIZE_T "d frame(s)", self->v->filename().c_str(), self->v->numberOfFrames()); - return 0; - } - - const bob::core::array::typeinfo& info = self->v->frame_type(); - - npy_intp shape[NPY_MAXDIMS]; - for (size_t k=0; k<info.nd; ++k) shape[k] = info.shape[k]; - - int type_num = PyBobIo_AsTypenum(info.dtype); - if (type_num == NPY_NOTYPE) return 0; ///< failure - - PyObject* retval = PyArray_SimpleNew(info.nd, shape, type_num); - if (!retval) return 0; - auto retval_ = make_safe(retval); - - try { - auto it = self->v->begin(); - it += i; - bobskin skin((PyArrayObject*)retval, info.dtype); - it.read(skin); - } - catch (std::exception& e) { - if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, e.what()); - return 0; - } - catch (...) { - if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading frame #%" PY_FORMAT_SIZE_T "d from file `%s'", i, self->v->filename().c_str()); - return 0; - } - - Py_INCREF(retval); - return retval; - -} - -static PyObject* PyBobIoVideoReader_GetSlice (PyBobIoVideoReaderObject* self, PySliceObject* slice) { - - Py_ssize_t start, stop, step, slicelength; -#if PY_VERSION_HEX < 0x03000000 - if (PySlice_GetIndicesEx(slice, -#else - if (PySlice_GetIndicesEx(reinterpret_cast<PyObject*>(slice), -#endif - self->v->numberOfFrames(), &start, &stop, &step, &slicelength) < 0) return 0; - - //creates the return array - const bob::core::array::typeinfo& info = self->v->frame_type(); - - int type_num = PyBobIo_AsTypenum(info.dtype); - if (type_num == NPY_NOTYPE) return 0; ///< failure - - if (slicelength <= 0) return PyArray_SimpleNew(0, 0, type_num); - - npy_intp shape[NPY_MAXDIMS]; - shape[0] = slicelength; - for (size_t k=0; k<info.nd; ++k) shape[k+1] = info.shape[k]; - - PyObject* retval = PyArray_SimpleNew(info.nd+1, shape, type_num); - if (!retval) return 0; - auto retval_ = make_safe(retval); - - Py_ssize_t counter; - Py_ssize_t lo, hi, st; - auto it = self->v->begin(); - if (start <= stop) { - lo = start, hi = stop, st = step; - it += lo, counter = 0; - } - else { - lo = stop, hi = start, st = -step; - it += lo + (hi-lo)%st, counter = slicelength - 1; - } - - for (auto i=lo; i<hi; i+=st) { - - //get slice to fill - PyObject* islice = Py_BuildValue("n", counter); - counter = (st == -step)? counter-1 : counter+1; - if (!islice) return 0; - auto islice_ = make_safe(islice); - - PyObject* item = PyObject_GetItem(retval, islice); - if (!item) return 0; - auto item_ = make_safe(item); - - try { - bobskin skin((PyArrayObject*)item, info.dtype); - it.read(skin); - it += (st-1); - } - catch (std::exception& e) { - if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, e.what()); - return 0; - } - catch (...) { - if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading frame #%" PY_FORMAT_SIZE_T "d from file `%s'", i, self->v->filename().c_str()); - return 0; - } - - } - - Py_INCREF(retval); - return retval; - -} - -static PyObject* PyBobIoVideoReader_GetItem (PyBobIoVideoReaderObject* self, PyObject* item) { - if (PyIndex_Check(item)) { - Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError); - if (i == -1 && PyErr_Occurred()) return 0; - return PyBobIoVideoReader_GetIndex(self, i); - } - if (PySlice_Check(item)) { - return PyBobIoVideoReader_GetSlice(self, (PySliceObject*)item); - } - else { - PyErr_Format(PyExc_TypeError, "VideoReader indices must be integers, not `%s'", - Py_TYPE(item)->tp_name); - return 0; - } -} - -Py_ssize_t PyBobIoVideoReader_Len(PyBobIoVideoReaderObject* self) { - return self->v->numberOfFrames(); -} - -static PyMappingMethods PyBobIoVideoReader_Mapping = { - (lenfunc)PyBobIoVideoReader_Len, //mp_lenght - (binaryfunc)PyBobIoVideoReader_GetItem, //mp_subscript - 0 /* (objobjargproc)PyBobIoVideoReader_SetItem //mp_ass_subscript */ -}; - -/***************************************** - * Definition of Iterator to VideoReader * - *****************************************/ - -#define VIDEOITERTYPE_NAME "VideoReader.iter" -PyDoc_STRVAR(s_videoreaderiterator_str, XBOB_EXT_MODULE_PREFIX "." VIDEOITERTYPE_NAME); - -static PyObject* PyBobIoVideoReaderIterator_New(PyTypeObject* type, PyObject*, PyObject*) { - - /* Allocates the python object itself */ - PyBobIoVideoReaderIteratorObject* self = (PyBobIoVideoReaderIteratorObject*)type->tp_alloc(type, 0); - - self->iter.reset(); - - return reinterpret_cast<PyObject*>(self); -} - -static PyObject* PyBobIoVideoReaderIterator_Iter (PyBobIoVideoReaderIteratorObject* self) { - Py_INCREF(self); - return reinterpret_cast<PyObject*>(self); -} - -static PyObject* PyBobIoVideoReaderIterator_Next (PyBobIoVideoReaderIteratorObject* self) { - - if ((*self->iter == self->pyreader->v->end()) || - (self->iter->cur() == self->pyreader->v->numberOfFrames())) { - self->iter->reset(); - self->iter.reset(); - Py_XDECREF((PyObject*)self->pyreader); - return 0; - } - - const bob::core::array::typeinfo& info = self->pyreader->v->frame_type(); - - npy_intp shape[NPY_MAXDIMS]; - for (size_t k=0; k<info.nd; ++k) shape[k] = info.shape[k]; - - int type_num = PyBobIo_AsTypenum(info.dtype); - if (type_num == NPY_NOTYPE) return 0; ///< failure - - PyObject* retval = PyArray_SimpleNew(info.nd, shape, type_num); - if (!retval) return 0; - auto retval_ = make_safe(retval); - - try { - bobskin skin((PyArrayObject*)retval, info.dtype); - self->iter->read(skin); - } - catch (std::exception& e) { - if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, e.what()); - return 0; - } - catch (...) { - if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while reading frame #%" PY_FORMAT_SIZE_T "d from file `%s'", self->iter->cur(), self->pyreader->v->filename().c_str()); - return 0; - } - - Py_INCREF(retval); - return retval; - -} - -#if PY_VERSION_HEX >= 0x03000000 -# define Py_TPFLAGS_HAVE_ITER 0 -#endif - -PyTypeObject PyBobIoVideoReaderIterator_Type = { - PyVarObject_HEAD_INIT(0, 0) - s_videoreaderiterator_str, /* tp_name */ - sizeof(PyBobIoVideoReaderIteratorObject), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, /* tp_flags */ - 0, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)PyBobIoVideoReaderIterator_Iter, /* tp_iter */ - (iternextfunc)PyBobIoVideoReaderIterator_Next /* tp_iternext */ -}; - -static PyObject* PyBobIoVideoReader_Iter (PyBobIoVideoReaderObject* self) { - - /* Allocates the python object itself */ - PyBobIoVideoReaderIteratorObject* retval = (PyBobIoVideoReaderIteratorObject*)PyBobIoVideoReaderIterator_New(&PyBobIoVideoReaderIterator_Type, 0, 0); - if (!retval) return 0; - - Py_INCREF(self); - retval->pyreader = self; - retval->iter.reset(new bob::io::VideoReader::const_iterator(self->v->begin())); - return reinterpret_cast<PyObject*>(retval); -} - -PyTypeObject PyBobIoVideoReader_Type = { - PyVarObject_HEAD_INIT(0, 0) - s_videoreader_str, /*tp_name*/ - sizeof(PyBobIoVideoReaderObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PyBobIoVideoReader_Delete, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - (reprfunc)PyBobIoVideoReader_Repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - &PyBobIoVideoReader_Mapping, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - (reprfunc)PyBobIoVideoReader_Print, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - s_videoreader_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)PyBobIoVideoReader_Iter, /* tp_iter */ - 0, /* tp_iternext */ - PyBobIoVideoReader_Methods, /* tp_methods */ - 0, /* tp_members */ - PyBobIoVideoReader_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)PyBobIoVideoReader_Init, /* tp_init */ - 0, /* tp_alloc */ - PyBobIoVideoReader_New, /* tp_new */ -}; - -#endif /* WITH_FFMPEG */ diff --git a/xbob/io/base/videowriter.cpp b/xbob/io/base/videowriter.cpp deleted file mode 100644 index 2261774c6fb39627f6659c13085c92f1689dedd7..0000000000000000000000000000000000000000 --- a/xbob/io/base/videowriter.cpp +++ /dev/null @@ -1,589 +0,0 @@ -/** - * @author Andre Anjos <andre.anjos@idiap.ch> - * @date Wed 6 Nov 21:44:34 2013 - * - * @brief Bindings to bob::io::VideoWriter - */ - -#define XBOB_IO_BASE_MODULE -#include "bobskin.h" -#include <xbob.io.base/api.h> - -#if WITH_FFMPEG -#include <boost/make_shared.hpp> -#include <numpy/arrayobject.h> -#include <xbob.blitz/cppapi.h> -#include <xbob.blitz/cleanup.h> -#include <stdexcept> - -#define VIDEOWRITER_NAME "VideoWriter" -PyDoc_STRVAR(s_videowriter_str, XBOB_EXT_MODULE_PREFIX "." VIDEOWRITER_NAME); - -PyDoc_STRVAR(s_videowriter_doc, -"VideoWriter(filename, height, width, [framerate=25., [bitrate=1500000., [gop=12, [codec='', [format='', [check=True]) -> new bob::io::VideoWriter\n\ -\n\ -Use this object to write frames to video files.\n\ -\n\ -Constructor parameters:\n\ -\n\ -filename\n\ - [str] The file path to the file you want to read data from\n\ -\n\ -height\n\ - [int] The height of the video (must be a multiple of 2)\n\ -\n\ -width\n\ - [int] The width of the video (must be a multiple of 2)\n\ -\n\ -framerate\n\ - [float, optional] The number of frames per second\n\ -\n\ -bitrate\n\ - [float, optional] The estimated bitrate of the output video\n\ -\n\ -gop\n\ - [int, optional] Group-of-Pictures (emit one intra frame\n\ - every ``gop`` frames at most).\n\ -\n\ -codec\n\ - [str, optional] If you must, specify a valid FFmpeg codec\n\ - name here and that will be used to encode the video stream\n\ - on the output file.\n\ -\n\ -format\n\ - [str, optional] If you must, specify a valid FFmpeg output\n\ - format name and that will be used to encode the video on the\n\ - output file. Leave it empty to guess from the filename extension.\n\ -\n\ -check\n\ - [bool, optional] The video will be created if the combination\n\ - of format and codec are known to work and have been tested,\n\ - otherwise an exception is raised. If you set this parameter to\n\ - ``False``, though, we will ignore this check.\n\ -\n\ -VideoWriter objects can write data to video files. The current\n\ -implementation uses `FFmpeg <http://ffmpeg.org>`_ (or\n\ -`libav <http://libav.org>`_ if FFmpeg is not available) which is\n\ -a stable freely available video encoding and decoding library,\n\ -designed specifically for these tasks. Videos are objects composed\n\ -of RGB colored frames. Each frame inserted should be a 3D\n\ -:py:class:`numpy.ndarray` composed of unsigned integers of 8 bits.\n\ -Each frame should have a shape equivalent to\n\ -``(plane, height, width)``.\n\ -"); - -/* How to create a new PyBobIoVideoWriterObject */ -static PyObject* PyBobIoVideoWriter_New(PyTypeObject* type, PyObject*, PyObject*) { - - /* Allocates the python object itself */ - PyBobIoVideoWriterObject* self = (PyBobIoVideoWriterObject*)type->tp_alloc(type, 0); - - self->v.reset(); - - return reinterpret_cast<PyObject*>(self); -} - -static void PyBobIoVideoWriter_Delete (PyBobIoVideoWriterObject* o) { - - o->v.reset(); - Py_TYPE(o)->tp_free((PyObject*)o); - -} - -/* The __init__(self) method */ -static int PyBobIoVideoWriter_Init(PyBobIoVideoWriterObject* self, - PyObject *args, PyObject* kwds) { - - /* Parses input arguments in a single shot */ - static const char* const_kwlist[] = { - "filename", "height", "width", //mandatory - "framerate", "bitrate", "gop", "codec", "format", "check", //optional - 0}; - static char** kwlist = const_cast<char**>(const_kwlist); - - PyObject* filename = 0; - - Py_ssize_t height = 0; - Py_ssize_t width = 0; - - double framerate = 25.; - double bitrate = 1500000.; - Py_ssize_t gop = 12; - char* codec = 0; - char* format = 0; - PyObject* pycheck = Py_True; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&nn|ddnssO", kwlist, - &PyBobIo_FilenameConverter, &filename, - &height, &width, &framerate, &bitrate, &gop, &codec, - &format, &pycheck)) return -1; - - auto filename_ = make_safe(filename); - - std::string codec_str = codec?codec:""; - std::string format_str = format?format:""; - bool check = PyObject_IsTrue(pycheck); - -#if PY_VERSION_HEX >= 0x03000000 - const char* c_filename = PyBytes_AS_STRING(filename); -#else - const char* c_filename = PyString_AS_STRING(filename); -#endif - - try { - self->v = boost::make_shared<bob::io::VideoWriter>(c_filename, - height, width, framerate, bitrate, gop, codec_str, format_str, check); - } - catch (std::exception& e) { - PyErr_SetString(PyExc_RuntimeError, e.what()); - return -1; - } - catch (...) { - PyErr_Format(PyExc_RuntimeError, "cannot open video file `%s' for writing: unknown exception caught", c_filename); - return -1; - } - - return 0; ///< SUCCESS -} - -PyObject* PyBobIoVideoWriter_Filename(PyBobIoVideoWriterObject* self) { - return Py_BuildValue("s", self->v->filename().c_str()); -} - -PyDoc_STRVAR(s_filename_str, "filename"); -PyDoc_STRVAR(s_filename_doc, -"[str] The full path to the file that will be decoded by this object"); - -PyObject* PyBobIoVideoWriter_Height(PyBobIoVideoWriterObject* self) { - return Py_BuildValue("n", self->v->height()); -} - -PyDoc_STRVAR(s_height_str, "height"); -PyDoc_STRVAR(s_height_doc, -"[int] The height of each frame in the video (a multiple of 2)"); - -PyObject* PyBobIoVideoWriter_Width(PyBobIoVideoWriterObject* self) { - return Py_BuildValue("n", self->v->width()); -} - -PyDoc_STRVAR(s_width_str, "width"); -PyDoc_STRVAR(s_width_doc, -"[int] The width of each frame in the video (a multiple of 2)"); - -PyObject* PyBobIoVideoWriter_NumberOfFrames(PyBobIoVideoWriterObject* self) { - return Py_BuildValue("n", self->v->numberOfFrames()); -} - -PyDoc_STRVAR(s_number_of_frames_str, "number_of_frames"); -PyDoc_STRVAR(s_number_of_frames_doc, -"[int] The number of frames in this video file"); - -PyObject* PyBobIoVideoWriter_Duration(PyBobIoVideoWriterObject* self) { - return Py_BuildValue("n", self->v->duration()); -} - -PyDoc_STRVAR(s_duration_str, "duration"); -PyDoc_STRVAR(s_duration_doc, -"[int] Total duration of this video file in microseconds (long)"); - -PyObject* PyBobIoVideoWriter_FormatName(PyBobIoVideoWriterObject* self) { - if (!self->v->is_opened()) { - PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed", - Py_TYPE(self)->tp_name, self->v->filename().c_str()); - return 0; - } - return Py_BuildValue("s", self->v->formatName().c_str()); -} - -PyDoc_STRVAR(s_format_name_str, "format_name"); -PyDoc_STRVAR(s_format_name_doc, -"[str] Short name of the format in which this video file was recorded in"); - -PyObject* PyBobIoVideoWriter_FormatLongName(PyBobIoVideoWriterObject* self) { - if (!self->v->is_opened()) { - PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed", - Py_TYPE(self)->tp_name, self->v->filename().c_str()); - return 0; - } - return Py_BuildValue("s", self->v->formatLongName().c_str()); -} - -PyDoc_STRVAR(s_format_long_name_str, "format_long_name"); -PyDoc_STRVAR(s_format_long_name_doc, -"[str] Full name of the format in which this video file was recorded in"); - -PyObject* PyBobIoVideoWriter_CodecName(PyBobIoVideoWriterObject* self) { - if (!self->v->is_opened()) { - PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed", - Py_TYPE(self)->tp_name, self->v->filename().c_str()); - return 0; - } - return Py_BuildValue("s", self->v->codecName().c_str()); -} - -PyDoc_STRVAR(s_codec_name_str, "codec_name"); -PyDoc_STRVAR(s_codec_name_doc, -"[str] Short name of the codec in which this video file was recorded in"); - -PyObject* PyBobIoVideoWriter_CodecLongName(PyBobIoVideoWriterObject* self) { - if (!self->v->is_opened()) { - PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed", - Py_TYPE(self)->tp_name, self->v->filename().c_str()); - return 0; - } - return Py_BuildValue("s", self->v->codecLongName().c_str()); -} - -PyDoc_STRVAR(s_codec_long_name_str, "codec_long_name"); -PyDoc_STRVAR(s_codec_long_name_doc, -"[str] Full name of the codec in which this video file was recorded in"); - -PyObject* PyBobIoVideoWriter_FrameRate(PyBobIoVideoWriterObject* self) { - return PyFloat_FromDouble(self->v->frameRate()); -} - -PyDoc_STRVAR(s_frame_rate_str, "frame_rate"); -PyDoc_STRVAR(s_frame_rate_doc, -"[float] Video's announced frame rate (note there are video formats\n\ -with variable frame rates)"); - -PyObject* PyBobIoVideoWriter_BitRate(PyBobIoVideoWriterObject* self) { - return PyFloat_FromDouble(self->v->bitRate()); -} - -PyDoc_STRVAR(s_bit_rate_str, "bit_rate"); -PyDoc_STRVAR(s_bit_rate_doc, -"[float] The indicative bit rate for this video file, given as a\n\ -hint to `FFmpeg` (compression levels are subject to the picture\n\ -textures)"); - -PyObject* PyBobIoVideoWriter_GOP(PyBobIoVideoWriterObject* self) { - return Py_BuildValue("n", self->v->gop()); -} - -PyDoc_STRVAR(s_gop_str, "gop"); -PyDoc_STRVAR(s_gop_doc, -"[int] Group of pictures setting (see the `Wikipedia entry\n\ -<http://en.wikipedia.org/wiki/Group_of_pictures>`_ for details\n\ -on this setting)"); - -PyObject* PyBobIoVideoWriter_VideoType(PyBobIoVideoWriterObject* self) { - return PyBobIo_TypeInfoAsTuple(self->v->video_type()); -} - -PyDoc_STRVAR(s_video_type_str, "video_type"); -PyDoc_STRVAR(s_video_type_doc, -"[tuple] Typing information to load all of the file at once"); - -PyObject* PyBobIoVideoWriter_FrameType(PyBobIoVideoWriterObject* self) { - return PyBobIo_TypeInfoAsTuple(self->v->frame_type()); -} - -PyDoc_STRVAR(s_frame_type_str, "frame_type"); -PyDoc_STRVAR(s_frame_type_doc, -"[tuple] Typing information to load each frame separatedly"); - -static PyObject* PyBobIoVideoWriter_Print(PyBobIoVideoWriterObject* self) { - if (!self->v->is_opened()) { - PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed", - Py_TYPE(self)->tp_name, self->v->filename().c_str()); - return 0; - } - - return Py_BuildValue("s", self->v->info().c_str()); -} - -PyDoc_STRVAR(s_info_str, "info"); -PyDoc_STRVAR(s_info_doc, -"[str] A string with lots of video information (same as ``str(x)``)"); - -static PyObject* PyBobIoVideoWriter_IsOpened(PyBobIoVideoWriterObject* self) { - if (self->v->is_opened()) Py_RETURN_TRUE; - Py_RETURN_FALSE; -} - -PyDoc_STRVAR(s_is_opened_str, "is_opened"); -PyDoc_STRVAR(s_is_opened_doc, -"[bool] A flag, indicating if the video is still opened for writing\n\ -(or has already been closed by the user using ``close()``)"); - -static PyGetSetDef PyBobIoVideoWriter_getseters[] = { - { - s_filename_str, - (getter)PyBobIoVideoWriter_Filename, - 0, - s_filename_doc, - 0, - }, - { - s_height_str, - (getter)PyBobIoVideoWriter_Height, - 0, - s_height_doc, - 0, - }, - { - s_width_str, - (getter)PyBobIoVideoWriter_Width, - 0, - s_width_doc, - 0, - }, - { - s_number_of_frames_str, - (getter)PyBobIoVideoWriter_NumberOfFrames, - 0, - s_number_of_frames_doc, - 0, - }, - { - s_duration_str, - (getter)PyBobIoVideoWriter_Duration, - 0, - s_duration_doc, - 0, - }, - { - s_format_name_str, - (getter)PyBobIoVideoWriter_FormatName, - 0, - s_format_name_doc, - 0, - }, - { - s_format_long_name_str, - (getter)PyBobIoVideoWriter_FormatLongName, - 0, - s_format_long_name_doc, - 0, - }, - { - s_codec_name_str, - (getter)PyBobIoVideoWriter_CodecName, - 0, - s_codec_name_doc, - 0, - }, - { - s_codec_long_name_str, - (getter)PyBobIoVideoWriter_CodecLongName, - 0, - s_codec_long_name_doc, - 0, - }, - { - s_frame_rate_str, - (getter)PyBobIoVideoWriter_FrameRate, - 0, - s_frame_rate_doc, - 0, - }, - { - s_bit_rate_str, - (getter)PyBobIoVideoWriter_BitRate, - 0, - s_bit_rate_doc, - 0, - }, - { - s_gop_str, - (getter)PyBobIoVideoWriter_GOP, - 0, - s_gop_doc, - 0, - }, - { - s_video_type_str, - (getter)PyBobIoVideoWriter_VideoType, - 0, - s_video_type_doc, - 0, - }, - { - s_frame_type_str, - (getter)PyBobIoVideoWriter_FrameType, - 0, - s_frame_type_doc, - 0, - }, - { - s_info_str, - (getter)PyBobIoVideoWriter_Print, - 0, - s_info_doc, - 0, - }, - { - s_is_opened_str, - (getter)PyBobIoVideoWriter_IsOpened, - 0, - s_is_opened_doc, - 0, - }, - {0} /* Sentinel */ -}; - -static PyObject* PyBobIoVideoWriter_Repr(PyBobIoVideoWriterObject* self) { - if (!self->v->is_opened()) { - PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed", - Py_TYPE(self)->tp_name, self->v->filename().c_str()); - return 0; - } - - return -# if PY_VERSION_HEX >= 0x03000000 - PyUnicode_FromFormat -# else - PyString_FromFormat -# endif - ("%s(filename='%s', height=%" PY_FORMAT_SIZE_T "d, width=%" PY_FORMAT_SIZE_T "d, framerate=%g, bitrate=%g, gop=%" PY_FORMAT_SIZE_T "d, codec='%s', format='%s')", Py_TYPE(self)->tp_name, self->v->filename().c_str(), self->v->height(), self->v->width(), self->v->frameRate(), self->v->bitRate(), self->v->gop(), self->v->codecName().c_str(), self->v->formatName().c_str()); -} - -static PyObject* PyBobIoVideoWriter_Append(PyBobIoVideoWriterObject* self, PyObject *args, PyObject* kwds) { - - if (!self->v->is_opened()) { - PyErr_Format(PyExc_RuntimeError, "`%s' for `%s' is closed", - Py_TYPE(self)->tp_name, self->v->filename().c_str()); - return 0; - } - - /* Parses input arguments in a single shot */ - static const char* const_kwlist[] = {"frame", 0}; - static char** kwlist = const_cast<char**>(const_kwlist); - - PyBlitzArrayObject* frame = 0; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&", kwlist, &PyBlitzArray_BehavedConverter, &frame)) return 0; - auto frame_ = make_safe(frame); - - if (frame->ndim != 3 && frame->ndim != 4) { - PyErr_Format(PyExc_ValueError, "input array should have 3 or 4 dimensions, but you passed an array with %" PY_FORMAT_SIZE_T "d dimensions", frame->ndim); - return 0; - } - - if (frame->type_num != NPY_UINT8) { - PyErr_Format(PyExc_TypeError, "input array should have dtype `uint8', but you passed an array with dtype == `%s'", PyBlitzArray_TypenumAsString(frame->type_num)); - return 0; - } - - try { - if (frame->ndim == 3) { - self->v->append(*PyBlitzArrayCxx_AsBlitz<uint8_t,3>(frame)); - } - else { - self->v->append(*PyBlitzArrayCxx_AsBlitz<uint8_t,4>(frame)); - } - } - catch (std::exception& e) { - if (!PyErr_Occurred()) PyErr_SetString(PyExc_RuntimeError, e.what()); - return 0; - } - catch (...) { - if (!PyErr_Occurred()) PyErr_Format(PyExc_RuntimeError, "caught unknown exception while writing frame #%" PY_FORMAT_SIZE_T "d to file `%s'", self->v->numberOfFrames(), self->v->filename().c_str()); - return 0; - } - - Py_RETURN_NONE; - -} - -PyDoc_STRVAR(s_append_str, "append"); -PyDoc_STRVAR(s_append_doc, -"x.append(frame) -> None\n\ -\n\ -Writes a new frame or set of frames to the file.\n\ -\n\ -The frame should be setup as a array with 3 dimensions organized\n\ -in this way (RGB color-bands, height, width). Sets of frames should\n\ -be setup as a 4D array in this way: (frame-number, RGB color-bands,\n\ -height, width). Arrays should contain only unsigned integers of 8\n\ -bits.\n\ -\n\ -.. note::\n\ - At present time we only support arrays that have C-style storages\n\ - (if you pass reversed arrays or arrays with Fortran-style storage,\n\ - the result is undefined).\n\ -\n\ -"); - -static PyObject* PyBobIoVideoWriter_Close(PyBobIoVideoWriterObject* self) { - self->v->close(); - Py_RETURN_NONE; -} - -PyDoc_STRVAR(s_close_str, "close"); -PyDoc_STRVAR(s_close_doc, -"x.close() -> None\n\ -\n\ -Closes the current video stream and forces writing the trailer.\n\ -After this point the video is finalized and cannot be written to\n\ -anymore.\n\ -"); - -static PyMethodDef PyBobIoVideoWriter_Methods[] = { - { - s_append_str, - (PyCFunction)PyBobIoVideoWriter_Append, - METH_VARARGS|METH_KEYWORDS, - s_append_doc, - }, - { - s_close_str, - (PyCFunction)PyBobIoVideoWriter_Close, - METH_NOARGS, - s_close_doc, - }, - {0} /* Sentinel */ -}; - -Py_ssize_t PyBobIoVideoWriter_Len(PyBobIoVideoWriterObject* self) { - return self->v->numberOfFrames(); -} - -static PyMappingMethods PyBobIoVideoWriter_Mapping = { - (lenfunc)PyBobIoVideoWriter_Len, //mp_lenght - 0, /* (binaryfunc)PyBobIoVideoWriter_GetItem, //mp_subscript */ - 0 /* (objobjargproc)PyBobIoVideoWriter_SetItem //mp_ass_subscript */ -}; - -PyTypeObject PyBobIoVideoWriter_Type = { - PyVarObject_HEAD_INIT(0, 0) - s_videowriter_str, /*tp_name*/ - sizeof(PyBobIoVideoWriterObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - (destructor)PyBobIoVideoWriter_Delete, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - (reprfunc)PyBobIoVideoWriter_Repr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - &PyBobIoVideoWriter_Mapping, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - (reprfunc)PyBobIoVideoWriter_Print, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - s_videowriter_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PyBobIoVideoWriter_Methods, /* tp_methods */ - 0, /* tp_members */ - PyBobIoVideoWriter_getseters, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)PyBobIoVideoWriter_Init, /* tp_init */ - 0, /* tp_alloc */ - PyBobIoVideoWriter_New, /* tp_new */ -}; - -#endif /* WITH_FFMPEG */