""" PriviGaze Inference Script - Run the student model on-device Usage: python inference.py --model ./checkpoints/student_best.pt --image face.jpg """ import argparse import torch import numpy as np from PIL import Image, ImageOps from models.student import PriviGazeStudent def load_model(checkpoint_path, device='cpu'): model = PriviGazeStudent() ckpt = torch.load(checkpoint_path, map_location=device) model.load_state_dict(ckpt.get('student_state_dict', ckpt)) model.eval().to(device) return model def preprocess(image_path, size=224): img = Image.open(image_path).convert('L') img = img.resize((size, size)) arr = np.array(img).astype(np.float32) / 255.0 tensor = torch.from_numpy(arr).unsqueeze(0).unsqueeze(0) return tensor * 2 - 1 def predict(model, tensor, device='cpu'): tensor = tensor.to(device) with torch.no_grad(): pitch, yaw, _ = model(tensor) return pitch.item(), yaw.item() def main(): p = argparse.ArgumentParser() p.add_argument('--model', type=str, required=True, help='Path to student checkpoint') p.add_argument('--image', type=str, required=True, help='Path to grayscale face image') p.add_argument('--device', type=str, default='cpu') args = p.parse_args() device = torch.device(args.device) model = load_model(args.model, device) tensor = preprocess(args.image) pitch, yaw = predict(model, tensor, device) print(f"Gaze: pitch={pitch:+.2f}deg, yaw={yaw:+.2f}deg") print(f"Direction: {'up' if pitch>0 else 'down'} {abs(pitch):.1f}deg, " f"{'right' if yaw>0 else 'left'} {abs(yaw):.1f}deg") if __name__ == "__main__": main()