Skip to content

Molyleaf/MT-Photos-AI-OpenVINO

Repository files navigation

MT-Photos AI (OpenVINO)

主服务提供 OCR、图像向量(QA-CLIP)和人脸向量(InsightFace),并会把 /clip/txt 代理转发到独立部署的 Text-CLIP CPU 服务。仓库另外补充了一个仅面向本地 Windows 开发机的并行 Image-CLIP CUDA 子项目,目录为 image-clip/,不影响主服务 OpenVINO 实现。本文档仅保留最终用户部署、运行和配置说明。

部署前准备

  • Python 3.12
  • 已准备模型目录(至少包含):
    • models/qa-clip/openvino(主线 FP16 IR;主服务镜像只打包 openvino_image_fp16.*,Text-CLIP 镜像只打包 openvino_text_fp16.*
    • models/qa-clip/openvino_fp32(FP32 对照组 IR,仅供本地精度观测脚本使用;默认不会进入 Docker 构建上下文或任一运行时镜像)
    • models/qa-clip/huggingface(仅 scripts/convert.py 和本地 Windows CUDA image-clip/ 子项目需要;默认不会进入 Docker 构建上下文)
    • models/insightface/models/antelopev2(主服务镜像需要;至少保留 scrfd_10g_bnkps.onnxglintr100.onnx
  • 主服务入口:app/server.py
  • 主服务仍由 app/server.py 暴露路由;同目录 app/bootstrap.pyapp/image_io.pyapp/text_clip_proxy.py 仅用于维护性拆分,不改变启动方式和接口语义
  • Text-CLIP 服务入口:text-clip/app/server.py
  • Windows 本地 CUDA Image-CLIP 子项目命令行入口:image-clip/starter.py
  • Windows 本地 CUDA Image-CLIP 子项目服务实现入口:image-clip/app/server.py
  • QA-CLIP 离线转换脚本:scripts/convert.py
  • scripts/convert.py 默认只清理 OpenVINO 导出产物与 OpenVINO cache,不会重复删除 Hugging Face cache 或本地 QA-CLIP snapshot;脚本会优先复用 models/qa-clip/huggingface 本地 snapshot,其次复用 cache/huggingface,仅在本地都缺失时才回源下载 TencentARC/QA-CLIP-ViT-L-14
  • scripts/convert.py 会顺序导出两套 QA-CLIP OpenVINO IR:主线 models/qa-clip/openvino/openvino_image_fp16.xmlmodels/qa-clip/openvino/openvino_text_fp16.xml 固定使用 OpenVINO 原生 ov.save_model(..., compress_to_fp16=True),同时生成 FP32 对照组 models/qa-clip/openvino_fp32/openvino_image_fp32.xmlmodels/qa-clip/openvino_fp32/openvino_text_fp32.xml
  • 仓库不再保留单独的 scripts/convert_fp16.py;FP16 与 FP32 对照组统一由 scripts/convert.py 一次导出完成
  • scripts/indicate_precision_impact.py 默认比较 models/qa-clip/openvino_fp32 与主线 models/qa-clip/openvino,并输出 models/qa-clip/openvino/precision_impact.json
  • 独立 Text-CLIP 服务已自带 tokenizer 与词表资源,不再依赖主服务 app/ 目录
  • requirements.txt 当前固定 insightface==0.7.3,并显式包含 onnxopencv-python-headless;其中 onnx 用于 InsightFace 首次懒加载时在受控 runtime copy 中修正 glintr100.onnx 的识别输出 batch 元数据,主服务镜像构建时还会先卸载依赖链带入的 opencv-python / opencv-contrib-python*,再只回装 opencv-python-headless

运行时环境变量

以下仅保留当前仍支持且建议用户配置的环境变量。兼容别名、已固定为内部基线的变量,以及 docker compose 插值变量不再写入 README。

主容器

环境变量 说明 默认值
API_AUTH_KEY API Key;为空或 no-key 时关闭鉴权 mt_photos_ai_extra
CLIP_IMAGE_BATCH /clip/img 标准预处理后的微批上限 8
CLIP_IMAGE_BATCH_WAIT_MS /clip/img 微批等待窗口,单位毫秒 5
CLIP_INFERENCE_DEVICE /clip/img 使用的 OpenVINO 设备;请求 AUTO/GPU 时需要可用 GPU 跟随 INFERENCE_DEVICE
INFERENCE_DEVICE 主容器默认 OpenVINO 设备 AUTO
INFERENCE_EXEC_TIMEOUT 非文本任务执行超时,单位秒 max(30, INFERENCE_TASK_TIMEOUT)
INFERENCE_QUEUE_MAX_SIZE /clip/img/ocr/represent 共享图片名额上限;运行时硬上限仍为 10 10
INFERENCE_QUEUE_TIMEOUT 非文本任务排队超时,单位秒 跟随 INFERENCE_TASK_TIMEOUT
INFERENCE_TASK_TIMEOUT 兼容旧配置的总超时基线;未单独设置时会派生排队/执行超时 10
INSIGHTFACE_BATCH_WAIT_MS /represent recognition 聚合窗口,单位毫秒 5
INSIGHTFACE_OV_DEVICE InsightFace OpenVINO EP 推理设备;支持 AUTO / CPU / GPU AUTO
INSIGHTFACE_OV_ENABLE_OPENCL_THROTTLING 是否启用 OpenCL throttling false
INSIGHTFACE_OV_NUM_THREADS InsightFace OpenVINO EP 线程数;-1 为运行时默认 -1
LOG_LEVEL 日志级别;同时作用于 mt_photos_ai.*uvicorn.* WARNING
MODEL_PATH 模型根目录 <repo>/models
NON_TEXT_IDLE_RELEASE_SECONDS 主容器非文本模型空闲释放窗口;<=0 表示关闭 60
OCR_EXEC_TIMEOUT OCR 执行超时,单位秒 max(30, INFERENCE_EXEC_TIMEOUT)
OCR_MAX_CONCURRENT_REQUESTS OCR 应用层最大并发请求数;不会超过共享图片名额 4
OCR_PREWARM_DELAY_SECONDS RapidOCR 一次性后台预热延迟,单位秒 1.0
OCR_PREWARM_ENABLED 是否启用一次性后台 RapidOCR 预热 false
OV_CACHE_DIR OpenVINO 编译缓存目录;当前主要作用于主服务自管的 OpenVINO 路径 <repo>/cache/openvino
PORT 服务端口;同时影响镜像入口与健康检查 8060
TEXT_CLIP_API_KEY 主服务转发 /clip/txt 时使用的上游 API Key;未设置时复用 API_AUTH_KEY 跟随 API_AUTH_KEY
TEXT_CLIP_REQUEST_TIMEOUT 主服务转发 /clip/txt 的上游请求超时,单位秒 30
TEXT_CLIP_SERVER_URL 独立 Text-CLIP 服务 URL;主服务会自动拼接 /clip/txt,并把请求体原样转发给上游 http://127.0.0.1:8061

Text-CLIP 容器

环境变量 说明 默认值
API_AUTH_KEY API Key;为空或 no-key 时关闭鉴权 mt_photos_ai_extra
LOG_LEVEL 日志级别;同时作用于 mt_photos_ai.text_clipuvicorn.* WARNING
MODEL_PATH 文本模型根目录 <repo>/models
OV_CACHE_DIR Text-CLIP OpenVINO 编译缓存目录 <repo>/cache/openvino
PORT Text-CLIP 服务端口;同时影响镜像入口与健康检查 8061

补充说明:

  • 开发机本地验证时,主服务建议把主要后端统一设为 CPUINFERENCE_DEVICE=CPUCLIP_INFERENCE_DEVICE=CPUINSIGHTFACE_OV_DEVICE=CPU;如需走主服务 /clip/txt 代理,请同时把 TEXT_CLIP_SERVER_URL 设为本机 Text-CLIP 服务地址。
  • 主容器的 Vision-CLIP / OCR / InsightFace 统一由 /app 内的非文本子进程管理,按请求懒加载,并可按 NON_TEXT_IDLE_RELEASE_SECONDS 自动释放;Text-CLIP 容器启动即加载文本模型,进程存活期间常驻内存,不参与空闲释放或主容器 /restart*,主服务对 /clip/txt 只做原始请求体 HTTP 转发。
  • 主服务 /clip/img 的视觉 PPP 预处理固定为 resize -> center crop -> BGR->RGB -> /255 -> mean/std -> NCHW;如果需要重建 QA-CLIP IR,请重新执行 py -3.12 scripts/convert.py 以保持与当前“主线 FP16 + FP32 对照组”导出基线一致。
  • 如需评估主线 FP16 与 FP32 对照组之间的 embedding 保真度、结构差异和体积变化,直接执行 py -3.12 scripts/indicate_precision_impact.py 即可。
  • 主容器在 /restart/restart_v2/restartV2/restartv2 或空闲释放完成后,会直接结束当前非文本子进程;匿名内存由子进程退出统一回收,下一次 /clip/img/ocr/represent 请求再按需重建新的非文本子进程与对应模型。
  • /represent/clip/img/ocr 混合到达时,主服务会继续保持“单活非文本模型族”串行切换,但等待旧模型族退场的过程不会再阻塞整个事件循环;父进程现在还会按到达顺序做 FIFO 轮换,避免某一族请求在高并发下插队或长期饥饿。
  • 主服务会在非文本子进程退出后输出一条进程/cgroup 内存拆分日志,至少包含 VmRSS/RssAnon/RssFile/RssShmemcgroup_anon/cgroup_file/cgroup_shmem,用于判断释放后剩余内存主要来自匿名内存、文件页缓存还是 shared memory。
  • InsightFace 现在会以低残留 ORT session 基线加载:关闭 CPU memory arena、关闭 memory pattern,并固定单 lane session inter_op/intra_op 线程数为 1。这会优先减少 /represent 卸载后的匿名内存残留,而不是追求极限吞吐。
  • /restart/restart_v2/restartV2/restartv2 现已统一语义:同步释放当前非文本子进程,不重启主服务进程。
  • OCR 已切到 rapidocr==3.8.0 原生最简初始化:仅把 Det/Cls/Rec.engine_type 设为 openvino,其它全部使用上游默认配置。
  • RapidOCR 会在首次加载 OCR 时按上游内置 URL 检查并下载默认模型;镜像不再内置 OCR 模型,因此首次 /ocr 在空缓存环境下需要联网,并且容器运行用户需要可写的 rapidocr/models 目录。
  • PORT 会同时影响容器入口和健康检查;如果修改它,请同步调整 docker-compose.ymlports:docker run -p
  • 如果手动执行 uvicorn server:app,最早期的 uvicorn bootstrap 日志仍以 CLI --log-level 为准。

Windows 本机部署

  1. 确认 Python 版本:
py -3.12 -V
  1. (可选)创建并激活虚拟环境:
py -3.12 -m venv .venv
.\.venv\Scripts\activate
  1. (可选)设置环境变量:
$env:API_AUTH_KEY="your_secret_key"
$env:INFERENCE_DEVICE="CPU"
$env:CLIP_INFERENCE_DEVICE="CPU"
$env:CLIP_IMAGE_BATCH="8"
$env:INSIGHTFACE_OV_DEVICE="CPU"
$env:TEXT_CLIP_SERVER_URL="http://127.0.0.1:8061"
$env:LOG_LEVEL="INFO"
  1. 如需使用 /clip/txt,先启动独立 Text-CLIP 服务:
cd text-clip\app
py -3.12 server.py
  1. 如需使用主服务接口,再另开终端启动主服务:
cd app
py -3.12 server.py

启动后,客户端可统一访问主服务端口;主服务收到 /clip/txt 请求时会按 TEXT_CLIP_SERVER_URL 把原始请求体转发到独立 Text-CLIP 服务。本地 Windows 验证和测试命令统一建议显式使用 py -3.12,避免落到其他解释器版本。

如需在本地 Windows 开发机上单独跑 CUDA 版 Image-CLIP,可改用 image-clip/ 子项目;该子项目使用独立依赖文件 image-clip/requirement.txt,可在仓库根目录直接执行 py -3.12 image-clip\starter.py,或进入 image-clip\ 后执行 py -3.12 starter.py。更完整的环境变量与冒烟说明见 image-clip/README.md

如需手动执行 uvicorn server:app,请显式传入 --port / --log-level,例如 uvicorn server:app --host 0.0.0.0 --port 8060 --log-level info

Debian/Linux Docker 部署

推荐宿主环境:

  • Debian 13(或兼容发行版)
  • Docker Engine 24+ 与 Docker Compose v2
  • Intel iGPU 场景下,宿主机可见 /dev/dri

如需在 Debian 13 宿主或自定义镜像中补齐 Intel Xe 依赖,可参考:

apt-get update && apt-get install -y --no-install-recommends \
  ca-certificates \
  libdrm2 \
  libglib2.0-0 \
  libgomp1 \
  libze1 \
  ocl-icd-libopencl1 \
  mesa-opencl-icd

说明:

  • 参考 OpenVINO 与 Intel GPU 官方文档,容器内 OpenVINO GPU 运行时需要 intel-opencl-icd + Level Zero 运行库(Debian 包名 libze-intel-gpu1),以及 libze1/ocl-icd-libopencl1
  • 当前主 Dockerfile 使用两阶段构建:builder 阶段在 /home/appuser/.venv/home/appuser/wheels 内完成编译链安装、wheel 预构建、离线依赖安装,并沿用“先卸载所有 OpenCV Python 变体,再只回装 opencv-python-headless”的清洁安装链;若传递依赖带入 opencv-python / opencv-contrib-python*,会在 builder 内卸载并清理对应 wheel,runtime 阶段只复制瘦身后的 .venv 并加入 PATH,不再重新执行 pip install,也不再把 wheelhouse 带进最终镜像。
  • builder 还会裁剪 venv 中运行时不需要的 pip/wheel 包、include/share 目录、__pycache__、测试目录和静态头文件,以进一步压缩最终镜像体积;当前服务依赖会直接随用户目录下的 .venv 一起进入最终镜像。
  • 根目录 .dockerignore 会先排除 Hugging Face snapshot、FP32 IR、InsightFace 运行时派生目录和本地开发目录;在 Docker Engine 24+ / BuildKit 下,Dockerfile.dockerignoretext-clip/DockerFile-TextCLIP.dockerignore 还会进一步按镜像裁掉不相关的模型与源码,避免主镜像构建时混入文本 IR,或 Text-CLIP 镜像构建时混入视觉 IR / InsightFace 模型。
  • 因此当前运行时镜像不再包含 libgl1libsm6libxext6libxrender1,也不再打包 mesa-vulkan-driversintel-media-va-driver-non-free
  • Intel GPU 固件属于宿主机职责;若宿主 Debian 13 需要补齐固件,请在宿主机安装 firmware-misc-nonfree(或兼容包名 firmware-misc-non-free),而不是放进应用容器。
  • Dockerfile 已固化为清华 APT + 清华 PyPI 镜像;APT 基础列表、sid pin 文件和安装包名单都直接写在仓库文件中,便于回溯与审计。
  • Dockerfile 使用 BuildKit cache mount 复用 apt/pip 下载缓存;在 Docker Engine 24+ / Compose v2 下,重复构建通常可直接命中这两类缓存。
  • 当前镜像构建阶段会临时启用 sid 源,仅安装 intel-opencl-icdlibze-intel-gpu1
  • 独立 Text-CLIP 镜像也改为两阶段构建,只复制瘦身后的 .venvopenvino_text_fp16.* 和 tokenizer 资源;它固定走 OpenVINO CPU,不安装 Intel GPU runtime、不需要 /dev/dri,也不会混入 OpenCV Python 包、视觉 FP16 IR、FP32 对照组或 Hugging Face snapshot。
  • 主服务镜像只保留 openvino_image_fp16.*、InsightFace antelopev2 和运行时必需脚本;不会混入 openvino_text_fp16.*openvino_fp32/*qa-clip/huggingface/*buffalo_l
  • 容器内不安装 xserver-xorg-video-intel(该包用于 Xorg 显示栈,不是本服务的无头推理运行前提)。
  • 服务上传读图链已统一改为 OpenCV 原生解码,镜像不再包含 ffmpeg/ffprobe、VAAPI/oneVPL/QSV 媒体栈,也不预装 clinfo 这类诊断工具。
  • 若服务日志出现 available_devices=['CPU'],即使 /dev/dri 可见,也通常意味着容器里缺少可用的 Intel GPU OpenVINO/OpenCL runtime,或 /dev/dri 并非真实的 Intel DRM render node。
  • 参考文档:

方式一:docker compose

  1. 先准备镜像。docker-compose.example.yml 只引用 image:,不会在 docker compose up 时构建:
docker build -t mt-photos-ai-openvino .
docker build -f text-clip/DockerFile-TextCLIP -t mt-photos-ai-text-clip .

如果镜像已经在本地存在,或你改为从镜像仓库拉取,可以跳过这一步。

  1. 准备配置文件:
cp docker-compose.example.yml docker-compose.yml
  1. 按宿主机实际情况修改 group_add 中的 video / render GID(Debian 默认通常是 44 / 109)。

  2. 按需调整 docker-compose.yml

  • 生产环境建议覆盖 API_AUTH_KEY
  • 主容器需要映射 /dev/dri 并设置正确的 video/render 组;mt-photos-ai-text-clip 固定走 CPU,不需要 /dev/dri
  • mt-photos-ai-text-clip 启动后会常驻加载文本模型;主服务请通过 TEXT_CLIP_SERVER_URL=http://mt-photos-ai-text-clip:8061 代理转发 /clip/txt
  • 有 Intel iGPU 且已映射 /dev/dri 时,主容器建议使用 INFERENCE_DEVICE=AUTOCLIP_INFERENCE_DEVICE=AUTOINSIGHTFACE_OV_DEVICE=AUTO
  • 如需修改服务监听端口,请同时调整 PORTports: 映射
  • 如需挂载自定义模型或自定义 OpenVINO cache 目录,再显式覆盖 MODEL_PATH / OV_CACHE_DIR
  • 首次 /ocr 若容器内还没有 RapidOCR 默认模型,会在线下载;离线环境请先完成一次带网络的 OCR 预热
  • /clip/img 仍未跑满 GPU,可结合业务流量逐步调大 CLIP_IMAGE_BATCH,并保持 CLIP_IMAGE_BATCH_WAIT_MS 在个位数毫秒级,避免明显放大单请求尾延迟
  • 如需限制 OCR 首次冷加载带来的单次长尾,可显式设置 OCR_EXEC_TIMEOUT=30
  • /represent 当前固定为单 lane OpenVINO EP 推理 + 4 请求聚合预算;如需权衡吞吐与尾延迟,可只小幅调整 INSIGHTFACE_BATCH_WAIT_MS
  1. 启动服务:
docker compose up -d
  1. 查看状态:
docker compose ps
docker compose logs -f mt-photos-ai-openvino mt-photos-ai-text-clip

方式二:docker run

先创建一个供两个容器互访的用户网络:

docker network create mt-photos-ai-net
docker run -d \
  --name mt-photos-ai-text-clip \
  --network mt-photos-ai-net \
  --init \
  -p 8061:8061 \
  -e API_AUTH_KEY=mt_photos_ai_extra \
  -e LOG_LEVEL=WARNING \
  -e PORT=8061 \
  mt-photos-ai-text-clip

docker run -d \
  --name mt-photos-ai-openvino \
  --network mt-photos-ai-net \
  --init \
  -p 8060:8060 \
  --device /dev/dri:/dev/dri \
  --group-add $(getent group video | cut -d: -f3) \
  --group-add $(getent group render | cut -d: -f3) \
  -e API_AUTH_KEY=mt_photos_ai_extra \
  -e CLIP_IMAGE_BATCH=8 \
  -e CLIP_INFERENCE_DEVICE=AUTO \
  -e INFERENCE_DEVICE=AUTO \
  -e INSIGHTFACE_OV_DEVICE=AUTO \
  -e LOG_LEVEL=WARNING \
  -e NON_TEXT_IDLE_RELEASE_SECONDS=60 \
  -e OCR_EXEC_TIMEOUT=30 \
  -e PORT=8060 \
  -e TEXT_CLIP_SERVER_URL=http://mt-photos-ai-text-clip:8061 \
  mt-photos-ai-openvino

如需自定义模型目录或 OpenVINO cache 目录,可继续追加 -e MODEL_PATH=...-e OV_CACHE_DIR=...。如需修改任一服务的 PORT,请同步调整对应的 -p <host_port>:<container_port>。如果 Text-CLIP 容器使用了不同的鉴权密钥,请额外给主容器设置 -e TEXT_CLIP_API_KEY=...

容器内设备检查

首次部署建议执行以下检查:

docker exec -it mt-photos-ai-openvino ls -l /dev/dri
docker exec -it mt-photos-ai-openvino python -c "import openvino as ov; print(ov.Core().available_devices)"

若请求了 GPU 推理但容器内 GPU 设备不可用,服务会直接报错并终止启动。

RapidOCR 默认模型下载

主服务不再随镜像内置 OCR 模型。/ocr 首次懒加载时会由 rapidocr==3.8.0 按上游内置 default_models.yaml 自动检查并下载默认模型,运行时只额外把 DetClsRecengine_type 改为 openvino

  • 首次加载 OCR 时需要可访问上游内置模型 URL
  • Docker 镜像已预创建并授权 rapidocr/models 目录,容器内无需再手动挂载 models/rapidocr
  • 如需验证上游内置样例图片,可直接使用官方文档里的 img_url=https://www.modelscope.cn/models/RapidAI/RapidOCR/resolve/master/resources/test_files/ch_en_num.jpg

冒烟检查

推荐先在 Docker 容器内执行 InsightFace 专项冒烟:

docker build -f text-clip/DockerFile-TextCLIP -t mt-photos-ai-text-clip .
docker build -t mt-photos-ai-openvino .
docker run --rm -it \
  -e INFERENCE_DEVICE=CPU \
  -e CLIP_INFERENCE_DEVICE=CPU \
  -e INSIGHTFACE_OV_DEVICE=CPU \
  mt-photos-ai-openvino \
  python scripts/smoke_insightface.py --device CPU

如宿主机已映射 Intel iGPU 的 /dev/dri,可进一步执行 GPU 冒烟:

docker run --rm -it \
  --device /dev/dri:/dev/dri \
  --group-add $(getent group video | cut -d: -f3) \
  --group-add $(getent group render | cut -d: -f3) \
  -e INFERENCE_DEVICE=CPU \
  -e CLIP_INFERENCE_DEVICE=CPU \
  -e INSIGHTFACE_OV_DEVICE=GPU \
  mt-photos-ai-openvino \
  python scripts/smoke_insightface.py --device GPU

脚本会校验:

  • OpenVINOExecutionProvider 仍是 detection/recognition 的首位 provider,且 device_type 与请求一致
  • InsightFace 运行事实已收敛到单 lane detector/recognition + 固定 CPU 预处理 + 最多 4 路预处理 worker + 默认 4 请求 admission/聚合上限
  • 运行时模型目录已经收敛到 _runtime_models/models/antelopev2
  • 本地 /represent pipeline 与原生 FaceAnalysis.get 的检测框、分数和 embedding 语义保持一致
  • 原生 detector.detect 与 batched recognition 路径均保持稳定
  • 并发 /represent 聚合路径与顺序 /represent 路径一致,不改变检测框、分数和 embedding 语义
  • release_models_for_restart() 后 face runtime 引用已释放,且能够重新加载

如需专项验证混合 /represent/clip/img/ocr 请求的乱序到达与轻量压力场景,可直接运行:

python scripts/smoke_non_text_process.py --mixed-burst 24 --concurrency 6

该脚本会依次做顺序切换校验、/represent -> /clip/img 乱序回归、六种有序两两轮换校验,以及一个使用假子进程 worker 的轻量微压测,并输出各操作的平均延迟、P95 和 worker 重启次数。

服务启动后,也可以再做基础端点检查:

curl -s http://127.0.0.1:8060/
curl -s -X POST http://127.0.0.1:8060/check -H "api-key: mt_photos_ai_extra"
curl -s -X POST http://127.0.0.1:8060/clip/txt -H "api-key: mt_photos_ai_extra" -H "Content-Type: application/json" -d '{"text":"smoke"}'
curl -s -X POST http://127.0.0.1:8061/check -H "api-key: mt_photos_ai_extra"
curl -s -X POST http://127.0.0.1:8061/clip/txt -H "api-key: mt_photos_ai_extra" -H "Content-Type: application/json" -d '{"text":"smoke"}'

GET / 外,业务端点在启用鉴权时都需要携带 api-key 请求头。

许可证

MIT License

About

使用 QA-CLIP + Insightface + RapidOCR + OpenVINO 的一站式 AI 服务。适用于 MT-Photos,可能也适用于 Immich。

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors