├── LICENSE ├── README.md ├── docs ├── conf.py ├── index.rst └── stuff.rst ├── runtests.py ├── setup.py └── skvideo ├── __init__.py ├── benchmarks ├── __init__.py └── bench_something.py ├── examples ├── converter.py ├── measure.py ├── player.py ├── player_pygame.py ├── test_capture.py └── test_writer.py ├── io └── __init__.py ├── metrics ├── __init__.py ├── psnr.py ├── ssim.py └── vifp.py ├── setup.py ├── tests ├── __init__.py └── test_version.py └── version.py /LICENSE: -------------------------------------------------------------------------------- 1 | Unless specified otherwise in an individual file, all code is: 2 | 3 | Copyright (C) 2014, Alex Izvorski 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 3. Neither the name of scikit-video nor the names of its contributors 17 | may be used to endorse or promote products derived from this 18 | software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 21 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 24 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 29 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | scikit-video 2 | ============ 3 | 4 | Video Processing SciKit *BETA* 5 | 6 | Video processing algorithms, including I/O, quality metrics, temporal filtering, motion/object detection, motion estimation... 7 | 8 | This is intended as a companion to scikit-image, containing all the algorithms which deal with *video*. There is a certain degree of overlap between image and video algorithms, for example a PSNR quality metric could be applied to pairs of images or pairs of video frames just as well. However, other algorithms are video-specific, for example a temporal denoise. This is the future home of the video-specific algorithms, as well as some of the algorithms which are not strictly video specific but are usually seen in a video context. 9 | 10 | This also has some overlap with OpenCV. Roughly, the algorithms implemented here would be easier to hack on, and more research-oriented. Rather than building on top of a C/C++ framework, this will stay Python all the way, using whichever combinaiton of Numba/Theano/etc seems best for performance. This should add flexibility and better future ability to use GPU compute. 11 | 12 | The project milestones are roughly: 13 | 14 | - Add skeleton project from scikit-example - DONE 15 | - Add video I/O by wrapping ffmpeg/avconv (similar to kanryu/pipeffmpeg) - DONE 16 | - Add video metrics (from aizvorski/video-quality) - DONE 17 | - More contributions roll in :) 18 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # scikit-video documentation build configuration file, created by 4 | # sphinx-quickstart on Thu May 9 17:07:16 2013. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.insert(0, os.path.abspath('.')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be extensions 27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 28 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.pngmath', 'numpydoc'] 29 | 30 | # Add any paths that contain templates here, relative to this directory. 31 | templates_path = ['_templates'] 32 | 33 | # The suffix of source filenames. 34 | source_suffix = '.rst' 35 | 36 | # The encoding of source files. 37 | #source_encoding = 'utf-8-sig' 38 | 39 | # The master toctree document. 40 | master_doc = 'index' 41 | 42 | # General information about the project. 43 | project = u'scikit-video' 44 | copyright = u'2013, Your Name' 45 | 46 | # The version info for the project you're documenting, acts as replacement for 47 | # |version| and |release|, also used in various other places throughout the 48 | # built documents. 49 | # 50 | # The short X.Y version. 51 | version = '0.1' 52 | # The full version, including alpha/beta/rc tags. 53 | release = '0.1' 54 | 55 | # The language for content autogenerated by Sphinx. Refer to documentation 56 | # for a list of supported languages. 57 | #language = None 58 | 59 | # There are two options for replacing |today|: either, you set today to some 60 | # non-false value, then it is used: 61 | #today = '' 62 | # Else, today_fmt is used as the format for a strftime call. 63 | #today_fmt = '%B %d, %Y' 64 | 65 | # List of patterns, relative to source directory, that match files and 66 | # directories to ignore when looking for source files. 67 | exclude_patterns = ['_build'] 68 | 69 | # The reST default role (used for this markup: `text`) to use for all documents. 70 | #default_role = None 71 | 72 | # If true, '()' will be appended to :func: etc. cross-reference text. 73 | #add_function_parentheses = True 74 | 75 | # If true, the current module name will be prepended to all description 76 | # unit titles (such as .. function::). 77 | #add_module_names = True 78 | 79 | # If true, sectionauthor and moduleauthor directives will be shown in the 80 | # output. They are ignored by default. 81 | #show_authors = False 82 | 83 | # The name of the Pygments (syntax highlighting) style to use. 84 | pygments_style = 'sphinx' 85 | 86 | # A list of ignored prefixes for module index sorting. 87 | #modindex_common_prefix = [] 88 | 89 | 90 | # -- Options for HTML output --------------------------------------------------- 91 | 92 | # The theme to use for HTML and HTML Help pages. See the documentation for 93 | # a list of builtin themes. 94 | html_theme = 'default' 95 | 96 | # Theme options are theme-specific and customize the look and feel of a theme 97 | # further. For a list of options available for each theme, see the 98 | # documentation. 99 | #html_theme_options = {} 100 | 101 | # Add any paths that contain custom themes here, relative to this directory. 102 | #html_theme_path = [] 103 | 104 | # The name for this set of Sphinx documents. If None, it defaults to 105 | # " v documentation". 106 | #html_title = None 107 | 108 | # A shorter title for the navigation bar. Default is the same as html_title. 109 | #html_short_title = None 110 | 111 | # The name of an image file (relative to this directory) to place at the top 112 | # of the sidebar. 113 | #html_logo = None 114 | 115 | # The name of an image file (within the static path) to use as favicon of the 116 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 117 | # pixels large. 118 | #html_favicon = None 119 | 120 | # Add any paths that contain custom static files (such as style sheets) here, 121 | # relative to this directory. They are copied after the builtin static files, 122 | # so a file named "default.css" will overwrite the builtin "default.css". 123 | html_static_path = ['_static'] 124 | 125 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 126 | # using the given strftime format. 127 | #html_last_updated_fmt = '%b %d, %Y' 128 | 129 | # If true, SmartyPants will be used to convert quotes and dashes to 130 | # typographically correct entities. 131 | #html_use_smartypants = True 132 | 133 | # Custom sidebar templates, maps document names to template names. 134 | #html_sidebars = {} 135 | 136 | # Additional templates that should be rendered to pages, maps page names to 137 | # template names. 138 | #html_additional_pages = {} 139 | 140 | # If false, no module index is generated. 141 | #html_domain_indices = True 142 | 143 | # If false, no index is generated. 144 | #html_use_index = True 145 | 146 | # If true, the index is split into individual pages for each letter. 147 | #html_split_index = False 148 | 149 | # If true, links to the reST sources are added to the pages. 150 | #html_show_sourcelink = True 151 | 152 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 153 | #html_show_sphinx = True 154 | 155 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 156 | #html_show_copyright = True 157 | 158 | # If true, an OpenSearch description file will be output, and all pages will 159 | # contain a tag referring to it. The value of this option must be the 160 | # base URL from which the finished HTML is served. 161 | #html_use_opensearch = '' 162 | 163 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 164 | #html_file_suffix = None 165 | 166 | # Output file base name for HTML help builder. 167 | htmlhelp_basename = 'scikit-videodoc' 168 | 169 | 170 | # -- Options for LaTeX output -------------------------------------------------- 171 | 172 | latex_elements = { 173 | # The paper size ('letterpaper' or 'a4paper'). 174 | #'papersize': 'letterpaper', 175 | 176 | # The font size ('10pt', '11pt' or '12pt'). 177 | #'pointsize': '10pt', 178 | 179 | # Additional stuff for the LaTeX preamble. 180 | #'preamble': '', 181 | } 182 | 183 | # Grouping the document tree into LaTeX files. List of tuples 184 | # (source start file, target name, title, author, documentclass [howto/manual]). 185 | latex_documents = [ 186 | ('index', 'scikit-video.tex', u'scikit-video Documentation', 187 | u'Your Name', 'manual'), 188 | ] 189 | 190 | # The name of an image file (relative to this directory) to place at the top of 191 | # the title page. 192 | #latex_logo = None 193 | 194 | # For "manual" documents, if this is true, then toplevel headings are parts, 195 | # not chapters. 196 | #latex_use_parts = False 197 | 198 | # If true, show page references after internal links. 199 | #latex_show_pagerefs = False 200 | 201 | # If true, show URL addresses after external links. 202 | #latex_show_urls = False 203 | 204 | # Documents to append as an appendix to all manuals. 205 | #latex_appendices = [] 206 | 207 | # If false, no module index is generated. 208 | #latex_domain_indices = True 209 | 210 | 211 | # -- Options for manual page output -------------------------------------------- 212 | 213 | # One entry per manual page. List of tuples 214 | # (source start file, name, description, authors, manual section). 215 | man_pages = [ 216 | ('index', 'scikit-video', u'scikit-video Documentation', 217 | [u'Your Name'], 1) 218 | ] 219 | 220 | # If true, show URL addresses after external links. 221 | #man_show_urls = False 222 | 223 | 224 | # -- Options for Texinfo output ------------------------------------------------ 225 | 226 | # Grouping the document tree into Texinfo files. List of tuples 227 | # (source start file, target name, title, author, 228 | # dir menu entry, description, category) 229 | texinfo_documents = [ 230 | ('index', 'scikit-video', u'scikit-video Documentation', 231 | u'Your Name', 'scikit-video', 'One line description of project.', 232 | 'Miscellaneous'), 233 | ] 234 | 235 | # Documents to append as an appendix to all manuals. 236 | #texinfo_appendices = [] 237 | 238 | # If false, no module index is generated. 239 | #texinfo_domain_indices = True 240 | 241 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 242 | #texinfo_show_urls = 'footnote' 243 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. scikit-video documentation master file, created by 2 | sphinx-quickstart on Thu May 9 17:07:16 2013. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to scikit-video's documentation! 7 | ========================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | stuff 15 | 16 | Indices and tables 17 | ================== 18 | 19 | * :ref:`genindex` 20 | * :ref:`modindex` 21 | * :ref:`search` 22 | 23 | -------------------------------------------------------------------------------- /docs/stuff.rst: -------------------------------------------------------------------------------- 1 | Stuff 2 | ===== 3 | 4 | .. autofunction:: skvideo.do_something 5 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | runtests.py [OPTIONS] [-- ARGS] 4 | 5 | Run tests, building the project first. 6 | 7 | Examples:: 8 | 9 | $ python runtests.py 10 | $ python runtests.py -s {SAMPLE_SUBMODULE} 11 | $ python runtests.py -t {SAMPLE_TEST} 12 | $ python runtests.py --ipython 13 | 14 | """ 15 | 16 | # 17 | # This is a generic test runner script for projects using Numpy's test 18 | # framework. Change the following values to adapt to your project: 19 | # 20 | 21 | PROJECT_MODULE = "skvideo" 22 | PROJECT_ROOT_FILES = ['setup.py'] 23 | SAMPLE_TEST = "skvideo/tests/test_stuff.py:test_do_something" 24 | SAMPLE_SUBMODULE = "stuff" 25 | 26 | # --------------------------------------------------------------------- 27 | 28 | __doc__ = __doc__.format(**globals()) 29 | 30 | import sys 31 | import os 32 | 33 | # In case we are run from the source directory, we don't want to import the 34 | # project from there: 35 | sys.path.pop(0) 36 | 37 | import shutil 38 | import subprocess 39 | from argparse import ArgumentParser, REMAINDER 40 | 41 | def main(argv): 42 | parser = ArgumentParser(usage=__doc__.lstrip()) 43 | parser.add_argument("--verbose", "-v", action="count", default=1, 44 | help="more verbosity") 45 | parser.add_argument("--no-build", "-n", action="store_true", default=False, 46 | help="do not build the project (use system installed version)") 47 | parser.add_argument("--build-only", "-b", action="store_true", default=False, 48 | help="just build, do not run any tests") 49 | parser.add_argument("--doctests", action="store_true", default=False, 50 | help="Run doctests in module") 51 | parser.add_argument("--coverage", action="store_true", default=False, 52 | help=("report coverage of project code. HTML output goes " 53 | "under build/coverage")) 54 | parser.add_argument("--mode", "-m", default="fast", 55 | help="'fast', 'full', or something that could be " 56 | "passed to nosetests -A [default: fast]") 57 | parser.add_argument("--submodule", "-s", default=None, 58 | help="Submodule whose tests to run (cluster, constants, ...)") 59 | parser.add_argument("--pythonpath", "-p", default=None, 60 | help="Paths to prepend to PYTHONPATH") 61 | parser.add_argument("--tests", "-t", action='append', 62 | help="Specify tests to run") 63 | parser.add_argument("--python", action="store_true", 64 | help="Start a Python shell with PYTHONPATH set") 65 | parser.add_argument("--ipython", "-i", action="store_true", 66 | help="Start IPython shell with PYTHONPATH set") 67 | parser.add_argument("--shell", action="store_true", 68 | help="Start Unix shell with PYTHONPATH set") 69 | parser.add_argument("--debug", "-g", action="store_true", 70 | help="Debug build") 71 | parser.add_argument("args", metavar="ARGS", default=[], nargs=REMAINDER, 72 | help="Arguments to pass to Nose") 73 | args = parser.parse_args(argv) 74 | 75 | if args.pythonpath: 76 | for p in reversed(args.pythonpath.split(os.pathsep)): 77 | sys.path.insert(0, p) 78 | 79 | if not args.no_build: 80 | site_dir = build_project(args) 81 | sys.path.insert(0, site_dir) 82 | os.environ['PYTHONPATH'] = site_dir 83 | 84 | if args.python: 85 | import code 86 | code.interact() 87 | sys.exit(0) 88 | 89 | if args.ipython: 90 | import IPython 91 | IPython.embed() 92 | sys.exit(0) 93 | 94 | if args.shell: 95 | shell = os.environ.get('SHELL', 'sh') 96 | print("Spawning a Unix shell...") 97 | os.execv(shell, [shell]) 98 | sys.exit(1) 99 | 100 | extra_argv = args.args 101 | 102 | if args.coverage: 103 | dst_dir = os.path.join('build', 'coverage') 104 | fn = os.path.join(dst_dir, 'coverage_html.js') 105 | if os.path.isdir(dst_dir) and os.path.isfile(fn): 106 | shutil.rmtree(dst_dir) 107 | extra_argv += ['--cover-html', 108 | '--cover-html-dir='+dst_dir] 109 | 110 | if args.build_only: 111 | sys.exit(0) 112 | elif args.submodule: 113 | modname = PROJECT_MODULE + '.' + args.submodule 114 | try: 115 | __import__(modname) 116 | test = sys.modules[modname].test 117 | except (ImportError, KeyError, AttributeError): 118 | print("Cannot run tests for %s" % modname) 119 | sys.exit(2) 120 | elif args.tests: 121 | def test(*a, **kw): 122 | extra_argv = kw.pop('extra_argv', ()) 123 | extra_argv = extra_argv + args.tests[1:] 124 | kw['extra_argv'] = extra_argv 125 | from numpy.testing import Tester 126 | return Tester(args.tests[0]).test(*a, **kw) 127 | else: 128 | __import__(PROJECT_MODULE) 129 | test = sys.modules[PROJECT_MODULE].test 130 | 131 | result = test(args.mode, 132 | verbose=args.verbose, 133 | extra_argv=args.args, 134 | doctests=args.doctests, 135 | coverage=args.coverage) 136 | 137 | if result.wasSuccessful(): 138 | sys.exit(0) 139 | else: 140 | sys.exit(1) 141 | 142 | def build_project(args): 143 | """ 144 | Build a dev version of the project. 145 | 146 | Returns 147 | ------- 148 | site_dir 149 | site-packages directory where it was installed 150 | 151 | """ 152 | 153 | root_dir = os.path.abspath(os.path.join(os.path.dirname(__file__))) 154 | root_ok = [os.path.exists(os.path.join(root_dir, fn)) 155 | for fn in PROJECT_ROOT_FILES] 156 | if not all(root_ok): 157 | print("To build the project, run runtests.py in " 158 | "git checkout or unpacked source") 159 | sys.exit(1) 160 | 161 | dst_dir = os.path.join(root_dir, 'build', 'testenv') 162 | 163 | from distutils.sysconfig import get_python_lib 164 | site_dir = get_python_lib(prefix=dst_dir) 165 | 166 | env = dict(os.environ) 167 | cmd = [sys.executable, 'setup.py'] 168 | 169 | # Always use ccache if available 170 | env['PATH'] = os.pathsep.join(['/usr/lib/ccache'] 171 | + env.get('PATH', '').split(os.pathsep)) 172 | 173 | if args.debug: 174 | # assume everyone uses gcc/gfortran 175 | env['OPT'] = '-O0 -ggdb' 176 | env['FOPT'] = '-O0 -ggdb' 177 | cmd += ["build", "--debug"] 178 | 179 | cmd += ['install', '--prefix=' + dst_dir] 180 | 181 | # Setup for setuptools 182 | cmd += ['--single-version-externally-managed', 183 | '--record=' + os.path.join(dst_dir, 'record.lst')] 184 | if not os.path.isdir(site_dir): 185 | os.makedirs(site_dir) 186 | env['PYTHONPATH'] = os.pathsep.join([site_dir] 187 | + env.get('PATH', '').split(os.pathsep)) 188 | 189 | # Build it. 190 | print("Building, see build.log...") 191 | with open('build.log', 'w') as log: 192 | ret = subprocess.call(cmd, env=env, stdout=log, stderr=log, 193 | cwd=root_dir) 194 | 195 | if ret == 0: 196 | print("Build OK") 197 | else: 198 | with open('build.log', 'r') as f: 199 | print(f.read()) 200 | print("Build failed!") 201 | sys.exit(1) 202 | 203 | return site_dir 204 | 205 | if __name__ == "__main__": 206 | main(argv=sys.argv[1:]) 207 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | descr = """\ 3 | Video Processing SciKit 4 | 5 | Video processing algorithms for SciPy, including IO, quality metrics, filtering, etc. 6 | 7 | """ 8 | 9 | DISTNAME = 'scikit-video' 10 | DESCRIPTION = 'Video processing routines for SciPy' 11 | LONG_DESCRIPTION = descr 12 | MAINTAINER = 'Alex Izvorski', 13 | MAINTAINER_EMAIL = 'aizvorski@gmail.com', 14 | URL = 'https://github.com/aizvorski/scikit-video/' 15 | LICENSE = 'GPLv3' 16 | DOWNLOAD_URL = URL 17 | PACKAGE_NAME = 'skvideo' 18 | EXTRA_INFO = dict( 19 | install_requires=['numpy'], 20 | classifiers=['Development Status :: 3 - Alpha', 21 | 'Environment :: Console', 22 | 'Intended Audience :: Developers', 23 | 'Intended Audience :: Science/Research', 24 | 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 25 | 'Programming Language :: C', 26 | 'Programming Language :: Python', 27 | 'Programming Language :: Python :: 3', 28 | 'Topic :: Scientific/Engineering', 29 | 'Topic :: Multimedia :: Video', 30 | 'Topic :: Multimedia :: Video :: Conversion', 31 | 'Operating System :: Microsoft :: Windows', 32 | 'Operating System :: POSIX', 33 | 'Operating System :: Unix', 34 | 'Operating System :: MacOS', 35 | ] 36 | ) 37 | 38 | 39 | import os 40 | import sys 41 | import subprocess 42 | 43 | import setuptools 44 | from numpy.distutils.core import setup 45 | 46 | def configuration(parent_package='', top_path=None, package_name=DISTNAME): 47 | if os.path.exists('MANIFEST'): os.remove('MANIFEST') 48 | 49 | from numpy.distutils.misc_util import Configuration 50 | config = Configuration(None, parent_package, top_path) 51 | 52 | # Avoid non-useful msg: "Ignoring attempt to set 'name' (from ... " 53 | config.set_options(ignore_setup_xxx_py=True, 54 | assume_default_configuration=True, 55 | delegate_options_to_subpackages=True, 56 | quiet=True) 57 | 58 | config.add_subpackage(PACKAGE_NAME) 59 | return config 60 | 61 | def get_version(): 62 | """Obtain the version number""" 63 | import imp 64 | mod = imp.load_source('version', os.path.join(PACKAGE_NAME, 'version.py')) 65 | return mod.__version__ 66 | 67 | # Documentation building command 68 | try: 69 | from sphinx.setup_command import BuildDoc as SphinxBuildDoc 70 | class BuildDoc(SphinxBuildDoc): 71 | """Run in-place build before Sphinx doc build""" 72 | def run(self): 73 | ret = subprocess.call([sys.executable, sys.argv[0], 'build_ext', '-i']) 74 | if ret != 0: 75 | raise RuntimeError("Building Scipy failed!") 76 | SphinxBuildDoc.run(self) 77 | cmdclass = {'build_sphinx': BuildDoc} 78 | except ImportError: 79 | cmdclass = {} 80 | 81 | # Call the setup function 82 | if __name__ == "__main__": 83 | setup(configuration=configuration, 84 | name=DISTNAME, 85 | maintainer=MAINTAINER, 86 | maintainer_email=MAINTAINER_EMAIL, 87 | description=DESCRIPTION, 88 | license=LICENSE, 89 | url=URL, 90 | download_url=DOWNLOAD_URL, 91 | long_description=LONG_DESCRIPTION, 92 | include_package_data=True, 93 | test_suite="nose.collector", 94 | cmdclass=cmdclass, 95 | version=get_version(), 96 | **EXTRA_INFO) 97 | -------------------------------------------------------------------------------- /skvideo/__init__.py: -------------------------------------------------------------------------------- 1 | from skvideo.version import __version__ 2 | 3 | # If you want to use Numpy's testing framerwork, use the following. 4 | # Tests go under directory tests/, benchmarks under directory benchmarks/ 5 | #from numpy.testing import Tester 6 | #test = Tester().test 7 | #bench = Tester().bench 8 | -------------------------------------------------------------------------------- /skvideo/benchmarks/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aizvorski/scikit-video/f03bf543e5fcde0ed27f31324aa8a502468033cc/skvideo/benchmarks/__init__.py -------------------------------------------------------------------------------- /skvideo/benchmarks/bench_something.py: -------------------------------------------------------------------------------- 1 | # Example benchmark file 2 | 3 | import time 4 | 5 | def bench_something(): 6 | start = time.clock() 7 | for k in range(2000): 8 | k**k 9 | end = time.clock() 10 | 11 | print(("Duration: %r" % (end - start,))) 12 | 13 | if __name__ == "__main__": 14 | bench_something() 15 | 16 | -------------------------------------------------------------------------------- /skvideo/examples/converter.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | 4 | from skvideo.io import VideoCapture, VideoWriter 5 | import sys 6 | 7 | cap_filename, wr_filename = sys.argv[1], sys.argv[2] 8 | 9 | cap = VideoCapture(cap_filename) 10 | cap.open() 11 | print(str(cap.get_info())) 12 | 13 | retval, image = cap.read() 14 | 15 | wr = VideoWriter(wr_filename, 'H264', 30, (image.shape[1], image.shape[0])) 16 | wr.open() 17 | 18 | frame_num = 0 19 | 20 | while True: 21 | retval, image = cap.read() 22 | if not retval: 23 | break 24 | wr.write(image) 25 | 26 | print("frame %d" % (frame_num)) 27 | frame_num += 1 28 | 29 | wr.release() 30 | cap.release() 31 | print("done") 32 | -------------------------------------------------------------------------------- /skvideo/examples/measure.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | 4 | from skvideo.io import VideoCapture 5 | from skvideo.metrics import ssim, psnr, vifp 6 | import sys 7 | import json 8 | 9 | filename1, filename2 = sys.argv[1], sys.argv[2] 10 | 11 | cap1 = VideoCapture(filename1) 12 | cap1.open() 13 | print(str(cap1.get_info())) 14 | 15 | cap2 = VideoCapture(filename2) 16 | cap2.open() 17 | print(str(cap2.get_info())) 18 | 19 | def rgb_to_y(img): 20 | return 0.299 * img[:,:,0] + 0.587 * img[:,:,1] + 0.114 * img[:,:,2] 21 | 22 | frame_num = 0 23 | while True: 24 | retval1, image1 = cap1.read() 25 | retval2, image2 = cap2.read() 26 | 27 | if not retval1 and not retval2: 28 | break 29 | elif not retval1 or not retval2: 30 | print("error: input files have different number of frames") 31 | break 32 | 33 | if image1.shape != image2.shape: 34 | print("error: input files have different resolutions") 35 | 36 | y_image1 = rgb_to_y(image1) 37 | y_image2 = rgb_to_y(image2) 38 | 39 | psnr_metric = psnr.psnr(image1, image2) 40 | ssim_metric = ssim.ssim(y_image1 / 255.0, y_image2 / 255.0) 41 | vifp_metric = vifp.vifp_mscale(y_image1, y_image2) 42 | 43 | print(json.dumps({ "frame_num":frame_num, "psnr":psnr_metric, "ssim":ssim_metric, "vifp":vifp_metric })) 44 | frame_num += 1 45 | 46 | cap1.release() 47 | cap2.release() 48 | print("done") 49 | -------------------------------------------------------------------------------- /skvideo/examples/player.py: -------------------------------------------------------------------------------- 1 | from skvideo.io import VideoCapture 2 | 3 | import sys 4 | import matplotlib.pyplot as plt 5 | import matplotlib.animation as animation 6 | 7 | filename = sys.argv[1] 8 | cap = VideoCapture(filename) 9 | print(str(cap.get_info())) 10 | 11 | 12 | cap.open() 13 | retval, image = cap.read() 14 | 15 | plt_fig = plt.figure() 16 | plt_image = plt.imshow(image) 17 | 18 | frame_num = 0 19 | 20 | def updatefig(*args): 21 | global cap, frame_num 22 | 23 | retval, image = cap.read() 24 | 25 | if not retval: 26 | print("done") 27 | sys.exit() 28 | 29 | print("frame %d" % (frame_num)) 30 | frame_num += 1 31 | 32 | plt_image.set_array(image) 33 | return plt_image, 34 | 35 | plt_ani = animation.FuncAnimation(plt_fig, updatefig, interval=33, blit=True) 36 | plt.show() 37 | 38 | -------------------------------------------------------------------------------- /skvideo/examples/player_pygame.py: -------------------------------------------------------------------------------- 1 | from skvideo.io import VideoCapture 2 | 3 | import sys 4 | 5 | filename = sys.argv[1] 6 | cap = VideoCapture(filename) 7 | print(str(cap.get_info())) 8 | 9 | 10 | cap.open() 11 | retval, image = cap.read() 12 | 13 | 14 | 15 | frame_num = 0 16 | 17 | 18 | import numpy 19 | import pygame 20 | 21 | while True: 22 | retval, image = cap.read() 23 | 24 | if not retval: 25 | print("done") 26 | sys.exit() 27 | 28 | surface = pygame.surfarray.make_surface(image) 29 | 30 | surface = pygame.Surface((100, 100)) 31 | numpy_surface = numpy.frombuffer(surface.get_buffer()) 32 | numpy_surface[...] = numpy.frombuffer(image) 33 | del numpy_surface 34 | 35 | screen = pygame.display.set_mode((100, 100)) 36 | screen.blit(surface, (0, 0)) 37 | pygame.display.flip() 38 | 39 | -------------------------------------------------------------------------------- /skvideo/examples/test_capture.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | 4 | from skvideo.io import VideoCapture 5 | import sys 6 | 7 | filename = sys.argv[1] 8 | cap = VideoCapture(filename) 9 | 10 | frame_num = 0 11 | 12 | cap.open() 13 | while True: 14 | retval, image = cap.read() 15 | if not retval: 16 | break 17 | 18 | print("frame %d" % (frame_num)) 19 | frame_num += 1 20 | 21 | cap.release() 22 | print("done") 23 | -------------------------------------------------------------------------------- /skvideo/examples/test_writer.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | 4 | from skvideo.io import VideoWriter 5 | import sys 6 | import numpy 7 | 8 | w, h = 640, 480 9 | 10 | filename = sys.argv[1] 11 | wr = VideoWriter(filename, frameSize=(w, h)) 12 | 13 | x = numpy.linspace(0, 2 * numpy.pi, w) 14 | y = numpy.linspace(0, 2 * numpy.pi, h).reshape(-1, 1) 15 | 16 | def f(x, y): 17 | return numpy.sin(x) + numpy.cos(y) 18 | 19 | frame_num = 0 20 | 21 | wr.open() 22 | while True: 23 | x += numpy.pi / 15. 24 | y += numpy.pi / 20. 25 | image = numpy.zeros((h, w, 3)) 26 | image[:,:,0] = (f(x, y) + 2)* 64 27 | image[:,:,1] = (f(x+numpy.pi/2, y+numpy.pi/2) + 2)* 64 28 | 29 | wr.write(image) 30 | 31 | print("frame %d" % (frame_num)) 32 | frame_num += 1 33 | if frame_num > 300: 34 | break 35 | 36 | wr.release() 37 | print("done") 38 | -------------------------------------------------------------------------------- /skvideo/io/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) 2014, Alex Izvorski 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 3. Neither the name of scikit-video nor the names of its contributors 16 | may be used to endorse or promote products derived from this 17 | software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 28 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | """ 31 | 32 | from __future__ import print_function 33 | from __future__ import division 34 | 35 | import numpy 36 | import subprocess 37 | import json 38 | 39 | class VideoCapture: 40 | """ 41 | Read video using avconv or ffmpeg in a subprocess. 42 | 43 | The API is modelled after cv2.VideoCapture, and in many cases is a drop-in replacement. 44 | """ 45 | 46 | def __init__(self, filename=None, frameSize=None): 47 | self.filename = filename 48 | # TODO find either avconv or ffmpeg, remember which one we found 49 | self.convert_command = "avconv" 50 | self.probe_command = "avprobe" 51 | self.proc = None 52 | 53 | if frameSize: 54 | self.do_resize = True 55 | self.width, self.height = frameSize 56 | else: 57 | self.do_resize = False 58 | 59 | if self.filename: 60 | self.info = self.get_info() 61 | if len(self.info["streams"]) == 0: 62 | raise ValueError("No streams found") 63 | if self.info["streams"][0]["codec_type"] != "video": 64 | raise ValueError("No video stream found") 65 | self.src_width = self.info["streams"][0]["width"] 66 | self.src_height = self.info["streams"][0]["height"] 67 | if not self.do_resize: 68 | self.width = self.src_width 69 | self.height = self.src_height 70 | 71 | self.depth = 3 # TODO other depths 72 | # print "Found video: %d x %d" %(self.width, self.height) 73 | self.open() 74 | 75 | def open(self): 76 | # TODO decide what is best behavior, reopen or leave as it if previously opened 77 | if self.isOpened(): 78 | self.release() 79 | cmd = [self.convert_command, '-loglevel', 'error', '-i', self.filename] 80 | if self.do_resize: 81 | cmd += ['-vf', 'scale=%d:%d' %(self.width, self.height)] 82 | cmd += ['-f', 'rawvideo', '-pix_fmt', 'rgb24', '-'] 83 | self.proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) 84 | self.buf = b'' 85 | 86 | def isOpened(self): 87 | return (self.proc != None) 88 | 89 | def read(self): 90 | retval = True 91 | 92 | nbytes = self.width * self.height * self.depth 93 | 94 | while len(self.buf) < nbytes: 95 | 96 | # Could poll here, but return code never seems to be set before we fail at reading anyway 97 | # self.proc.poll() 98 | 99 | if self.proc.returncode != None: 100 | if self.proc.returncode < 0: 101 | raise ValueError("Command exited with return code %d" % (self.proc.returncode)) # TODO subprocess.CalledProcessError? 102 | else: 103 | return False, None 104 | 105 | buf = self.proc.stdout.read( nbytes - len(self.buf) ) 106 | # print "Read %d" % (len(buf)) 107 | 108 | # Reading no data seems to be a reliable end-of-file indicator; return code is not. 109 | if len(buf) == 0: 110 | break 111 | 112 | self.buf += buf 113 | 114 | if len(self.buf) < nbytes: 115 | # We didn't get any data, assume end-of-file 116 | if len(self.buf) == 0: 117 | return False, None 118 | # We got some data but not enough, this is an error 119 | else: 120 | raise ValueError("Not enough data at end of file, expected %d bytes, read %d" % (nbytes, len(self.buf))) 121 | 122 | image = numpy.fromstring(self.buf[:nbytes], dtype=numpy.uint8).reshape((self.height, self.width, self.depth)) 123 | 124 | # If there is data left over, move it to beginning of buffer for next frame 125 | if len(self.buf) > nbytes: 126 | self.buf = self.buf[nbytes:] # TODO this is a relatively slow operation, optimize 127 | # Otherwise just forget the buffer 128 | else: 129 | self.buf = b'' 130 | 131 | return retval, image 132 | 133 | def seek(self, time): 134 | raise NotImplementedError() 135 | 136 | def release(self): 137 | self.proc.kill() 138 | self.proc = None 139 | self.buf = None 140 | 141 | def get(self, propId): 142 | # CV_CAP_PROP_FRAME_COUNT 143 | raise NotImplementedError() 144 | 145 | def set(self, propId, value): 146 | raise NotImplementedError() 147 | 148 | def get_info(self): 149 | # NOTE requires a fairly recent avprobe/ffprobe, older versions don't have -of json and only produce INI-like output 150 | # TODO parse old INI-like output 151 | cmd = [self.probe_command] + "-loglevel error -of json -show_format -show_streams".split() + [self.filename] 152 | output = subprocess.check_output(cmd, universal_newlines=True) 153 | info = json.loads(output) 154 | return info 155 | 156 | class VideoWriter: 157 | def __init__(self, filename, fourcc='XVID', fps=30, frameSize=(640, 480), isColor=True): 158 | self.filename = filename 159 | self.convert_command = "avconv" 160 | 161 | self.fourcc = fourcc 162 | self.fps = fps 163 | self.width, self.height = frameSize 164 | self.depth = 3 # TODO other depths 165 | 166 | if not isColor: 167 | raise NotImplementedError() 168 | 169 | 170 | def open(self): 171 | cmd = [self.convert_command, '-loglevel', 'error', '-f', 'rawvideo', '-pix_fmt', 'rgb24', '-s', '%dx%d' %(self.width, self.height), '-r', str(self.fps), '-i', '-'] 172 | codecs_map = { 173 | 'XVID': 'mpeg4', 174 | 'DIVX': 'mpeg4', 175 | 'H264': 'libx264', 176 | 'MJPG': 'mjpeg', 177 | } 178 | if self.fourcc in codecs_map: 179 | vcodec = codecs_map[self.fourcc] 180 | else: 181 | vcodec = self.fourcc 182 | cmd += ['-vcodec', vcodec] 183 | cmd += [self.filename] 184 | self.proc = subprocess.Popen(cmd, stdin=subprocess.PIPE) 185 | 186 | def isOpened(self): 187 | return (self.proc != None) 188 | 189 | def write(self, image): 190 | if image.shape[0] != self.height or image.shape[1] != self.width or image.shape[2] != self.depth: 191 | raise ValueError('Image dimensions do not match') 192 | self.proc.stdin.write( image.astype(numpy.uint8).tostring() ) 193 | 194 | def release(self): 195 | self.proc.stdin.close() 196 | self.proc.wait() 197 | self.proc = None 198 | 199 | 200 | -------------------------------------------------------------------------------- /skvideo/metrics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aizvorski/scikit-video/f03bf543e5fcde0ed27f31324aa8a502468033cc/skvideo/metrics/__init__.py -------------------------------------------------------------------------------- /skvideo/metrics/psnr.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) 2014, Alex Izvorski 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 3. Neither the name of scikit-video nor the names of its contributors 16 | may be used to endorse or promote products derived from this 17 | software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 28 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | """ 31 | 32 | import numpy 33 | import math 34 | 35 | def psnr(img1, img2): 36 | mse = numpy.mean( (img1 - img2) ** 2 ) 37 | PIXEL_MAX = 255.0 38 | return 20 * math.log10(PIXEL_MAX / math.sqrt(mse)) 39 | -------------------------------------------------------------------------------- /skvideo/metrics/ssim.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright (C) 2014, Alex Izvorski 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in 13 | the documentation and/or other materials provided with the 14 | distribution. 15 | 3. Neither the name of scikit-video nor the names of its contributors 16 | may be used to endorse or promote products derived from this 17 | software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 23 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 27 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 28 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 | POSSIBILITY OF SUCH DAMAGE. 30 | """ 31 | 32 | import numpy 33 | from scipy.ndimage import gaussian_filter 34 | 35 | from numpy.lib.stride_tricks import as_strided as ast 36 | 37 | # Hat tip: http://stackoverflow.com/a/5078155/1828289 38 | def block_view(A, block=(3, 3)): 39 | """Provide a 2D block view to 2D array. No error checking made. 40 | Therefore meaningful (as implemented) only for blocks strictly 41 | compatible with the shape of A.""" 42 | # simple shape and strides computations may seem at first strange 43 | # unless one is able to recognize the 'tuple additions' involved ;-) 44 | shape= (A.shape[0]/ block[0], A.shape[1]/ block[1])+ block 45 | strides= (block[0]* A.strides[0], block[1]* A.strides[1])+ A.strides 46 | return ast(A, shape= shape, strides= strides) 47 | 48 | 49 | def ssim(img1, img2, C1=0.01**2, C2=0.03**2): 50 | 51 | bimg1 = block_view(img1, (4,4)) 52 | bimg2 = block_view(img2, (4,4)) 53 | s1 = numpy.sum(bimg1, (-1, -2)) 54 | s2 = numpy.sum(bimg2, (-1, -2)) 55 | ss = numpy.sum(bimg1*bimg1, (-1, -2)) + numpy.sum(bimg2*bimg2, (-1, -2)) 56 | s12 = numpy.sum(bimg1*bimg2, (-1, -2)) 57 | 58 | vari = ss - s1*s1 - s2*s2 59 | covar = s12 - s1*s2 60 | 61 | ssim_map = (2*s1*s2 + C1) * (2*covar + C2) / ((s1*s1 + s2*s2 + C1) * (vari + C2)) 62 | return numpy.mean(ssim_map) 63 | 64 | # FIXME there seems to be a problem with this code 65 | def ssim_exact(img1, img2, sd=1.5, C1=0.01**2, C2=0.03**2): 66 | 67 | mu1 = gaussian_filter(img1, sd) 68 | mu2 = gaussian_filter(img2, sd) 69 | mu1_sq = mu1 * mu1 70 | mu2_sq = mu2 * mu2 71 | mu1_mu2 = mu1 * mu2 72 | sigma1_sq = gaussian_filter(img1 * img1, sd) - mu1_sq 73 | sigma2_sq = gaussian_filter(img2 * img2, sd) - mu2_sq 74 | sigma12 = gaussian_filter(img1 * img2, sd) - mu1_mu2 75 | 76 | ssim_num = ((2 * mu1_mu2 + C1) * (2 * sigma12 + C2)) 77 | 78 | ssim_den = ((mu1_sq + mu2_sq + C1) * (sigma1_sq + sigma2_sq + C2)) 79 | 80 | ssim_map = ssim_num / ssim_den 81 | return numpy.mean(ssim_map) 82 | 83 | -------------------------------------------------------------------------------- /skvideo/metrics/vifp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Portions Copyright (c) 2014 CiiNOW Inc. 5 | Written by Alex Izvorski , 6 | 2014-03-03 Ported from matlab to python/numpy/scipy 7 | 2014-03-04 Added utility functions to read/compare images and video 8 | """ 9 | 10 | """ 11 | -----------COPYRIGHT NOTICE STARTS WITH THIS LINE------------ 12 | Copyright (c) 2005 The University of Texas at Austin 13 | All rights reserved. 14 | 15 | Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, 16 | modify, and distribute this code (the source files) and its documentation for 17 | any purpose, provided that the copyright notice in its entirety appear in all copies of this code, and the 18 | original source of this code, Laboratory for Image and Video Engineering (LIVE, http://live.ece.utexas.edu) 19 | at the University of Texas at Austin (UT Austin, 20 | http://www.utexas.edu), is acknowledged in any publication that reports research using this code. The research 21 | is to be cited in the bibliography as: 22 | 23 | H. R. Sheikh and A. C. Bovik, "Image Information and Visual Quality", IEEE Transactions on 24 | Image Processing, (to appear). 25 | 26 | IN NO EVENT SHALL THE UNIVERSITY OF TEXAS AT AUSTIN BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, 27 | OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS DATABASE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF TEXAS 28 | AT AUSTIN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | THE UNIVERSITY OF TEXAS AT AUSTIN SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 31 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE DATABASE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, 32 | AND THE UNIVERSITY OF TEXAS AT AUSTIN HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 33 | 34 | -----------COPYRIGHT NOTICE ENDS WITH THIS LINE------------ 35 | 36 | This software release consists of a MULTISCALE PIXEL DOMAIN, SCALAR GSM implementation of the algorithm described in the paper: 37 | 38 | H. R. Sheikh and A. C. Bovik, "Image Information and Visual Quality"., IEEE Transactions on Image Processing, (to appear). 39 | Download manuscript draft from http://live.ece.utexas.edu in the Publications link. 40 | 41 | THE PIXEL DOMAIN ALGORITHM IS NOT DESCRIBED IN THE PAPER. THIS IS A COMPUTATIONALLY SIMPLER 42 | DERIVATIVE OF THE ALGORITHM PRESENTED IN THE PAPER 43 | 44 | Input : (1) img1: The reference image as a matrix 45 | (2) img2: The distorted image (order is important) 46 | 47 | Output: (1) VIF the visual information fidelity measure between the two images 48 | 49 | Default Usage: 50 | Given 2 test images img1 and img2, whose dynamic range is 0-255 51 | 52 | vif = vifvec(img1, img2); 53 | 54 | Advanced Usage: 55 | Users may want to modify the parameters in the code. 56 | (1) Modify sigma_nsq to find tune for your image dataset. 57 | Email comments and bug reports to hamid.sheikh@ieee.org 58 | """ 59 | 60 | 61 | import numpy 62 | import scipy.signal 63 | import scipy.ndimage 64 | 65 | def vifp_mscale(ref, dist): 66 | sigma_nsq=2 67 | eps = 1e-10 68 | 69 | num = 0.0 70 | den = 0.0 71 | for scale in range(1, 5): 72 | 73 | N = 2**(4-scale+1) + 1 74 | sd = N/5.0 75 | 76 | if (scale > 1): 77 | ref = scipy.ndimage.gaussian_filter(ref, sd) 78 | dist = scipy.ndimage.gaussian_filter(dist, sd) 79 | ref = ref[::2, ::2] 80 | dist = dist[::2, ::2] 81 | 82 | mu1 = scipy.ndimage.gaussian_filter(ref, sd) 83 | mu2 = scipy.ndimage.gaussian_filter(dist, sd) 84 | mu1_sq = mu1 * mu1 85 | mu2_sq = mu2 * mu2 86 | mu1_mu2 = mu1 * mu2 87 | sigma1_sq = scipy.ndimage.gaussian_filter(ref * ref, sd) - mu1_sq 88 | sigma2_sq = scipy.ndimage.gaussian_filter(dist * dist, sd) - mu2_sq 89 | sigma12 = scipy.ndimage.gaussian_filter(ref * dist, sd) - mu1_mu2 90 | 91 | sigma1_sq[sigma1_sq<0] = 0 92 | sigma2_sq[sigma2_sq<0] = 0 93 | 94 | g = sigma12 / (sigma1_sq + eps) 95 | sv_sq = sigma2_sq - g * sigma12 96 | 97 | g[sigma1_sq