147 lines
4.3 KiB
Markdown
147 lines
4.3 KiB
Markdown
# Face SDK Web 服务
|
||
|
||
把 Android Face SDK 的人脸贴图渲染逻辑迁移到 Python Web 服务:上传一张人物图 + 效果 ID,
|
||
返回叠加渲染后的 PNG。CPU 实现(OpenCV 仿射 warp + alpha 混合),不依赖 GPU。
|
||
|
||
---
|
||
|
||
## 渲染原理(与 Android SDK 一致)
|
||
|
||
```
|
||
input image ──► MediaPipe FaceLandmarker ──► 478 normalized landmarks
|
||
│
|
||
▼
|
||
face_picture_3dmax.obj (468 顶点+UV / 852 三角形)
|
||
每个 obj 顶点 i 的画面位置 = landmarks[INDEX_MAP[i]]
|
||
│
|
||
▼
|
||
每个三角形:
|
||
src = obj UV × effect 贴图尺寸 (在 512×512 effect 里)
|
||
dst = landmarks 像素位置 (在 input image 里)
|
||
cv2.getAffineTransform → cv2.warpAffine → alpha 混合
|
||
│
|
||
▼
|
||
output PNG(同原图尺寸)
|
||
```
|
||
|
||
`INDEX_MAP[468]` 同步自 `vulkan/hardcode_data.h::indexMap`,UV 的 V 翻转与
|
||
`vulkan/FaceApp.cpp::LoadOBJ` 行为一致。
|
||
|
||
---
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
python/
|
||
├── server/
|
||
│ ├── main.py FastAPI 入口(/health /effects /render)
|
||
│ ├── face_renderer.py 渲染核心
|
||
│ ├── mesh.py OBJ 解析
|
||
│ ├── index_map.py OBJ vertex → MediaPipe landmark 映射
|
||
│ ├── assets/
|
||
│ │ ├── face_picture_3dmax.obj ← 来自 app/src/main/assets
|
||
│ │ └── face_landmarker.task ← 可选;不放则自动回退到 app/src/main/assets/
|
||
│ └── effects/
|
||
│ └── 1.png 512×512 RGBA 效果贴图,文件名是效果 ID(1-99)
|
||
└── requirements.txt
|
||
```
|
||
|
||
---
|
||
|
||
## 一次性安装
|
||
|
||
需要 Python 3.10–3.12(mediapipe 当前不支持 3.13)。
|
||
|
||
```powershell
|
||
# Windows PowerShell(或 cmd)
|
||
cd D:\face_sdk\python
|
||
python -m venv venv
|
||
.\venv\Scripts\Activate.ps1 # cmd 用 .\venv\Scripts\activate.bat
|
||
pip install --upgrade pip
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
```bash
|
||
# Linux / macOS
|
||
cd /path/to/face_sdk/python
|
||
python3 -m venv venv
|
||
source venv/bin/activate
|
||
pip install --upgrade pip
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
---
|
||
|
||
## 启动服务
|
||
|
||
```powershell
|
||
cd D:\face_sdk\python
|
||
.\venv\Scripts\Activate.ps1
|
||
uvicorn server.main:app --host 0.0.0.0 --port 8000
|
||
```
|
||
|
||
启动日志看到 `Application startup complete.` 即就绪。
|
||
|
||
---
|
||
|
||
## API
|
||
|
||
### `GET /health`
|
||
```json
|
||
{"ok": true}
|
||
```
|
||
|
||
### `GET /effects`
|
||
返回当前 `server/effects/` 下存在的效果 ID(按 `<id>.png` 文件名提取,1–99):
|
||
```json
|
||
{"effects": [1]}
|
||
```
|
||
|
||
### `POST /render`
|
||
- form-data:
|
||
- `image`: 文件(jpeg / png / webp,≤ 20MB)
|
||
- `effect_id`: 整数(1–99,必须在 `/effects` 列表内)
|
||
- 成功:`200`,`Content-Type: image/png`,body 是 PNG 字节
|
||
- 没检测到人脸:`400 {"error":"no_face"}`
|
||
- effect_id 不存在:`404`
|
||
- 参数错误(图非法 / 太大 / id 越界):`400`
|
||
|
||
#### curl 示例
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8000/render \
|
||
-F image=@/path/to/photo.jpg \
|
||
-F effect_id=1 \
|
||
--output result.png
|
||
```
|
||
|
||
#### Python 示例
|
||
|
||
```python
|
||
import requests
|
||
files = {"image": open("photo.jpg", "rb")}
|
||
data = {"effect_id": 1}
|
||
r = requests.post("http://localhost:8000/render", files=files, data=data)
|
||
r.raise_for_status()
|
||
open("result.png", "wb").write(r.content)
|
||
```
|
||
|
||
---
|
||
|
||
## 添加新效果
|
||
|
||
把一张 512×512 的 RGBA PNG 放到 `python/server/effects/<id>.png`(`<id>` 是 1–99 的整数)。
|
||
服务下次响应 `/effects` 时会自动列出;首次被请求时按需载入并缓存内存。
|
||
|
||
PNG 必须有 alpha 通道——透明区域不会盖到原图上。
|
||
|
||
---
|
||
|
||
## 已知限制
|
||
|
||
- 纯 2D 仿射 warp,与 Android SDK 行为一致:人脸侧面角度大时贴图会拉伸。
|
||
- MediaPipe FaceLandmarker 每次 `detect()` 不保证多线程并发安全,服务用锁串行化。
|
||
单进程同步部署够用;要更高并发请用多 worker 或前置队列。
|
||
- 单张 4K 图渲染约 100–300ms(852 三角形 × `cv2.warpAffine`);如需更快可改成
|
||
`cv2.remap` 一次性贴整脸,但需要更多代码。
|