From 4aa5a6aa32c5df19c6ee51c5be2ef8f765ca0d40 Mon Sep 17 00:00:00 2001 From: Andre Anjos <andre.dos.anjos@gmail.com> Date: Thu, 24 Apr 2014 12:43:50 +0200 Subject: [PATCH] Complete, with documentation, tests and doctests --- MANIFEST.in | 1 + README.rst | 97 ++++----- buildout.cfg | 2 + doc/conf.py | 290 +++++++++++++++++++++++++++ doc/guide.rst | 128 ++++++++++++ doc/img/favicon.ico | Bin 0 -> 4286 bytes doc/img/logo.png | Bin 0 -> 11280 bytes doc/index.rst | 71 +++++++ doc/links.rst | 82 ++++++++ doc/plot/show_lena.py | 23 +++ doc/py_api.rst | 13 ++ setup.py | 8 +- xbob/ip/flandmark/flandmark.cpp | 2 + xbob/ip/flandmark/script/__init__.py | 0 xbob/ip/flandmark/script/annotate.py | 131 ------------ 15 files changed, 658 insertions(+), 190 deletions(-) create mode 100644 doc/conf.py create mode 100644 doc/guide.rst create mode 100644 doc/img/favicon.ico create mode 100644 doc/img/logo.png create mode 100644 doc/index.rst create mode 100644 doc/links.rst create mode 100644 doc/plot/show_lena.py create mode 100644 doc/py_api.rst delete mode 100644 xbob/ip/flandmark/script/__init__.py delete mode 100644 xbob/ip/flandmark/script/annotate.py diff --git a/MANIFEST.in b/MANIFEST.in index 9589412..ecdd145 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include README.rst buildout.cfg bootstrap.py +recursive-include doc *.rst *.py recursive-include xbob *.cpp *.h *.dat diff --git a/README.rst b/README.rst index e2c010e..774b605 100644 --- a/README.rst +++ b/README.rst @@ -14,14 +14,13 @@ :target: https://pypi.python.org/pypi/xbob.ip.flandmark ============================== - Python Bindings to flandmark + Python Bindings to Flandmark ============================== -This package is a simple Boost.Python wrapper to the (rather quick) open-source -facial landmark detector `flandmark -<http://cmp.felk.cvut.cz/~uricamic/flandmark/index.php>`_, **version 1.0.7** -(or the github state as of 10/february/2013). -If you use this package, the author asks you to cite the following paper:: +This package is a simple Python wrapper to the (rather quick) open-source +facial landmark detector `Flandmark`_, **version 1.0.7** (or the github state +as of 10/february/2013). If you use this package, the author asks you to cite +the following paper:: @inproceedings{Uricar-Franc-Hlavac-VISAPP-2012, author = {U{\v{r}}i{\v{c}}{\'{a}}{\v{r}}, Michal and Franc, Vojt{\v{e}}ch and Hlav{\'{a}}{\v{c}}, V{\'{a}}clav}, @@ -46,8 +45,8 @@ If you use this package, the author asks you to cite the following paper:: www = {http://www.visapp.visigrapp.org}, } -You should also cite `Bob <http://www.idiap.ch/software/bob/>`_, as a core -framework:: +You should also cite `Bob`_, as a core framework, in which these bindings are +based on:: @inproceedings{Anjos_ACMMM_2012, author = {A. Anjos AND L. El Shafey AND R. Wallace AND M. G\"unther AND C. McCool AND S. Marcel}, @@ -62,67 +61,59 @@ framework:: Installation ------------ -You can just add a dependence for ``xbob.ip.flandmark`` on your ``setup.py`` to -automatically download and have this package available at your satellite -package. This works well if Bob_ is installed centrally at your machine. +Install it through normal means, via PyPI or use ``zc.buildout`` to bootstrap +the package and run test units. -Otherwise, you will need to tell ``buildout`` how to build the package locally -and how to find Bob_. For that, just add a custom egg recipe to your -buildout that will fetch the package and compile it locally, setting the -buildout variable ``prefixes`` to where Bob_ is installed (a build directory -will work as well). For example:: +Documentation +------------- - [buildout] - parts = flandmark <other parts here...> - ... - prefixes = /Users/andre/work/bob/build/debug +You can generate the documentation for this package, after installation, using +Sphinx:: - ... + $ sphinx-build -b html doc sphinx - [flandmark] - recipe = xbob.buildout:develop +This shall place in the directory ``sphinx``, the current version for the +documentation of the package. - ... +Testing +------- -Development ------------ +You can run a set of tests using the nose test runner:: -To develop these bindings, you will need the open-source library Bob_ installed -somewhere. At least version 1.1 of Bob is required. If you have compiled Bob -yourself and installed it on a non-standard location, you will need to note -down the path leading to the root of that installation. + $ nosetests -sv xbob.ap -Just type:: +.. warning:: - $ python bootstrap.py - $ ./bin/buildout + If Bob <= 1.2.1 is installed on your python path, nose will automatically + load the old version of the insulate plugin available in Bob, which will + trigger the loading of incompatible shared libraries (from Bob itself), in + to your working binary. This will cause a stack corruption. Either remove + the centrally installed version of Bob, or build your own version of Python + in which Bob <= 1.2.1 is not installed. -If Bob is installed in a non-standard location, edit the file ``buildout.cfg`` -to set the root to Bob's local installation path. Remember to use the **same -python interpreter** that was used to compile Bob_, then execute the same steps -as above. +You can run our documentation tests using sphinx itself:: -Usage ------ + $ sphinx-build -b doctest doc sphinx -Pretty simple, just do something like:: +You can test overall test coverage with:: - import bob - from xbob import flandmark + $ nosetests --with-coverage --cover-package=xbob.ip.flandmark - video = bob.io.VideoReader('myvideo.avi') - localizer = flandmark.Localizer() +The ``coverage`` egg must be installed for this to work properly. - for frame in video: - print localizer(frame) +Development +----------- -If you already have a detected bounding box, you can plug the coordinates of the bounding box into the localizer call:: +To develop this package, install using ``zc.buildout``, using the buildout +configuration found on the root of the package:: - landmarks = localizer(image, top, left, height, width) + $ python bootstrap.py + ... + $ ./bin/buildout -In total, 8 ``landmarks`` are returned by the localizer. -For the list and the interpretation of the landmarks, please have a look `here <http://cmp.felk.cvut.cz/~uricamic/flandmark/index.php>`_. +Tweak the options in ``buildout.cfg`` to disable/enable verbosity and debug +builds. -.. warning:: - Since version 1.1 of this package, the landmarks are returned in the Bob_-typical order, which is ``(y,x)``. - Please update your code to this new behavior. +.. Place your references here: +.. _flandmark: http://cmp.felk.cvut.cz/~uricamic/flandmark/index.php +.. _bob: https://www.idiap.ch/software/bob diff --git a/buildout.cfg b/buildout.cfg index 2455a14..6eb656b 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -12,6 +12,7 @@ develop = src/xbob.extension src/xbob.blitz src/xbob.io src/xbob.ip.color + src/xbob.ip.draw . ; options for xbob.buildout extension @@ -25,6 +26,7 @@ xbob.extension = git https://github.com/bioidiap/xbob.extension branch=prototype xbob.blitz = git https://github.com/bioidiap/xbob.blitz xbob.io = git https://github.com/bioidiap/xbob.io xbob.ip.color = git https://github.com/bioidiap/xbob.ip.color +xbob.ip.draw = git https://github.com/bioidiap/xbob.ip.draw [scripts] recipe = xbob.buildout:scripts diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..072d633 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : +# Andre Anjos <andre.anjos@idiap.ch> +# Tue 15 Oct 16:37:18 2013 CEST +# +# Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland + +import os +import sys +import glob +import pkg_resources + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = [ + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.pngmath', + 'sphinx.ext.ifconfig', + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'matplotlib.sphinxext.plot_directive', + ] + +# The viewcode extension appeared only on Sphinx >= 1.0.0 +import sphinx +if sphinx.__version__ >= "1.0": + extensions.append('sphinx.ext.viewcode') + +# Always includes todos +todo_include_todos = True + +# If we are on OSX, the 'dvipng' path maybe different +dvipng_osx = '/opt/local/libexec/texlive/binaries/dvipng' +if os.path.exists(dvipng_osx): pngmath_dvipng = dvipng_osx + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'xbob.ip.flandmark' +import time +copyright = u'%s, Idiap Research Institute' % time.strftime('%Y') + +# Grab the setup entry +distribution = pkg_resources.require('xbob.ip.flandmark')[0] + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = distribution.version +# The full version, including alpha/beta/rc tags. +release = distribution.version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['links.rst'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +if sphinx.__version__ >= "1.0": + html_theme = 'nature' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = 'xbob_ip_flandmark' + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'img/logo.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = 'img/favicon.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'xbob_ip_flandmark_doc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +latex_paper_size = 'a4' + +# The font size ('10pt', '11pt' or '12pt'). +latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'xbob_ip_flandmark.tex', u'Python bindings to the Flandmark frontal-face keypoint localizer', u'Biometrics Group, Idiap Research Institute', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +latex_logo = '' + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + +# Included after all input documents +rst_epilog = """ +.. |project| replace:: Bob +.. |url| replace:: https://www.idiap.ch/software/bob/ +.. |version| replace:: %s +.. |current-year| date:: %%Y +""" % (version,) + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'xbob_ip_flandmark', u'Python bindings to Flandmark frontal-face keypoint localizer', [u'Idiap Research Institute'], 1) +] + +# Default processing flags for sphinx +autoclass_content = 'both' +autodoc_member_order = 'bysource' +autodoc_default_flags = ['members', 'undoc-members', 'inherited-members', 'show-inheritance'] + +def smaller_than(v1, v2): + """Compares scipy/numpy version numbers""" + + c1 = v1.split('.') + c2 = v2.split('.')[:len(c1)] #clip to the compared version + for i, k in enumerate(c2): + n1 = c1[i] + n2 = c2[i] + try: + n1 = int(n1) + n2 = int(n2) + except ValueError: + n1 = str(n1) + n2 = str(n2) + if n1 > n2: return False + return True + +# Some name mangling to find the correct sphinx manuals for some packages +numpy_version = __import__('numpy').version.version +if smaller_than(numpy_version, '1.5.z'): + numpy_version = '.'.join(numpy_version.split('.')[:-1]) + '.x' +else: + numpy_version = '.'.join(numpy_version.split('.')[:-1]) + '.0' +numpy_manual = 'http://docs.scipy.org/doc/numpy-%s/' % numpy_version + +# For inter-documentation mapping: +intersphinx_mapping = { + 'http://docs.python.org/%d.%d/' % sys.version_info[:2]: None, + numpy_manual: None, + } + +try: + __import__('cv2') + has_opencv = True +except ImportError: + has_opencv = False + +def setup(app): + app.add_config_value('has_opencv', has_opencv, True) diff --git a/doc/guide.rst b/doc/guide.rst new file mode 100644 index 0000000..6c5841b --- /dev/null +++ b/doc/guide.rst @@ -0,0 +1,128 @@ +.. vim: set fileencoding=utf-8 : +.. Andre Anjos <andre.dos.anjos@gmail.com> +.. Sat 16 Nov 20:52:58 2013 + +.. testsetup:: + + def get_file(f): + from os.path import join + from pkg_resources import resource_filename + return resource_filename('xbob.ip.flandmark', join('data', f)) + + LENA = get_file('lena.jpg') + MULTI = get_file('multi.jpg') + CASCADE = get_file('haarcascade_frontalface_alt.xml') + +============= + Users Guide +============= + +Flandmark detects 8 coordinates of important keypoints in **frontal** human +faces. To properly work, the keypoint localizer requires the input of an image +(of type ``uint8``, gray-scaled) and of a bounding box describing a rectangle +where the face is supposed to be located in the image (see +:py:class:`xbob.ip.flandmark.Flandmark.locate`). + +The keypoints returned are, in this order: + +[0] + Face center + +[1] + Canthus-rl (inner corner of the right eye). + + .. note:: + + The "right eye" means the right eye at the face w.r.t. the person on the + image. That is the left eye in the image, from the viewer's perspective. + +[2] + Canthus-lr (inner corner of the left eye) + +[3] + Mouth-corner-r (right corner of the mouth) + +[4] + Mouth-corner-l (left corner of the mouth) + +[5] + Canthus-rr (outer corner of the right eye) + +[6] + Canthus-ll (outer corner of the left eye) + +[7] + Nose + +Each point is returned as tuple defining the pixel positions in the form +``(y, x)``. + +The input bounding box describes the rectangle coordinates using 4 values: +``(y, x, height, width)``. Square bounding boxes, i.e. when ``height == +width``, will give best results. + +If you don't know the bounding box coordinates of faces on the provided image, +you will need to either manually annotate them or use an automatic face +detector. OpenCV_, if compiled with Python support, provides an easy to use +frontal face detector. The code below shall detect most frontal faces in a +provided (gray-scaled) image: + +.. ifconfig:: not has_opencv + + .. warning:: + + OpenCV for the current installation in which this manual was generated was + not compiled with Python support. The code below cannot be tested and it + may work differently than what is announced. In doubt, consult the OpenCV + guide. + + .. code-block:: python + + >>> from cv2 import CascadeClassifier, cv + >>> from xbob.io import load + >>> from xbob.ip.color import rgb_to_gray + >>> cc = CascadeClassifier(CASCADE) # uses 'haarcascade_frontalface_alt.xml' + >>> lena_gray = rgb_to_gray(load(LENA) # uses 'lena.jpg' + >>> face_bbxs = cc.detectMultiScale(lena_gray, 1.3, 4, 0, (20, 20)) + >>> print face_bbxs + [[214, 202, 183, 183]] + +.. ifconfig:: has_opencv + + .. doctest:: + :options: +NORMALIZE_WHITESPACE, +ELLIPSIS + + >>> from cv2 import CascadeClassifier, cv + >>> from xbob.io import load + >>> from xbob.ip.color import rgb_to_gray + >>> cc = CascadeClassifier(get_file('haarcascade_frontalface_alt.xml')) + >>> lena_gray = rgb_to_gray(load(get_file('lena.jpg'))) + >>> face_bbxs = cc.detectMultiScale(lena_gray, 1.3, 4, 0, (20, 20)) + >>> print face_bbxs + [[214, 202, 183, 183]] + +The function ``detectMultiScale`` returns OpenCV_ rectangles as 2D +:py:class:`numpy.ndarray`'s. Each row corresponds to a detected face at the +input image. Notice the format of each bounding box differs from that of Bob_. +Their format is ``(x, y, width, height)``. + +Once in possession of bounding boxes for the provided, gray-scaled image, you +can find the keypoints in the following way: + +.. doctest:: + :options: +NORMALIZE_WHITESPACE, +ELLIPSIS + + >>> x, y, width, height = [214, 202, 183, 183] #or from OpenCV + >>> from xbob.ip.flandmark import Flandmark + >>> localizer = Flandmark() + >>> keypoints = localizer.locate(lena_gray, y, x, height, width) + >>> keypoints + [[...]] + +You can use the package ``xbob.ip.draw`` to draw the rectangles and keypoints +on the target image. A complete script would be something like: + +.. plot:: plot/show_lena.py + :include-source: True + +.. include:: links.rst diff --git a/doc/img/favicon.ico b/doc/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4cc3264302627d40868261add69eb755856611b6 GIT binary patch literal 4286 zcmb`Lc~BE~6vr2J29j)cv)MD7gm4qFctEW<D4>FtQQLY@tG0My4=wf>9c>xM*w%WY zsNjhxh!&729^h4lzuJzr(@xu&I@3R-9Xrm{>L{IBm=RvzZ<Am^La-*xm)-sSe((GF zz4tbd4Mm0FPgE2|ep59jijpXba-wjE%0iKG?S*qpm`9!giUNSA7RKsKI^7D6)|Z)? zh(?Ll|0J@yW`(886i&ZP<&5KS&EWsz9V~{`>1J_;@E^^z4tS#u1fw2AMh_Be02!s2 zXs{4*#@njdxDxjf7R+;?^PEAq2DzO)vIS&|kJ%3tR{t%s+X-9cXt0`%U_;q?=5nk| zKQ~ZDpml;?pTg4-4|;GJ%$cDZSeE5ToSkK3Ro1x5YNmg;@eE-@q`-iKXYWQ!?5by} zg<v#X)#{A3CUy4ewk%VI-g~wQbY>*a+;a#lM2Rd!izbMXOb{n>rM=e<<l2NU<ULfh z{u;dfC3QBQ2^5dqIng5fZ!~fVJ67f(PUgDeEnHEcm_Xwy%TBgnPqn^s#_rx)1!ira z7&0CsavQx|h*!*zU@=3Y#oXbv30eKc^`5iy^m6aL<msueucC2D@3sEd(UK&`E8L%6 zE;v;lhN6a9#jo&Q805dUzw;=8nM-=<>!)8&*Y_7k<`OM@3E?JLk!uqm*)G5cy9gsA z<)y)5ee(%6ofUi7q4n``FG)XZd}{(7lOoN-l5IlQaE&R#D2E8CQ4%~KE!GE$>1!PC zn;%FIeSP%S^j>nL=e^fgjBhN(A>7iK$bBJNf-x~NjE#}+rmCvmw<g%pshTTD9km`} zEt9N*R?lH}etEFiv{<<%jWA<nNRN|YT$}<K@d{)*tz(15`sR~ua`JGy@Hgq*h)B{u zPc2g%;!Vs$@U83Xh`bZyk(ZI6KvseUCL~&5qSFG|PTS1B)(09(jgmKx#u|>s8v5!; z@=A+U_6CX}<H);Cc$1te<P23|{wOOHjkm$(iRz;M;(E`eCkn>&c<Dwu){oRs%gK{h zN0w9R^F2K^IWneTwA$v%MBXG+jt#a<vBK6Dtx%k+j_S<_c0D0pou}0gYdFc9Q#R(5 zlVrV?m-poVTx8X2yj&}6n~Kb7sFzgO{<8X8Fjk=Xq73_)O%uKOWP5X(j5$rVYJXc@ zE^1KmbZc7iH1$I9bd3c&UQuDkOchFISzu?L1$O0G8i@av99zMrY}>US%E4<C?z@Mu zX4kgn;#jWr2Fay6AHwkD)P|hzOJ=BlmCQocY%gmL@?NvR?zyP>3Y5-QZtk9|ItkyE zMGe7yeogk-L&VSHNoob#$^K_}rRCxA_c3<9iVPoTp5o!Wu0YuvsC)(XEJQ7mVb5ae zk39vJRR7xU%u~l<O&*fE5Fej3Kd)!^{)smVbAVFxFZKHSGxjc)Veb+d_7$L(%CP@U z)LT-=fp?@+70cx@{%qfQ*#c!7dbDX;fJYSW`LPzsZMgq7^ln3MP3Yf-{+%9wWV|f_ zGT^{62@Wok?v%ePUoS6|+~q5z&4&u55k6L*NBo?2cwtn;)9PTMyrX=D2#4N7y)Qz= z2dEE4II>c#?URE&59N4up_w_dn*XJ8wE&fCP#+0U^)aeQ=sLQV&krRlkk>I6KeO7! zcj6I_t>@wRCuTVQDe5zGt9yfB2^1H~@tTcXb?s&|)NbLRb}I*UMB6y1-)^c3B`1)V zdzaaEqQrE|y~_md-6l9$iYn98>E36a9Vjjo<L>=zO2a-=e#3sY=JWv;P9H>-vvB4R zs)B*D70j<^kC^f`9cJP~!SwfNZl;Eut7JEvt7QIZJj#HkY9llqLmfB5`7cm4cwT4x zrmE`6&+GnP&vM>aOJ`iDr@y;cPs2sGN0(00aOo5cmmBCimrfZ*K8ycAV(aQC-IYc| z3920hUUgk*)E5jSK2&S2o;OUn(q#CV@V{&{l!waqX?&WRC<gcOd2<~_2aIdS$K(6d zI%uUbp(ToimNhK69)*LeYcMJdT;Ng5Iu~flbU`bTgM#~P7_>*=HwyR_13vG&C@Q># eq8u&VkKC?xfI+BC09Uw2tt<i^0FQ92EAv18#ibAc literal 0 HcmV?d00001 diff --git a/doc/img/logo.png b/doc/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b9dd573a01019afd1af58a881996930e5212699d GIT binary patch literal 11280 zcmW++1zc3m6Msh?os!bsDbjs3QqtWB(j0Mw+)1l|AV-&kAR!<rd4Pa~fHcw|-QDo# z_kW+yzI|_ZW_D)x&F*}@Z(?<|RS9ruZ~*`yP*;QKKi18U#Q_`Z@xE}W@bR(2@Ksbd zz<#_!upMF^-*LRuOnm_WE$zPodOoJQ_}EAdRW^aX^mKv-*!nmE0RaL0E*`Ia?QOjr z`8|D{Gxw!w9*OAxBQkJ-x;X-ho_35{p3a_(0WQ#2jQWmFZjSa)7f%m%(mR<)O49!* z75t#DJbeHK4+kGdMg<R7Pv7n|@s`J4s{eYGd>n26<6ty!ad!kng!qMo`Gp0AgiQ|f zpFh$-{!ioS1@m!negy@Tlo%DDP#+gNzyI^Rf<nC{1qAH>FT>x_-qYRP)5G^aRR!E! z>>WLP9eoAtU;-lif&!<#2mb(o5m1LH7zAeRg~GDv4D0qA{3ob`_qCg(L~vLch2K#c z5bNe&>Q?3(gF;I-&-yWymH&V(l&`i7^?r!+#9SVJ<s5pO&loOFoUG2s$i##g+&T^U zQu;TzRjzb9%hIn@JJ<Ib+Jw`3ER|hq*?g{hR#Dg9danC(FfA39UZx0f2Pn{{`v$wW z3fatF7Q)g~C-F$mIEF(HqV*4&4>f?^7h*Csn2s5#Fqf~ydPe0Ib=<yd#_fvW@*p8M z^Na);VHL=1+Et&^ST^{kWsr+0Ke8_%`$qfZ=cnOfOuSSOpwGoR6f|45rbHyG4Ow(r zZ+%fDGd#25$d64@)II9RqtDQ|Syt;aag;pd(CPi}T2+i(plPWcxNV`nmnAxfrTa1E zWA-()?^LY!6^DGh1{?iUN!I_{F#mn%`WTricw!m1E}~<<?l>6{qhDJU=5p=(aAN4a zv`xnKsc*8SLlFPu@>9J24g(BK;_kdo5cf~)KdIzQ`ECEwo!e)m*T&!fM4>dWK%@55 zg{tFd*m>vYZZ)@|IX!~|vgFJf`T@g9r(l8m@8Yk1_a*n$8}nl^GjnIPdR<)>WWHm7 zJ@f*%<yQaKb;el{(%LsR9AbH#Y;37<7Y;V!2P|SSWN5vY^))um7b|(*+=XqgAKZw> z9jPp4kbv?~gW5a;Cl=()!e1*wz0X&4tl0=HYEr(t(|H-d^9=}1(Is4Z%egQ?toW;q zWbwibVW(L^S2u6){F%)NmOiq38Uy=)Akn!CHVFxwt?#9_6x&tl*n{RN5j{3Dx34b% zB96X<%`EI_eCEO^s+k$z@`TleY$9GjPA#3@G&#Y}RcSuS927)nl;sA9NC{VnepDz} z&IW&LE-q9#m(6NkSs6>4$+;TcZ@1$$O&n~wYw?UN;(0&{&nzr;sjfNx)T6x1xL{C? z_{#T`;kV^&8<Eh-fh*Fvy=E5zq07BF_sg%jfD6Z?2d;C6fKP4R96YzypUOQaI^ELj zEi%sOif?_Aa%Mao@1rCUXK)SCVAUu8SmQL)8crPQ9gZU{efo@Tj0oxMN1||gUMzfY z4;M0ool6ZY995U`3sLa3(?%^DwxM~HqQN-jB}apnaw`8-HXkc4EYc71Vw31yO8CFo zHW!PH46ppQW`%!S39^0>pOE_IuAm{=<8I>j?_}rp@4GWKLzYP)9hwWOjOhB9eB$6O zEqQ54mIlwzXJPZm+%#~|Yt~y@DcRHXnh$Gb+Du#2u(-A=k66yP4N6>fN3ogZvv2Nh zoCa#F5i-^5zjnChWwE14*0<$1XRg5aVPy6!MT-xoa_KO)+$ZM}>|39#$I{Jiu64-R zMv;WvV1g!<*PR}!b+3DS=28XPZ6r^9+Cjln(cc0aOlo&AM)`vHzI|iRRF!<n(A*KU zG<-1_sE}KbB<Zmys$zbjkz5gUm9Vb=!C%Vfja}#UNd<$ue|>?#P4`ONZrRXo(a^#J zbDFyNE}@hZ?#G2;J$M)6)*UTo(&M9UbNz$w@wI?e=b&r-&UZPD-eB1_Bj|aK5sU-> zLdI|*;YOCq;Em4xO(JiD+pXnMz4hF)d7oWM8y&i%l>@4+_%%s*0etVRRq%8?T0%k` z#Y5oO)u6QG?AbH}8|>E~u9|?uvQD)+V^n!7$AM*J$3Z(wUGT=s9|soRRy-~C!$CA- ze*0^cViXmD%IV-9Kga-Ip^9^I@_7n@oWOj*2E&EAZEko{=$d&O>-YKBOPi<NQX_Nk z3x81F3G%P`PKE|pm%fXXso)%_`TM;%LALq@l)ptW#=)q8^j}1V(r{C=QCERP5>vFl zti(~cK*+0{w!sh39KU5DmHG|*B(20}MYmSm2ZdUGw0%P&I`c-2XFa)frP9N$RnGBY zds83WHe502O+<~Ob)2CZB0{oO*Ry*E&g+RY-+J#kA*`f$#ul2f(10hZQ=o6?i^kZZ zwZWSE#f#FZ=@mxEEmKYVrlGcffxdtG$C#*}Xj+Oh;{%$Gp0m)G%M0cnvtZ>=ZH<d& z*)y)hEy;4><$+IB7tE=lJNW$MK9dDI{HZL>0rlUthBXFqOlrAp{V&2^rhV1M^%EzV zAUWf0^OPB1n9uA9yN$dMs>#}G+D#aR{cR?CCOrS3jjM7<7t!jF;TOQ2ZS-SxtNFNL z{xIAqb;s0YMoK$)ikx}OuS6;^f@*+%&v--MqviCnbwlLju#I!aS1;-PREQ4icf3cP zsi^rLa6TMXGibwU7n>ylW{DQM2aAkSc_?-v<6y@5ZTw;$cY)Bcq`^dv>=&e+*|G0m z!J{`n+_<GzI6t4uzBw7DXU}K5DIFYU+k&02Ij7^LGOX(0l$a%x2=SqE>j#Xgb+3Ep z7rp%5xJmUW=eQ=jjp|T|WF4ad;?;bxam{wT-hJG+96u4S5A8=+dtRV$iz;-W@3XS8 zQJbbM{hJoLrfZ$cH_W3K@rK@Zok7=$?J)*YC|=?=0q(A^PFj;mL95yU?An25Zagd7 zM&jqPrA<!fmE!siQa1yXrR*V}T{SA@+>PUj_PC|Jb`o~?p5}P@d(NQe0X9@9dH~kA z)Bt=CEdUa24%(LRrZVv;bLoImLdLzcQux#QZyhJUE@NUCT+&(k-<sKW+;Z*b$lpqS z>+V+gIoQvcz6Eo7CYmq7tuY?zeKNpO>4HF9Z7k$+F6}Je%fuTxG@IBorx_dBDw=kC z^OHyI?dg;YNMA;SQLZfT-(JR_CS<5gFwegQSOm5Fc(vxqiQM{t#(;?$`~#fw`90ZO z1*FLH3d&n4j70l*-d6S!iDTv}{Qinc%uA3-PftOmx|p7FmHW?K>p(dB4R%ysYi(3i z;4>-fYV^%*T1nL3q_4<5Tt~0ihy5TzNdjGi()4=oZG`-)$P#Bx?gi!rB!h@9_=D<{ zLQ(M;uA;xn<ShpjU-PN2wR!D)o-dAeS$=|4HJlq!Am6MDr?OEoKj%tz{1Q=0$lV?D zGP&f(n)jBnnQO$CeU*qQTs*{FvAE%+wPZc*Om#)o{k}PF!W`_W#fWF6s5})AJRBCX zj<^1{n*jqy4|89fTVJ4RbrTPKP~kqmx`j9Kp@43jgdW2Uqw2Nkz1h)<LsRV+!oMp$ zWheJ$rx>EV!320#$oc@C7mg*~`wiH;(<vg~_tNK@tQTzDEgxP23El1$dRW05JHt}q zKaLOOUY1tqfcd)LN}x>(_3Jst78mmV*{Fs)?XmJ(-`xkjil>kNTKTdf(0gkSFa2#M z_?^M~obe`lbf0)!BGb)ZRaMO6pE0=Qo~HzFrdY2rwB1Pp7s`J!zVH08fQ|%kCGPQ5 zGUJ%9j(8V#=a5cM;OfU0XHOM9TfXhO*z(@SIq4iYu<1v>_>AW*{|&ctHODE^PN(y) z2~K=dwBiwSdF5-<AKbKN;#<p2et2+Gys;BDc>lYl*0kf$4yZhS;`XMi+;yGjGs$Z( z$GuONSj^_dVAj}cXEgbPnbgp6lg8UfL;2iMS265eCwV%_;$J(+X9b+vOWC4|zS}KE zjvTKMf!3xMW*(o3FZg@$7s=F>Ll~nfBk8fOhMZN9%9CT*H;b*(reGNMska8eg8W_n zrmxT8i&W3U<vb36*kW`VHcdUJnh&QW{)DsEUXJZ$j2M)O$FQcYdJUD`K|6cmF`y>c zixB-%lvmP-fGS?8SQ14{A@;Yp<z<p(lBRZ0ak1t_fOpFLO#v6CA(tlcCE}SPEQOp2 z<64c45lh|*%93ar^iyw$i6ch|l9>kklv|!|5{POrk>pB#r?h5P^$`D7o@1|(R0rLd z;Qeos3VAk<OBz%Jek{aXg040GWh2{6H7?)-S4$L>M7vO_!wt%)>r|RDF;lkV2NmXj zGmfB&;7z0!VwMpdHT{bTf5!@uQe(}xSB6VOHAPfjeX5$~T30Z@h$!t_w0!+ny;DnN z?8$`=@!$dj+B8>Oc*KjSKOo^GKOV(GQiGRIN&BF)Os{qLWbP7fVmDo#at`0yplt$z z&g{x{6>m&+dOKbpkoT2Kky4^>!|S4N+)O0CH=g{dBv=f)>y;#W<LB(Obk!RJIqzny z#)oLe3@WLo;jN<R<idraD0ud4UFh(gv2_Za_{v8A&{L0@C){IHa}Q&d1IERYU{4B_ z?|497i?SGZ?Q&YF)rT$(v#UhDR3v+VW#*oQjbP$_5Dw0XATdEwq6G%&FbC;804)1; zgoH94gvn&h*^}a2%(bhqmsj&W4OqO0BSLE=JKDn(kY1m!)HyA0iHb<~IhgpSy=-M* z<lz?5bud&Lm1{Ny+k9=Dv4<|e&T<tZ{6T@;3=##S!YPm$(J<grfXIR_3WIh3w%T?b zGPK<?42Ez3X$x<nd|kCKmcBAPAOuD3kQ1sUT}{nkAD>rUhj&xf>t{W^AUDiIO}V{p zf!0f1jaqBvPH`O8Bu7l{jnzg*gXbnQaNfoZw`QBu%4+oGWQs4d!8&Pk*Ha8vlPuq5 z%XDrr*BSya-Cu{!b@OXWi#MGeSRYB62LsaO)(6(GIU14lR(w`%o^V;-32Eikt%Y+* zW>9#wc;cSS<Pg!rbC2ACh42SaTc>LQa*aX@$N2SA0By|w?)~}Zxw&uVgHL8u^Uc{Q zSm(#7N#b_nlTHgwMTc<4b+tAt4@|`?w&oOmQ7N!CbAEGL`o<i)`W;8;p{Uc%QXDJk zmRVlBq_O`SJG<2iXDzp@G0)<D=Ogcp_RM5orbJ1Meyl9CG)qRMZI8c#VH>DKL#p}4 zfPv5rVc&mqm-ootY-d&xV?Exx7*a7Jwu<FHbP1Jw+WVn4rBW0X5?h9h*QALiWOOV; zQc8%tm472$UFHlyPetUYko_nHfxD*ktt*`vj9<SR`NGuz6)a>GhD~8OjCkqExd(OA zLk8u{)iT${R{@E$_F;seta{WB-d|HBwlhi<fZ+;7&X2J9&8D4Un{_^4*LR-&M|vt# zpDUXcQCdn-PS2ubcns-jyN5RGXE}23eJ$~X>nm^mifR&ub`v4>14v`-Z-1V)doxZA zzYq~gQ}b(i@;OHifSxf-Be!U8vEdLe8-XZp%p_;i{<!<b<fri%mL~qv-cx+<31zn& zin3;7Auy$>>9*H_4l#52X<zG$It3HZb&;M2-&rOLg<GAgK7m0tADCXvgz~%woG2jd zHckKwo|7RbzWZf}j64|b-roGLsi^Y$7TrxsEKxA!=aJuJe1RF<p*MdGu)?+($TDmB z16$&eb!Pob^e1%&X`H{qGe(Cg{#=&^yb*xlc-}R0c2Uul5YqnHaC3HYQ4;6yDe`-+ zosmrnpefIFv5{MYVbYP%rSTSt#)C<k&o?U*__o2`T|UXZb&-|HNM0QyOk<$yp$|1& z%`O(dZxW`?()?-fa>G(8<|>;vd;X18d7=5b&sAN}=CW02gzb5u6c%&4M21?er6!~m zyKtkZz<^D_5q*-Xv6eDx7K?uI@z6xSNRaWn3D~Z{1Ke8I^1+dvkyY91YFJNLS}<x_ z-bX}z>Oa<sO5ESm<QLzhvLUQ6*p@l282A9WneDS3<pKKOh5+UHTVL~zf&|3H0#Tha z-S|lBQeyPy9tHt&f4N)b-hOw_XN!9Yi*1;wsdTm*ftF*^@Fh-kQ4DRSL3#jj06OEB zxD{9EDdXAN0^VO6u5h`$SnmzE%XOOU-JkZlum)Y#xH1m(4>ucCIDA86Pnub?U9+rO zN%Q?B7*FtMmbH4Z9qXy2l)vr8Wlr4V*Z%p>wY2Y!YM7SByatm^Gisy1w73e5Q!EFb zu5}@IK>VK>8h*St-||k;rZFFveiui6-ICuup_!;=FgHB8i(gAtu>L0Yb;giIUs>A3 zZ0t{4TWza7js(f#=KwnJ90ZX3dAp_>mW@qUV*l4~I?%j3R_eH5HuG=GnC79r6HGFZ zs-#U#A?3?W=N*;eWq&3*JicvhMr$fRf#hTPc&}zOKsaZO6Oe$kUyJtS)FzY}h5D&X zg=NM4<8r7E(9b6m6yaCK)k~F+Hj#S%a{dm&x{fgoju+oq9CTO@%n8Be#j^qnp-nsL z8r-M)yS%a+{`6>g+sti%TjMOX^d{Sd@IY5T0t6U|!~*h>zW@^~qTN<a!kZG}o<zr- zZ^0p7h_{mKwRd6Lw_D|<i$9KXmJ#Sym)K-P7?Pf#BTIHOHQ3t6S71SG8y28&_PUK* ztsFX~2?1tXxuz%1I_a9LY@xmOsiNkTz;w{VL$JdUlfWXjE&Xrc<EEEXr|cnFHayWk z71+jW7&sG}1wn@-r)O^n)#@%PE<MHjlI1=cMg;(@6HCrTg@#nEpOYD`dTa4IUCplq z2{#k-9I*qg?1%s;zsaA}{__^1@(+RabfE|&H}aUp+Glq~B#xq861+evVaXiyYdp>f z<bj1!1fB*{(1iptPaI*Te!EO9Q^{H)z);k&P?yMgI`6Ni>2Gc?{hK!d3)>+MAw>8j z0K{0880C4CxLJ<Yo(G|jzbOxItq49sb_yET=$Byba+eL^yR3<Q*bm%Yt(dmUKsL!? zce;M;z3!vNYS9^G)puWH%I_vW(2UEE<X7i;K>a(M^n7M^zFAxJMR2WpRkzI><&3_6 zEJo$DO~VXTsM*fY$2eGpGgrN&X>S#*ui4`J65grPOnqaIL2k_=0jqjh5*QJ3c#~qS zj;5PoSarcK6Y_$4%$L32s0(~yV3hrt)|pwZzTm62MY7<4-(Z%noa7htpES@ODZ~DH zV0W9^qIFWOaH>hZa?PsT2w=-@{ZXC05$U`w%2Pg`yEM8b4sH(aF-seEOMZz~RS|H+ zbRhcsRH)oRwCXHaG_0u-d$hVfesAV<!6>vN!c=DM9J6Fp5w=mibiv!sOMU|MIFs+j z8r;+o=cg*!OjQJC83t&O1^nyv*OCY{sN7u$S&WYyh^ImjyT37_#XNOA*$lT&Anx(Q z7;4IijR{PY5JB-)jQ)%>^3Lba4U*~PeIQ)NVcv!+F-IMpB^sbMm~7Zi`m`v?s40b} z%eC<FLFVlOK%gP-T2y6{zsplIHr|6MDX@AYogKj(;vHs(4r7&QqW$_z%`P6%Wok_v zWZPt_bP6qCx*t8>a89z`d9_y3TW_U_G90Fikk)cGk%;_ukK7=O-Kchjz9UhREaKpZ zZm|42D)6StCvCukL`Lc-Xz^h{U%TG#+?ox8?_x`nwrU`(&a0POT3q7$nK@q>9^hm+ z3bN?4Mh}=i?sET|T-ysbh05UCT0rEJlx!;Vd6~+FG|al^Mnp&X-Q68w6#P<y7`_xA zC+Zu&h(&hro_tZu!ZFGxrQu*Qv`y#HinmzUe(@Nh0!vyZp^CefcoUNMx)eCq)*f=W zAg`BK{tR;p%DVmZLve1wVP%^prY3Y4A0NF;a{5kR+0-8m!BjE#c#xoE8?P_f7`=77 zMK=(%_h0?#(VA=Vq<mOrzj^qjn|zHN<R*I-Kzn9A;YaF3sm6;teTz-=2qo3$aIN*e znXvf!8oSI9AVuEFq^TMR(5hnlew3fLDcV8=>naC7Bf<xLG+M@KPR7&Pca0P*LoeWK ze%-oS-e#9#<h?@uIQ%!-9dyMsYKC8+9lH8|h}Up?a(c)06pz3y_DVuVMsg4Jx89|M zk6?H{*`P{!13S9}uxrvSl@9xUgf>OtB+IMzG9->bcy7cJFd_N4%uPU~&rG>m&V`m| zGShuKo#3=GuvDJq`0w|K1Hp*Fq)KTM7IB_KU-fYUT8*;3Nh|MCH8!GkE-y-2nAeeC zuB~~^hz>}WTH##b)O=P4GU-#tD)`<pJzhIItjOhDDEK2lWxGqGEdny`ModYSSi~x? z?i`^8z0_bZz$G$>_|?lw7i`QWcm2tQQdTk9hmbP=Z;-Fy&Wbs#p*-G_HpSnTP7u$A z!Nku^EM`(3J-FPFC=fywZ#lPZa@FiqXrn>b)fpD!-BLuujLR*cb~^DO6Rts{VV0Rb zajo*+%$UENFkz8ar7T+ZxQ{<Mwqzi;jJ~8=XIf%+7>Z!a9<>L|HV{XJVOM&P!PmFl zf)gq80<^HeuYmR6ZjL+Qz2JeNZFdYP29_l=$jW_&j_2Vb{ra3lWKy@}KzFD$>Qh7- z4hD<1LaAAVM${<(`Wq>-<_Z6c%3V7fQycPBdM4DYDLT;;kqy+GIlr$OH8Y{5=5VR* zU|fhNdmGb$20fvoTn%?u<6Jajd-_+bHaiz`cK^E(fiYRIlt^Vc(Gt==RbBGO?d5*T z)r8;HzT6U$^+QMCjs<}O7K+!md|XAr&i2&(=)jW8xz>QHz2FDw6a`y$0G=&Wp^RuJ zTrrW40)eE(Jh5V~F8`33CycSOlZnaMOXFYOxoBoeWSv|)O!*}p;Hl)bx(DnKIv9?W z(HcJz7caMVgR6L);Vp{YAnE?^HA%`vS(YOD>^;%3wnj(ZcdRSyYtzkYRJ2%r(_{=t zd>%s#!958<AxEkA>1Dj)c_;IwnT&L=Gi}*LkR-eBbSD&aQ;|s$fYcY`Ls!$Pf3Tl6 zo(7VyTEkIOQl;PCO8S4*vW1(1nrR~hb-l%~_yVU1wgOC$$;yKO!|s;#gaQhWr6TBq z<9SDUvTvnA4@osyuU%w^w8+xtuB8u^8tpr5RFFhLKjmtU23JKUzR1yeiOcV`6a<g0 z;}QvAL<ZPREkEb!uU_QUvX4x7FWyE^=$vB2C3ozeZM1jO{j~hLpXbmNk=`*v0Sd%L z?cUw`vYn)Xywn`SNFg#d31%+s$8wWl`;2KxL+5rL|LlkZemI^}@<gnH56)IOi+VtV z@BzJ0(xp>_HLvJ*q4r(6@vWxnH#ANjL_g2&Uz~*`Cl!bsO(<aH_G+m(?sgAb!y0W3 z4h?Vu%2x`y`&!#a#xf!axl4Ba@%`j^dN^Or%oB@(0vg~c>R(#LK55mp_Jw(Olo#;U z_L`*8^>V+_kvw0IUnoKfz<(~7y@*MhP%D%2O^<IYib@ZnP7{^WC?p4`=E@+_@%*_i zW(>aY5IJ>aza_2xhoI<q&?y#*Z!JRlf#BaT2dwSVzV=_+Onw(2^1qsFQ*<`zJL>$I z^S)C`b~l(t$@PP{+HI*cg{1MtJvY}$MLn$(voD((FTscDl=tKW{(x>jEI;k(efiTu zcKf?YAp<lZ8ESUSWNwLXYxP22XF_D+b|eUPYfH5@O_YqgDyQZose#WA|3iUnTVNt! z@9y`0#8C!@b*PNrmb}P(iT_b2z!Gi@%EhinwihHbRQTu}_*Rh#3fGZe+;880{K%dU zo%8=0r%_+viPCbquTaeF6e{IweLXJWjPyyp;n#Jde?P>OAN&-cInydbW9suirBh}0 zwi~&HMQ4x`QAQ(a_{n?amMTwO4@|SrtKiJMunFm>+`S}<RG}5+I#X}goUvJ8P*d-& zir8<>8L~XR3y<;58-;PseO@pB4^7QBud>I4!4W+~^S_Y$jCEb+1mn0QD#xyHip#lY zzrbY@W!C&_CKuD)eYljYA0S89K26udtLJbu>`=%!sl5_}og_3eX13oVi)EX1|0Ud1 zW5>xQb)Sc$M><}&wQM_|8v7xGI2PSZj}j340Kv-Bfq#Txb2xUI^5e~Yhkt%D>(3RI zZEvolTGTw;F|=d}`j~3V0P;DoeM=2tCa`hg=^vS#c_1XU<ExbpUorw!UCMR)yf^G` z<q#zP*;0V}>m^iM=G{19z}9Rn+x%oGbxeFbW*woCfn+gncCW_OpQVl6M|4c8kelL` zwIm_N?AWinejnk9(<R+_Op6GTuXNb*{8S(%OPRXw=2qm*lD7Akk;v3sumubofr{uj zHxoYTynP0mF<XerGkmG5vHxB3$HSgUWL!|tU`POYm0|@1Hq_?ivb?W$c)K;pf!vd( zcgPyX7nx2fv1w!CsYsr4Bj;GmOU6B?OJgX}pNugQzx@DgV|c0g;XM6xzTZ)!&bk0N zg+*YKYc=-j<r&A<$%-|dyja%$%3mXIf9d%SrujlMUL(!Zj|t+*oGycy>qfo6A*p-X zcbdTX%9|X8ZX++4H4$S^E>MO0DePUzNW$lFD>{>9Bj)~2(pXv2p7${&TLCs{v61`R zoR4l@vBB`Yr~IFGadKFhJNdn-;nQ^nVc+UeTuAZLFB+YKV0oxp&7(&_>vtHt)Iw6Q z-5uGupUN;2o@)HfJRwu6tf{d39U6WvF@3Qg_%`-X7x;Im@C`IyOkqWZNa}<3_5p;R z?AFZN{ZQUUqmSOT-y95o3v&z1oJ4QXHV6JN%!<kSo%(cEW+>t;@pmxH_T3jPN6lY* zPRw^^Kv_n)jx}?7D@oo2=v@S-pwSaOF45iTqLXs{eStq`GiTT}K}n*kWFC%PxO??2 z`5fu4VKOA5%D?eNeuyV}JmBEiS8Knx^?@Mhfn*>5l;Z;DWLz>GZDjB6E_P2Np)MOh z^J@<8@Yxjjhn2T&#`~C`12HA-iMuf+Vq<Mf%~66nU=s+sMOf^c9|n37lVataKc`%q zWK8b{o0;T8nI&qT=dt`U0WCer?~xW!j6oPQt!_Y8Ffg`tKlAHlSypH0YHtZQC*O2G zF}<b#>`YbyW?rBap^p9c8JHy1Q)M|5`m}}qoeu8}O9ae8*_7nZHZu^!wq{i(c$Cxm z?^mmzOoIyU1UL1<k7XyUdn$F)JQ*#^<_<>Tphu>Ay1c1LdxaQIO-0_{{=Apfwi%U< zip)bARXJiJc?MSMHxDVe_Nhltc>bkd4GiAeRFRofmwVHdex&CF0Nn2XasgDa&C=8y zhta*bNt8Xs=*o4XiF-;RO?OV%-e}<jgq$XVxs2qW?0NTw&5Pz58jqJ&xuK@rO{#x> zV=IB8h<lO@zU;IDR@#s~zwu(8#6j9bmz`E+s`W&q^A~EV2I+gm(;=XS)@Qd<757tc zn2e$9+U=Lp3oZ3w6rwl$s(}%T3QJ-7*vz!{er+e-Fhp2R=#(1n3DS+Y3B8eCLZg*O zlYgG*8XU@ult_rU3K2x3%aQ-S7BVmQ4o1YYF;;XtE%sxGt1-PkCjhN6<MWG)3AS|! zk-X$uhR0~Y8JNS&!?+mutz03i4(zEgU-{dzE-0|J#0TBh@`oixW{P^+uUXin9=ESZ z8k0CmUCV+-9N*|8C006W2bkW_I?YzUzj)l;bHQGfM!881VTIPYLk9IXtxm$BQN-)t z<!Q{CJdEzE#qhuUWAEi|VR=-)n2X#vjJXEWAK2WohVjX72tH#}L9@g=`KDQ1`v?t- z%@)lqro+VMDmF#|3bv&54K{su&qzT+HeDJf9ui$%KAow}0S=^lYk@)5t#84ZdV}90 z+S0gSRk#s%2qik)fh{+A^a`1DuH+>rSw+21sfR@%a;0dS=6NokQ$8dSqyfJ%ndlm= zUidd%R66vEudnu^D8>s6rv$5(%*lx=3|AG^($`h2R2ErU5ZQ>$QRm12SfA2|R)0gT zp<RMTpib-$hiHFQG!5aY8j?Hjek6)i=zm+LE~vpjH+*Y)hXpA{!ychH07lBai%J}8 z%AXA282O|jY1o*Wu=DXE2;oTz4w3sr|G)sRX@C3QqB+xed(k>y1<6??=AkMULxD4y z<5oIL7r96R0ww@gCvF05A9Hp*VGtQvPi?k){H0IJ^PpWcu3PZ9^JoPh0BHws;^DEp zk~=ZXO;9v>f>0R~@jFO<rW%w)4;=GBLyE+&-HIAbY0X4ZOe=oB<>SsJG&p(iI#sOc z#@GpT&qCD;{HbL#>3LUI2^o{1D@`uUVBfkBF*)L_X?g?^?q1apjiwrW@#R7V$Uh}3 zdDCNYVjz&spIFG|ibHzh;dJ(*m;X(CarH6};P5v%0qv3<o?_r`g`2ELhZ!{bO{rO% zDUz!<9wc4!mz(b9_%XKK*(pNm3Gdi|;ro%`Q}~stR-go+1m1xn=pBC#C}^yuO-OQ8 z;O*(({|UO<HAvEGs6;zd0(!Ad2$O5gwnHg}ay>b0lBokY9jyxsYrc}mnrjO0$vmKq z)B%J<^^7mgueN|3(>>6M=ew&vn!EaK%dHn(!W^i5&5gH?xlAKJ0O?`K6F(QyO+>I^ zBDq{ip>MTe*5wYgTsez%%$*%x)*&9=fgmD>gi9J>GAlO}1MHwSRjxBzw!IXt3jXx- z9A5YV`Y8?I6u41ggqG7$FnG-0oc2&<m^D4kU6lRu(YB$sA_2A$HXz=)AslO77%Qa8 z%BEG`c#s7i^tR~M?yKON6KLjT5Nl$83rXI^+beN^y5gs(L?~S+5CxPwuMZ^G9;D+G z5ok75+{i#yXaNC0&rT9f6#B$O=)}YB&97=rO3IT0q6RaU4ch5Ymjf#(2F|KJAi&&d zC#W@vqRqoNqO}w{(XI;mknS0<yK+HSR^@B`82mUf`;o@T{yTpWNPJBN?ltw3Nd{1$ zKq>hh&^-aq8JUylvk7sOmfGXM1P~~{ylRs@y(BCF6f#|>=7xarq|rMWJz^I<9!c?{ zgBHCT8W|rY@cgYuXZXjo3KKYNTL@7d_*Q7^{%~QS7{r>Uhhad7!a&&%Qh@@~kk{dl zQ(ciX<n^0mB$PAC)=7S44bVofqFDkpiZIsL6fRwF;Q_~9#!hjacUGH%aBCj60}R2* z(l4P+NosX|=}yN;Ml($aSg3#jviGmvj*FRecalvNg%P8&Jn5K_blhX-ap^hchda*W zOTTM-gvFi&Xo(~a;j;YkFu&6Cys!uZR!v`Wc`}=j@)LQm=th~wp+pgrZFy!DPkb&) zGyy@fJ}oJ=!d-ojCmx~i=JLD6ULf~OBk%&>(={y-YM%mX9ide?27gs?C{M2iz&68f zvYVVt`54@OTZ%=p?A&MG#O3cX1IQofJ&sc5#~yQ+KI`XdH)`y*Gg!M}?C7HjV%fZ6 zmz+c#|Msg2ZkxmeQo7DYAvg2epKuxrJ}Ft)FPvz-n-V5K-7T4HJWH|*WJH+fPI{17 z76+z;kjzDjCcZY_c@m{Lb$c=9)Ah``V;>Ph%+L9~&R&d-t<l`qE&D5BB0v4FR1~JN zE7o^TU>et^w=K{6u?1xEflD<YGfBPiCBA<{siZIIuGL8!4WEUtnUY&0k>7?E;-|Tz zpWs~lrj{lNYRt=J=!3)3n*iKsRQCI4V@c>$k>-bc<q}|jPk!iNL@_mz*<zrv0XZBI z6ST}fB50QKDl`dvh~E&%V!})V*W|i5J3aJnJaY-M|0;p$@7X!#s+EwcV|)xeuggE# z_^s#Z%BGL!NQ+uGg5FE#38cSC{KD1+<aqQTrJYgQ5cNii*7Q9#R*R3?K@Vhe?9VX+ zp49&_QtWh>%xqxZmp(=RleHr0m3crrcTH6?UQ8Az$Aae(iu)oN6Oe~k0!GhZ&2bu` zxLyMXQ^k034W!DAxN{#BGRSN$LVlNx(E6oE%(f^S{oJAQ@0zdA*Za6@7trLcv~nhq zQN+*UC6(^&f(Nt$*ECwWAh(U_7uhS_JHF)*-*?TK>Gs7`!F2POUN_EhaH~!~{_(xY z3?7?<@S9^Aq%_?949Y7razj){k`BkaZpwVYOat-t==N@;TMIfCTxt@0x_26n^4puZ zTZw%|-2#&#==b8wi#`@y4qBu+Ya#H?wx-GQt9s2}u9Eqk)IxJY7gL&ke%8i{!s4}5 z(fiQ?``>Qs$>C)Nev5f=3eQSSFvqt-kL&qO$8E0RQbnIX8~qVcyL&ZfEf*_kk?afF zmEj!ra8vUalR8}Ti^v4se)|e2LhwfB8d2v?Qi(ZeV(6D#^PjAECOBIi8u>g{v(=0d zHzVRp9AA8NhPF56THh5d`TwFJ0ef&1aoMh`3mm8b%)lw}pTj31lCeEIFEipBBGOg+ z<{fKE;H*pjg!U3Gs(ctUl{#Zm{30STuLLY2U4ACyGBG!&ytFmy*k3#FEF6u|s?Sr- zI6e0K>6-3k{<-lh7S1z5{C3_k<l{_Dsw}v?i}~vcJ<iaxKovQ>cn`kRYpn2OIeo4P z(YAOmM~PY(?8-V1fgT{@KYLFZzl{+V@@R3|5pmCz6{#`K)qb`vstyBUgT7$VR1!7V zy^#^&Av%m3t>Go<D-j-iKAq{pjOa0ct9Aqk$M#70ccQCZB1kjPRW&8G2YFkspKVJ# zvU8<cn$Iu33N?E^9FtDR-2eIL2gIdP7L@fUfZU0M*2<2Mg#6ECji3x_$I&G!sSc`# zBQl#4U8Z-CQi-~k_$;whAEa-I<6%j2cEmw!2h<Sw$M!G`X*DM7vX7A;_8*vbI%OQB zn<yA6`W_fDn&g;PCa8*OzOsYI$3%F{JO4gY>%fd9=+he1W_>r4Yv*cv5DA=|ePta{ yOxHHeBX^#>+dUB$xY2NO9@u`&9yR^=Ktv+iRQ9F6AMY_)7Eo8#hI~`BdHX+-x^6E3 literal 0 HcmV?d00001 diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..b1795da --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,71 @@ +.. vim: set fileencoding=utf-8 : +.. Andre Anjos <andre.anjos@idiap.ch> +.. Thu 3 Apr 13:47:28 2014 CEST +.. +.. Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland + +======================================================================= + Python Bindings to the Flandmark Keypoint Localizer for Frontal Faces +======================================================================= + +.. todolist:: + + +This package is a simple Python wrapper to the (rather quick) open-source +facial landmark detector `Flandmark`_, **version 1.0.7** (or the github state +as of 10/february/2013). If you use this package, the author asks you to cite +the following paper:: + + @inproceedings{Uricar-Franc-Hlavac-VISAPP-2012, + author = {U{\v{r}}i{\v{c}}{\'{a}}{\v{r}}, Michal and Franc, Vojt{\v{e}}ch and Hlav{\'{a}}{\v{c}}, V{\'{a}}clav}, + title = {Detector of Facial Landmarks Learned by the Structured Output {SVM}}, + year = {2012}, + pages = {547-556}, + booktitle = {VISAPP '12: Proceedings of the 7th International Conference on Computer Vision Theory and Applications}, + editor = {Csurka, Gabriela and Braz, Jos{\'{e}}}, + publisher = {SciTePress --- Science and Technology Publications}, + address = {Portugal}, + volume = {1}, + isbn = {978-989-8565-03-7}, + book_pages = {747}, + month = {February}, + day = {24-26}, + venue = {Rome, Italy}, + keywords = {Facial Landmark Detection, Structured Output Classification, Support Vector Machines, Deformable Part Models}, + prestige = {important}, + authorship = {50-40-10}, + status = {published}, + project = {FP7-ICT-247525 HUMAVIPS, PERG04-GA-2008-239455 SEMISOL, Czech Ministry of Education project 1M0567}, + www = {http://www.visapp.visigrapp.org}, + } + +You should also cite `Bob`_, as a core framework, in which these bindings are +based on:: + + @inproceedings{Anjos_ACMMM_2012, + author = {A. Anjos AND L. El Shafey AND R. Wallace AND M. G\"unther AND C. McCool AND S. Marcel}, + title = {Bob: a free signal processing and machine learning toolbox for researchers}, + year = {2012}, + month = oct, + booktitle = {20th ACM Conference on Multimedia Systems (ACMMM), Nara, Japan}, + publisher = {ACM Press}, + url = {http://publications.idiap.ch/downloads/papers/2012/Anjos_Bob_ACMMM12.pdf}, + } + +Documentation +------------- + +.. toctree:: + :maxdepth: 2 + + guide + py_api + +Indices and tables +------------------ + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +.. include:: links.rst diff --git a/doc/links.rst b/doc/links.rst new file mode 100644 index 0000000..cc43362 --- /dev/null +++ b/doc/links.rst @@ -0,0 +1,82 @@ +.. vim: set fileencoding=utf-8 : +.. Andre Anjos <andre.anjos@idiap.ch> +.. Tue 20 Mar 2012 08:57:32 CET +.. +.. Copyright (C) 2011-2014 Idiap Research Institute, Martigny, Switzerland + +.. This file contains all links we use for documentation in a centralized place + +.. Place here references to all citations in lower case + +.. _argparse: http://code.google.com/p/argparse/ +.. _blitz++: http://www.oonumerics.org/blitz +.. _bob's idiap guide: http://github.com/idiap/bob/wiki/Using-Bob-at-Idiap +.. _bob's website: https://www.idiap.ch/software/bob +.. _bob: https://www.idiap.ch/software/bob +.. _boost: http://www.boost.org +.. _buildbot: http://trac.buildbot.net +.. _buildout: http://pypi.python.org/pypi/zc.buildout/ +.. _c++: http://www2.research.att.com/~bs/C++.html +.. _cmake: http://www.cmake.org +.. _doxygen: http://www.doxygen.org +.. _dvipng: http://savannah.nongnu.org/projects/dvipng/ +.. _ffmpeg: http://ffmpeg.org +.. _libav: http://libav.org +.. _fftw: http://www.fftw.org/ +.. _fink: http://www.finkproject.org +.. _git: http://git-scm.com/ +.. _github: http://github.com/ +.. _google perftools: http://code.google.com/p/google-perftools +.. _hdf5: http://www.hdfgroup.org/HDF5 +.. _idiap: http://www.idiap.ch +.. _ipython: http://ipython.scipy.org +.. _lapack: http://www.netlib.org/lapack +.. _latex: http://www.latex-project.org/ +.. _libjpeg: http://libjpeg.sourceforge.net/ +.. _libnetpbm: http://netpbm.sourceforge.net/doc/libnetpbm.html +.. _libpng: http://libpng.org/pub/png/libpng.html +.. _libsvm: http://www.csie.ntu.edu.tw/~cjlin/libsvm/ +.. _libtiff: http://www.remotesensing.org/libtiff/ +.. _giflib: http://giflib.sourceforge.net/ +.. _macports installation instructions: http://www.macports.org/install.php +.. _macports: http://www.macports.org +.. _matio: http://matio.sourceforge.net +.. _matlab: http://www.mathworks.ch/products/matlab/ +.. _matplotlib: http://matplotlib.sourceforge.net +.. _numpy: http://numpy.scipy.org +.. _nose: http://nose.readthedocs.org +.. _opencv: http://opencv.org/ +.. _pil: http://www.pythonware.com/products/pil/ +.. _pillow: https://pypi.python.org/pypi/Pillow/ +.. _python: http://www.python.org +.. _pypi: http://pypi.python.org +.. _qt4: http://qt.nokia.com/ +.. _satellite packages: https://github.com/idiap/bob/wiki/Satellite-Packages +.. _scipy: http://www.scipy.org +.. _setuptools: http://trac.edgewall.org/wiki/setuptools +.. _sphinx: http://sphinx.pocoo.org +.. _sqlalchemy: http://www.sqlalchemy.org/ +.. _sqlite: http://www.sqlite.org/ +.. _submit a new bug report: https://github.com/idiap/bob/issues +.. _torch 3 vision: http://torch3vision.idiap.ch +.. _torch 3: http://www.torch.ch +.. _torch 5: http://torch5.sourceforge.net +.. _torch: https://github.com/andresy/torch +.. _vlfeat launchpad webpage: https://launchpad.net/~gezakovacs/+archive/vlfeat +.. _vlfeat: http://www.vlfeat.org/ +.. _flandmark: http://cmp.felk.cvut.cz/~uricamic/flandmark/index.php + +.. Place here references to licenses + +.. _apache-2.0: http://www.opensource.org/licenses/Apache-2.0 +.. _artistic-2.0: http://www.opensource.org/licenses/Artistic-2.0 +.. _bsd-2-clause: http://www.opensource.org/licenses/BSD-2-Clause +.. _bsd-3-clause: http://www.opensource.org/licenses/BSD-3-Clause +.. _bsl-1.0: http://www.opensource.org/licenses/BSL-1.0 +.. _gpl-2.0: http://www.opensource.org/licenses/GPL-2.0 +.. _gpl-3.0: http://www.opensource.org/licenses/GPL-3.0 +.. _hdf5 license: ftp://ftp.hdfgroup.org/HDF5/current/src/unpacked/COPYING +.. _lgpl-2.1: http://www.opensource.org/licenses/LGPL-2.1 +.. _libpng license: http://www.libpng.org/pub/png/src/libpng-LICENSE.txt +.. _mit: http://www.opensource.org/licenses/MIT +.. _python-2.0: http://www.opensource.org/licenses/Python-2.0 diff --git a/doc/plot/show_lena.py b/doc/plot/show_lena.py new file mode 100644 index 0000000..1997264 --- /dev/null +++ b/doc/plot/show_lena.py @@ -0,0 +1,23 @@ +from matplotlib import pyplot +from xbob.ip.flandmark import Flandmark +from xbob.ip.draw import box, cross +from xbob.ip.color import rgb_to_gray + +def get_data(f): + from os.path import join + from pkg_resources import resource_filename + from xbob.io import load + return load(resource_filename('xbob.ip.flandmark', join('data', f))) + +lena = get_data('lena.jpg') +lena_gray = rgb_to_gray(lena) +x, y, width, height = [214, 202, 183, 183] #or from OpenCV +localizer = Flandmark() +keypoints = localizer.locate(lena_gray, y, x, height, width) + +# draw the keypoints and bounding box +box(lena, (y, x), (height, width), (255, 0, 0)) # red bounding box +for k in keypoints: + cross(lena, k.astype(int), 5, (255, 255, 0)) # yellow key points + +pyplot.imshow(lena.transpose(1, 2, 0)) diff --git a/doc/py_api.rst b/doc/py_api.rst new file mode 100644 index 0000000..d37bb04 --- /dev/null +++ b/doc/py_api.rst @@ -0,0 +1,13 @@ +.. vim: set fileencoding=utf-8 : +.. Andre Anjos <andre.dos.anjos@gmail.com> +.. Sat 16 Nov 20:52:58 2013 + +============ + Python API +============ + +This section includes information for using the pure Python API of +``xbob.ip.flandmark``. + +.. automodule:: xbob.ip.flandmark + diff --git a/setup.py b/setup.py index d4e837e..ece118d 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,8 @@ setup( 'xbob.blitz', 'xbob.io', #for tests 'xbob.ip.color', #for tests + 'xbob.ip.draw', #for doc generation + 'matplotlib', #for doc generation ], namespace_packages=[ @@ -43,12 +45,6 @@ setup( "xbob.ip", ], - entry_points = { - 'console_scripts': [ - 'xbob_flandmark.py = xbob.flandmark.script.annotate:main', - ], - }, - ext_modules=[ Extension("xbob.ip.flandmark.version", [ diff --git a/xbob/ip/flandmark/flandmark.cpp b/xbob/ip/flandmark/flandmark.cpp index 2b47b3c..8d0fadc 100644 --- a/xbob/ip/flandmark/flandmark.cpp +++ b/xbob/ip/flandmark/flandmark.cpp @@ -174,7 +174,9 @@ static auto s_call = xbob::extension::FunctionDoc( "\n" "0. Face center\n" "1. Canthus-rl (inner corner of the right eye).\n" + "\n" " .. note::\n" + " \n" " The \"right eye\" means the right eye at the face w.r.t. the person " "on the image. That is the left eye in the image, from the viewer's " "perspective.\n" diff --git a/xbob/ip/flandmark/script/__init__.py b/xbob/ip/flandmark/script/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/xbob/ip/flandmark/script/annotate.py b/xbob/ip/flandmark/script/annotate.py deleted file mode 100644 index 1963e52..0000000 --- a/xbob/ip/flandmark/script/annotate.py +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -# Andre Anjos <andre.anjos@idiap.ch> -# Fri 21 Sep 2012 10:43:12 CEST - -"""Annotates videos, dumps annotations as text files. - -The text files will contain one line per annotated frame. Each line contains a -single detected face and associated landmarks. Only the biggest detection found -on the frame is considered. The entries in each line are: - -[0] - The frame number (starting from 0) - -[1:5] 4 items - The bounding-box coordinates as detected by OpenCV (x, y, width, height) - -[5:] 8 pairs - Each pair corresponds to a keypoint in the order defined by the model: - - [5:7] - Face center (as defined by the OpenCV detected bounding box) - - [7:9] - Canthus-rl (inner corner of the right eye). Note: The "right eye" means - the right eye at face w.r.t. itself - that is the left eye in the image. - - [9:11] - Canthus-lr (inner corder of the left eye) - - [11:13] - Mouth-corner-r (right corner of the mouth) - - [13:15] - Mouth-corner-l (left corner of the mouth) - - [15:17] - Canthus-rr (outer corner of the right eye) - - [17:19] - Canthus-ll (outer corner of the left eye) - - [19:21] - Nose - -If no faces are found on the frame, the line will only display an invalid -bounding box with zeros, e.g. `327 0 0 0 0`. No landmarks will be displayed in -this case. -""" - -import os -import sys -import bob -from .. import Localizer -import pkg_resources - -def get_biggest(dets): - """Returns the biggest detection found""" - retval = dets[0] - for d in dets[1:]: - if retval['bbox'][2] < d['bbox'][2]: retval = d - return retval - -def main(): - - import argparse - - parser = argparse.ArgumentParser(description=__doc__, - formatter_class=argparse.RawDescriptionHelpFormatter) - - parser.add_argument('video', metavar='VIDEO', type=str, - help="Video file to load") - - parser.add_argument('output', metavar='OUTPUT', type=str, - default=None, nargs='?', - help="If you prefer the output diverged to a file, enter this argument") - - parser.add_argument('-v', '--verbose', default=False, action='store_true', - help="Increases the output verbosity level") - - from ..version import __version__ - name = os.path.basename(os.path.splitext(sys.argv[0])[0]) - parser.add_argument('-V', '--version', action='version', - version='Automatic Video Keypoint Annotation Tool v%s (%s)' % (__version__, name)) - - args = parser.parse_args() - - if not os.path.exists(args.video): - parser.error("Input video file '%s' cannot be read" % args.video) - - output = sys.stdout - - if args.output is not None: - dirname = os.path.dirname(args.output) - - if dirname and not os.path.exists(dirname): - try: - os.makedirs(dirname) - except OSError as exc: - import errno - if exc.errno == errno.EEXIST: pass - else: raise - - output = open(args.output, 'wt') - - op = Localizer() - v = bob.io.VideoReader(args.video) - if args.verbose and args.output is not None: - print "Locating faces in %d frames" % len(v), - for k, frame in enumerate(v): - dets = op(frame) - if dets: - biggest = get_biggest(dets) - bbox = biggest['bbox'] - landmarks = biggest['landmark'] - output.write("%d %d %d %d %d " % ((k,) + bbox)) - lstr = " ".join("%d %d" % (round(p[1]), round(p[0])) for p in landmarks) - output.write(lstr + "\n") - if args.verbose and args.output is not None: - sys.stdout.write('.') - sys.stdout.flush() - - else: - output.write("%d 0 0 0 0\n" % k) - if args.verbose and args.output is not None: - sys.stdout.write('x') - sys.stdout.flush() - - if args.verbose and args.output is not None: - sys.stdout.write('\n') - sys.stdout.flush() -- GitLab