aruntandra's picture
Upload exp_recognition.py
5751632 verified
Raw
History Blame Contribute Delete
3.62 kB
import os
import numpy as np
import cv2
import torch
from PIL import Image
# Make sure these modules exist in the same package folder
from .exp_recognition_model import trnscm, load_model, classes
#############################################################################################################################
# Caution: Don't change any of the filenames, function names and definitions #
# Always use the current_path + file_name for refering any files, without it we cannot access files on the server #
#############################################################################################################################
current_path = os.path.dirname(os.path.abspath(__file__))
_MODEL = None
_DEVICE = None
def _to_bgr_np(img):
"""
Convert various inputs -> OpenCV BGR numpy array.
Supports: filepath str, PIL.Image, numpy RGB/gray/RGBA.
"""
if img is None:
return None
# filepath string
if isinstance(img, str):
return cv2.imread(img)
# PIL image
if isinstance(img, Image.Image):
rgb = np.array(img.convert("RGB"))
return cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)
# numpy array
if isinstance(img, np.ndarray):
arr = img
# grayscale (H,W)
if arr.ndim == 2:
return cv2.cvtColor(arr, cv2.COLOR_GRAY2BGR)
# RGBA -> RGB
if arr.ndim == 3 and arr.shape[2] == 4:
arr = arr[:, :, :3]
# assume RGB (common from Gradio type="numpy")
if arr.ndim == 3 and arr.shape[2] == 3:
return cv2.cvtColor(arr, cv2.COLOR_RGB2BGR)
return arr
return None
def detected_face(image_bgr):
"""
Returns PIL grayscale cropped face with maximum area.
Returns 0 if not detected or cascade not available.
"""
face_haar = os.path.join(current_path, "haarcascade_frontalface_default.xml")
face_cascade = cv2.CascadeClassifier(face_haar)
# if cascade missing or failed to load
if face_cascade.empty():
return 0
gray = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
if faces is None or len(faces) == 0:
return 0
# pick max area
x, y, w, h = max(faces, key=lambda f: f[2] * f[3])
crop = gray[y:y + h, x:x + w]
return Image.fromarray(crop)
def _load_cached_model():
"""
Load the expression model once and cache it.
"""
global _MODEL, _DEVICE
if _MODEL is not None:
return _MODEL, _DEVICE
_DEVICE = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
ckpt_path = os.path.join(current_path, "best_resnet18_expression.pt")
if not os.path.exists(ckpt_path):
raise FileNotFoundError(f"Missing checkpoint: {ckpt_path}")
_MODEL = load_model(ckpt_path, device=str(_DEVICE), num_classes=len(classes))
return _MODEL, _DEVICE
def get_expression(img):
"""
img: can be numpy array (RGB), PIL.Image, or filepath string
returns: expression string
"""
model, device = _load_cached_model()
img_bgr = _to_bgr_np(img)
if img_bgr is None:
return "UNKNOWN"
face = detected_face(img_bgr)
if face == 0:
# fallback: use full frame grayscale
face = Image.fromarray(cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY))
x = trnscm(face).unsqueeze(0).to(device) # [1,3,H,W] if trnscm ensures RGB
with torch.no_grad():
logits = model(x)
pred = int(torch.argmax(logits, dim=1).item())
return classes.get(pred, "UNKNOWN").capitalize()