{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Face Detection with UniFace\n",
    "\n",
    "This notebook demonstrates face detection using the **UniFace** library.\n",
    "\n",
    "## 1. Install UniFace"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install -q uniface"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Import Libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1.3.1\n"
     ]
    }
   ],
   "source": [
    "import cv2\n",
    "import IPython.display as display\n",
    "from PIL import Image\n",
    "\n",
    "import uniface\n",
    "from uniface.detection import RetinaFace\n",
    "from uniface.visualization import draw_detections\n",
    "\n",
    "print(uniface.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Initialize the Detector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "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 and Display Input Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "[encoded data, 147632 characters]",
      "image/png": "[encoded data, 2019188 characters]",
      "text/plain": [
       "<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1024x624>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "image_path = '../assets/test.jpg'\n",
    "pil_image = Image.open(image_path)\n",
    "pil_image"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Detect Faces"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Detected 27 face(s)\n"
     ]
    },
    {
     "data": {
      "image/jpeg": "[encoded data, 166472 characters]",
      "image/png": "[encoded data, 1998188 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=1024x624>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Load image\n",
    "image = cv2.imread(image_path)\n",
    "\n",
    "# Detect faces - returns list of face dictionaries\n",
    "faces = detector.detect(image)\n",
    "print(f'Detected {len(faces)} face(s)')\n",
    "\n",
    "# Unpack face data for visualization\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",
    "\n",
    "# Draw detections\n",
    "draw_detections(image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=0.6, fancy_bbox=True)\n",
    "\n",
    "# Display result\n",
    "output_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
    "display.display(Image.fromarray(output_image))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Detect Top-K Faces\n",
    "\n",
    "Use `max_num` to limit the number of detected faces.\n",
    "\n",
    "### Top-2 faces:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Detected 2 face(s)\n"
     ]
    },
    {
     "data": {
      "image/jpeg": "[encoded data, 149072 characters]",
      "image/png": "[encoded data, 2017408 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=1024x624>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "image = cv2.imread(image_path)\n",
    "\n",
    "faces = detector.detect(image, max_num=2)\n",
    "print(f'Detected {len(faces)} face(s)')\n",
    "\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",
    "\n",
    "draw_detections(image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=0.6, fancy_bbox=True)\n",
    "\n",
    "output_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
    "display.display(Image.fromarray(output_image))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Top-5 faces:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Detected 5 face(s)\n"
     ]
    },
    {
     "data": {
      "image/jpeg": "[encoded data, 151276 characters]",
      "image/png": "[encoded data, 2014508 characters]",
      "text/plain": [
       "<PIL.Image.Image image mode=RGB size=1024x624>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "image = cv2.imread(image_path)\n",
    "\n",
    "faces = detector.detect(image, max_num=5)\n",
    "print(f'Detected {len(faces)} face(s)')\n",
    "\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",
    "\n",
    "draw_detections(image=image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=0.6, fancy_bbox=True)\n",
    "\n",
    "output_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
    "display.display(Image.fromarray(output_image))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Notes\n",
    "\n",
    "- `detect()` returns a list of dictionaries with keys: `bbox`, `confidence`, `landmarks`\n",
    "- Adjust `conf_thresh` and `nms_thresh` for your use case\n",
    "- Use `max_num` to limit detected faces"
   ]
  },
  {
   "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
}
