face_verify / face_verifier.py
subhan971's picture
Update face_verifier.py
6796282 verified
Raw
History Blame Contribute Delete
7.59 kB
"""
Face Verification Module
Uses DeepFace or face_recognition for face embedding extraction and comparison
"""
import cv2
import numpy as np
import logging
from typing import Optional, Tuple
from scipy.spatial.distance import cosine
logger = logging.getLogger(__name__)
class FaceVerifier:
"""
Face verification using deep learning embeddings
"""
def __init__(self, model_name: str = "Facenet"):
"""
Initialize face verifier
Args:
model_name: Model to use ('Facenet', 'VGG-Face', 'OpenFace', 'DeepFace')
"""
self.model_name = model_name
self.backend = None
self._initialize_backend()
def _initialize_backend(self):
"""Initialize the face recognition backend"""
try:
# Use custom implementation (no external dependencies)
self.backend = "custom"
logger.info("✓ Using custom face verification (OpenCV-based)")
except Exception as e:
logger.error(f"Backend initialization error: {e}")
self.backend = "custom"
def extract_embedding(self, image: np.ndarray, face: Tuple[int, int, int, int]) -> Optional[np.ndarray]:
"""
Extract face embedding vector
Args:
image: Input image (BGR format)
face: Face bounding box (x, y, w, h)
Returns:
Face embedding vector or None
"""
try:
return self._extract_custom(image, face)
except Exception as e:
logger.error(f"Embedding extraction error: {e}")
return None
def _extract_custom(self, image: np.ndarray, face: Tuple[int, int, int, int]) -> Optional[np.ndarray]:
"""
Extract custom embedding using OpenCV and traditional CV features
Optimized for face verification without heavy ML libraries
"""
try:
x, y, w, h = face
face_roi = image[y:y+h, x:x+w]
# Resize to standard size
face_resized = cv2.resize(face_roi, (128, 128))
# Convert to grayscale
gray = cv2.cvtColor(face_resized, cv2.COLOR_BGR2GRAY)
# Extract multiple features for robust embedding
features = []
# 1. HOG (Histogram of Oriented Gradients) features
hog = cv2.HOGDescriptor((128, 128), (16, 16), (8, 8), (8, 8), 9)
hog_features = hog.compute(gray)
features.append(hog_features.flatten())
# 2. LBP (Local Binary Patterns) features
lbp = self._compute_lbp(gray)
lbp_hist, _ = np.histogram(lbp.ravel(), bins=256, range=(0, 256))
lbp_hist = lbp_hist.astype("float")
lbp_hist /= (lbp_hist.sum() + 1e-7)
features.append(lbp_hist)
# 3. Pixel intensity histogram
hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
hist = hist.flatten()
hist /= (hist.sum() + 1e-7)
features.append(hist)
# 4. Color histograms (RGB channels)
for i in range(3):
color_hist = cv2.calcHist([face_resized], [i], None, [64], [0, 256])
color_hist = color_hist.flatten()
color_hist /= (color_hist.sum() + 1e-7)
features.append(color_hist)
# 5. Edge features
edges = cv2.Canny(gray, 50, 150)
edge_hist, _ = np.histogram(edges.ravel(), bins=64, range=(0, 256))
edge_hist = edge_hist.astype("float")
edge_hist /= (edge_hist.sum() + 1e-7)
features.append(edge_hist)
# Concatenate all features
embedding = np.concatenate(features)
# Normalize to unit length
embedding = embedding / (np.linalg.norm(embedding) + 1e-7)
return embedding
except Exception as e:
logger.error(f"Custom extraction error: {e}")
return None
def _compute_lbp(self, image: np.ndarray, radius: int = 1, n_points: int = 8) -> np.ndarray:
"""Compute Local Binary Pattern"""
h, w = image.shape
lbp = np.zeros((h, w), dtype=np.uint8)
for i in range(radius, h - radius):
for j in range(radius, w - radius):
center = image[i, j]
code = 0
for k in range(n_points):
angle = 2 * np.pi * k / n_points
x = int(round(i + radius * np.cos(angle)))
y = int(round(j + radius * np.sin(angle)))
if 0 <= x < h and 0 <= y < w:
if image[x, y] >= center:
code |= (1 << k)
lbp[i, j] = code
return lbp
def compare_embeddings(self, embedding1: np.ndarray, embedding2: np.ndarray) -> float:
"""
Compare two face embeddings
Args:
embedding1: First face embedding
embedding2: Second face embedding
Returns:
Similarity score (0 to 1, higher is more similar)
"""
try:
# Ensure embeddings are numpy arrays
emb1 = np.array(embedding1).flatten()
emb2 = np.array(embedding2).flatten()
# Check if embeddings have same dimension
if emb1.shape != emb2.shape:
logger.error(f"Embedding dimension mismatch: {emb1.shape} vs {emb2.shape}")
return 0.0
# Compute cosine similarity
# cosine distance = 1 - cosine similarity
distance = cosine(emb1, emb2)
similarity = 1 - distance
# Ensure similarity is in [0, 1]
similarity = max(0.0, min(1.0, similarity))
return similarity
except Exception as e:
logger.error(f"Embedding comparison error: {e}")
return 0.0
def verify(self, image1: np.ndarray, face1: Tuple[int, int, int, int],
image2: np.ndarray, face2: Tuple[int, int, int, int],
threshold: float = 0.6) -> Tuple[bool, float]:
"""
Verify if two faces belong to the same person
Args:
image1: First image
face1: Face bounding box in first image
image2: Second image
face2: Face bounding box in second image
threshold: Similarity threshold for verification
Returns:
(is_same_person, similarity_score)
"""
try:
# Extract embeddings
emb1 = self.extract_embedding(image1, face1)
emb2 = self.extract_embedding(image2, face2)
if emb1 is None or emb2 is None:
logger.error("Failed to extract embeddings")
return False, 0.0
# Compare embeddings
similarity = self.compare_embeddings(emb1, emb2)
# Determine if same person
is_same = similarity >= threshold
return is_same, similarity
except Exception as e:
logger.error(f"Verification error: {e}")
return False, 0.0