这是一个本地运行的 Web 应用:前端用于快速浏览/筛选/打标,后端负责扫描目录、生成缩略图缓存、SQLite 持久化;并通过一个本地 AI 服务进行鸟种识别(BioCLIP2)。
# 1. 启动服务(首次需构建)
docker compose up --build -d
# 2. 生成全球全量鸟种标签(推荐,大幅提高识别率;已生成可跳过)
npm run labels:world && docker compose restart ai
# 3. 浏览器访问:http://localhost:3001
# 4. 在页面添加库路径 /photos 并扫描 -> 识别(如需覆盖重跑,点“重识别”)
# 5. 如需清除识别结果:支持单张清除(详情页垃圾桶)与按库一键清除(顶部“清除识别”)使用开源镜像站搜索镜像:https://docker.aityp.com/ 本项目dockerfile已直接添加国内镜像源,无需额外配置。
在项目根目录:
docker compose up --build -d打开:http://localhost:3001/
如果你本机有 GNU Make(例如 macOS/Linux,或 Windows 的 Git Bash/MSYS2),可以直接:
make help
make up
make logs
make ai-health
make data-clean容器内默认挂载了 ./sample-photos 到 /photos。你可以在页面里新增库路径为:/photos,然后点击“扫描”。
仓库内的 sample-photos 默认只保留少量示例图片。
如果要换成你自己的目录,请修改 docker-compose.yml 的 app.volumes,把宿主机目录挂载到容器(示例):
volumes:
- D:/BirdJpg:/photos:ro然后在页面新增库路径填:/photos。
默认内置了少量常见鸟示例标签,用于验证链路。你可以通过 BIRD_LABELS_PATH 提供一个 CSV,格式为:
中文名,学名
麻雀,Passer montanus
喜鹊,Pica pica将该 CSV 挂载进 ai 容器并设置环境变量即可。
识别时并不是直接把原图丢给 AI,而是由后端先生成“识别专用图片”,再调用 AI 服务:
- 输入图片:从原图生成 JPEG(默认最大边 4096、质量 90),用于尽量保留细节
- 多裁剪(单张识别默认开启):全图 + 多个方形裁剪分别识别,再把结果融合(同一物种取各次识别中的最高分)
- 批量识别:默认只跑全图(更快);可通过环境变量开启多裁剪
识别入口:
- 单张识别:详情页点击“识别”
- 批量识别:库顶部点击“一键识别”(覆盖重跑用“重识别”)
识别输入调优支持两种方式:
-
推荐:页面顶部“调优”面板修改(保存到本地 SQLite,立即生效)
-
环境变量:设置在
app服务(Docker 在docker-compose.yml;本地开发写入.env) -
IDENTIFY_MAX_SIZE:识别输入最大边(默认4096,范围 512–8192) -
IDENTIFY_JPEG_QUALITY:识别输入 JPEG 质量(默认90,范围 30–100) -
IDENTIFY_CROPS_SINGLE:单张识别裁剪次数(默认9,范围 1–9) -
IDENTIFY_CROPS_BATCH:批量识别裁剪次数(默认3,范围 1–9) -
IDENTIFY_CROP_SCALES_SINGLE/IDENTIFY_CROP_SCALES_BATCH:多尺度裁剪(逗号分隔,范围 0.35–0.9;例如0.6,0.45)
Docker 示例(修改 docker-compose.yml 的 app.environment):
environment:
- IDENTIFY_MAX_SIZE=4096
- IDENTIFY_JPEG_QUALITY=90
- IDENTIFY_CROPS_SINGLE=9
- IDENTIFY_CROPS_BATCH=3
- IDENTIFY_CROP_SCALES_SINGLE=0.6,0.45
- IDENTIFY_CROP_SCALES_BATCH=0.6,0.45识别率不理想时的排查顺序:
- 先看 AI 候选集是否正确:打开
http://localhost:3001/api/ai/health,确认labels数量与预期一致 - 再看图片是否“小鸟占比太低”:优先用单张识别(默认多裁剪)验证效果;必要时提高
IDENTIFY_CROPS_SINGLE或调大IDENTIFY_CROP_SCALE - 仍不理想:增大
IDENTIFY_MAX_SIZE(例如 6144)或提高IDENTIFY_JPEG_QUALITY(例如 95)
当离线模型识别“置信度不高”(Top1 分数低或 Top1-Top2 间隔小)时,后端可选地调用多模态大模型对“候选 TopK”做一次兜底判断,并把结果展示在详情页的“兜底”区域。
说明:
- 兜底只会在离线模型给出的候选 TopK 里做选择,避免大模型乱猜
- 开启后会产生联网请求与费用
- 推荐用页面顶部“调优”面板启用并填写 Key(Key 存本地 SQLite,不会回显也不会写回
.env)
用环境变量配置(参考 .env.example):
# Qwen (DashScope OpenAI 兼容模式;模型需支持图片输入,通常为 *vl* 系列)
QWEN_API_KEY=你的 key
QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
QWEN_MODEL_NAME=qwen-vl-plus
# 可选:触发阈值(默认与前端提示一致)
LLM_FALLBACK_TRIGGER_SCORE=0.6
LLM_FALLBACK_TRIGGER_MARGIN=0.1项目内置了一个脚本,使用 eBird API 拉取「中国(CN)区域曾记录的物种代码」+「eBird taxonomy(支持中文 locale)」并生成 data/models/labels.csv。
说明:eBird API 大多数接口需要 API Key,并通过请求头 x-ebirdapitoken 传入。参考 eBird 官方 API 文档:https://documenter.getpostman.com/view/664302/S1ENwy59
-
生成 eBird API Key(登录 eBird 后在 Keygen 页面生成)
-
在项目根目录运行(PowerShell):
$env:EBIRD_API_KEY = "你的 key"
npm run labels:cn或者直接在.env添加key:
EBIRD_API_KEY=你的 key成功后会生成:data/models/labels.csv(容器内对应 /models/labels.csv),重启 AI 容器即可生效:
docker compose up -d --force-recreate ai说明:默认不再把 AI 服务端口暴露到宿主机(避免 Windows 端口占用/权限问题);应用容器通过 http://ai:8000 在 Docker 网络内访问即可。
如果你的 Docker 环境没有可用的 NVIDIA GPU:
-
在
docker-compose.yml的ai.environment里加:FORCE_CPU=1 -
删除或注释
ai.gpus: all
优先打开后端探测接口查看更具体的原因:
http://localhost:3001/api/ai/health
常见原因:
BIRD_AI_URL配置不正确:容器内通常是http://ai:8000;宿主机本地运行则应使用可访问的地址(例如http://localhost:8000)ai容器未启动或启动失败:尤其是在没有 NVIDIA GPU 的环境中,需按上面的“强制 CPU”处理ai容器刚启动/重启:模型在加载期会短暂返回loading,等几十秒后再重试即可
为提升“小鸟占比低/主体不居中”的识别率,ai 服务内置了目标检测(YOLO)来自动找鸟并裁剪后再做 BioCLIP 识别;若检测不到,也会对全图做少量方形裁剪兜底。
默认行为:
- 后端默认只发送 1 张全图给
ai(更快) ai服务在内部做检测与多裁剪融合(更像 superpicky 的效果)
可选环境变量(配置在 ai 服务):
DETECT_ENABLED:是否启用检测(默认1)DETECT_MODEL:检测模型(默认yolov8n.pt)DETECT_CONF:检测阈值(默认0.25)DETECT_CLASS_ID:检测类别(默认14,COCO 的 bird)DETECT_MAX_CROPS:检测到目标后最多跑几张裁剪(默认3)DETECT_FALLBACK_CROPS:检测不到时是否做兜底裁剪(默认1)DETECT_FALLBACK_MAX:兜底裁剪最多跑几张(默认4)
建议先打开健康检查确认检测是否启用与是否报错:
http://localhost:3001/api/ai/health(会返回 detectEnabled/detectError)
如果你的照片里包含明显不属于中国鸟类范围的物种(例如金刚鹦鹉),只生成中国(CN)物种标签会导致模型“只能在 CN 候选里硬猜”,结果会很不对。
可以用脚本生成全量 eBird taxonomy(全球物种)作为候选集:
npm run labels:world然后在页面里点击“重识别”(覆盖重跑),让已有照片按新标签集重新识别。
可选参数:
--region:默认CN(中国);也可以用 eBird 的其它 regionCode--locale:默认zh_CN(中文)
脚本位置:scripts/ebird_labels.mjs
npm install
npm run dev环境变量建议使用 .env(参考 .env.example),不要把真实 key 提交到仓库。
前端:http://localhost:5173/(代理到后端)
后端:http://localhost:3001/
- SQLite 存储位置:由
CACHE_DIR决定,默认是data/cache(库文件为catalog.sqlite) - 数据库 schema 通过 migrations 管理:启动时自动执行
server/migrations/*.sql(或MIGRATIONS_DIR指定目录),并记录到schema_migrations表 - 批量识别任务状态已持久化到 SQLite 的
jobs表(用于轮询与取消) - 任务并发:用
JOB_CONCURRENCY控制同时运行的任务数(默认 1,建议保持 1 避免把 IO/AI 打满) - 生成数据不进仓库:
data/cache、data/models等为运行产物/模型与标签缓存,默认已加入.gitignore
后端约定为:
server/routes:只做 HTTP 适配(解析参数/返回响应),不要写 SQL/文件系统/AI 调用细节server/services:业务编排(“扫描库”“识别单张/批量”等)server/repos:SQL 与数据访问server/lib:基础设施与通用工具(AI client、缩略图、migrations、校验/错误等)
- 右侧详情面板增强:结构化文件信息 + EXIF(相机/镜头/焦距/光圈/快门/ISO 等)
- EXIF 提取与缓存:点开照片自动回填,失败会在顶部错误条提示原因
- 美学分批量回填与实时刷新:解决“只有第一张有分”的体验问题
- 扫描去重修复:按路径/指纹合并并清理重复记录,避免列表出现重复照片
- 鸟种识别增强:AI 服务增加 ROI 目标检测裁剪与兜底多裁剪,提高识别率
- 增加低置信度兜底配置与调优面板
- 修复兜底 Key 优先级与失败原因展示
- 修复单张清除后立即识别的竞态问题
- 识别输入升级:从原图生成识别专用 JPEG(默认 4096px / Q90),提升细节保留
- 多裁剪识别:单张识别默认开启“全图 + 多位置裁剪”并融合结果,提高小目标命中率
- 新增识别输入参数:支持通过环境变量调节输入尺寸、质量与裁剪策略
- 后端架构调整:routes/services/repos 分层,SQLite schema 引入 migrations,批量识别任务状态持久化并支持并发控制