Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
be6ae17
first commit where the new module was created
warmspringwinds Jun 26, 2015
0b42e48
pep8 correction
warmspringwinds Jun 26, 2015
bdd624e
pep8 once again
warmspringwinds Jun 26, 2015
a301eff
the object detection class implemented finally
warmspringwinds Jun 30, 2015
375db34
removed comments
warmspringwinds Jun 30, 2015
432fe35
updated tests and made the setup correct
warmspringwinds Jul 6, 2015
ebc7324
documentation was written for the implemented cascade class
warmspringwinds Jul 6, 2015
b4abe6f
wrong class name mistake corrected
warmspringwinds Jul 6, 2015
5efec34
wrong class name mistake corrected
warmspringwinds Jul 6, 2015
531aba4
fixing the issue with round from libc.math that is not working on win…
warmspringwinds Jul 6, 2015
e917979
updated the bento file
warmspringwinds Jul 6, 2015
06fec5c
updated the bento file
warmspringwinds Jul 6, 2015
b20c9ef
updated the data submodule structure and respective tests
warmspringwinds Jul 6, 2015
29bc49c
updated documentation
warmspringwinds Jul 7, 2015
d0211ef
updated documentation
warmspringwinds Jul 7, 2015
ab395b9
clustering detection windows was added
warmspringwinds Jul 10, 2015
815e771
Detection cluster struct introduced and bint used instead of int
warmspringwinds Jul 14, 2015
88e65fa
fmax and fmin were implemented. the standart ones from libc.math didn…
warmspringwinds Jul 14, 2015
7c99d90
documentation for the structs was added
warmspringwinds Jul 14, 2015
9c1ba8b
documentation for all the functions was added
warmspringwinds Jul 14, 2015
342ad78
made the intersection score threshold parameter available in the func…
warmspringwinds Jul 14, 2015
c90e896
the implementation of _get_valid_scale_factors changes as suggested t…
warmspringwinds Jul 14, 2015
70714d9
the grayscale image input to rgb2gray() bug fixed
warmspringwinds Jul 14, 2015
79370ce
added exception error message and made the loading of xml file more s…
warmspringwinds Jul 14, 2015
e92d9f4
python3 map object is not subsciptable error fix attempt
warmspringwinds Jul 15, 2015
46cafe9
python3 map bug fix attempt again
warmspringwinds Jul 15, 2015
211a591
test corrected
warmspringwinds Jul 17, 2015
5b979f3
error message was made more elaborate
warmspringwinds Jul 17, 2015
d70c280
docstring corrected
warmspringwinds Jul 17, 2015
20bc829
pep8 style
warmspringwinds Jul 17, 2015
b8be66b
documentation clarification, pep8, variables naming
warmspringwinds Jul 22, 2015
063e4a1
rectangle type
warmspringwinds Jul 22, 2015
9aa3348
amount to number fix
warmspringwinds Jul 22, 2015
3b5a4dd
more rectangle typo fixes
warmspringwinds Jul 22, 2015
51ef679
back-ticks in documentation
warmspringwinds Jul 22, 2015
bb57c4e
Cacade class description
warmspringwinds Jul 22, 2015
631f03b
amount -> number
warmspringwinds Jul 22, 2015
4921994
cascade class description was finished
warmspringwinds Jul 22, 2015
462c17f
pep 8
warmspringwinds Jul 22, 2015
ee9c8aa
more tests were added
warmspringwinds Jul 22, 2015
b9a92cc
changed name of the module and added safety checks for openmp
warmspringwinds Aug 16, 2015
e806d4d
header file error was corrected
warmspringwinds Aug 16, 2015
8e839ae
gallery example was added and more information about xml specified
warmspringwinds Aug 17, 2015
3fee96d
python3 import issue corrected
warmspringwinds Aug 17, 2015
36ba7ae
update for compilers that don't support openmp
warmspringwinds Jan 4, 2016
5f250ca
update for compilers that don't support openmp
warmspringwinds Jan 4, 2016
820abe6
fix to make it compile using other compilers. still one problem.
warmspringwinds Jan 5, 2016
a7a3c30
python 3 support
warmspringwinds Jan 5, 2016
eeab27c
final conditional support added to root setup file
warmspringwinds Jan 5, 2016
2e6d39f
possible correction to last commit for travis
warmspringwinds Jan 5, 2016
7face0e
lena was removed, documentation was updated
warmspringwinds Jun 12, 2016
059dfc9
object detection ipython notebook was added
warmspringwinds Jun 16, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*.pyd
*.bak
*.c
*.cpp
*.new
*.md5
*.old
Expand Down
5 changes: 4 additions & 1 deletion bento.info
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Library:
Packages:
skimage, skimage.color, skimage.data, skimage.draw, skimage.exposure,
skimage.feature, skimage.filters, skimage.future, skimage.future.graph,
skimage.graph, skimage.io,
skimage.graph, skimage.io, skimage.future.detect_obj
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanv Your thoughts on the module name ?

skimage.io._plugins, skimage.measure, skimage.morphology,
skimage.scripts, skimage.restoration, skimage.segmentation,
skimage.transform, skimage.util
Expand Down Expand Up @@ -167,6 +167,9 @@ Library:
Extension: skimage.transform._seam_carving
Sources:
skimage/transform/_seam_carving.pyx
Extension: skimage.future.detect.cascade
Sources:
skimage/future/detect/cascade.pyx

Executable: skivi
Module: skimage.scripts.skivi
Expand Down
99 changes: 99 additions & 0 deletions doc/examples/plot_face_detection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""
==============
Face Detection
==============

This example shows how to detect faces on an image using object detection framework.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the object detection framework

I can do one pass of language checking on the text here before we merge and file a PR.


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>`_.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update us about the latest state of the training in scikit-image?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanv it's about to be finished. I wrote a blog post where I wrote about the
reason of delay.
http://warmspringwinds.github.io/gsoc/2015/08/23/google-summer-of-code-implementing-the-training-part-of-face-detection/

"""

import skimage.data as data
import skimage.future.detect as detect

from matplotlib import pyplot as plt
from matplotlib import patches

# Load the trained file from the module root.
trained_file = data.detect.frontal_face_cascade_xml()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd remove the trailing _xml.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanv will do. Also seems better for me.


# Initialize the detector cascade.
detector = detect.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()

"""
.. image:: PLOT2RST.current_figure
"""
98 changes: 98 additions & 0 deletions object_detection.ipynb

Large diffs are not rendered by default.

54 changes: 53 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,57 @@

import setuptools
from distutils.command.build_py import build_py
from numpy.distutils.command.build_ext import build_ext
from distutils.errors import CompileError, LinkError
import tempfile
import shutil

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:
Expand Down Expand Up @@ -122,6 +173,7 @@ def configuration(parent_package='', top_path=None):
'console_scripts': ['skivi = skimage.scripts.skivi:main'],
},

cmdclass={'build_py': build_py},
cmdclass={'build_py': build_py,
'build_ext': ConditionalOpenMP},
**extra
)
6 changes: 6 additions & 0 deletions skimage/_shared/interpolation.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,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,
Expand Down
5 changes: 4 additions & 1 deletion skimage/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .. import data_dir
from ..io import imread, use_plugin
from ._binary_blobs import binary_blobs
from . import detect

__all__ = ['load',
'camera',
Expand All @@ -27,7 +28,8 @@
'coffee',
'hubble_deep_field',
'rocket',
'astronaut']
'astronaut',
'detect']


def load(f):
Expand Down Expand Up @@ -261,3 +263,4 @@ def rocket():

"""
return load("rocket.jpg")

16 changes: 16 additions & 0 deletions skimage/data/detect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os as _os
from .. import data_dir


def frontal_face_cascade_xml():
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use this function to parse the XML as well?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefanv I made it inside the original function because it takes time to load
and parse that file and it's a little bit faster in cython.
Taking into account that usually people will use this framework for detection only on one image, the loading should also be fast.

"""
Returns the file's path to the trained xml file for frontal face detection
that was taken from 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')
Loading