iOS · Vision · Flick

不上传一张照片:Flick 的本地相似照片检测

用 Vision 特征向量、Laplacian 方差和 SQLite 缓存,在设备端完成相册分析。

Flick 是一个滑动清理照片的 App。它的两个核心能力——找相似照片、找模糊照片——都在设备端完成,不依赖任何服务器,也没有第三方 SDK。这篇讲实现思路。

相似检测:Vision 特征向量

苹果的 Vision 框架提供 VNGenerateImageFeaturePrintRequest,输入一张图,输出一个特征向量(feature print)。两张图的向量距离越近,内容越相似:

let request = VNGenerateImageFeaturePrintRequest()
try VNImageRequestHandler(cgImage: image).perform([request])
let print = request.results?.first as? VNFeaturePrintObservation
// print.computeDistance(_:to:) 计算两图距离

拿到全库照片的向量后,按距离阈值聚类,连拍、截图重复、九连拍选一张这类场景就都能分到同一组里。剩下的就是把组呈现给用户,让他滑动挑一张保留。

模糊检测:Laplacian 方差

判断一张照片糊不糊,经典做法是对图像做 Laplacian 卷积后算方差:清晰的图边缘丰富、方差大,糊的图方差小。用 vImage 做卷积、vDSP 算方差,几毫秒一张,纯 CPU 也够快。这套算法几十年历史,不需要机器学习模型。

工程细节

  • 特征缓存:几万张照片不可能每次启动都重算。特征向量存进 SQLite——直接用 C API,没引 ORM,这个量级够用了。
  • 分析流水线:后台协调器按批处理,随时可取消,避免首次分析把设备烤热。
  • iCloud 占位文件:开了 iCloud 照片优化存储的设备上,很多「照片」本地只有缩略图。这类只读元数据、跳过分析,绝不能触发下载——否则用户的流量和 iCloud 空间都会遭殃。

为什么坚持全本地

隐私不是一句口号,是架构决定的:没有服务器,就不存在「照片被上传」这个问题,隐私政策也只需要一句话。对独立开发者还有个附带好处——零服务器成本,App 卖一份是一份,没有月度账单。