Skip to content

Commit dffdddd

Browse files
authored
Merge pull request #274 from yanghaojin/master
Adding pre-trained models, scripts, datasets for masked facial detection using Ultra-L face models.
2 parents ce8829e + 23156cf commit dffdddd

26 files changed

Lines changed: 673 additions & 13 deletions

masked_face/README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Masked Face Detection
2+
3+
![img1](https://github.com/yanghaojin/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/masked_face/readme_imgs/img4.jpeg)
4+
5+
# Extending Ultra-L face model for masked facial detection
6+
7+
Ultra-L face detection model achieves great popularity in edge and client based applications. It has a surprising balance of model size and accuracy performance, e.g.,
8+
- The default FP32 *.pth model size is **1.04~1.1MB**, and the inference framework int8 quantization size is about **300KB**.
9+
- Only **90~109 MFlops** for 320x240 input resolution.
10+
- Supported inference code for [NCNN](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/master/ncnn), [MNN](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/master/MNN), [INT8](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/master/MNN/model),
11+
[Onnx](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/master/caffe), [OpencvDNN](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/caffe/ultra_face_opencvdnn_inference.py), etc.
12+
13+
COVID-19 has ravaged the world in the past two years, and wearing masks has become the norm in our lives on many occasions. However, most traditional face datasets such as Wider Face currently lack face samples with masks. Therefore, the face detection model based on conventional datasets will fail in the scenario where all attendants wear masks.
14+
[Face-Mask-Detection](https://github.com/chandrikadeb7/Face-Mask-Detection) is the most popular face detection model we can find on Github that supports Mask detection.
15+
However, this model is trained only using 4095 images (2165 masked / 1930 without mask), which is a pretty small dataset.
16+
We will experience many false positives in the actual application scenarios.
17+
18+
This original intention inspired me to build a larger dataset to provide better open-source masked facial detection models and help the world survive the pandemic.
19+
The main contribution of this project is to provide balanced facial training data combining the [wider_face_add_lm_10_10](https://drive.google.com/open?id=1OBY-Pk5hkcVBX1dRBOeLI4e4OCvqJRnH) and [MAFA face](https://imsg.ac.cn/research/maskedface.html) dataset. The [MAFA](https://imsg.ac.cn/research/maskedface.html) data was converted to pascal-VOC format and merged into the [wider_face_add_lm_10_10](https://drive.google.com/open?id=1OBY-Pk5hkcVBX1dRBOeLI4e4OCvqJRnH).
20+
21+
## About the WIDER_MAFA_Balanced dataset
22+
The *Wider_MAFA_Balanced* dataset (**4.8GB**) can be downloaded at [HPI owncloud](https://owncloud.hpi.de/s/L4MUGqrpeENLbSv).
23+
It contains 38225 images in total where 31084 for training and 7141 for testing, respectively.
24+
The specific composition information is shown in the following table:
25+
26+
Source| Class | Train | Test |Total|
27+
----|------|-------|------|-----
28+
MAFA face| masked_face | 15542 | 3922 | 19464 |
29+
Wider face| face | 12859 | 3219 | 16078 |
30+
*MAFA human body* | face | 2683 | 0 |2683
31+
32+
*MAFA human body* indicates the extracted training samples with human body occlusions.
33+
34+
I use this script for converting MAFA data format to pascal VOC:
35+
```Shell
36+
masked_face/mafa2voc.py
37+
```
38+
39+
## About the pre-trained models
40+
```Shell
41+
masked_face/
42+
pretrained/
43+
RFB-320-masked_face-v2.pth # trained with 320x240
44+
RFB-640-masked_face-v2.pth # trained with 640x480
45+
RFB-640-masked_face-v2.onnx # suitable for 640x480
46+
RFB-1280-masked_face-v2.onnx # suitable for 1280x960
47+
```
48+
49+
## Detection Result (input resolution: 1280x960)
50+
51+
The following visual results are created by using this script:
52+
```Shell
53+
masked_face/detect_imgs.py
54+
```
55+
![img1](https://github.com/yanghaojin/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/masked_face/readme_imgs/img1.jpeg)
56+
![img1](https://github.com/yanghaojin/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/masked_face/readme_imgs/img2.jpeg)
57+
![img1](https://github.com/yanghaojin/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/masked_face/readme_imgs/img3.jpg)
58+
![img1](https://github.com/yanghaojin/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/masked_face/readme_imgs/img5.jpeg)
59+
![img1](https://github.com/yanghaojin/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/masked_face/readme_imgs/img6.webp)
60+
![img1](https://github.com/yanghaojin/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/masked_face/readme_imgs/img7.webp)
61+
![img1](https://github.com/yanghaojin/Ultra-Light-Fast-Generic-Face-Detector-1MB/blob/master/masked_face/readme_imgs/img8.jpeg)
62+
63+
Author: Haojin Yang

masked_face/detect_imgs.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""
2+
This code is used to batch detect images in a folder.
3+
"""
4+
import os, sys
5+
currentdir = os.path.dirname(os.path.realpath(__file__))
6+
parentdir = os.path.dirname(currentdir)
7+
sys.path.append(parentdir)
8+
9+
import argparse
10+
import os
11+
import sys
12+
13+
import cv2
14+
15+
from vision.ssd.config.fd_config import define_img_size
16+
17+
parser = argparse.ArgumentParser(
18+
description='detect_imgs')
19+
20+
parser.add_argument('--net_type', default="RFB", type=str,
21+
help='The network architecture ,optional: RFB (higher precision) or slim (faster)')
22+
parser.add_argument('--input_size', default=1280, type=int,
23+
help='define network input size,default optional value 128/160/320/480/640/1280')
24+
parser.add_argument('--threshold', default=0.3, type=float,
25+
help='score threshold')
26+
parser.add_argument('--candidate_size', default=1200, type=int,
27+
help='nms candidate size')
28+
parser.add_argument('--path', default="imgs", type=str,
29+
help='imgs dir')
30+
parser.add_argument('--test_device', default="cpu", type=str,
31+
help='cuda:0 or cpu')
32+
args = parser.parse_args()
33+
define_img_size(args.input_size) # must put define_img_size() before 'import create_mb_tiny_fd, create_mb_tiny_fd_predictor'
34+
35+
from vision.ssd.mb_tiny_fd import create_mb_tiny_fd, create_mb_tiny_fd_predictor
36+
from vision.ssd.mb_tiny_RFB_fd import create_Mb_Tiny_RFB_fd, create_Mb_Tiny_RFB_fd_predictor
37+
38+
result_path = "detect_imgs_results"
39+
label_path = "./voc-model-labels.txt"
40+
test_device = args.test_device
41+
42+
class_names = [name.strip() for name in open(label_path).readlines()]
43+
44+
if args.net_type == 'RFB':
45+
model_path = "pretrained/RFB-640-masked_face-v2.pth"
46+
net = create_Mb_Tiny_RFB_fd(len(class_names), is_test=True, device=test_device)
47+
predictor = create_Mb_Tiny_RFB_fd_predictor(net, candidate_size=args.candidate_size, device=test_device)
48+
else:
49+
print("The net type is wrong!")
50+
sys.exit(1)
51+
net.load(model_path)
52+
53+
if not os.path.exists(result_path):
54+
os.makedirs(result_path)
55+
listdir = os.listdir(args.path)
56+
sum = 0
57+
for file_path in listdir:
58+
img_path = os.path.join(args.path, file_path)
59+
orig_image = cv2.imread(img_path)
60+
if orig_image is None: continue
61+
image = cv2.cvtColor(orig_image, cv2.COLOR_BGR2RGB)
62+
boxes, labels, probs = predictor.predict(image, args.candidate_size / 2, args.threshold)
63+
sum += boxes.size(0)
64+
for i in range(boxes.size(0)):
65+
box = boxes[i, :]
66+
label_index = labels[i].item()
67+
cv2.rectangle(orig_image, (int(box[0]), int(box[1])), (int(box[2]), int(box[3])), (0, 165, 255), 2)
68+
# label = f"""{voc_dataset.class_names[labels[i]]}: {probs[i]:.2f}"""
69+
label = f"{probs[i]:.2f}"
70+
# cv2.putText(orig_image, label, (box[0], box[1] - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
71+
cv2.putText(orig_image, class_names[label_index],
72+
(int(box[0]), int(box[1]) - 10),
73+
cv2.FONT_HERSHEY_SIMPLEX,
74+
0.5, # font scale
75+
(100, 0, 255),
76+
1) # line type
77+
cv2.putText(orig_image, str(boxes.size(0)), (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
78+
cv2.imwrite(os.path.join(result_path, file_path), orig_image)
79+
print(f"Found {len(probs)} faces. The output image is {result_path}")
80+
print(sum)

masked_face/imgs/img1.jpeg

439 KB
Loading

masked_face/imgs/img2.jpeg

112 KB
Loading

masked_face/imgs/img3.jpg

53.3 KB
Loading

masked_face/imgs/img4.jpeg

119 KB
Loading

masked_face/imgs/img5.jpeg

121 KB
Loading

masked_face/imgs/img6.webp

149 KB
Loading

masked_face/imgs/img7.webp

483 KB
Loading

masked_face/imgs/img8.jpeg

285 KB
Loading

0 commit comments

Comments
 (0)