{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "plaintext"
    }
   },
   "source": [
    "# Face Detection and Alignment with UniFace\n",
    "\n",
    "This notebook demonstrates face detection and alignment using the **UniFace** library.\n",
    "\n",
    "## 1. Install UniFace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -q uniface"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Import Libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "from uniface.detection import RetinaFace\n",
    "from uniface.face_utils import face_alignment\n",
    "from uniface.visualization import draw_detections"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Initialize the Detector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model loaded (CoreML (Apple Silicon))\n"
     ]
    }
   ],
   "source": [
    "detector = RetinaFace(\n",
    "    conf_thresh=0.5,\n",
    "    nms_thresh=0.4,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Load Images and Perform Detection + Alignment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Processed 5 images\n"
     ]
    }
   ],
   "source": [
    "image_paths = [\n",
    "    '../assets/test_images/image0.jpg',\n",
    "    '../assets/test_images/image1.jpg',\n",
    "    '../assets/test_images/image2.jpg',\n",
    "    '../assets/test_images/image3.jpg',\n",
    "    '../assets/test_images/image4.jpg',\n",
    "]\n",
    "\n",
    "original_images = []\n",
    "detection_images = []\n",
    "aligned_images = []\n",
    "\n",
    "for image_path in image_paths:\n",
    "    # Load image\n",
    "    image = cv2.imread(image_path)\n",
    "    if image is None:\n",
    "        print(f'Error: Could not read {image_path}')\n",
    "        continue\n",
    "\n",
    "    # Detect faces\n",
    "    faces = detector.detect(image)\n",
    "    if not faces:\n",
    "        print(f'No faces detected in {image_path}')\n",
    "        continue\n",
    "\n",
    "    # Draw detections\n",
    "    bbox_image = image.copy()\n",
    "    bboxes = [f['bbox'] for f in faces]\n",
    "    scores = [f['confidence'] for f in faces]\n",
    "    landmarks = [f['landmarks'] for f in faces]\n",
    "    draw_detections(bbox_image, bboxes, scores, landmarks, vis_threshold=0.6)\n",
    "\n",
    "    # Align first detected face (returns aligned image and inverse transform matrix)\n",
    "    first_landmarks = faces[0]['landmarks']\n",
    "    aligned_image, _ = face_alignment(image, first_landmarks, image_size=112)\n",
    "\n",
    "    # Convert BGR to RGB for visualization\n",
    "    original_images.append(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))\n",
    "    detection_images.append(cv2.cvtColor(bbox_image, cv2.COLOR_BGR2RGB))\n",
    "    aligned_images.append(cv2.cvtColor(aligned_image, cv2.COLOR_BGR2RGB))\n",
    "\n",
    "print(f'Processed {len(original_images)} images')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Visualize Results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 1223744 characters]",
      "text/plain": [
       "<Figure size 1500x1000 with 15 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(3, len(original_images), figsize=(15, 10))\n",
    "\n",
    "row_titles = ['Original', 'Detection', 'Aligned']\n",
    "\n",
    "for row, images in enumerate([original_images, detection_images, aligned_images]):\n",
    "    for col, img in enumerate(images):\n",
    "        axes[row, col].imshow(img)\n",
    "        axes[row, col].axis('off')\n",
    "        if col == 0:\n",
    "            axes[row, col].set_title(row_titles[row], fontsize=12, loc='left')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Notes\n",
    "\n",
    "- `detect()` returns a list of face dictionaries with `bbox`, `confidence`, `landmarks`\n",
    "- `face_alignment()` uses 5-point landmarks to align and crop the face\n",
    "- Default output size is 112x112 (standard for face recognition models)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "base",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
