Skip to content

Commit 5ad8bf6

Browse files
author
xinyuangui
committed
add doc and finish school project
1 parent 2fea36d commit 5ad8bf6

File tree

6 files changed

+159
-7
lines changed

6 files changed

+159
-7
lines changed

Diff for: README.md

+13-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This project can be divided into two steps:
44
1. Get static training model.
55
2. Use training model to recognize names in videos.
66

7+
## Dockerfile
8+
9+
* The `Dockerfile.gpu` contains one Dockerfile which can run the `dlib` library to detect and recognize faces.
10+
711
## Training Step
812

913
### Main Idea
@@ -28,6 +32,13 @@ python3 train_cli.py train-with-video <your-video-location> <username>
2832
python3 recognize_cli.py recognize-faces-in-video <your-video-location>
2933
```
3034

31-
## TODO
35+
## Doc
36+
37+
* There is one detail report in `doc` folder.
38+
39+
## Bash script example
3240

33-
* Increase the processing speed to make the recognition real-time.
41+
* `test_experiment_cli.sh`: Detect faces in the video and save as images
42+
* `test_recognize_cli.sh`: Recognize faces in the video and output result video
43+
* `test_recognize_faces_in_images.sh`: Recognize faces in the image
44+
* `test_train_cli.sh`: Train with video

Diff for: apis.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ def _raw_face_locations(img, number_of_times_to_upsample=1, model='cnn'):
3737
:return: a list of dlib 'rect' objects of found face locations
3838
"""
3939
if model == 'cnn':
40-
return cnn_face_detector(img, number_of_times_to_upsample)
40+
rectangles = cnn_face_detector(img, number_of_times_to_upsample)
41+
return [rectangle.rect for rectangle in rectangles]
4142
else:# hog
4243
return face_detector(img, number_of_times_to_upsample)
4344

@@ -49,11 +50,11 @@ def _raw_face_landmarks(face_image, face_locations=None, model='large'):
4950
:param face_image: image to search
5051
:param face_location: optionally provide a list of face locations to search
5152
:param model: 'large' (default) uses 68 points, 'small' uses 5 points which is faster
52-
:return: alist of dicts of face features locations
53+
:return: a list of dicts of face features locations
5354
"""
5455
if face_locations == None:
5556
face_locations = _raw_face_locations(img=face_image)
56-
face_locations = [face_location.rect for face_location in face_locations]
57+
face_locations = [face_location for face_location in face_locations]
5758
else:
5859
face_locations = [_css_to_rect(face_location) for face_location in face_locations]
5960

@@ -95,6 +96,7 @@ def ransac_mean(features, ratio_threshold=0.9, dist_threshold=0.4):
9596
pick_feature = features[pick_index]
9697
inliers = np.linalg.norm(features - pick_feature, axis=1) < dist_threshold
9798
inliers_count = np.sum(inliers)
99+
print('{}, {}'.format(inliers_count, feature_count))
98100
if inliers_count >= ratio_threshold * feature_count:
99101
inliers = features[inliers]
100102
print('final iterations: {}, inliers_count: {}'.format(i, inliers_count))
@@ -115,7 +117,7 @@ def recognize_faces_in_images(face_image, features, dist_threshold=0.4):
115117
If no match, min_dist_index = None
116118
"""
117119
face_locations = _raw_face_locations(img=face_image)
118-
face_locations = [_rect_to_css(face_location.rect) for face_location in face_locations]
120+
face_locations = [_rect_to_css(face_location) for face_location in face_locations]
119121

120122
face_features = face_encodings(face_image, face_locations)
121123
result = [None] * len(face_features)

Diff for: doc/report.pdf

9.36 MB
Binary file not shown.

Diff for: experiment.py

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import click
2+
import apis
3+
import cv2
4+
import numpy as np
5+
import os
6+
import multiprocessing
7+
8+
@click.group()
9+
def experiment():
10+
pass
11+
12+
@click.command()
13+
@click.argument('video-location')
14+
@click.argument('output-location')
15+
@click.option('--frame-count', default=100, help='number of frames to calculate')
16+
def test_hog_cnn(video_location, output_location, frame_count):
17+
"""
18+
This function will read one video. Split it into different frames.
19+
Detect faces in these frames and output result to output folder.
20+
"""
21+
cap = cv2.VideoCapture(video_location)
22+
if not cap.isOpened():
23+
click.echo('cannot open this video', err=True)
24+
return
25+
video_length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT) - 1)
26+
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
27+
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
28+
frame_count = min(frame_count, video_length)
29+
seperate = int(video_length / frame_count)
30+
frame_list = [None] * frame_count
31+
result_index = 0
32+
frame_index = 0
33+
while cap.isOpened():
34+
ret, frame = cap.read()
35+
if frame_index % seperate == 0 and result_index < frame_count:
36+
frame_list[result_index] = frame
37+
result_index = result_index + 1
38+
frame_index = frame_index + 1
39+
# if there is no more frames left
40+
if frame_index >= video_length:
41+
cap.release()
42+
break
43+
# if the frame_list is not filled
44+
if not result_index == frame_count:
45+
frame_list = frame_list[:result_index]
46+
# Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
47+
frame_list = [frame[:, :, ::-1] for frame in frame_list]
48+
49+
# firstly use cnn
50+
print('start using cnn to detect faces')
51+
cnn_faces = [detect_faces_cnn(frame, index) for index, frame in enumerate(frame_list)]
52+
print('finish detecting faces in cnn')
53+
54+
# use hog to detect
55+
print('start using hog to detect faces')
56+
arguments = [[frame, index] for index, frame in enumerate(frame_list)]
57+
with multiprocessing.Pool(processes=4) as pool:
58+
hog_faces = pool.starmap(detect_faces_hog, arguments)
59+
60+
# draw cnn rectangles
61+
print('begin writing to files')
62+
index = 0
63+
for faces in cnn_faces:
64+
frame = frame_list[index][:,:,::-1]
65+
for top, right, bottom, left in faces:
66+
cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
67+
index += 1
68+
index = 0
69+
for faces in hog_faces:
70+
frame = frame_list[index][:,:,::-1]
71+
for top, right, bottom, left in faces:
72+
cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
73+
cv2.imwrite(os.path.join(output_location, '{}.png'.format(index)), frame)
74+
index += 1
75+
76+
77+
def detect_faces_cnn(frame, index):
78+
print('cnn {} begins'.format(index))
79+
face_locations_rect = apis._raw_face_locations(frame, model='cnn')
80+
return [apis._rect_to_css(rect) for rect in face_locations_rect]
81+
82+
83+
def detect_faces_hog(frame, index):
84+
print('hog {} begins'.format(index))
85+
face_locations_rect = apis._raw_face_locations(frame, model='hog')
86+
return [apis._rect_to_css(rect) for rect in face_locations_rect]
87+
88+
experiment.add_command(test_hog_cnn)
89+
90+
91+
if __name__ == "__main__":
92+
experiment()

Diff for: recognize_cli.py

+47
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import numpy as np
55
import os
66
import glob
7+
import time
78

89
@click.group()
910
def recognize():
@@ -71,7 +72,53 @@ def get_username(full_path):
7172
output_movie.release()
7273

7374

75+
@click.command()
76+
@click.argument('image-folder')
77+
@click.option('--saved-feature-loc', default='data/trained_features', help='the folder to save trained feature')
78+
@click.option('--save-result-folder', default='data/result_images', help='the folder we save result video')
79+
def recognize_faces_in_images(image_folder, saved_feature_loc, save_result_folder):
80+
def get_username(full_path):
81+
"""
82+
get username from full-path
83+
"""
84+
basename = os.path.basename(full_path)
85+
return basename.split('.')[0]
86+
feature_files = glob.glob(os.path.join(saved_feature_loc, '*.npy'))
87+
features = [np.load(feature_file) for feature_file in feature_files]
88+
usernames = [get_username(feature_file) for feature_file in feature_files]
89+
90+
file_formats = ('*.png', '*.jpg', '*.jpeg')
91+
img_files = []
92+
for file_format in file_formats:
93+
img_files.extend(glob.glob(os.path.join(image_folder, file_format)))
94+
for img_file in img_files:
95+
recognize_faces_in_image(img_file, features, usernames, os.path.join(save_result_folder, os.path.basename(img_file)))
96+
97+
98+
def recognize_faces_in_image(image_location, features, usernames, result_location):
99+
# read image
100+
img = cv2.imread(image_location)
101+
img = img[:,:,::-1]
102+
103+
start = time.time()
104+
face_info_tuple = apis.recognize_faces_in_images(img, features)
105+
img = img[:,:,::-1]
106+
# draw rectangles
107+
for top, right, bottom, left, username in face_info_tuple:
108+
if username is None:
109+
username = 'unknown'
110+
else:
111+
username = usernames[username]
112+
cv2.rectangle(img, (left, top), (right, bottom), (0, 0, 255), 2)
113+
font = cv2.FONT_HERSHEY_DUPLEX
114+
cv2.putText(img, username, (left + 6, bottom - 6), font, 0.5, (255, 255, 255), 1)
115+
end = time.time()
116+
print('{} spends time: {}'.format(os.path.basename(image_location), end - start))
117+
cv2.imwrite(result_location, img)
118+
119+
74120
recognize.add_command(recognize_faces_in_video)
121+
recognize.add_command(recognize_faces_in_images)
75122

76123
if __name__ == "__main__":
77124
recognize()

Diff for: train_cli.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def train():
1212
@click.command()
1313
@click.argument('video-location')
1414
@click.argument('username')
15-
@click.option('--frame-count', default=100, help='number of frames to calculate')
15+
@click.option('--frame-count', default=200, help='number of frames to calculate')
1616
@click.option('--output-frame', '-o', is_flag=True, default=False, help='output frames')
1717
@click.option('--frame-output-loc', default='data/frames', help='the folder to output frame')
1818
@click.option('--saved-feature-loc', default='data/trained_features', help='the folder to save trained feature')

0 commit comments

Comments
 (0)