2024-11-21 05:55:55 +00:00
|
|
|
{
|
|
|
|
|
"cells": [
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {
|
|
|
|
|
"vscode": {
|
|
|
|
|
"languageId": "plaintext"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"source": [
|
2025-11-26 00:06:13 +09:00
|
|
|
"# Face Detection and Alignment with UniFace\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
"\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
"This notebook demonstrates face detection and alignment using the **UniFace** library.\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"## 1. Install UniFace"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
2025-12-10 00:18:11 +09:00
|
|
|
"execution_count": 1,
|
2024-11-21 05:55:55 +00:00
|
|
|
"metadata": {},
|
2025-12-10 00:18:11 +09:00
|
|
|
"outputs": [
|
|
|
|
|
{
|
|
|
|
|
"name": "stdout",
|
|
|
|
|
"output_type": "stream",
|
|
|
|
|
"text": [
|
|
|
|
|
"Note: you may need to restart the kernel to use updated packages.\n"
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
2024-11-21 05:55:55 +00:00
|
|
|
"source": [
|
2025-12-07 19:51:08 +09:00
|
|
|
"%pip install -q uniface"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
2025-11-26 00:06:13 +09:00
|
|
|
"## 2. Import Libraries"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
2025-12-10 00:18:11 +09:00
|
|
|
"execution_count": 2,
|
2024-11-21 05:55:55 +00:00
|
|
|
"metadata": {},
|
2025-12-07 19:51:08 +09:00
|
|
|
"outputs": [
|
|
|
|
|
{
|
|
|
|
|
"name": "stdout",
|
|
|
|
|
"output_type": "stream",
|
|
|
|
|
"text": [
|
2025-12-10 00:18:11 +09:00
|
|
|
"1.3.1\n"
|
2025-12-07 19:51:08 +09:00
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
2024-11-21 05:55:55 +00:00
|
|
|
"source": [
|
|
|
|
|
"import cv2\n",
|
|
|
|
|
"import matplotlib.pyplot as plt\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
"import numpy as np\n",
|
|
|
|
|
"\n",
|
2025-12-07 19:51:08 +09:00
|
|
|
"import uniface\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
"from uniface.detection import RetinaFace\n",
|
|
|
|
|
"from uniface.face_utils import face_alignment\n",
|
2025-12-07 19:51:08 +09:00
|
|
|
"from uniface.visualization import draw_detections\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"print(uniface.__version__)"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
2025-11-26 00:06:13 +09:00
|
|
|
"## 3. Initialize the Detector"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
2025-12-10 00:18:11 +09:00
|
|
|
"execution_count": 3,
|
2024-11-21 05:55:55 +00:00
|
|
|
"metadata": {},
|
|
|
|
|
"outputs": [
|
|
|
|
|
{
|
2025-11-26 00:06:13 +09:00
|
|
|
"name": "stdout",
|
2024-11-21 05:55:55 +00:00
|
|
|
"output_type": "stream",
|
|
|
|
|
"text": [
|
2025-12-07 19:51:08 +09:00
|
|
|
"✓ Model loaded (CoreML (Apple Silicon))\n"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"source": [
|
2025-11-26 00:06:13 +09:00
|
|
|
"detector = RetinaFace(\n",
|
|
|
|
|
" conf_thresh=0.5,\n",
|
|
|
|
|
" nms_thresh=0.4,\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
")"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
2025-11-26 00:06:13 +09:00
|
|
|
"## 4. Load Images and Perform Detection + Alignment"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
2025-12-10 00:18:11 +09:00
|
|
|
"execution_count": 4,
|
2024-11-21 05:55:55 +00:00
|
|
|
"metadata": {},
|
2025-11-26 00:06:13 +09:00
|
|
|
"outputs": [
|
|
|
|
|
{
|
|
|
|
|
"name": "stdout",
|
|
|
|
|
"output_type": "stream",
|
|
|
|
|
"text": [
|
|
|
|
|
"Processed 5 images\n"
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
],
|
2024-11-21 05:55:55 +00:00
|
|
|
"source": [
|
|
|
|
|
"image_paths = [\n",
|
2025-11-30 20:32:07 +09:00
|
|
|
" '../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",
|
2024-11-21 05:55:55 +00:00
|
|
|
"]\n",
|
|
|
|
|
"\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
"original_images = []\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
"detection_images = []\n",
|
|
|
|
|
"aligned_images = []\n",
|
|
|
|
|
"\n",
|
|
|
|
|
"for image_path in image_paths:\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
" # Load image\n",
|
|
|
|
|
" image = cv2.imread(image_path)\n",
|
|
|
|
|
" if image is None:\n",
|
2025-11-30 20:32:07 +09:00
|
|
|
" print(f'Error: Could not read {image_path}')\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
" continue\n",
|
|
|
|
|
"\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
" # Detect faces\n",
|
|
|
|
|
" faces = detector.detect(image)\n",
|
|
|
|
|
" if not faces:\n",
|
2025-11-30 20:32:07 +09:00
|
|
|
" print(f'No faces detected in {image_path}')\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
" continue\n",
|
|
|
|
|
"\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
" # Draw detections\n",
|
|
|
|
|
" bbox_image = image.copy()\n",
|
2025-11-30 20:32:07 +09:00
|
|
|
" bboxes = [f['bbox'] for f in faces]\n",
|
|
|
|
|
" scores = [f['confidence'] for f in faces]\n",
|
|
|
|
|
" landmarks = [f['landmarks'] for f in faces]\n",
|
2025-12-07 19:51:08 +09:00
|
|
|
" draw_detections(image=bbox_image, bboxes=bboxes, scores=scores, landmarks=landmarks, vis_threshold=0.6, fancy_bbox=True)\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
"\n",
|
|
|
|
|
" # Align first detected face (returns aligned image and inverse transform matrix)\n",
|
2025-11-30 20:32:07 +09:00
|
|
|
" first_landmarks = faces[0]['landmarks']\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
" 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",
|
2025-11-30 20:32:07 +09:00
|
|
|
"print(f'Processed {len(original_images)} images')"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "markdown",
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"source": [
|
2025-11-26 00:06:13 +09:00
|
|
|
"## 5. Visualize Results"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
2025-12-10 00:18:11 +09:00
|
|
|
"execution_count": 5,
|
2024-11-21 05:55:55 +00:00
|
|
|
"metadata": {},
|
|
|
|
|
"outputs": [
|
|
|
|
|
{
|
|
|
|
|
"data": {
|
2025-12-10 00:18:11 +09:00
|
|
|
"image/png": "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
|
2024-11-21 05:55:55 +00:00
|
|
|
"text/plain": [
|
2025-01-09 05:16:32 +00:00
|
|
|
"<Figure size 1500x1000 with 15 Axes>"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"output_type": "display_data"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"source": [
|
2025-11-26 00:06:13 +09:00
|
|
|
"fig, axes = plt.subplots(3, len(original_images), figsize=(15, 10))\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
"\n",
|
2025-11-30 20:32:07 +09:00
|
|
|
"row_titles = ['Original', 'Detection', 'Aligned']\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
"\n",
|
2025-01-09 05:16:32 +00:00
|
|
|
"for row, images in enumerate([original_images, detection_images, aligned_images]):\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
" for col, img in enumerate(images):\n",
|
|
|
|
|
" axes[row, col].imshow(img)\n",
|
2025-11-30 20:32:07 +09:00
|
|
|
" axes[row, col].axis('off')\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
" if col == 0:\n",
|
2025-11-30 20:32:07 +09:00
|
|
|
" axes[row, col].set_title(row_titles[row], fontsize=12, loc='left')\n",
|
2024-11-21 05:55:55 +00:00
|
|
|
"\n",
|
|
|
|
|
"plt.tight_layout()\n",
|
2025-11-26 00:06:13 +09:00
|
|
|
"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"
|
2024-11-21 05:55:55 +00:00
|
|
|
]
|
2025-01-09 05:16:32 +00:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"cell_type": "code",
|
|
|
|
|
"execution_count": null,
|
|
|
|
|
"metadata": {},
|
|
|
|
|
"outputs": [],
|
|
|
|
|
"source": []
|
2024-11-21 05:55:55 +00:00
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"metadata": {
|
|
|
|
|
"kernelspec": {
|
2025-03-26 11:55:56 +09:00
|
|
|
"display_name": "base",
|
2024-11-21 05:55:55 +00:00
|
|
|
"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",
|
2025-11-26 00:06:13 +09:00
|
|
|
"version": "3.13.5"
|
2024-11-21 05:55:55 +00:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"nbformat": 4,
|
|
|
|
|
"nbformat_minor": 2
|
|
|
|
|
}
|