Skip to content

Commit dc51a36

Browse files
committed
Add face_detection CLI tool
1 parent 851cc62 commit dc51a36

File tree

6 files changed

+161
-12
lines changed

6 files changed

+161
-12
lines changed

README.md

+31-4
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,19 @@ While Windows isn't officially supported, helpful users have posted instructions
109109

110110
## Usage
111111

112-
#### Command-Line Interface
112+
### Command-Line Interface
113113

114-
When you install `face_recognition`, you get a simple command-line program
115-
called `face_recognition` that you can use to recognize faces in a
116-
photograph or folder full for photographs.
114+
When you install `face_recognition`, you get a two simple command-line
115+
programs:
116+
117+
* `face_recognition` - Recognize faces in a photograph or folder full for
118+
photographs.
119+
* `face_detection` - Find faces in a photograph or folder full for photographs.
120+
121+
#### `face_recognition` command line tool
122+
123+
The `face_recognition` command lets you recognize faces in a photograph or
124+
folder full for photographs.
117125

118126
First, you need to provide a folder with one picture of each person you
119127
already know. There should be one image file for each person with the
@@ -142,6 +150,25 @@ with the filename and the name of the person found.
142150
An `unknown_person` is a face in the image that didn't match anyone in
143151
your folder of known people.
144152

153+
#### `face_detection` command line tool
154+
155+
The `face_detection` lets you find the location (pixel coordinatates)
156+
of any faces in an image.
157+
158+
Just run the command `face_detection`, passing in a folder of images
159+
to check (or a single image):
160+
161+
```bash
162+
$ face_detection ./folder_with_pictures/
163+
164+
examples/image1.jpg,65,215,169,112
165+
examples/image2.jpg,62,394,211,244
166+
examples/image2.jpg,95,941,244,792
167+
```
168+
169+
It prints one line for each face that was detected. The coordinates
170+
reported are the top, right, bottom and left coordinates of the face (in pixels).
171+
145172
##### Adjusting Tolerance / Sensitivity
146173

147174
If you are getting multiple matches for the same person, it might be that

examples/face_recognition_knn.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import pickle
3939
from PIL import Image, ImageDraw
4040
import face_recognition
41-
from face_recognition.cli import image_files_in_folder
41+
from face_recognition.face_recognition_cli import image_files_in_folder
4242

4343
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
4444

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import print_function
3+
import click
4+
import os
5+
import re
6+
import face_recognition.api as face_recognition
7+
import multiprocessing
8+
import sys
9+
import PIL.Image
10+
import itertools
11+
import numpy as np
12+
13+
14+
def print_result(filename, location):
15+
top, right, bottom, left = location
16+
print("{},{},{},{},{}".format(filename, top, right, bottom, left))
17+
18+
19+
def test_image(image_to_check, model):
20+
unknown_image = face_recognition.load_image_file(image_to_check)
21+
face_locations = face_recognition.face_locations(unknown_image, number_of_times_to_upsample=0, model=model)
22+
23+
for face_location in face_locations:
24+
print_result(image_to_check, face_location)
25+
26+
27+
def image_files_in_folder(folder):
28+
return [os.path.join(folder, f) for f in os.listdir(folder) if re.match(r'.*\.(jpg|jpeg|png)', f, flags=re.I)]
29+
30+
31+
def process_images_in_process_pool(images_to_check, number_of_cpus, model):
32+
if number_of_cpus == -1:
33+
processes = None
34+
else:
35+
processes = number_of_cpus
36+
37+
# macOS will crash due to a bug in libdispatch if you don't use 'forkserver'
38+
context = multiprocessing
39+
if "forkserver" in multiprocessing.get_all_start_methods():
40+
context = multiprocessing.get_context("forkserver")
41+
42+
pool = context.Pool(processes=processes)
43+
44+
function_parameters = zip(
45+
images_to_check,
46+
itertools.repeat(model),
47+
)
48+
49+
pool.starmap(test_image, function_parameters)
50+
51+
52+
@click.command()
53+
@click.argument('image_to_check')
54+
@click.option('--cpus', default=1, help='number of CPU cores to use in parallel. -1 means "use all in system"')
55+
@click.option('--model', default="hog", help='Which face detection model to use. Options are "hog" or "cnn".')
56+
57+
def main(image_to_check, cpus, model):
58+
# Multi-core processing only supported on Python 3.4 or greater
59+
if (sys.version_info < (3, 4)) and cpus != 1:
60+
click.echo("WARNING: Multi-processing support requires Python 3.4 or greater. Falling back to single-threaded processing!")
61+
cpus = 1
62+
63+
if os.path.isdir(image_to_check):
64+
if cpus == 1:
65+
[test_image(image_file, model) for image_file in image_files_in_folder(image_to_check)]
66+
else:
67+
process_images_in_process_pool(image_files_in_folder(image_to_check), cpus, model)
68+
else:
69+
test_image(image_to_check, model)
70+
71+
72+
if __name__ == "__main__":
73+
main()
File renamed without changes.

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
},
4040
entry_points={
4141
'console_scripts': [
42-
'face_recognition=face_recognition.cli:main'
42+
'face_recognition=face_recognition.face_recognition_cli:main',
43+
'face_detection=face_recognition.face_detection_cli:main'
4344
]
4445
},
4546
install_requires=requirements,

tests/test_face_recognition.py

+54-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
from click.testing import CliRunner
1616

1717
from face_recognition import api
18-
from face_recognition import cli
18+
from face_recognition import face_recognition_cli
19+
from face_recognition import face_detection_cli
1920

2021

2122
class Test_face_recognition(unittest.TestCase):
@@ -231,7 +232,7 @@ def test_compare_faces_empty_lists(self):
231232
def test_command_line_interface_options(self):
232233
target_string = 'Show this message and exit.'
233234
runner = CliRunner()
234-
help_result = runner.invoke(cli.main, ['--help'])
235+
help_result = runner.invoke(face_recognition_cli.main, ['--help'])
235236
self.assertEqual(help_result.exit_code, 0)
236237
self.assertTrue(target_string in help_result.output)
237238

@@ -241,7 +242,7 @@ def test_command_line_interface(self):
241242
image_folder = os.path.join(os.path.dirname(__file__), 'test_images')
242243
image_file = os.path.join(os.path.dirname(__file__), 'test_images', 'obama.jpg')
243244

244-
result = runner.invoke(cli.main, args=[image_folder, image_file])
245+
result = runner.invoke(face_recognition_cli.main, args=[image_folder, image_file])
245246

246247
self.assertEqual(result.exit_code, 0)
247248
self.assertTrue(target_string in result.output)
@@ -252,7 +253,7 @@ def test_command_line_interface_big_image(self):
252253
image_folder = os.path.join(os.path.dirname(__file__), 'test_images')
253254
image_file = os.path.join(os.path.dirname(__file__), 'test_images', 'obama3.jpg')
254255

255-
result = runner.invoke(cli.main, args=[image_folder, image_file])
256+
result = runner.invoke(face_recognition_cli.main, args=[image_folder, image_file])
256257

257258
self.assertEqual(result.exit_code, 0)
258259
self.assertTrue(target_string in result.output)
@@ -263,7 +264,7 @@ def test_command_line_interface_tolerance(self):
263264
image_folder = os.path.join(os.path.dirname(__file__), 'test_images')
264265
image_file = os.path.join(os.path.dirname(__file__), 'test_images', 'obama.jpg')
265266

266-
result = runner.invoke(cli.main, args=[image_folder, image_file, "--tolerance", "0.55"])
267+
result = runner.invoke(face_recognition_cli.main, args=[image_folder, image_file, "--tolerance", "0.55"])
267268

268269
self.assertEqual(result.exit_code, 0)
269270
self.assertTrue(target_string in result.output)
@@ -274,7 +275,54 @@ def test_command_line_interface_show_distance(self):
274275
image_folder = os.path.join(os.path.dirname(__file__), 'test_images')
275276
image_file = os.path.join(os.path.dirname(__file__), 'test_images', 'obama.jpg')
276277

277-
result = runner.invoke(cli.main, args=[image_folder, image_file, "--show-distance", "1"])
278+
result = runner.invoke(face_recognition_cli.main, args=[image_folder, image_file, "--show-distance", "1"])
278279

279280
self.assertEqual(result.exit_code, 0)
280281
self.assertTrue(target_string in result.output)
282+
283+
def test_fd_command_line_interface_options(self):
284+
target_string = 'Show this message and exit.'
285+
runner = CliRunner()
286+
help_result = runner.invoke(face_detection_cli.main, ['--help'])
287+
self.assertEqual(help_result.exit_code, 0)
288+
self.assertTrue(target_string in help_result.output)
289+
290+
def test_fd_command_line_interface(self):
291+
runner = CliRunner()
292+
image_file = os.path.join(os.path.dirname(__file__), 'test_images', 'obama.jpg')
293+
294+
result = runner.invoke(face_detection_cli.main, args=[image_file])
295+
self.assertEqual(result.exit_code, 0)
296+
parts = result.output.split(",")
297+
self.assertTrue("obama.jpg" in parts[0])
298+
self.assertEqual(len(parts), 5)
299+
300+
def test_fd_command_line_interface_folder(self):
301+
runner = CliRunner()
302+
image_file = os.path.join(os.path.dirname(__file__), 'test_images')
303+
304+
result = runner.invoke(face_detection_cli.main, args=[image_file])
305+
self.assertEqual(result.exit_code, 0)
306+
self.assertTrue("obama_partial_face2.jpg" in result.output)
307+
self.assertTrue("obama.jpg" in result.output)
308+
self.assertTrue("obama2.jpg" in result.output)
309+
self.assertTrue("obama3.jpg" in result.output)
310+
self.assertTrue("biden.jpg" in result.output)
311+
312+
def test_fd_command_line_interface_hog_model(self):
313+
target_string = 'obama.jpg'
314+
runner = CliRunner()
315+
image_file = os.path.join(os.path.dirname(__file__), 'test_images', 'obama.jpg')
316+
317+
result = runner.invoke(face_detection_cli.main, args=[image_file, "--model", "hog"])
318+
self.assertEqual(result.exit_code, 0)
319+
self.assertTrue(target_string in result.output)
320+
321+
def test_fd_command_line_interface_cnn_model(self):
322+
target_string = 'obama.jpg'
323+
runner = CliRunner()
324+
image_file = os.path.join(os.path.dirname(__file__), 'test_images', 'obama.jpg')
325+
326+
result = runner.invoke(face_detection_cli.main, args=[image_file, "--model", "cnn"])
327+
self.assertEqual(result.exit_code, 0)
328+
self.assertTrue(target_string in result.output)

0 commit comments

Comments
 (0)