diff --git a/MANIFEST.in b/MANIFEST.in index 9589412a6da52a4da932b2f4b14c750b5125692e..ecdd145e33e6effefb2af300491e1fab95b74f08 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 e2c010ecf1ccb18d4e6de45a21c427774c5ad783..774b60537d90585a95995291a8f0e601b5c99e34 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 2455a144662d3a46954cd05c2d54091513d3c4aa..6eb656bbee19526c57f9586a591571fa0a3bc79d 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 0000000000000000000000000000000000000000..072d633f897539f0eb6d73517a6337d962e4ee68 --- /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 0000000000000000000000000000000000000000..6c5841b7021065c2feee589877bdb941082157ed --- /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 Binary files /dev/null and b/doc/img/favicon.ico differ diff --git a/doc/img/logo.png b/doc/img/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b9dd573a01019afd1af58a881996930e5212699d Binary files /dev/null and b/doc/img/logo.png differ diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..b1795dac637c59dd9eb65fdb3bb558cd7e213f66 --- /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 0000000000000000000000000000000000000000..cc43362a09c18c3824fd8c87df2faa7d79ecff8b --- /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 0000000000000000000000000000000000000000..19972642201f7a335e410c90909609ba7b067a8a --- /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 0000000000000000000000000000000000000000..d37bb045ae020abe55e01acc4b675f96ddfa09de --- /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 d4e837e2584a6270afa0058517242eef57ff5e3f..ece118d44821cca4833b532fe957f752f20634f3 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 2b47b3cf2698b5d2927c6cb81cc45944d4c2e701..8d0fadc84b41ea78a26a947ae5adc5f47dd17c06 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/xbob/ip/flandmark/script/annotate.py b/xbob/ip/flandmark/script/annotate.py deleted file mode 100644 index 1963e52e239639afab6eae8780e482dfabc0e5a4..0000000000000000000000000000000000000000 --- 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()