Skip to content

Commit

Permalink
Merge pull request scikit-image#3267 from glemaitre/is/detector
Browse files Browse the repository at this point in the history
Add object detector module
Rate limit · GitHub

Access has been restricted

You have triggered a rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

stefanv authored Sep 6, 2018
2 parents 68a0818 + 9679ef8 commit 2f436f1
Showing 16 changed files with 2,661 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ __pycache__
*.pyd
*.bak
*.c
skimage/feature/_haar.cpp
*.cpp
*.new
*.md5
*.old
102 changes: 102 additions & 0 deletions doc/examples/xx_applications/plot_face_detection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""
=========================================
Face detection using a cascade classifier
=========================================
This computer vision example shows how to detect faces on an image using object
detection framework based on machine learning.
First, you will need an xml file, from which the trained data can be read. The
framework works with files, trained using Multi-block Local Binary Patterns
Features (See `MB-LBP <plot_multiblock_local_binary_pattern.html>`_) and Gentle
Adaboost with attentional cascade. So, the detection framework will also work
with `xml files from OpenCV
<https://github.com/Itseez/opencv/tree/master/data/lbpcascades>`_. There you
can find files that were trained to detect cat faces, profile faces and other
things. But if you want to detect frontal faces, the respective file is
already included in scikit-image.
Next you will have to specify the parameters for the ``detect_multi_scale``
function. Here you can find the meaning of each of them.
First one is ``scale_ratio``. To find all faces, the algorithm does the search
on multiple scales. This is done by changing the size of searching window. The
smallest window size is the size of window that was used in training. This size
is specified in the xml file with trained parameters. The ``scale_ratio``
parameter specifies by which ratio the search window is increased on each
step. If you increase this parameter, the search time decreases and the
accuracy decreases. So, faces on some scales can be not detected.
``step_ratio`` specifies the step of sliding window that is used to search for
faces on each scale of the image. If this parameter is equal to one, then all
the possible locations are searched. If the parameter is greater than one, for
example, two, the window will be moved by two pixels and not all of the
possible locations will be searched for faces. By increasing this parameter we
can reduce the working time of the algorithm, but the accuracy will also be
decreased.
``min_size`` is the minimum size of search window during the scale
search. ``max_size`` specifies the maximum size of the window. If you know the
size of faces on the images that you want to search, you should specify these
parameters as precisely as possible, because you can avoid doing expensive
computations and possibly decrease the amount of false detections. You can save
a lot of time by increasing the ``min_size`` parameter, because the majority of
time is spent on searching on the smallest scales.
``min_neighbour_number`` and ``intersection_score_threshold`` parameters are
made to cluster the excessive detections of the same face and to filter out
false detections. True faces usually has a lot of dectections around them and
false ones usually have single detection. First algorithm searches for
clusters: two rectangle detections are placed in the same cluster if the
intersection score between them is larger then
``intersection_score_threshold``. The intersection score is computed using the
equation (intersection area) / (small rectangle ratio). The described
intersection criteria was chosen over intersection over union to avoid a corner
case when small rectangle inside of a big one have small intersection score.
Then each cluster is thresholded using ``min_neighbour_number`` parameter which
leaves the clusters that have a same or bigger number of detections in them.
You should also take into account that false detections are inevitable and if
you want to have a really precise detector, you will have to train it yourself
using `OpenCV train cascade utility
<http://docs.opencv.org/doc/user_guide/ug_traincascade.html>`_.
"""

from skimage import data
from skimage.feature import Cascade

import matplotlib.pyplot as plt
from matplotlib import patches

# Load the trained file from the module root.
trained_file = data.lbp_frontal_face_cascade_filename()

# Initialize the detector cascade.
detector = Cascade(trained_file)

img = data.astronaut()

detected = detector.detect_multi_scale(img=img,
scale_factor=1.2,
step_ratio=1,
min_size=(60, 60),
max_size=(123, 123))

plt.imshow(img)
img_desc = plt.gca()
plt.set_cmap('gray')

for patch in detected:

img_desc.add_patch(
patches.Rectangle(
(patch['c'], patch['r']),
patch['width'],
patch['height'],
fill=False,
color='r',
linewidth=2
)
)

plt.show()
57 changes: 57 additions & 0 deletions setup.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -20,10 +20,16 @@

import os
import sys
import tempfile
import shutil

import setuptools
from distutils.command.build_py import build_py
from distutils.command.sdist import sdist
from distutils.errors import CompileError, LinkError

from numpy.distutils.command.build_ext import build_ext


if sys.version_info < (3, 5):

@@ -49,6 +55,56 @@
# machinery.
builtins.__SKIMAGE_SETUP__ = True

# Support for openmp

compile_flags = ['-fopenmp']
link_flags = ['-fopenmp']

code = """#include <omp.h>
int main(int argc, char** argv) { return(0); }"""


class ConditionalOpenMP(build_ext):

def can_compile_link(self):

cc = self.compiler
fname = 'test.c'
cwd = os.getcwd()
tmpdir = tempfile.mkdtemp()

try:
os.chdir(tmpdir)
with open(fname, 'wt') as fobj:
fobj.write(code)
try:
objects = cc.compile([fname],
extra_postargs=compile_flags)
except CompileError:
return False
try:
# Link shared lib rather then executable to avoid
# http://bugs.python.org/issue4431 with MSVC 10+
cc.link_shared_lib(objects, "testlib",
extra_postargs=link_flags)
except (LinkError, TypeError):
return False
finally:
os.chdir(cwd)
shutil.rmtree(tmpdir)
return True

def build_extensions(self):
""" Hook into extension building to check compiler flags """

if self.can_compile_link():

for ext in self.extensions:
ext.extra_compile_args += compile_flags
ext.extra_link_args += link_flags

build_ext.build_extensions(self)


with open('skimage/__init__.py') as fid:
for line in fid:
@@ -157,6 +213,7 @@ def configuration(parent_package='', top_path=None):
},

cmdclass={'build_py': build_py,
'build_ext': ConditionalOpenMP,
'sdist': sdist},
**extra
)
6 changes: 6 additions & 0 deletions skimage/_shared/interpolation.pxd
Original file line number Diff line number Diff line change
@@ -22,6 +22,12 @@ from libc.math cimport ceil, floor
cdef inline Py_ssize_t round(double r) nogil:
return <Py_ssize_t>((r + 0.5) if (r > 0.0) else (r - 0.5))

cdef inline Py_ssize_t fmax(Py_ssize_t one, Py_ssize_t two) nogil:
return one if one > two else two

cdef inline Py_ssize_t fmin(Py_ssize_t one, Py_ssize_t two) nogil:
return one if one < two else two


cdef inline double nearest_neighbour_interpolation(double* image,
Py_ssize_t rows,
2 changes: 2 additions & 0 deletions skimage/data/__init__.py
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
from .._shared._warnings import expected_warnings, warn
from ..util.dtype import img_as_bool
from ._binary_blobs import binary_blobs
from ._detect import lbp_frontal_face_cascade_filename

import os.path as osp
data_dir = osp.abspath(osp.dirname(__file__))
@@ -31,6 +32,7 @@
'horse',
'hubble_deep_field',
'immunohistochemistry',
'lbp_frontal_face_cascade_filename',
'lfw_subset',
'logo',
'moon',
17 changes: 17 additions & 0 deletions skimage/data/_detect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import os as _os
data_dir = _os.path.abspath(_os.path.dirname(__file__))


def lbp_frontal_face_cascade_filename():
"""
Returns the path to the XML file containing information about the weak
classifiers of a cascade classifier trained using LBP features. It is part
of the OpenCV repository [1]_.
References
----------
.. [1] OpenCV lbpcascade trained files
https://github.com/Itseez/opencv/tree/master/data/lbpcascades
"""

return _os.path.join(data_dir, 'lbpcascade_frontalface_opencv.xml')
Rate limit · GitHub

Access has been restricted

You have triggered a rate limit.

Please wait a few minutes before you try again;
in some cases this may take up to an hour.

0 comments on commit 2f436f1

Please sign in to comment.