├── MANIFEST.in ├── docs ├── .gitignore ├── sphinxext │ ├── __init__.py │ ├── theme_hack.py │ └── ref_prettify.py ├── xref_map.py ├── source │ ├── api │ │ ├── menpodetect │ │ │ ├── detect │ │ │ │ ├── detect.rst │ │ │ │ ├── menpo_image_to_uint8.rst │ │ │ │ └── index.rst │ │ │ ├── dlib │ │ │ │ ├── train_dlib_detector.rst │ │ │ │ ├── load_dlib_frontal_face_detector.rst │ │ │ │ ├── DlibDetector.rst │ │ │ │ └── index.rst │ │ │ └── opencv │ │ │ │ ├── load_opencv_eye_detector.rst │ │ │ │ ├── load_opencv_frontal_face_detector.rst │ │ │ │ ├── load_opencv_profile_face_detector.rst │ │ │ │ ├── OpenCVDetector.rst │ │ │ │ └── index.rst │ │ └── index.rst │ ├── _static │ │ └── rtd_hack.css │ ├── index.rst │ └── conf.py ├── rtd_environment.yml ├── Makefile └── generate_rst.py ├── menpodetect ├── tests │ ├── __init__.py │ ├── test_black.py │ ├── test_dlib.py │ ├── test_opencv.py │ └── test_detector.py ├── __init__.py ├── dlib │ ├── __init__.py │ ├── conversion.py │ ├── train.py │ └── detect.py ├── opencv │ ├── __init__.py │ ├── conversion.py │ └── detect.py ├── _static_version.py ├── paths.py ├── detect.py └── _version.py ├── menpodetect-logo.png ├── .gitattributes ├── conda ├── .coveragerc └── meta.yaml ├── readthedocs.yml ├── AUTHORS.txt ├── .gitignore ├── setup.py ├── .circleci └── config.yml ├── LICENSE.txt └── README.md /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include AUTHORS.txt -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | source/api/generated/* 2 | _build/* 3 | -------------------------------------------------------------------------------- /docs/sphinxext/__init__.py: -------------------------------------------------------------------------------- 1 | from . import ref_prettify 2 | -------------------------------------------------------------------------------- /menpodetect/tests/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = "pts08" 2 | -------------------------------------------------------------------------------- /menpodetect-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/menpo/menpodetect/HEAD/menpodetect-logo.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | menpodetect/_version.py export-subst 2 | menpodetect/_static_version.py export-subst -------------------------------------------------------------------------------- /menpodetect/__init__.py: -------------------------------------------------------------------------------- 1 | from menpodetect.dlib import * 2 | from menpodetect.opencv import * 3 | 4 | from ._version import __version__ 5 | -------------------------------------------------------------------------------- /docs/xref_map.py: -------------------------------------------------------------------------------- 1 | xref_map = { 2 | 'as_vector': ('function', 'menpo.base.Vectorizable.as_vector'), 3 | 'Affine': ('class', 'menpo.transform.Affine'), 4 | } 5 | -------------------------------------------------------------------------------- /docs/sphinxext/theme_hack.py: -------------------------------------------------------------------------------- 1 | def setup(app): 2 | app.add_stylesheet('rtd_hack.css') 3 | 4 | return {'parallel_read_safe': True, 'parallel_write_safe': True} 5 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/detect/detect.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-detect-detect: 2 | 3 | .. currentmodule:: menpodetect.detect 4 | 5 | detect 6 | ====== 7 | .. autofunction:: detect 8 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/dlib/train_dlib_detector.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-dlib-train_dlib_detector: 2 | 3 | .. currentmodule:: menpodetect.dlib 4 | 5 | train_dlib_detector 6 | =================== 7 | .. autofunction:: train_dlib_detector 8 | -------------------------------------------------------------------------------- /conda/.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch=True 3 | source=menpodetect 4 | omit = 5 | */visualize/* 6 | */_version.* 7 | 8 | [report] 9 | exclude_lines = 10 | raise NotImplementedError 11 | 12 | ignore_errors=True 13 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/detect/menpo_image_to_uint8.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-detect-menpo-image-to-uint8: 2 | 3 | .. currentmodule:: menpodetect.detect 4 | 5 | menpo_image_to_uint8 6 | ==================== 7 | .. autofunction:: menpo_image_to_uint8 8 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/opencv/load_opencv_eye_detector.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-opencv-load_opencv_eye_detector: 2 | 3 | .. currentmodule:: menpodetect.opencv 4 | 5 | load_opencv_eye_detector 6 | ======================== 7 | .. autofunction:: load_opencv_eye_detector 8 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/dlib/load_dlib_frontal_face_detector.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-dlib-load_dlib_frontal_face_detector: 2 | 3 | .. currentmodule:: menpodetect.dlib 4 | 5 | load_dlib_frontal_face_detector 6 | =============================== 7 | .. autofunction:: load_dlib_frontal_face_detector 8 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/opencv/load_opencv_frontal_face_detector.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-opencv-load_opencv_frontal_face_detector: 2 | 3 | .. currentmodule:: menpodetect.opencv 4 | 5 | load_opencv_frontal_face_detector 6 | ================================= 7 | .. autofunction:: load_opencv_frontal_face_detector 8 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/opencv/load_opencv_profile_face_detector.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-opencv-load_opencv_profile_face_detector: 2 | 3 | .. currentmodule:: menpodetect.opencv 4 | 5 | load_opencv_profile_face_detector 6 | ================================= 7 | .. autofunction:: load_opencv_profile_face_detector 8 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/dlib/DlibDetector.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-dlib-DlibDetector: 2 | 3 | .. currentmodule:: menpodetect.dlib 4 | 5 | DlibDetector 6 | ============ 7 | .. autoclass:: DlibDetector 8 | :members: 9 | :inherited-members: 10 | :show-inheritance: 11 | 12 | .. automethod:: __call__ 13 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/opencv/OpenCVDetector.rst: -------------------------------------------------------------------------------- 1 | .. _menpodetect-opencv-OpenCVDetector: 2 | 3 | .. currentmodule:: menpodetect.opencv 4 | 5 | OpenCVDetector 6 | ============== 7 | .. autoclass:: OpenCVDetector 8 | :members: 9 | :inherited-members: 10 | :show-inheritance: 11 | 12 | .. automethod:: __call__ 13 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | # Build documentation in the docs/ directory with Sphinx 4 | sphinx: 5 | configuration: docs/source/conf.py 6 | 7 | formats: all 8 | 9 | conda: 10 | environment: docs/rtd_environment.yml 11 | 12 | python: 13 | version: 3.7 14 | install: 15 | - method: pip 16 | path: . 17 | -------------------------------------------------------------------------------- /menpodetect/dlib/__init__.py: -------------------------------------------------------------------------------- 1 | from menpo.base import MenpoMissingDependencyError 2 | 3 | 4 | try: 5 | from .detect import load_dlib_frontal_face_detector, DlibDetector 6 | from .train import train_dlib_detector 7 | except MenpoMissingDependencyError: 8 | pass 9 | 10 | # Remove from scope 11 | del MenpoMissingDependencyError 12 | -------------------------------------------------------------------------------- /docs/rtd_environment.yml: -------------------------------------------------------------------------------- 1 | name: menpo_rtd 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python 3.7.* 6 | - setuptools 7 | 8 | # Docs Only 9 | - sphinx >=2,<3 10 | - sphinx_rtd_theme 11 | # Note that dlib and opencv are mocked otherwise readthedocs runs out of 12 | # memory setting up the environment 13 | - menpo >=0.9,<0.12 14 | - pip: 15 | - sphinxmapxrefrole>=0.2 16 | -------------------------------------------------------------------------------- /menpodetect/opencv/__init__.py: -------------------------------------------------------------------------------- 1 | from menpo.base import MenpoMissingDependencyError 2 | 3 | 4 | try: 5 | from .detect import ( 6 | load_opencv_frontal_face_detector, 7 | load_opencv_profile_face_detector, 8 | load_opencv_eye_detector, 9 | OpenCVDetector, 10 | ) 11 | except MenpoMissingDependencyError: 12 | pass 13 | 14 | # Remove from scope 15 | del MenpoMissingDependencyError 16 | -------------------------------------------------------------------------------- /menpodetect/_static_version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of 'miniver': https://github.com/jbweston/miniver 3 | # 4 | # This file will be overwritten by setup.py when a source or binary 5 | # distribution is made. The magic value "__use_git__" is interpreted by 6 | # version.py. 7 | 8 | version = "__use_git__" 9 | 10 | # These values are only set if the distribution was created with 'git archive' 11 | refnames = "HEAD -> master, tag: v0.6.3" 12 | git_hash = "e96f955" 13 | -------------------------------------------------------------------------------- /AUTHORS.txt: -------------------------------------------------------------------------------- 1 | Menpodetect has received contributions from the following: 2 | 3 | Institutions 4 | ------------ 5 | Imperial College London 6 | 7 | Individuals 8 | ----------- 9 | James Booth 10 | Patrick Snape 11 | Joan Alabort-i-Medina 12 | Epameinondas Antonakos 13 | Stefanos Zafeiriou 14 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/detect/index.rst: -------------------------------------------------------------------------------- 1 | .. _api-detect-index: 2 | 3 | :mod:`menpodetect.detect` 4 | ========================= 5 | This module contains a base implementation of the generic detection method. It 6 | also provides other helper methods that are useful for all detectors. In general 7 | you will never instantiate one of these directly. 8 | 9 | Core 10 | ---- 11 | 12 | .. toctree:: 13 | :maxdepth: 1 14 | 15 | detect 16 | 17 | Convenience 18 | ----------- 19 | 20 | .. toctree:: 21 | :maxdepth: 1 22 | 23 | menpo_image_to_uint8 24 | -------------------------------------------------------------------------------- /docs/source/api/index.rst: -------------------------------------------------------------------------------- 1 | .. _api-index: 2 | 3 | The MenpoDetect API 4 | ################### 5 | This section attempts to provide a simple browsing experience for the 6 | MenpoDetect documentation. In MenpoDetect, we use legible docstrings, and 7 | therefore, all documentation should be easily accessible in any sensible IDE 8 | (or IPython) via tab completion. However, this section should make most of the 9 | core classes available for viewing online. 10 | 11 | .. toctree:: 12 | :maxdepth: 1 13 | 14 | menpodetect/detect/index 15 | menpodetect/dlib/index 16 | menpodetect/opencv/index 17 | -------------------------------------------------------------------------------- /menpodetect/tests/test_black.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | import os 4 | 5 | import black 6 | import pytest 7 | from click.testing import CliRunner 8 | 9 | root = Path(__file__).parent.parent 10 | 11 | 12 | @pytest.mark.skipif( 13 | os.getenv("CI") and sys.version_info[:2] != (3, 7), 14 | reason="Only run on one Python version to save testing time", 15 | ) 16 | def test_black(): 17 | runner = CliRunner() 18 | # Manually ignore the file generated by miniver 19 | result = runner.invoke( 20 | black.main, [str(root), "--check", "--exclude", "_static_version.py"] 21 | ) 22 | assert result.exit_code == 0, result.output 23 | -------------------------------------------------------------------------------- /menpodetect/paths.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def src_dir_path(): 5 | r"""The path to the top of the MenpoDetect Python package. 6 | 7 | Useful for locating where the models folder is stored. 8 | 9 | Returns 10 | ------- 11 | path : `str` 12 | The full path to the top of the MenpoDetect package 13 | """ 14 | return os.path.split(os.path.abspath(__file__))[0] 15 | 16 | 17 | def models_dir_path(): 18 | r"""The path to the models directory of the MenpoDetect Python package. 19 | 20 | Returns 21 | ------- 22 | path : `str` 23 | The full path to the models directory of the MenpoDetect package 24 | """ 25 | return os.path.join(src_dir_path(), "models") 26 | -------------------------------------------------------------------------------- /docs/sphinxext/ref_prettify.py: -------------------------------------------------------------------------------- 1 | from sphinx.domains.python import PyXRefRole 2 | 3 | 4 | def setup(app): 5 | """ 6 | Any time a python class is referenced, make it a pretty link that doesn't 7 | include the full package path. This makes the base classes much prettier. 8 | """ 9 | app.add_role_to_domain('py', 'class', truncate_class_role) 10 | return {'parallel_read_safe': True} 11 | 12 | 13 | def truncate_class_role(name, rawtext, text, lineno, inliner, 14 | options={}, content=[]): 15 | if '<' not in rawtext: 16 | text = '{} <{}>'.format(text.split('.')[-1], text) 17 | rawtext = ':{}:`{}`'.format(name, text) 18 | 19 | # Return a python x-reference 20 | py_xref = PyXRefRole() 21 | return py_xref('py:class', rawtext, text, lineno, 22 | inliner, options=options, content=content) 23 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/dlib/index.rst: -------------------------------------------------------------------------------- 1 | .. _api-dlib-index: 2 | 3 | :mod:`menpodetect.dlib` 4 | ======================= 5 | This module contains a wrapper of the detector provided by the Dlib [1]_ [2]_ 6 | project. In particular, it provides access to a frontal face detector that 7 | implements the work from [3]_. The Dlib detector is also trainable. 8 | 9 | Detection 10 | --------- 11 | 12 | .. toctree:: 13 | :maxdepth: 1 14 | 15 | DlibDetector 16 | load_dlib_frontal_face_detector 17 | 18 | Training 19 | -------- 20 | 21 | .. toctree:: 22 | :maxdepth: 1 23 | 24 | train_dlib_detector 25 | 26 | 27 | References 28 | ---------- 29 | .. [1] http://dlib.net/ 30 | .. [2] King, Davis E. "Dlib-ml: A machine learning toolkit." The Journal of 31 | Machine Learning Research 10 (2009): 1755-1758. 32 | .. [3] King, Davis E. "Max-Margin Object Detection." arXiv preprint 33 | arXiv:1502.00046 (2015). 34 | -------------------------------------------------------------------------------- /docs/source/_static/rtd_hack.css: -------------------------------------------------------------------------------- 1 | /* Code References (using the class or map commands) */ 2 | a>code.xref.py.py-class.docutils.literal,a>code.xref.py.py-function.docutils.literal { 3 | color: #2980B9; 4 | font-weight: bold; 5 | padding: 0; 6 | font-size: 90%; 7 | } 8 | a:hover>code.xref.py.py-class.docutils.literal,a:hover>code.xref.py.py-function.docutils.literal { 9 | color: purple; 10 | } 11 | /* Class names */ 12 | code.descclassname, code.descname { 13 | background-color: transparent; 14 | font-size: 100% !important; 15 | border: none; 16 | padding: 0; 17 | color: #000000; 18 | } 19 | /* Inline Code */ 20 | code.docutils.literal { 21 | color: #000000; 22 | font-weight: bold; 23 | } 24 | /* Inline Code Links e.g. for module links*/ 25 | a>code.docutils.literal { 26 | color: gray; 27 | font-weight: bold; 28 | border: none; 29 | background: transparent; 30 | font-size: 80%; 31 | } 32 | -------------------------------------------------------------------------------- /docs/source/api/menpodetect/opencv/index.rst: -------------------------------------------------------------------------------- 1 | .. _api-opencv-index: 2 | 3 | :mod:`menpodetect.opencv` 4 | ========================= 5 | This module contains a wrapper of the detector provided by the OpenCV [1]_ 6 | project. At the moment, we assume the use of OpenCV v2.x and therefore 7 | this detector will not be available for Python 3.x. We provide a number 8 | of pre-trained models that have been provided by the OpenCV community, all 9 | of which are implementations of the Viola-Jones method [2]_. 10 | 11 | Detection 12 | --------- 13 | 14 | .. toctree:: 15 | :maxdepth: 1 16 | 17 | OpenCVDetector 18 | load_opencv_frontal_face_detector 19 | load_opencv_profile_face_detector 20 | load_opencv_eye_detector 21 | 22 | References 23 | ---------- 24 | .. [1] http://opencv.org/ 25 | .. [2] Viola, Paul, and Michael Jones. "Rapid object detection using a boosted 26 | cascade of simple features." Computer Vision and Pattern Recognition, 27 | 2001. CVPR 2001. 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | 56 | # IntelliJ 57 | *.iml 58 | .idea/* 59 | 60 | # OSX 61 | .DS_Store 62 | 63 | # cyffld2 files 64 | tmp.txt 65 | wisdom.fftw 66 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | 4 | def get_version_and_cmdclass(package_path): 5 | """Load version.py module without importing the whole package. 6 | 7 | Template code from miniver 8 | """ 9 | import os 10 | from importlib.util import module_from_spec, spec_from_file_location 11 | 12 | spec = spec_from_file_location("version", os.path.join(package_path, "_version.py")) 13 | module = module_from_spec(spec) 14 | spec.loader.exec_module(module) 15 | return module.__version__, module.cmdclass 16 | 17 | 18 | version, cmdclass = get_version_and_cmdclass("menpodetect") 19 | 20 | 21 | setup( 22 | name="menpodetect", 23 | version=version, 24 | cmdclass=cmdclass, 25 | description="Object detection for Menpo", 26 | author="The Menpo Development Team", 27 | author_email="patricksnape@gmail.com", 28 | packages=find_packages(), 29 | package_data={"menpodetect": ["models/opencv/*.xml"]}, 30 | install_requires=["menpo>=0.9.0,<0.12.0"], 31 | tests_require=["pytest>=5.0", "black>=20.0"], 32 | ) 33 | -------------------------------------------------------------------------------- /menpodetect/opencv/conversion.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from menpodetect.paths import models_dir_path 4 | from menpo.shape import bounding_box 5 | 6 | 7 | # Paths to the OpenCV shipped with menpodetect 8 | opencv_models_path = Path(models_dir_path(), "opencv") 9 | opencv_frontal_face_path = Path(opencv_models_path, "haarcascade_frontalface_alt.xml") 10 | opencv_profile_face_path = Path(opencv_models_path, "haarcascade_profileface.xml") 11 | opencv_eye_path = Path(opencv_models_path, "haarcascade_eye.xml") 12 | 13 | 14 | def pointgraph_from_rect(rect): 15 | r""" 16 | Convert an opencv detection to a menpo.shape.PointDirectedGraph. 17 | This enforces a particular point ordering. 18 | 19 | Parameters 20 | ---------- 21 | rect : `tuple` 22 | The bounding box to convert. Result of calling an opencv detection. 23 | 24 | Returns 25 | ------- 26 | bounding_box : `menpo.shape.PointDirectedGraph` 27 | A menpo PointDirectedGraph giving the bounding box. 28 | """ 29 | x, y, w, h = rect 30 | return bounding_box((y, x), (y + h, x + w)) 31 | -------------------------------------------------------------------------------- /menpodetect/tests/test_dlib.py: -------------------------------------------------------------------------------- 1 | from menpodetect.dlib import load_dlib_frontal_face_detector 2 | import menpo.io as mio 3 | 4 | takeo = mio.import_builtin_asset.takeo_ppm() 5 | 6 | 7 | def test_frontal_face_detector(): 8 | takeo_copy = takeo.copy() 9 | dlib_detector = load_dlib_frontal_face_detector() 10 | pcs = dlib_detector(takeo_copy) 11 | assert len(pcs) == 1 12 | assert takeo_copy.n_channels == 3 13 | assert takeo_copy.landmarks["dlib_0"].n_points == 4 14 | 15 | 16 | def test_frontal_face_detector_rgb(): 17 | takeo_copy = takeo.copy() 18 | assert takeo_copy.n_channels == 3 19 | dlib_detector = load_dlib_frontal_face_detector() 20 | pcs = dlib_detector(takeo_copy, greyscale=False) 21 | assert len(pcs) == 1 22 | assert takeo_copy.n_channels == 3 23 | assert takeo_copy.landmarks["dlib_0"].n_points == 4 24 | 25 | 26 | def test_frontal_face_detector_upscales(): 27 | takeo_copy = takeo.copy() 28 | dlib_detector = load_dlib_frontal_face_detector() 29 | pcs = dlib_detector(takeo_copy, n_upscales=1) 30 | assert len(pcs) == 1 31 | assert takeo_copy.n_channels == 3 32 | assert takeo_copy.landmarks["dlib_0"].n_points == 4 33 | -------------------------------------------------------------------------------- /menpodetect/dlib/conversion.py: -------------------------------------------------------------------------------- 1 | import dlib 2 | from menpo.shape import bounding_box 3 | 4 | 5 | def rect_to_pointgraph(rect): 6 | r""" 7 | Convert a dlib.rect to a menpo.shape.PointDirectedGraph. 8 | This enforces a particular point ordering. 9 | 10 | Parameters 11 | ---------- 12 | rect : `dlib.rect` 13 | The bounding box to convert. 14 | 15 | Returns 16 | ------- 17 | bounding_box : `menpo.shape.PointDirectedGraph` 18 | A menpo PointDirectedGraph giving the bounding box. 19 | """ 20 | return bounding_box((rect.top(), rect.left()), (rect.bottom(), rect.right())) 21 | 22 | 23 | def pointgraph_to_rect(pg): 24 | r""" 25 | Convert a `menpo.shape.PointCloud` to a `dlib.rect`. 26 | 27 | Parameters 28 | ---------- 29 | pg : `menpo.shape.PointDirectedGraph` 30 | The Menpo PointDirectedGraph to convert into a rect. No check is done 31 | to see if the PointDirectedGraph actually is a rectangle. 32 | 33 | Returns 34 | ------- 35 | bounding_rect : `dlib.rect` 36 | A dlib Rectangle. 37 | """ 38 | min_p, max_p = pg.bounds() 39 | return dlib.rectangle( 40 | left=int(min_p[1]), top=int(min_p[0]), right=int(max_p[1]), bottom=int(max_p[0]) 41 | ) 42 | -------------------------------------------------------------------------------- /conda/meta.yaml: -------------------------------------------------------------------------------- 1 | package: 2 | name: menpodetect 3 | version: {{ environ['CONDACI_VERSION'] }} 4 | 5 | source: 6 | path: ../ 7 | 8 | build: 9 | number: 0 10 | script: python setup.py install --single-version-externally-managed --record=record.txt && python setup.py sdist 11 | 12 | requirements: 13 | build: 14 | - python 15 | - setuptools 16 | 17 | run: 18 | - python 19 | - dlib >=18.18 20 | - opencv >=3.1 21 | - menpo >=0.9.0,<0.12.0 22 | 23 | test: 24 | requires: 25 | - pytest >=6.0,<7.0 26 | - pytest-cov >=2.0,<3.0 27 | - pytest-mock >=3.0,<4.0 28 | - black >= 20.0 29 | - libopencv *=headless* # [linux] 30 | 31 | files: 32 | - .coveragerc 33 | 34 | imports: 35 | - menpodetect 36 | - menpodetect.dlib 37 | - menpodetect.opencv 38 | 39 | commands: 40 | - pytest $SP_DIR/menpodetect -v --cov=menpodetect --cov-config .coveragerc 41 | 42 | about: 43 | home: https://github.com/menpo/menpodetect/ 44 | license: BSD 3-Clause 45 | summary: The Menpo Project Python package for detecting faces 46 | description: | 47 | MenpoDetect is a Menpo Project package for detecting faces in images. 48 | doc_url: https://menpodetect.readthedocs.io/en/stable/ 49 | dev_url: https://github.com/menpo/menpodetect 50 | -------------------------------------------------------------------------------- /menpodetect/tests/test_opencv.py: -------------------------------------------------------------------------------- 1 | from menpodetect.opencv import ( 2 | load_opencv_frontal_face_detector, 3 | load_opencv_eye_detector, 4 | ) 5 | import menpo.io as mio 6 | 7 | takeo = mio.import_builtin_asset.takeo_ppm() 8 | 9 | 10 | def test_frontal_face_detector(): 11 | takeo_copy = takeo.copy() 12 | opencv_detector = load_opencv_frontal_face_detector() 13 | pcs = opencv_detector(takeo_copy) 14 | assert len(pcs) == 1 15 | assert takeo_copy.n_channels == 3 16 | assert takeo_copy.landmarks["opencv_0"].n_points == 4 17 | 18 | 19 | def test_frontal_face_detector_min_neighbors(): 20 | takeo_copy = takeo.copy() 21 | opencv_detector = load_opencv_frontal_face_detector() 22 | pcs = opencv_detector(takeo_copy, min_neighbours=100) 23 | assert len(pcs) == 0 24 | assert takeo_copy.n_channels == 3 25 | 26 | 27 | def test_eye_detector(): 28 | takeo_copy = takeo.copy() 29 | opencv_detector = load_opencv_eye_detector() 30 | pcs = opencv_detector(takeo_copy, min_size=(5, 5), min_neighbours=0) 31 | assert len(pcs) > 0 32 | assert takeo_copy.n_channels == 3 33 | # This is because appyveyor and travis (automated testing) return 34 | # a different number of detections 35 | first_l = list(takeo_copy.landmarks.items_matching("opencv_*"))[0][1] 36 | assert first_l.n_points == 4 37 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | workflows: 4 | version: 2 5 | test: 6 | jobs: 7 | - linux_python_3.8: 8 | context: 9 | - menpo 10 | - linux_python_3.9: 11 | filters: 12 | tags: 13 | only: /.*/ 14 | context: 15 | - menpo 16 | - linux_python_3.10: 17 | context: 18 | - menpo 19 | 20 | template: &linux_template 21 | resource_class: medium+ 22 | docker: 23 | - image: circleci/python:latest 24 | steps: 25 | - checkout 26 | - run: 27 | name: Setup Miniconda 28 | command: | 29 | wget https://raw.githubusercontent.com/menpo/condaci/circleci/condaci.py -O condaci.py 30 | python condaci.py setup --channels conda-forge 31 | - run: 32 | name: Run conda build 33 | command: | 34 | ~/miniconda/bin/python condaci.py build ./conda 35 | 36 | jobs: 37 | linux_python_3.8: 38 | <<: *linux_template 39 | environment: 40 | CONDACI_PYPI_SDIST_UPLOAD_PY_VER: 3.9 41 | PYTHON_VERSION: 3.8 42 | linux_python_3.9: 43 | <<: *linux_template 44 | environment: 45 | CONDACI_PYPI_SDIST_UPLOAD_PY_VER: 3.9 46 | PYTHON_VERSION: 3.9 47 | linux_python_3.10: 48 | <<: *linux_template 49 | environment: 50 | CONDACI_PYPI_SDIST_UPLOAD_PY_VER: 3.9 51 | PYTHON_VERSION: "3.10" -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Imperial College London and others. Please see the 2 | AUTHORS file in the main source directory for a full list of copyright 3 | holders. 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 met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * The name of Imperial College London or that of other 14 | contributors may not be used to endorse or promote products 15 | derived from this software without specific prior written 16 | permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTERS 19 | ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 23 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 24 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 25 | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 26 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 27 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 28 | USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 29 | DAMAGE. 30 | 31 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | Welcome 2 | ======= 3 | **Welcome to the MenpoDetect documentation!** 4 | 5 | MenpoDetect is a Python package designed to make object detection, in particular 6 | face detection, simple. MenpoDetect relies on the core package of Menpo, and 7 | thus the output of MenpoDetect is always assumed to be Menpo core types. If you 8 | aren't sure what Menpo is, please take a look over at 9 | `Menpo.org `_. 10 | 11 | A short example is often more illustrative than a verbose explanation. Let's 12 | assume that you want to load a set of images and that we want to detect 13 | all the faces in the images. We could do this using the Viola-Jones detector 14 | provided by OpenCV as follows: 15 | 16 | .. code-block:: python 17 | 18 | import menpo.io as mio 19 | from menpodetect import load_opencv_frontal_face_detector 20 | 21 | opencv_detector = load_opencv_frontal_face_detector() 22 | 23 | images = [] 24 | for image in mio.import_images('./images_folder'): 25 | opencv_detector(image) 26 | images.append(image) 27 | 28 | Where we use Menpo to load the images from disk and then detect as many 29 | faces as possible using OpenCV. The detections are automatically attached 30 | to each image in the form of a set of landmarks. 31 | 32 | Supported Detectors 33 | ------------------- 34 | MenpoDetect was not designed for performing novel object detection research. 35 | Therefore, it relies on a number of existing packages and merely normalizes 36 | the inputs and outputs so that they are consistent with core Menpo types. 37 | These projects are as follows: 38 | 39 | - `dlib `_ - Provides the detection capabilities of the 40 | Dlib project. This is a HOG-SVM based detector that will return a very 41 | low number of false positives. 42 | - `OpenCV `_ - Provides the detection capabilities of the 43 | OpenCV project. OpenCV implements a Viola-Jones detector 44 | and provides models for both frontal and profile faces as well as eyes. 45 | 46 | We would be very happy to see this collection expand, so pull requests 47 | are very welcome! 48 | 49 | .. toctree:: 50 | :maxdepth: 2 51 | :hidden: 52 | 53 | api/index 54 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | N_CORES = $(shell getconf _NPROCESSORS_ONLN) 6 | SPHINXOPTS = -j $(N_CORES) 7 | SPHINXBUILD = sphinx-build 8 | PAPER = 9 | SRC_DIR = source 10 | BUILDDIR = _build 11 | 12 | # Internal variables. 13 | PAPEROPT_a4 = -D latex_paper_size=a4 14 | PAPEROPT_letter = -D latex_paper_size=letter 15 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRC_DIR) 16 | 17 | .PHONY: help clean html changes linkcheck 18 | 19 | default: html 20 | 21 | help: 22 | @echo "Please use \`make ' where is one of" 23 | @echo " html standalone HTML files" 24 | @echo " changes an overview over all changed/added/deprecated items" 25 | @echo " linkcheck check all external links for integrity (takes a long time)" 26 | @echo 27 | @echo "Compound utility targets:" 28 | @echo "pdf latex and then runs the PDF generation" 29 | @echo "all html and pdf" 30 | @echo "dist all, and then puts the results in dist/" 31 | 32 | clean: 33 | -rm -rf $(BUILDDIR)/* 34 | 35 | pdf: latex 36 | cd $(BUILDDIR)/latex && make all-pdf 37 | 38 | all: html pdf 39 | 40 | html: 41 | mkdir -p $(BUILDDIR)/html $(BUILDDIR)/doctrees 42 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 43 | @echo 44 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 45 | 46 | latex: 47 | mkdir -p $(BUILDDIR)/latex $(BUILDDIR)/doctrees 48 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 49 | @echo 50 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 51 | @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ 52 | "run these through (pdf)latex." 53 | 54 | changes: 55 | mkdir -p $(BUILDDIR)/changes $(BUILDDIR)/doctrees 56 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 57 | @echo 58 | @echo "The overview file is in $(BUILDDIR)/changes." 59 | 60 | linkcheck: 61 | mkdir -p $(BUILDDIR)/linkcheck $(BUILDDIR)/doctrees 62 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 63 | @echo 64 | @echo "Link check complete; look for any errors in the above output " \ 65 | "or in $(BUILDDIR)/linkcheck/output.rst." 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 | menpodetect 4 |

5 | PyPI Release 6 | BSD License 7 |
8 | Python 3.6 Support 9 | Python 3.7 Support 10 |

11 | 12 | 13 | menpodetect - Simple object detection 14 | ===================================== 15 | Simple object detection within the [Menpo Project](http://www.menpo.org/). We do not attempt 16 | to implement novel techniques, but instead wrap existing projects so that they 17 | integrate nicely with Menpo. At the moment the current libraries are wrapped: 18 | 19 | - **[dlib](http://dlib.net/) (Boost Software License - Version 1.0)** 20 | Frontal face detection, arbitrary dlib models and training code is all 21 | wrapped. 22 | - **[opencv](http://opencv.org/) (BSD)** 23 | Frontal face detection, profile face detection, eye detection and arbitrary 24 | OpenCV cascade files (via loading from disk) are all provided. 25 | 26 | Important 27 | --------- 28 | This project aims to wrap existing object detection libraries for easy 29 | integration with Menpo. The core project is under a BSD license, but since 30 | other projects are wrapped, they may not be compatible with this BSD license. 31 | Therefore, we urge caution be taken when interacting with this library for 32 | non-academic purposes. 33 | 34 | Installation 35 | ------------ 36 | Here in the Menpo team, we are firm believers in making installation as simple 37 | as possible. Unfortunately, we are a complex project that relies on satisfying 38 | a number of complex 3rd party library dependencies. The default Python packing 39 | environment does not make this an easy task. Therefore, we evangelise the use 40 | of the conda ecosystem, provided by 41 | [Anaconda](https://store.continuum.io/cshop/anaconda/). In order to make things 42 | as simple as possible, we suggest that you use conda too! To try and persuade 43 | you, go to the [Menpo website](http://www.menpo.io/installation/) to find 44 | installation instructions for all major platforms. 45 | 46 | If you want to try pip installing this package, note that you will need 47 | to satisfy the dependencies as specified in the meta.yaml BEFORE install. 48 | 49 | #### Build Status 50 | 51 | | CI Host | OS | Build Status | 52 | |:--------:|:-----------------------------------------:|:-----------------------------------------------------:| 53 | | Travis | Ubuntu 16.04 (x64) and OSX 10.12 (x64) | [![Travis Build Status][travis_shield]][travis] | 54 | 55 | 56 | [travis]: https://travis-ci.org/menpo/menpodetect 57 | [travis_shield]: http://img.shields.io/travis/menpo/menpodetect.svg?style=flat 58 | 59 | 60 | Documentation 61 | ------------- 62 | See our documentation on [ReadTheDocs](http://menpodetect.readthedocs.org) 63 | -------------------------------------------------------------------------------- /menpodetect/tests/test_detector.py: -------------------------------------------------------------------------------- 1 | from unittest.mock import MagicMock 2 | 3 | import menpo.io as mio 4 | import numpy as np 5 | from menpo.shape import PointDirectedGraph 6 | from menpodetect.detect import detect, menpo_image_to_uint8 7 | from numpy.testing import assert_allclose 8 | 9 | takeo = mio.import_builtin_asset.takeo_ppm() 10 | takeo_uint8 = mio.import_image(mio.data_path_to("takeo.ppm"), normalize=False) 11 | fake_box = np.array([[0, 0], [1, 0], [1, 1], [0, 1]]) 12 | fake_detector = lambda x: ( 13 | [ 14 | PointDirectedGraph.init_from_edges( 15 | fake_box.copy(), np.array([[0, 1], [1, 2], [2, 3], [3, 0]]) 16 | ) 17 | ] 18 | ) 19 | 20 | 21 | def test_rescaling_image(): 22 | takeo_copy = takeo.copy() 23 | ratio = 200.0 / takeo_copy.diagonal() 24 | pcs = detect(fake_detector, takeo_copy, image_diagonal=200) 25 | assert len(pcs) == 1 26 | assert takeo_copy.n_channels == 3 27 | assert takeo_copy.landmarks["object_0"].n_points == 4 28 | assert_allclose( 29 | takeo_copy.landmarks["object_0"].points, fake_box * (1.0 / ratio), atol=10e-2 30 | ) 31 | 32 | 33 | def test_passing_uint8_image(): 34 | takeo_copy = takeo_uint8.copy() 35 | pcs = detect(fake_detector, takeo_copy, greyscale=False) 36 | assert len(pcs) == 1 37 | assert takeo_copy.n_channels == 3 38 | assert takeo_copy.landmarks["object_0"].n_points == 4 39 | 40 | 41 | def test_passing_uint8_image_greyscale(): 42 | takeo_copy = takeo_uint8.copy() 43 | pcs = detect(fake_detector, takeo_copy, greyscale=True) 44 | assert len(pcs) == 1 45 | 46 | 47 | def test_passing_uint8_rgb_image_greyscale_no_convert(): 48 | fake_image = MagicMock() 49 | fake_image.n_channels = 3 50 | pcs = detect(fake_detector, fake_image, greyscale=True) 51 | assert len(pcs) == 1 52 | fake_image.as_greyscale.assert_called_once_with(mode="luminosity") 53 | 54 | 55 | def test_passing_uint8_greyscale_image_greyscale_pass_through(): 56 | fake_image = MagicMock() 57 | fake_image.n_channels = 1 58 | pcs = detect(fake_detector, fake_image, greyscale=True) 59 | assert len(pcs) == 1 60 | fake_image.as_greyscale.assert_not_called() 61 | 62 | 63 | def test_image_to_uint8(): 64 | takeo_copy = takeo.copy() 65 | np_im = menpo_image_to_uint8(takeo_copy) 66 | shi = takeo_copy.pixels.shape 67 | shnp = np_im.shape 68 | assert np_im.dtype == np.uint8 69 | assert shi[0] == shnp[2] and shi[1] == shnp[0] and shi[2] == shnp[1] 70 | 71 | 72 | def test_image_to_uint8_greyscale(): 73 | takeo_copy = takeo.as_greyscale() 74 | np_im = menpo_image_to_uint8(takeo_copy) 75 | shi = takeo_copy.pixels.shape 76 | shnp = np_im.shape 77 | assert np_im.ndim == 2 78 | assert np_im.dtype == np.uint8 79 | assert shi[1] == shnp[0] and shi[2] == shnp[1] 80 | 81 | 82 | def test_image_to_uint8_channels_at_front(): 83 | takeo_copy = takeo.copy() 84 | np_im = menpo_image_to_uint8(takeo_copy, channels_at_back=False) 85 | shi = takeo_copy.pixels.shape 86 | shnp = np_im.shape 87 | assert np_im.dtype == np.uint8 88 | assert shi[0] == shnp[0] and shi[1] == shnp[1] and shi[2] == shnp[2] 89 | 90 | 91 | def test_image_to_uint8_greyscale_channels_at_front(): 92 | takeo_copy = takeo.as_greyscale() 93 | np_im = menpo_image_to_uint8(takeo_copy, channels_at_back=False) 94 | shi = takeo_copy.pixels.shape 95 | shnp = np_im.shape 96 | assert np_im.ndim == 2 97 | assert np_im.dtype == np.uint8 98 | assert shi[1] == shnp[0] and shi[2] == shnp[1] 99 | -------------------------------------------------------------------------------- /menpodetect/dlib/train.py: -------------------------------------------------------------------------------- 1 | from menpo.base import MenpoMissingDependencyError 2 | 3 | try: 4 | import dlib 5 | except ImportError: 6 | raise MenpoMissingDependencyError("dlib") 7 | 8 | from menpodetect.detect import menpo_image_to_uint8 9 | from .conversion import pointgraph_to_rect 10 | 11 | 12 | def train_dlib_detector( 13 | images, 14 | epsilon=0.01, 15 | add_left_right_image_flips=False, 16 | verbose_stdout=False, 17 | C=5, 18 | detection_window_size=6400, 19 | num_threads=None, 20 | ): 21 | r""" 22 | Train a dlib detector with the given list of images. 23 | 24 | This is intended to easily train a list of menpo images that have their 25 | bounding boxes attached as landmarks. Each landmark group on the image 26 | will have a tight bounding box extracted from it and then dlib will 27 | train given these images. 28 | 29 | Parameters 30 | ---------- 31 | images : `list` of `menpo.image.Image` 32 | The set of images to learn the detector from. Must have landmarks 33 | attached to **every** image, a bounding box will be extracted for each 34 | landmark group. 35 | epsilon : `float`, optional 36 | The stopping epsilon. Smaller values make the trainer's solver more 37 | accurate but might take longer to train. 38 | add_left_right_image_flips : `bool`, optional 39 | If ``True``, assume the objects are left/right symmetric and add in 40 | left right flips of the training images. This doubles the size of the 41 | training dataset. 42 | verbose_stdout : `bool`, optional 43 | If ``True``, will allow dlib to output its verbose messages. These 44 | will only be printed to the stdout, so will **not** appear in an IPython 45 | notebook. 46 | C : `int`, optional 47 | C is the usual SVM C regularization parameter. Larger values of C will 48 | encourage the trainer to fit the data better but might lead to 49 | overfitting. 50 | detection_window_size : `int`, optional 51 | The number of pixels inside the sliding window used. The default 52 | parameter of ``6400 = 80 * 80`` window size. 53 | num_threads : `int` > 0 or ``None`` 54 | How many threads to use for training. If ``None``, will query 55 | multiprocessing for the number of cores. 56 | 57 | Returns 58 | ------- 59 | detector : `dlib.simple_object_detector` 60 | The trained detector. To save this detector, call save on the returned 61 | object and pass a string path. 62 | 63 | Examples 64 | -------- 65 | Training a simple object detector from a list of menpo images and save it 66 | for later use: 67 | 68 | >>> images = list(mio.import_images('./images/path')) 69 | >>> in_memory_detector = train_dlib_detector(images, verbose_stdout=True) 70 | >>> in_memory_detector.save('in_memory_detector.svm') 71 | """ 72 | rectangles = [ 73 | [pointgraph_to_rect(lgroup.bounding_box()) for lgroup in im.landmarks.values()] 74 | for im in images 75 | ] 76 | image_pixels = [menpo_image_to_uint8(im) for im in images] 77 | 78 | if num_threads is None: 79 | import multiprocessing 80 | 81 | num_threads = multiprocessing.cpu_count() 82 | 83 | options = dlib.simple_object_detector_training_options() 84 | options.epsilon = epsilon 85 | options.add_left_right_image_flips = add_left_right_image_flips 86 | options.be_verbose = verbose_stdout 87 | options.C = C 88 | options.detection_window_size = detection_window_size 89 | options.num_threads = num_threads 90 | 91 | return dlib.train_simple_object_detector(image_pixels, rectangles, options) 92 | -------------------------------------------------------------------------------- /docs/generate_rst.py: -------------------------------------------------------------------------------- 1 | from inspect import isclass, isfunction, ismodule 2 | from functools import partial 3 | 4 | is_func_or_partial = lambda f: isfunction(f) or isinstance(f, partial) 5 | 6 | 7 | def write_docs_for_module(module, path, modules_to_skip=None, 8 | generate_index=False): 9 | if modules_to_skip is None: 10 | modules_to_skip = {} 11 | module_name = module.__name__ 12 | doc_dir = path / module_name 13 | if not doc_dir.is_dir(): 14 | doc_dir.mkdir() 15 | for k, v in module.__dict__.iteritems(): 16 | if ismodule(v): 17 | file_to_doc = docs_for_module(k, v, module_name, 18 | generate_index=generate_index) 19 | if len(file_to_doc) == 0 or k in modules_to_skip: 20 | continue 21 | mod_dir = doc_dir / k 22 | if not mod_dir.is_dir(): 23 | mod_dir.mkdir() 24 | for f_name in file_to_doc: 25 | doc_file = mod_dir / (f_name + '.rst') 26 | with open(str(doc_file), 'wb') as f: 27 | f.write(file_to_doc[f_name]) 28 | 29 | 30 | def docs_for_module(module_name, module, package_name, generate_index=False): 31 | file_to_doc = {} 32 | for k, v in module.__dict__.iteritems(): 33 | if isclass(v): 34 | file_to_doc[k] = generate_class_rst(module_name, k, 35 | module.__name__, package_name) 36 | elif is_func_or_partial(v): 37 | file_to_doc[k] = generate_function_rst(module_name, k, 38 | module.__name__, 39 | package_name) 40 | # only make an index if there is something to index 41 | if generate_index and len(file_to_doc) > 0: 42 | file_to_doc['index'] = generate_module_index(module_name, module) 43 | return file_to_doc 44 | 45 | 46 | def generate_module_index(module_name, module): 47 | breadcrumb = '.. _api-{}-index:\n\n'.format(module_name) 48 | title = ":mod:`{}`".format(module.__name__) 49 | title = "{}\n{}\n".format(title, '=' * len(title)) 50 | toctree = "\n.. toctree::\n :maxdepth: 1\n\n " 51 | items = [i for i, v in module.__dict__.items() if isclass(v) or 52 | is_func_or_partial(v)] 53 | return breadcrumb + title + toctree + "\n ".join(items) 54 | 55 | 56 | def generate_class_rst(module_name, class_name, module, package_name): 57 | breadcrumb = '.. _{}-{}-{}:\n\n'.format(package_name, module_name, 58 | class_name) 59 | current_module = '.. currentmodule:: {}\n\n'.format(module) 60 | title = "{}\n{}\n".format(class_name, '=' * len(class_name)) 61 | body = (".. autoclass:: {}\n :members:\n :inherited-members:" 62 | "\n :show-inheritance:\n".format(class_name)) 63 | return breadcrumb + current_module + title + body 64 | 65 | 66 | def generate_function_rst(module_name, function_name, module, package_name): 67 | breadcrumb = '.. _{}-{}-{}:\n\n'.format(package_name, module_name, 68 | function_name) 69 | current_module = '.. currentmodule:: {}\n\n'.format(module) 70 | title = "{}\n{}\n".format(function_name, '=' * len(function_name)) 71 | body = ".. autofunction:: {}\n".format(function_name) 72 | return breadcrumb + current_module + title + body 73 | 74 | 75 | 76 | if __name__ == '__main__': 77 | from pathlib import Path 78 | import menpodetect 79 | 80 | path = Path(__file__).parent / 'source' / 'api' 81 | 82 | # Flip generate_index to True to make index.rst files too! 83 | write_docs_for_module(menpodetect, path, generate_index=False, 84 | modules_to_skip={'_version'}) 85 | -------------------------------------------------------------------------------- /menpodetect/detect.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | import numpy as np 3 | from menpo.transform import UniformScale 4 | 5 | 6 | def _greyscale(image): 7 | r""" 8 | Convert image to greyscale if needed. If the image has more than 3 channels, 9 | then the average greyscale is taken. A copy of the image as greyscale is 10 | returned (single channel). 11 | 12 | Parameters 13 | ---------- 14 | image : `menpo.image.Image` 15 | The image to convert. 16 | 17 | Returns 18 | ------- 19 | image : `menpo.image.Image` 20 | A greyscale version of the image. 21 | """ 22 | if image.n_channels != 1: 23 | if image.n_channels == 3: 24 | # Use luminosity for RGB images 25 | image = image.as_greyscale(mode="luminosity") 26 | else: 27 | # Fall back to the average of the channels for other kinds 28 | # of images 29 | image = image.as_greyscale(mode="average") 30 | return image 31 | 32 | 33 | def menpo_image_to_uint8(image, channels_at_back=True): 34 | r""" 35 | Return the given image as a uint8 array. This is a copy of the image. 36 | 37 | Parameters 38 | ---------- 39 | image : `menpo.image.Image` 40 | The image to convert. If already uint8, only the channels will be 41 | rolled to the last axis. 42 | channels_at_back : `bool`, optional 43 | If ``True``, the image channels are placed onto the last axis (the back) 44 | as is common in many imaging packages. This is contrary to the Menpo 45 | default where channels are the first axis (at the front). 46 | 47 | Returns 48 | ------- 49 | uint8_image : `ndarray` 50 | `uint8` Numpy array, channels as the back (last) axis if 51 | ``channels_at_back == True``. 52 | """ 53 | if channels_at_back: 54 | uint8_im = image.pixels_with_channels_at_back(out_dtype=np.uint8) 55 | # Handle the dead axis on greyscale images 56 | if uint8_im.ndim == 3 and uint8_im.shape[-1] == 1: 57 | uint8_im = uint8_im[..., 0] 58 | else: 59 | from menpo.image.base import denormalize_pixels_range 60 | 61 | uint8_im = denormalize_pixels_range(image.pixels, np.uint8) 62 | # Handle the dead axis on greyscale images 63 | if uint8_im.ndim == 3 and uint8_im.shape[0] == 1: 64 | uint8_im = uint8_im[0] 65 | return uint8_im 66 | 67 | 68 | def detect( 69 | detector_callable, 70 | image, 71 | greyscale=True, 72 | image_diagonal=None, 73 | group_prefix="object", 74 | channels_at_back=True, 75 | ): 76 | r""" 77 | Apply the general detection framework. 78 | 79 | This involves converting the image to greyscale if necessary, rescaling 80 | the image to a given diagonal, performing the detection, and attaching 81 | the scaled landmarks back onto the original image. 82 | 83 | uint8 images cannot be converted to greyscale by this framework, so must 84 | already be greyscale or ``greyscale=False``. 85 | 86 | Parameters 87 | ---------- 88 | detector_callable : `callable` or `function` 89 | A callable object that will perform detection given a single parameter, 90 | a `uint8` numpy array with either no channels, or channels as the 91 | *last* axis. 92 | image : `menpo.image.Image` 93 | A Menpo image to detect. The bounding boxes of the detected objects 94 | will be attached to this image. 95 | greyscale : `bool`, optional 96 | Convert the image to greyscale or not. 97 | image_diagonal : `int`, optional 98 | The total size of the diagonal of the image that should be used for 99 | detection. This is useful for scaling images up and down for detection. 100 | group_prefix : `str`, optional 101 | The prefix string to be appended to each each landmark group that is 102 | stored on the image. Each detection will be stored as group_prefix_# 103 | where # is a count starting from 0. 104 | channels_at_back : `bool`, optional 105 | If ``True``, the image channels are placed onto the last axis (the back) 106 | as is common in many imaging packages. This is contrary to the Menpo 107 | default where channels are the first axis (at the front). 108 | 109 | Returns 110 | ------- 111 | bounding_boxes : `list` of `menpo.shape.PointDirectedGraph` 112 | A list of bounding boxes representing the detections found. 113 | """ 114 | d_image = image 115 | 116 | if greyscale: 117 | d_image = _greyscale(d_image) 118 | 119 | if image_diagonal is not None: 120 | scale_factor = image_diagonal / image.diagonal() 121 | d_image = d_image.rescale(scale_factor) 122 | 123 | pcs = detector_callable( 124 | menpo_image_to_uint8(d_image, channels_at_back=channels_at_back) 125 | ) 126 | 127 | if image_diagonal is not None: 128 | s = UniformScale(1 / scale_factor, n_dims=2) 129 | pcs = [s.apply(pc) for pc in pcs] 130 | 131 | padding_magnitude = len(str(len(pcs))) 132 | for i, pc in enumerate(pcs): 133 | key = "{prefix}_{num:0{mag}d}".format( 134 | mag=padding_magnitude, prefix=group_prefix, num=i 135 | ) 136 | image.landmarks[key] = pc 137 | return pcs 138 | -------------------------------------------------------------------------------- /menpodetect/dlib/detect.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from functools import partial 3 | from pathlib import Path 4 | 5 | from menpo.base import MenpoMissingDependencyError 6 | 7 | try: 8 | import dlib 9 | except ImportError: 10 | raise MenpoMissingDependencyError("dlib") 11 | 12 | from menpodetect.detect import detect 13 | from .conversion import rect_to_pointgraph 14 | 15 | 16 | class _dlib_detect(object): 17 | r""" 18 | A utility callable that allows the caching of a dlib detector. 19 | 20 | This callable is important for presenting the correct parameters to the 21 | user. It also marshalls the return type of the detector back to 22 | `menpo.shape.PointDirectedGraph`. 23 | 24 | Parameters 25 | ---------- 26 | model : `Path` or `str` or `dlib.simple_object_detector` 27 | Either a path to a `dlib.simple_object_detector` or a 28 | `dlib.fhog_object_detector` or the detector itself. 29 | 30 | Raises 31 | ------ 32 | ValueError 33 | If a path was provided and it does not exist. 34 | """ 35 | 36 | def __init__(self, model): 37 | if isinstance(model, str) or isinstance(model, Path): 38 | m_path = Path(model) 39 | if not Path(m_path).exists(): 40 | raise ValueError("Model {} does not exist.".format(m_path)) 41 | # There are two different kinds of object detector, the 42 | # simple_object_detector and the fhog_object_detector, but we 43 | # can't tell which is which from the file name. Therefore, try one 44 | # and then the other. Unfortunately, it throws a runtime error, 45 | # which we have to catch. 46 | try: 47 | model = dlib.simple_object_detector(str(m_path)) 48 | except RuntimeError: 49 | model = dlib.fhog_object_detector(str(m_path)) 50 | self._dlib_model = model 51 | 52 | def __call__(self, uint8_image, n_upscales=0): 53 | r""" 54 | Perform a detection using the cached dlib detector. 55 | 56 | Parameters 57 | ---------- 58 | uint8_image : `ndarray` 59 | An RGB (3 Channels) or Greyscale (1 Channel) numpy array of uint8 60 | n_upscales : `int`, optional 61 | Number of times to upscale the image when performing the detection, 62 | may increase the chances of detecting smaller objects. 63 | 64 | Returns 65 | ------ 66 | bounding_boxes : `list` of `menpo.shape.PointDirectedGraph` 67 | The detected objects. 68 | """ 69 | # Dlib doesn't handle the dead last axis 70 | if uint8_image.shape[-1] == 1: 71 | uint8_image = uint8_image[..., 0] 72 | rects = self._dlib_model(uint8_image, n_upscales) 73 | return [rect_to_pointgraph(r) for r in rects] 74 | 75 | 76 | class DlibDetector(object): 77 | r""" 78 | A generic dlib detector. 79 | 80 | Wraps a dlib object detector inside the menpodetect framework and provides 81 | a clean interface to expose the dlib arguments. 82 | """ 83 | 84 | def __init__(self, model): 85 | self._detector = _dlib_detect(model) 86 | 87 | def __call__( 88 | self, 89 | image, 90 | greyscale=False, 91 | image_diagonal=None, 92 | group_prefix="dlib", 93 | n_upscales=0, 94 | ): 95 | r""" 96 | Perform a detection using the cached dlib detector. 97 | 98 | The detections will also be attached to the image as landmarks. 99 | 100 | Parameters 101 | ---------- 102 | image : `menpo.image.Image` 103 | A Menpo image to detect. The bounding boxes of the detected objects 104 | will be attached to this image. 105 | greyscale : `bool`, optional 106 | Convert the image to greyscale or not. 107 | image_diagonal : `int`, optional 108 | The total size of the diagonal of the image that should be used for 109 | detection. This is useful for scaling images up and down for 110 | detection. 111 | group_prefix : `str`, optional 112 | The prefix string to be appended to each each landmark group that is 113 | stored on the image. Each detection will be stored as group_prefix_# 114 | where # is a count starting from 0. 115 | n_upscales : `int`, optional 116 | Number of times to upscale the image when performing the detection, 117 | may increase the chances of detecting smaller objects. 118 | 119 | Returns 120 | ------ 121 | bounding_boxes : `list` of `menpo.shape.PointDirectedGraph` 122 | The detected objects. 123 | """ 124 | detect_partial = partial(self._detector, n_upscales=n_upscales) 125 | return detect( 126 | detect_partial, 127 | image, 128 | greyscale=greyscale, 129 | image_diagonal=image_diagonal, 130 | group_prefix=group_prefix, 131 | ) 132 | 133 | 134 | def load_dlib_frontal_face_detector(): 135 | r""" 136 | Load the dlib frontal face detector. 137 | 138 | Returns 139 | ------- 140 | detector : `DlibDetector` 141 | The frontal face detector. 142 | """ 143 | return DlibDetector(dlib.get_frontal_face_detector()) 144 | -------------------------------------------------------------------------------- /menpodetect/_version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This file is part of 'miniver': https://github.com/jbweston/miniver 3 | # 4 | from collections import namedtuple 5 | import os 6 | import subprocess 7 | 8 | from distutils.command.build_py import build_py as build_py_orig 9 | from setuptools.command.sdist import sdist as sdist_orig 10 | 11 | Version = namedtuple("Version", ("release", "dev", "labels")) 12 | 13 | # No public API 14 | __all__ = [] 15 | 16 | package_root = os.path.dirname(os.path.realpath(__file__)) 17 | package_name = os.path.basename(package_root) 18 | distr_root = os.path.dirname(package_root) 19 | 20 | STATIC_VERSION_FILE = "_static_version.py" 21 | 22 | 23 | def get_version(version_file=STATIC_VERSION_FILE): 24 | version_info = get_static_version_info(version_file) 25 | version = version_info["version"] 26 | if version == "__use_git__": 27 | version = get_version_from_git() 28 | if not version: 29 | version = get_version_from_git_archive(version_info) 30 | if not version: 31 | version = Version("unknown", None, None) 32 | return pep440_format(version) 33 | else: 34 | return version 35 | 36 | 37 | def get_static_version_info(version_file=STATIC_VERSION_FILE): 38 | version_info = {} 39 | with open(os.path.join(package_root, version_file), "rb") as f: 40 | exec(f.read(), {}, version_info) 41 | return version_info 42 | 43 | 44 | def version_is_from_git(version_file=STATIC_VERSION_FILE): 45 | return get_static_version_info(version_file)["version"] == "__use_git__" 46 | 47 | 48 | def pep440_format(version_info): 49 | release, dev, labels = version_info 50 | 51 | version_parts = [release] 52 | if dev: 53 | if release.endswith("-dev") or release.endswith(".dev"): 54 | version_parts.append(dev) 55 | else: # prefer PEP440 over strict adhesion to semver 56 | version_parts.append(".dev{}".format(dev)) 57 | 58 | if labels: 59 | version_parts.append("+") 60 | version_parts.append(".".join(labels)) 61 | 62 | return "".join(version_parts) 63 | 64 | 65 | def get_version_from_git(): 66 | try: 67 | p = subprocess.Popen( 68 | ["git", "rev-parse", "--show-toplevel"], 69 | cwd=distr_root, 70 | stdout=subprocess.PIPE, 71 | stderr=subprocess.PIPE, 72 | ) 73 | except OSError: 74 | return 75 | if p.wait() != 0: 76 | return 77 | if not os.path.samefile(p.communicate()[0].decode().rstrip("\n"), distr_root): 78 | # The top-level directory of the current Git repository is not the same 79 | # as the root directory of the distribution: do not extract the 80 | # version from Git. 81 | return 82 | 83 | # git describe --first-parent does not take into account tags from branches 84 | # that were merged-in. The '--long' flag gets us the 'dev' version and 85 | # git hash, '--always' returns the git hash even if there are no tags. 86 | for opts in [["--first-parent"], []]: 87 | try: 88 | p = subprocess.Popen( 89 | ["git", "describe", "--long", "--always"] + opts, 90 | cwd=distr_root, 91 | stdout=subprocess.PIPE, 92 | stderr=subprocess.PIPE, 93 | ) 94 | except OSError: 95 | return 96 | if p.wait() == 0: 97 | break 98 | else: 99 | return 100 | 101 | description = ( 102 | p.communicate()[0] 103 | .decode() 104 | .strip("v") # Tags can have a leading 'v', but the version should not 105 | .rstrip("\n") 106 | .rsplit("-", 2) # Split the latest tag, commits since tag, and hash 107 | ) 108 | 109 | try: 110 | release, dev, git = description 111 | except ValueError: # No tags, only the git hash 112 | # prepend 'g' to match with format returned by 'git describe' 113 | git = "g{}".format(*description) 114 | release = "unknown" 115 | dev = None 116 | 117 | labels = [] 118 | if dev == "0": 119 | dev = None 120 | else: 121 | labels.append(git) 122 | 123 | try: 124 | p = subprocess.Popen(["git", "diff", "--quiet"], cwd=distr_root) 125 | except OSError: 126 | labels.append("confused") # This should never happen. 127 | else: 128 | if p.wait() == 1: 129 | labels.append("dirty") 130 | 131 | return Version(release, dev, labels) 132 | 133 | 134 | # TODO: change this logic when there is a git pretty-format 135 | # that gives the same output as 'git describe'. 136 | # Currently we can only tell the tag the current commit is 137 | # pointing to, or its hash (with no version info) 138 | # if it is not tagged. 139 | def get_version_from_git_archive(version_info): 140 | try: 141 | refnames = version_info["refnames"] 142 | git_hash = version_info["git_hash"] 143 | except KeyError: 144 | # These fields are not present if we are running from an sdist. 145 | # Execution should never reach here, though 146 | return None 147 | 148 | if git_hash.startswith("$Format") or refnames.startswith("$Format"): 149 | # variables not expanded during 'git archive' 150 | return None 151 | 152 | VTAG = "tag: v" 153 | refs = set(r.strip() for r in refnames.split(",")) 154 | version_tags = set(r[len(VTAG) :] for r in refs if r.startswith(VTAG)) 155 | if version_tags: 156 | release, *_ = sorted(version_tags) # prefer e.g. "2.0" over "2.0rc1" 157 | return Version(release, dev=None, labels=None) 158 | else: 159 | return Version("unknown", dev=None, labels=["g{}".format(git_hash)]) 160 | 161 | 162 | __version__ = get_version() 163 | 164 | 165 | # The following section defines a module global 'cmdclass', 166 | # which can be used from setup.py. The 'package_name' and 167 | # '__version__' module globals are used (but not modified). 168 | 169 | 170 | def _write_version(fname): 171 | # This could be a hard link, so try to delete it first. Is there any way 172 | # to do this atomically together with opening? 173 | try: 174 | os.remove(fname) 175 | except OSError: 176 | pass 177 | with open(fname, "w") as f: 178 | f.write( 179 | "# This file has been created by setup.py.\n" 180 | "version = '{}'\n".format(__version__) 181 | ) 182 | 183 | 184 | class _build_py(build_py_orig): 185 | def run(self): 186 | super().run() 187 | _write_version(os.path.join(self.build_lib, package_name, STATIC_VERSION_FILE)) 188 | 189 | 190 | class _sdist(sdist_orig): 191 | def make_release_tree(self, base_dir, files): 192 | super().make_release_tree(base_dir, files) 193 | _write_version(os.path.join(base_dir, package_name, STATIC_VERSION_FILE)) 194 | 195 | 196 | cmdclass = dict(sdist=_sdist, build_py=_build_py) 197 | -------------------------------------------------------------------------------- /menpodetect/opencv/detect.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from functools import partial 3 | from pathlib import Path 4 | 5 | from menpo.base import MenpoMissingDependencyError 6 | 7 | try: 8 | import cv2 9 | except ImportError: 10 | raise MenpoMissingDependencyError("opencv") 11 | 12 | from menpodetect.detect import detect 13 | from .conversion import ( 14 | pointgraph_from_rect, 15 | opencv_frontal_face_path, 16 | opencv_profile_face_path, 17 | opencv_eye_path, 18 | ) 19 | 20 | 21 | def _get_default_flags(): 22 | version = cv2.__version__.split(".")[0] 23 | if version == "2": 24 | return cv2.cv.CV_HAAR_SCALE_IMAGE 25 | elif version == "3" or version == "4": 26 | return cv2.CASCADE_SCALE_IMAGE 27 | else: 28 | raise ValueError("Unsupported OpenCV version: {}".format(version)) 29 | 30 | 31 | class _opencv_detect(object): 32 | r""" 33 | A utility callable that allows the caching of an opencv detector. 34 | 35 | This callable is important for presenting the correct parameters to the 36 | user. It also marshalls the return type of the detector back to 37 | menpo.shape.PointDirectedGraph. 38 | 39 | Parameters 40 | ---------- 41 | model : `Path` or `str` or `opencv.CascadeClassifier` 42 | Either a path to an `opencv.CascadeClassifier` or the detector itself. 43 | 44 | Raises 45 | ------ 46 | ValueError 47 | If a path was provided and it does not exist. 48 | """ 49 | 50 | def __init__(self, model): 51 | if isinstance(model, (str, Path)): 52 | m_path = Path(model) 53 | if not Path(m_path).exists(): 54 | raise ValueError("Model {} does not exist.".format(m_path)) 55 | model = cv2.CascadeClassifier(str(m_path)) 56 | self._opencv_model = model 57 | 58 | def __call__( 59 | self, 60 | uint8_image, 61 | scale_factor=1.1, 62 | min_neighbours=5, 63 | min_size=(30, 30), 64 | flags=None, 65 | ): 66 | r""" 67 | Perform a detection using the cached opencv detector. 68 | 69 | Parameters 70 | ---------- 71 | uint8_image : `ndarray` 72 | A Greyscale (1 Channel) numpy array of uint8 73 | scale_factor : `float`, optional 74 | The amount to increase the sliding windows by over the second 75 | pass. 76 | min_neighbours : `int`, optional 77 | The minimum number of neighbours (close detections) before 78 | Non-Maximum suppression to be considered a detection. Use 0 79 | to return all detections. 80 | min_size : `tuple` of 2 ints 81 | The minimum object size in pixels that the detector will consider. 82 | flags : `int` 83 | The flags to be passed through to the detector. 84 | 85 | Returns 86 | ------ 87 | bounding_boxes : `list` of `menpo.shape.PointDirectedGraph` 88 | The detected objects. 89 | """ 90 | if flags is None: 91 | flags = _get_default_flags() 92 | rects = self._opencv_model.detectMultiScale( 93 | uint8_image, 94 | scaleFactor=scale_factor, 95 | minNeighbors=min_neighbours, 96 | minSize=min_size, 97 | flags=flags, 98 | ) 99 | return [pointgraph_from_rect(r) for r in rects] 100 | 101 | 102 | class OpenCVDetector(object): 103 | r""" 104 | A generic opencv detector. 105 | 106 | Wraps an opencv object detector inside the menpodetect framework and 107 | provides a clean interface to expose the opencv arguments. 108 | """ 109 | 110 | def __init__(self, model): 111 | self._detector = _opencv_detect(model) 112 | 113 | def __call__( 114 | self, 115 | image, 116 | image_diagonal=None, 117 | group_prefix="opencv", 118 | scale_factor=1.1, 119 | min_neighbours=5, 120 | min_size=(30, 30), 121 | flags=None, 122 | ): 123 | r""" 124 | Perform a detection using the cached opencv detector. 125 | 126 | The detections will also be attached to the image as landmarks. 127 | 128 | Parameters 129 | ---------- 130 | image : `menpo.image.Image` 131 | A Menpo image to detect. The bounding boxes of the detected objects 132 | will be attached to this image. 133 | image_diagonal : `int`, optional 134 | The total size of the diagonal of the image that should be used for 135 | detection. This is useful for scaling images up and down for 136 | detection. 137 | group_prefix : `str`, optional 138 | The prefix string to be appended to each each landmark group that is 139 | stored on the image. Each detection will be stored as group_prefix_# 140 | where # is a count starting from 0. 141 | scale_factor : `float`, optional 142 | The amount to increase the sliding windows by over the second 143 | pass. 144 | min_neighbours : `int`, optional 145 | The minimum number of neighbours (close detections) before 146 | Non-Maximum suppression to be considered a detection. Use 0 147 | to return all detections. 148 | min_size : `tuple` of 2 ints 149 | The minimum object size in pixels that the detector will consider. 150 | flags : `int`, optional 151 | The flags to be passed through to the detector. 152 | 153 | Returns 154 | ------ 155 | bounding_boxes : `list` of `menpo.shape.PointDirectedGraph` 156 | The detected objects. 157 | """ 158 | if flags is None: 159 | flags = _get_default_flags() 160 | detect_partial = partial( 161 | self._detector, 162 | scale_factor=scale_factor, 163 | min_neighbours=min_neighbours, 164 | min_size=min_size, 165 | flags=flags, 166 | ) 167 | return detect( 168 | detect_partial, 169 | image, 170 | greyscale=True, 171 | image_diagonal=image_diagonal, 172 | group_prefix=group_prefix, 173 | ) 174 | 175 | 176 | def load_opencv_frontal_face_detector(): 177 | r""" 178 | Load the opencv frontal face detector: haarcascade_frontalface_alt.xml 179 | 180 | Returns 181 | ------- 182 | detector : OpenCVDetector 183 | The frontal face detector. 184 | """ 185 | return OpenCVDetector(opencv_frontal_face_path) 186 | 187 | 188 | def load_opencv_profile_face_detector(): 189 | r""" 190 | Load the opencv profile face detector: haarcascade_profileface.xml 191 | 192 | Returns 193 | ------- 194 | detector : OpenCVDetector 195 | The profile face detector. 196 | """ 197 | return OpenCVDetector(opencv_profile_face_path) 198 | 199 | 200 | def load_opencv_eye_detector(): 201 | r""" 202 | Load the opencv eye detector: haarcascade_eye.xml 203 | 204 | Returns 205 | ------- 206 | detector : OpenCVDetector 207 | The eye detector. 208 | """ 209 | return OpenCVDetector(opencv_eye_path) 210 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | # on_rtd is whether we are on readthedocs.org, 5 | # this line of code grabbed from docs.readthedocs.org 6 | on_rtd = os.environ.get("READTHEDOCS", None) == "True" 7 | 8 | if on_rtd: 9 | from unittest.mock import MagicMock 10 | 11 | MOCK_MODULES = ["dlib", "cv2"] 12 | for mod_name in MOCK_MODULES: 13 | sys.modules[mod_name] = MagicMock() 14 | 15 | # Add the folder above so we can grab the sphinx extensions 16 | sys.path.insert(0, os.path.abspath("..")) 17 | if not on_rtd: 18 | # Add the menpodetect root so we can grab the version 19 | sys.path.insert(0, os.path.abspath("../../")) 20 | 21 | import menpodetect 22 | 23 | # add an up to date mathjax path 24 | mathjax_path = ( 25 | "https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" 26 | ) 27 | 28 | # -- General configuration ----------------------------------------------------- 29 | 30 | # If your documentation needs a minimal Sphinx version, state it here. 31 | needs_sphinx = "2.0" 32 | 33 | # Add any Sphinx extension module names here, as strings. They can be extensions 34 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 35 | extensions = [ 36 | "sphinx.ext.autodoc", 37 | "sphinx.ext.todo", 38 | "sphinx.ext.coverage", 39 | "sphinx.ext.mathjax", 40 | "sphinx.ext.viewcode", 41 | "sphinx.ext.autosummary", 42 | "sphinx.ext.napoleon", 43 | "sphinxext.ref_prettify", 44 | "sphinxext.theme_hack", 45 | "sphinxmapxrefrole", 46 | ] 47 | 48 | # Import the mapping dictionary and set it for sphinxmapxrefrole 49 | from xref_map import xref_map 50 | 51 | xref_mapping_dict = xref_map 52 | 53 | # Add any paths that contain templates here, relative to this directory. 54 | templates_path = ["_templates"] 55 | html_static_path = ["_static"] 56 | 57 | # Otherwise Sphinx emits thousands of warnings 58 | napoleon_include_special_with_doc = False 59 | napoleon_numpy_docstring = True 60 | napoleon_use_rtype = False 61 | napoleon_use_ivar = False 62 | napoleon_use_param = True 63 | napoleon_use_admonition_for_examples = True 64 | napoleon_use_admonition_for_notes = True 65 | napoleon_use_admonition_for_references = True 66 | 67 | # The suffix of source filenames. 68 | # source_suffix = '.rst' 69 | 70 | # The encoding of source files. 71 | # source_encoding = 'utf-8-sig' 72 | 73 | # The master toctree document. 74 | master_doc = "index" 75 | 76 | # Sort attributes by type (functions separate from properties) 77 | autodoc_member_order = "groupwise" 78 | 79 | # General information about the project. 80 | project = u"MenpoDetect" 81 | authors = ( 82 | u"Joan Alabort-i-Medina, Epameinondas Antonakos, James Booth," 83 | u" Patrick Snape, and Stefanos Zafeiriou" 84 | ) 85 | copyright = u"2014, " + authors 86 | 87 | # The version info for the project you're documenting, acts as replacement for 88 | # |version| and |release|, also used in various other places throughout the 89 | # built documents. 90 | # 91 | # The short X.Y version. 92 | version = menpodetect.__version__ 93 | # The full version, including alpha/beta/rc tags. 94 | release = menpodetect.__version__ 95 | 96 | # The language for content autogenerated by Sphinx. Refer to documentation 97 | # for a list of supported languages. 98 | # language = None 99 | 100 | # There are two options for replacing |today|: either, you set today to some 101 | # non-false value, then it is used: 102 | # today = '' 103 | # Else, today_fmt is used as the format for a strftime call. 104 | # today_fmt = '%B %d, %Y' 105 | 106 | # List of patterns, relative to source directory, that match files and 107 | # directories to ignore when looking for source files. 108 | exclude_patterns = ["_build", "**/test/**"] 109 | 110 | # The reST default role (used for this markup: `text`) to use for all documents. 111 | # default_role = None 112 | 113 | # If true, '()' will be appended to :func: etc. cross-reference text. 114 | # add_function_parentheses = True 115 | 116 | # If true, the current module name will be prepended to all description 117 | # unit titles (such as .. function::). 118 | # add_module_names = True 119 | 120 | # If true, sectionauthor and moduleauthor directives will be shown in the 121 | # output. They are ignored by default. 122 | # show_authors = False 123 | 124 | # The name of the Pygments (syntax highlighting) style to use. 125 | # pygments_style = 'sphinx' 126 | 127 | # A list of ignored prefixes for module index sorting. 128 | # modindex_common_prefix = [] 129 | 130 | # If true, keep warnings as "system message" paragraphs in the built documents. 131 | # keep_warnings = False 132 | 133 | 134 | # -- Options for HTML output --------------------------------------------------- 135 | 136 | # The theme to use for HTML and HTML Help pages. See the documentation for 137 | # a list of builtin themes. 138 | if not on_rtd: # only import and set the theme if we're building docs locally 139 | import sphinx_rtd_theme 140 | 141 | html_theme = "sphinx_rtd_theme" 142 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 143 | 144 | # Theme options are theme-specific and customize the look and feel of a theme 145 | # further. For a list of options available for each theme, see the 146 | # documentation. 147 | # html_theme_options = {} 148 | 149 | # Add any paths that contain custom themes here, relative to this directory. 150 | # html_theme_path = [] 151 | 152 | # The name for this set of Sphinx documents. If None, it defaults to 153 | # " v documentation". 154 | # html_title = None 155 | 156 | # A shorter title for the navigation bar. Default is the same as html_title. 157 | # html_short_title = None 158 | 159 | # The name of an image file (relative to this directory) to place at the top 160 | # of the sidebar. 161 | # html_logo = None 162 | 163 | # The name of an image file (within the static path) to use as favicon of the 164 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 165 | # pixels large. 166 | # html_favicon = None 167 | 168 | # Add any paths that contain custom static files (such as style sheets) here, 169 | # relative to this directory. They are copied after the builtin static files, 170 | # so a file named "default.css" will overwrite the builtin "default.css". 171 | # html_static_path = ['_static'] 172 | 173 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 174 | # using the given strftime format. 175 | # html_last_updated_fmt = '%b %d, %Y' 176 | 177 | # If true, SmartyPants will be used to convert quotes and dashes to 178 | # typographically correct entities. 179 | # html_use_smartypants = True 180 | 181 | # Custom sidebar templates, maps document names to template names. 182 | # html_sidebars = {} 183 | 184 | # Additional templates that should be rendered to pages, maps page names to 185 | # template names. 186 | # html_additional_pages = {} 187 | 188 | # If false, no module index is generated. 189 | # html_domain_indices = True 190 | 191 | # If false, no index is generated. 192 | # html_use_index = True 193 | 194 | # If true, the index is split into individual pages for each letter. 195 | # html_split_index = False 196 | 197 | # If true, links to the reST sources are added to the pages. 198 | # html_show_sourcelink = True 199 | 200 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 201 | # html_show_sphinx = True 202 | 203 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 204 | # html_show_copyright = True 205 | 206 | # If true, an OpenSearch description file will be output, and all pages will 207 | # contain a tag referring to it. The value of this option must be the 208 | # base URL from which the finished HTML is served. 209 | # html_use_opensearch = '' 210 | 211 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 212 | # html_file_suffix = None 213 | 214 | # Output file base name for HTML help builder. 215 | htmlhelp_basename = "Menpodoc" 216 | 217 | # -- Options for LaTeX output -------------------------------------------------- 218 | 219 | latex_preamble = r""" 220 | \usepackage{enumitem} 221 | \setlistdepth{999} 222 | """ 223 | latex_elements = { 224 | # The paper size ('letterpaper' or 'a4paper'). 225 | # 'papersize': 'letterpaper', 226 | # The font size ('10pt', '11pt' or '12pt'). 227 | # 'pointsize': '10pt', 228 | # Additional stuff for the LaTeX preamble. 229 | "preamble": latex_preamble 230 | } 231 | 232 | # Grouping the document tree into LaTeX files. List of tuples 233 | # (source start file, target name, title, author, documentclass [howto/manual]). 234 | latex_documents = [ 235 | ("index", "Menpo.tex", u"Menpo Documentation", authors, "manual"), 236 | ] 237 | 238 | # The name of an image file (relative to this directory) to place at the top of 239 | # the title page. 240 | # latex_logo = None 241 | 242 | # For "manual" documents, if this is true, then toplevel headings are parts, 243 | # not chapters. 244 | # latex_use_parts = False 245 | 246 | # If true, show page references after internal links. 247 | # latex_show_pagerefs = False 248 | 249 | # If true, show URL addresses after external links. 250 | # latex_show_urls = False 251 | 252 | # Documents to append as an appendix to all manuals. 253 | # latex_appendices = [] 254 | 255 | # If false, no module index is generated. 256 | # latex_domain_indices = True 257 | 258 | 259 | # -- Options for manual page output -------------------------------------------- 260 | 261 | # One entry per manual page. List of tuples 262 | # (source start file, name, description, authors, manual section). 263 | man_pages = [("index", "menpodetect", u"MenpoDetect Documentation", [authors], 1)] 264 | 265 | # If true, show URL addresses after external links. 266 | # man_show_urls = False 267 | 268 | 269 | # -- Options for Texinfo output ------------------------------------------------ 270 | 271 | # Grouping the document tree into Texinfo files. List of tuples 272 | # (source start file, target name, title, author, 273 | # dir menu entry, description, category) 274 | texinfo_documents = [ 275 | ( 276 | "index", 277 | "MenpoDetect", 278 | u"MenpoDetect Documentation", 279 | authors, 280 | "MenpoDetect", 281 | "Object Detection in Python.", 282 | "Miscellaneous", 283 | ), 284 | ] 285 | 286 | # Documents to append as an appendix to all manuals. 287 | # texinfo_appendices = [] 288 | 289 | # If false, no module index is generated. 290 | # texinfo_domain_indices = True 291 | 292 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 293 | # texinfo_show_urls = 'footnote' 294 | 295 | # If true, do not generate a @detailmenu in the "Top" node's menu. 296 | # texinfo_no_detailmenu = False 297 | --------------------------------------------------------------------------------