Projects · · 5 分钟阅读

WingLens:让索尼相机拍下的鸟,自动变成一条观鸟记录

从 A7C2 获取最新照片,用 YOLO 找鸟、BioCLIP 识别鸟种,再由 iPhone 自动记录时间与地点。

鸟类识别 Sony A7C II YOLO BioCLIP SwiftUI OrniVision

拍鸟的时候,我经常遇到一个问题。

相机里积累了很多照片,但真正回到家整理时,需要先筛选,再判断鸟种,然后补拍摄时间、地点和备注。一天拍得多了,整理就变成负担。

于是我开始做一个轻量级观鸟识别工具:WingLens

它的目标不是替代相机,也不是做一个普通的「上传图片识物」应用,而更像相机和观鸟记录之间的一座桥:

相机拍鸟iPhone 获取最新照片YOLO 检测鸟区域BioCLIP 识别鸟种记录时间 / 地点 / 结果复核与导出

为什么从索尼 A7C2 开始

项目主要围绕我已有的 Sony A7C II 展开。

专业相机在画质、对焦和长焦上远强于手机,但相机本身不适合承担复杂的识别和记录管理。iPhone 则很适合做中间层:

  • 连接相机、获取最新照片
  • 获取当前位置
  • 运行轻量检测模型
  • 调用识别服务
  • 保存观鸟记录
  • 展示和修改识别结果

因此第一版的核心不是实时取景,而是「拿到刚刚拍下的照片」。实时画面可以关闭,也不是 MVP 的必要能力。

两条相机连接路径

iPhone 可以通过 USB 或 Wi-Fi 连接相机。但索尼开放能力、系统权限和传输方式存在限制,因此还设计了一条更工程化的路径:

text
Sony A7C2
    │ USB-C / Wi-Fi
    ▼
Mac / Linux / 树莓派 / 小主机
    │ Camera Remote Command
    ▼
本地相机控制服务
    │ HTTP / WebSocket
    ▼
iPhone App

本地服务负责:监听拍摄事件、获取最新图片、暴露统一 API、封装不同相机协议、向 iPhone 推送新照片状态。

这条路径更适合后续自己开发 App,也方便扩展到其他品牌。它并不限于观鸟,还可用于照相亭、医疗影像、智能交通等相机集成场景。

一张照片可能有不止一只鸟

鸟类识别不能直接把整张照片丢给分类模型。照片里可能有大片天空或水面、复杂树枝背景、多只不同位置的鸟、主体只占很小一块,以及主体和陪体并存。

因此识别拆成两个阶段。

第一步:YOLO 检测鸟的位置

YOLO 返回所有鸟框:

json
{
  "boxes": [
    {
      "x": 812,
      "y": 436,
      "width": 620,
      "height": 498,
      "confidence": 0.91
    }
  ]
}

多只鸟时要保留多个框,而不是只识别最大的一只。iPhone 可跑 YOLOv8n 或 Core ML 模型,服务端也可以跑 YOLO,形成两种模式:

模式 A:客户端已完成 YOLO — iPhone 上传原图、鸟框坐标或已裁剪鸟图,服务端直接分类。

模式 B:客户端未完成 YOLO — iPhone 上传原图,服务端执行 YOLO → crop → BioCLIP → Top5

两种模式可共存:性能较好的手机优先本地检测,旧设备把完整流程交给服务端。

为什么使用 BioCLIP

最初我收集了北京常见鸟类图片,大约前 50 个目录,多数类别约 20 张,部分更少。这个规模不适合从头训练大型分类网络。

因此采用:

text
YOLO 检测鸟位置
        ↓
裁剪鸟图
        ↓
BioCLIP 提取视觉特征
        ↓
轻量分类或相似度匹配
        ↓
Top-5 鸟种候选

BioCLIP 已学习大量生物图像和文本知识。第一阶段不必重训整个模型,只需固定 BioCLIP、提取向量,再训练轻量分类头,或与类别文本、样本向量算相似度。这比用几十张图从零训练更现实。

第一版不追求完美 Top-1

近似鸟种很难区分,雌鸟、幼鸟、冬羽和夏羽外观也可能不同。因此第一版成功标准不是每次给出唯一正确答案,而是:

正确鸟种是否出现在 Top-3 或 Top-5 中。

例如:

json
{
  "predictions": [
    { "bird_id": "4230", "name": "白骨顶", "confidence": 0.62 },
    { "bird_id": "4218", "name": "黑水鸡", "confidence": 0.21 }
  ]
}

用户可以快速确认,而不是从几百种鸟里手动查找。目录名中的编号也会保留:训练目录 白骨顶_4230 对应 API 的 bird_id: 4230,便于与图鉴和观鸟记录系统稳定关联。

地区和季节也是识别信息

单独靠照片,有时难以区分相似鸟种;位置和拍摄时间可以显著缩小候选范围:

  • 当前地区是否有该鸟种分布
  • 当前月份是否处于迁徙期
  • 该鸟种在北京是否常见
  • 拍摄环境是湿地、山林还是城市
  • 用户最近是否连续观察到同一群鸟

最终排序可综合:视觉置信度 + 地理分布 + 季节 + 常见程度 + 用户历史。eBird 一类数据更适合做地区和季节过滤,而不是直接承担图像识别。

iPhone 端的产品流程

WingLens 的 iPhone 端使用 SwiftUI。第一版围绕三个动作:

1. 获取照片

从相机获取最新照片,或从系统相册选择;以后再加自动监听相机新照片。图片可能是 JPG 或 HEIF/HIF,需要先稳定解码。

2. 识别与确认

展示原图、检测到的鸟框、每只鸟的 Top-5、鸟名编号与置信度、手动修改入口。多鸟照片里,每个框都应可单独确认。

3. 保存观鸟记录

确认后自动保存:鸟种与 ID、原图或裁剪图、拍摄时间、GPS、相机与镜头信息、识别置信度、用户备注。后续可在 Mac 端整理、复核、导出和同步。

OrniVision:识别服务端

鸟类识别后端命名为 OrniVision,是独立的 FastAPI 应用,核心接口类似 POST /api/predict

当前设计支持:上传原图;YOLO 检测合格鸟框(置信度阈值约 0.60);每框进入 BioCLIP 分类并返回 Top-5;保存原图与裁剪图;对错误结果人工标注;按鸟种目录积累新训练数据。

服务端既是识别 API,也是数据闭环工具:用户纠正后,原图和裁剪图可进入对应鸟种目录,成为未来训练数据。

本地混合推理

随着识别量增加,还规划了本地混合推理:

text
CPU 服务器
  ├─ Nginx / Web / API
  ├─ 认证 / 队列 / 图片存储
  └─ 可选 YOLO

Jetson Orin Nano Super 8GB
  └─ BioCLIP 分类 Worker

CPU 负责常规服务和调度,Jetson 专注模型推理。更轻量的开发环境是在 Mac mini M4 上用 OrbStack 和 uv 管理 Python 项目。

价值在于:图片不必全部交给第三方;可控长期成本;模型和鸟种库可持续更新;可针对本地常见鸟类优化;相机、iPhone 和 Mac 可共享同一识别服务。

数据采集是最费时间的部分

模型方案确定后,真正耗时的是数据。我曾设计半自动 ADB 流程:脚本读鸟名列表、复制到 Android 剪贴板,用户手动搜索选图,按回车触发截图,脚本经 ADB 拉回并按目录保存。

脚本不自动抓图,也不改原始目录,只减少重复的命名、截图和整理。这让我更清楚:

鸟类识别项目的壁垒,最终不只是模型,而是高质量、可追溯、符合真实拍摄场景的数据。

为什么叫 WingLens

项目最终定名为 羽感 WingLens。「Wing」代表鸟与羽翼,「Lens」代表相机和观察。「羽感」更接近我想要的体验:不是给出冷冰冰的分类结果,而是让相机拍下的一瞬间,自动连接到鸟种、地点、时间和观察记录。

它应该是对自然观察的增强,而不是替代。

写在最后

WingLens 第一版不必实现所有能力。最重要的是先跑通:

text
选择或获取照片
→ 找到所有鸟
→ 返回 Top-5
→ 用户确认
→ 保存观鸟记录

之后再逐步增加:索尼相机自动取图、本地 Core ML 检测、地区与季节过滤、Mac 端复核、离线模型、鸟声识别、相机端自动跟踪拍摄。

拍照识别鸟,看起来只是图像分类。但完整产品需要连接相机、手机、模型、地理信息、数据管理和用户确认。当这些环节串起来之后,一张照片才会真正变成一次可保存、可回忆的观鸟记录。