def process_faces(self, faces: List[Face], extract_embedding: bool = True, extract_ga: bool = True, return_face_data: bool = False): chunked_faces = to_chunks(faces, self.max_rec_batch_size) for chunk in chunked_faces: chunk = list(chunk) crops = [e.facedata for e in chunk] total = len(crops) embeddings = [None] * total ga = [[None, None]] * total if extract_embedding: t0 = time.time() embeddings = self.rec_model.get_embedding(crops) t1 = time.time() took = t1 - t0 logging.debug( f'Embedding {total} faces took: {took} ({took/total} per face)' ) if extract_ga: t0 = time.time() ga = [self.ga_model.get(face.facedata) for face in chunk] t1 = time.time() logging.debug( f'Extracting g/a for {total} faces took: {t1 - t0}') for i, crop in enumerate(crops): embedding = None embedding_norm = None normed_embedding = None gender = None age = None embedding = embeddings[i] if extract_embedding: embedding_norm = norm(embedding) normed_embedding = embedding / embedding_norm _ga = ga[i] if extract_ga: gender = int(_ga[0]) age = _ga[1] face = chunk[i] if return_face_data is False: face = face._replace(facedata=None) face = face._replace(embedding=embedding, embedding_norm=embedding_norm, normed_embedding=normed_embedding, gender=gender, age=age) yield face
def embed_faces(self, faces: List[Face]): chunked_faces = to_chunks(faces, self.max_rec_batch_size) for chunk in chunked_faces: chunk = list(chunk) crops = [e.facedata for e in chunk] embeddings = self.rec_model.get_embedding(crops) for i, emb in enumerate(embeddings): embedding = emb embedding_norm = norm(embedding) normed_embedding = embedding / embedding_norm face = chunk[i]._replace(embedding=embedding, embedding_norm=embedding_norm, normed_embedding=normed_embedding) yield face
async def get(self, images, extract_embedding: bool = True, extract_ga: bool = True, detect_masks: bool = True, return_face_data: bool = True, max_size: List[int] = None, threshold: float = 0.6, mask_thresh: float = 0.89, limit_faces: int = 0): ts = time.perf_counter() # If detector has input_shape attribute, use it instead of provided value try: max_size = self.det_model.retina.input_shape[2:][::-1] except: pass # Pre-assign max_size to resize function _partial_resize = partial(resize_image, max_size=max_size) # Pre-assign threshold to detect function _partial_detect = partial(self.det_model.detect, threshold=threshold) # Initialize resied images iterator res_images = map(_partial_resize, images) batches = to_chunks(res_images, self.max_det_batch_size) faces = [] faces_per_img = {} for bid, batch in enumerate(batches): batch_imgs, scales = zip(*batch) t0 = time.perf_counter() det_predictions = zip(*_partial_detect(batch_imgs)) t1 = time.perf_counter() logging.debug(f'Detection took: {(t1 - t0) * 1000:.3f} ms.') for idx, pred in enumerate(det_predictions): await asyncio.sleep(0) orig_id = (bid * self.max_det_batch_size) + idx boxes, probs, landmarks = pred faces_per_img[orig_id] = len(boxes) if not isinstance(boxes, type(None)): t0 = time.perf_counter() if limit_faces > 0: boxes, probs, landmarks = self.sort_boxes(boxes, probs, landmarks, shape=batch_imgs[idx].shape, max_num=limit_faces) # Translate points to original image size boxes = reproject_points(boxes, scales[idx]) landmarks = reproject_points(landmarks, scales[idx]) # Crop faces from original image instead of resized to improve quality if extract_ga or extract_embedding or return_face_data or detect_masks: crops = face_align.norm_crop_batched(images[orig_id], landmarks) else: crops = [None] * len(boxes) for i, _crop in enumerate(crops): face = Face(bbox=boxes[i], landmark=landmarks[i], det_score=probs[i], num_det=i, scale=scales[idx], facedata=_crop) faces.append(face) t1 = time.perf_counter() logging.debug(f'Cropping {len(boxes)} faces took: {(t1 - t0) * 1000:.3f} ms.') # Process detected faces faces = list(self.process_faces(faces, extract_embedding=extract_embedding, extract_ga=extract_ga, return_face_data=return_face_data, detect_masks=detect_masks, mask_thresh=mask_thresh)) faces_by_img = [] offset = 0 for key in faces_per_img: value = faces_per_img[key] faces_by_img.append(faces[offset:offset + value]) offset += value tf = time.perf_counter() logging.debug(colorize_log(f'Full processing took: {(tf - ts) * 1000:.3f} ms.', 'red')) return faces_by_img
def process_faces(self, faces: List[Face], extract_embedding: bool = True, extract_ga: bool = True, return_face_data: bool = False, detect_masks: bool = True, mask_thresh: float = 0.89): chunked_faces = to_chunks(faces, self.max_rec_batch_size) for chunk in chunked_faces: chunk = list(chunk) crops = [e.facedata for e in chunk] total = len(crops) embeddings = [None] * total ga = [[None, None]] * total if extract_embedding: t0 = time.perf_counter() embeddings = self.rec_model.get_embedding(crops) took = time.perf_counter() - t0 logging.debug( f'Embedding {total} faces took: {took * 1000:.3f} ms. ({(took / total) * 1000:.3f} ms. per face)') if extract_ga and self.ga_model: t0 = time.perf_counter() ga = self.ga_model.get(crops) t1 = time.perf_counter() took = t1 - t0 logging.debug( f'Extracting g/a for {total} faces took: {took * 1000:.3f} ms. ({(took / total) * 1000:.3f} ms. per face)') if detect_masks and self.mask_model: t0 = time.perf_counter() masks = self.mask_model.get(crops) t1 = time.perf_counter() took = t1 - t0 logging.debug( f'Detecting masks for {total} faces took: {took * 1000:.3f} ms. ({(took / total) * 1000:.3f} ms. per face)') for i, crop in enumerate(crops): embedding_norm = None normed_embedding = None gender = None age = None mask = None mask_probs = None embedding = embeddings[i] if extract_embedding: embedding_norm = norm(embedding) normed_embedding = embedding / embedding_norm if extract_ga and self.ga_model: _ga = ga[i] gender = int(_ga[0]) age = _ga[1] if detect_masks and self.mask_model: _masks = masks[i] mask = False mask_prob = float(_masks[0]) no_mask_prob = float(_masks[1]) if mask_prob > no_mask_prob and mask_prob >= mask_thresh: mask = True mask_probs = dict(mask=mask_prob, no_mask=no_mask_prob) face = chunk[i] if return_face_data is False: face = face._replace(facedata=None) face = face._replace(embedding=embedding, embedding_norm=embedding_norm, normed_embedding=normed_embedding, gender=gender, age=age, mask=mask, mask_probs=mask_probs) yield face