|
|
"""Face detector, used only for the demo to crop faces, as datasets have already been face-cropped"""
|
|
|
|
|
|
import os
|
|
|
import cv2
|
|
|
import numpy as np
|
|
|
|
|
|
|
|
|
DEFAULT_FACE_DETECTOR = "utils/res10_300x300_ssd_iter_140000_fp16.caffemodel"
|
|
|
DEFAULT_DEPLOY = "utils/deploy.prototxt"
|
|
|
|
|
|
def enclosing_square(rect):
|
|
|
|
|
|
x, y, w, h = rect
|
|
|
side = max(w, h)
|
|
|
|
|
|
cx = x + w // 2
|
|
|
cy = y + h // 2
|
|
|
x_new = cx - side // 2
|
|
|
y_new = cy - side // 2
|
|
|
return (x_new, y_new, side, side)
|
|
|
|
|
|
|
|
|
def cut(frame, roi):
|
|
|
pA = (int(roi[0]), int(roi[1]))
|
|
|
pB = (int(roi[0] + roi[2]), int(roi[1] + roi[3]))
|
|
|
W, H = frame.shape[1], frame.shape[0]
|
|
|
A0 = pA[0] if pA[0] >= 0 else 0
|
|
|
A1 = pA[1] if pA[1] >= 0 else 0
|
|
|
data = frame[A1:pB[1], A0:pB[0]]
|
|
|
if pB[0] < W and pB[1] < H and pA[0] >= 0 and pA[1] >= 0:
|
|
|
return data
|
|
|
w, h = int(roi[2]), int(roi[3])
|
|
|
img = np.zeros((h, w, frame.shape[2]), dtype=np.uint8)
|
|
|
offX = int(-roi[0]) if roi[0] < 0 else 0
|
|
|
offY = int(-roi[1]) if roi[1] < 0 else 0
|
|
|
np.copyto(img[offY:offY + data.shape[0], offX:offX + data.shape[1]], data)
|
|
|
return img
|
|
|
|
|
|
class FaceDetector:
|
|
|
"""Face detector to spot faces inside a picture."""
|
|
|
def __init__(self, face_detector = DEFAULT_FACE_DETECTOR, deploy=DEFAULT_DEPLOY, confidence_threshold=0.8):
|
|
|
self.detector = cv2.dnn.readNetFromCaffe(deploy, face_detector)
|
|
|
self.confidence_threshold = confidence_threshold
|
|
|
|
|
|
def detect(self, image, pad_rect=True):
|
|
|
blob = cv2.dnn.blobFromImage(image, 1.0, (300, 300), [104, 117, 123], False, False)
|
|
|
frameHeight, frameWidth, channels = image.shape
|
|
|
self.detector.setInput(blob)
|
|
|
detections = self.detector.forward()
|
|
|
|
|
|
faces_result = []
|
|
|
for i in range(detections.shape[2]):
|
|
|
confidence = detections[0, 0, i, 2]
|
|
|
if confidence > self.confidence_threshold:
|
|
|
x1 = int(detections[0, 0, i, 3] * frameWidth)
|
|
|
y1 = int(detections[0, 0, i, 4] * frameHeight)
|
|
|
x2 = int(detections[0, 0, i, 5] * frameWidth)
|
|
|
y2 = int(detections[0, 0, i, 6] * frameHeight)
|
|
|
f = (x1, y1, x2 - x1, y2 - y1)
|
|
|
if f[2] > 1 and f[3] > 1:
|
|
|
rect = enclosing_square(f) if pad_rect else f
|
|
|
img_crop = cut(image, rect)
|
|
|
if img_crop.shape[0] > 0 and img_crop.shape[1] > 0:
|
|
|
faces_result.append((img_crop, confidence, rect))
|
|
|
if len(faces_result) == 0:
|
|
|
return None
|
|
|
return faces_result
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
input_folder = "src/demo_images"
|
|
|
output_crop_folder = "./test/detector/crop"
|
|
|
output_bbox_folder = "./test/detector/bbox"
|
|
|
|
|
|
os.makedirs(output_crop_folder, exist_ok=True)
|
|
|
os.makedirs(output_bbox_folder, exist_ok=True)
|
|
|
|
|
|
face_detector = FaceDetector(confidence_threshold=0.8)
|
|
|
|
|
|
image_files = sorted([
|
|
|
f for f in os.listdir(input_folder)
|
|
|
if os.path.isfile(os.path.join(input_folder, f))
|
|
|
])
|
|
|
|
|
|
for img_file in image_files:
|
|
|
img_path = os.path.join(input_folder, img_file)
|
|
|
img = cv2.imread(img_path)
|
|
|
if img is None:
|
|
|
continue
|
|
|
|
|
|
faces = face_detector.detect(img, pad_rect=True)
|
|
|
base_name = os.path.splitext(os.path.basename(img_path))[0]
|
|
|
|
|
|
if faces is not None:
|
|
|
|
|
|
for idx, (crop, confidence, bbox) in enumerate(faces):
|
|
|
crop_path = os.path.join(output_crop_folder, f"{base_name}_face{idx}.jpg")
|
|
|
cv2.imwrite(crop_path, crop)
|
|
|
|
|
|
|
|
|
img_bbox = img.copy()
|
|
|
for idx, (_, _, bbox) in enumerate(faces):
|
|
|
x, y, w, h = bbox
|
|
|
cv2.rectangle(img_bbox, (x, y), (x + w, y + h), (0, 0, 255), 2)
|
|
|
bbox_path = os.path.join(output_bbox_folder, f"{base_name}_bbox.jpg")
|
|
|
cv2.imwrite(bbox_path, img_bbox)
|
|
|
|