Meta 在智能眼镜上搭载人脸识别功能
Meta在智能眼镜上搭载了人脸识别功能
Meta 为其智能眼镜产品推出了人脸识别功能,用户可通过眼镜识别他人身份,并获取相关信息。该功能目前正通过早期测试版本向部分用户开放,旨在增强增强现实设备的社交与信息交互能力。
Meta 把完整的人脸识别栈塞进了智能眼镜 App,这事一旦激活,公共场所的匿名性将被重新定义。作者的技术取证链条扎实,迫使 Meta 必须正面回应。
Stella 是 Meta 智能眼镜的配套应用。在检查 Android 版本 273.0.0.21(com.facebook.stella)时,我发现了用于端侧人脸识别的完整计算与存储栈:三个人脸模型、一个本地数据库 schema、一个维度与模型匹配的余弦相似度向量索引、一条将生物特征记录暂存到磁盘的写入路径、一套完整接线的通知界面,以及一个面向用户的“Connections”小组件。
我想精确说明这意味着什么、又不意味着什么,因为两者之间的差距很重要。
我能证明的是:这套机制确实存在,而且已经接线连通。其中包含多个面部提取和面部指纹模型,我能够在一张测试图像上端到端运行识别流水线,它检测到了一张人脸,生成了一个 2048 维的生物特征嵌入向量,搜索了本地索引,并在匹配时触发了一条 Android 通知,向用户显示“Person Recognized”。
为了让流水线运行起来,我直接用一张测试照片调用了它现有的处理程序。
我无法证明的是:这些功能对普通用户是否处于启用状态。在一个原装、未注册的账户上,面向用户的 UI 不会出现,而识别通知深度链接到的那个界面在这个构建版本中也不存在。我也没有在我的测试账户上观察到 Meta 服务器向相关数据库推送身份数据。
所以这并不是“Meta 在秘密识别你看到的人”。而是:完成这件事的整套装置就放在设备上,已经组装好并且可以运行,由 Meta 控制其开关。
以下所有发现均可在 com.facebook.stella v273.0.0.21 上复现。
设备上预装了三个人脸识别模型(约 100 MB)
三个 ExecuTorch(.pte)模型通过 NMLML(Meta 的资源分发系统)从 Meta 下载到设备上。
| 资源名称(Meta 的命名) | 文件 | 大小 | 功能 |
|---|---|---|---|
android_facerec_scrfd | SCRFD.pte | 3.4 MB | 检测图像中的人脸 |
android_facerec_kps_aligner | KPSAligner.pte | 117 KB | 裁剪并对齐每张检测到的人脸 |
android_facerec_sface | SFace.pte | 96 MB | 将一张人脸转换为一个 2048 维的嵌入向量(即生物特征指纹) |
这些对应到开源架构,也就是其他应用和学术项目已经在使用的相同模型系列:
- SCRFD Sample and Computation Redistribution for Efficient Face Detection(InsightFace,ICLR 2022)。参考实现:
github.com/deepinsight/insightface。 - SFace Sigmoid-Constrained Hypersphere Loss for Face Recognition(Zhong 等,2021)。参考:
github.com/zhongyy/SFace - KPSAligner 基于关键点的对齐,自 2015 年以来的标准做法(MTCNN、dlib、InsightFace)。
Meta 的 SFace 变体似乎比公开参考版本规模更大(96 MB 对约 40 MB;2048 维输出对参考版本的 128–512)。
值得直说:发布检测和嵌入模型本身并不构成人脸识别的证据。许多应用在设备端运行人脸检测,用于取景或自动对焦。
一个余弦相似度人脸索引,其维度与设备端指纹器完全对应
实际运行并读取该数据库的识别流水线:
/data/user/0/com.facebook.stella/files/rldrive/person_profiles/objects.db
它位于 RLDrive 之下,这是 Meta 的跨设备同步框架,处于一个 person_profiles 命名空间中,该命名空间被设计为可远程填充。我没有直接观察到 Meta 专门向我的测试账号上的 person_profiles 推送数据。我想明确说明,我描述的是该通道的存在,而非观察到的传输行为。
该 schema:
CREATE TABLE person (
nodeid INTEGER PRIMARY KEY,
name TEXT,
uri TEXT,
blob BLOB,
deleted INTEGER,
version BLOB
);
CREATE TABLE face (
nodeid INTEGER PRIMARY KEY,
mediaPath TEXT, -- the face_id used in the deep link
personUri TEXT, -- soft reference back to person.uri
blob BLOB,
deleted INTEGER,
uri TEXT,
version BLOB
);
CREATE VIRTUAL TABLE face_mediaPath_vec
USING vec0(mediaPath float[2048] distance_metric=cosine);
-- 2048-float biometric fingerprint per face, cosine-distance search
-- (uses the sqlite-vec extension)
每个 face 行通过 person 指向一个 personUri。每个 face.mediaPath 是 face_mediaPath_vec 的主键,后者存储了 2048 维的嵌入向量。识别过程是对该索引执行一次余弦相似度查询,随后再 join 到 person.name 以获取通知文本。
有几件事对上了:
vec0是开源的 sqlite-vec 扩展,它把 SQLite 变成了一个向量相似度引擎。- 维度
float[2048]正是该应用所搭载的 SFace 嵌入器的确切输出形状。 cosine指标是比较人脸嵌入向量的标准选择。
该 schema 允许存在多个face行对应每个personUri(无UNIQUE约束),但生产部署采用的是一对一还是一对多,从一台未注册的设备上是看不出来的。
端到端测试确认了两个分支,并隔离出写入的去向。 我对数据库做了 SHA-256 快照并统计了行数,然后运行了完整的识别流水线两次:一次针对空索引(无匹配),一次针对预加载了单个嵌入向量的索引(匹配):
- 无匹配(空
face_mediaPath_vec):一对(uuid.jpg, uuid.emb)被写入NameTagsPending/。没有通知。 - 匹配:一条 Android 通知通过生产环境的
nametags_recognition通道触发——标题为 “Person recognized”,正文为 “Recognized Michel Foucault”。没有任何内容被添加到NameTagsPending/。
未识别的面孔会被暂存到磁盘:裁剪图像加指纹,存放在 NameTagsPending/
当设备看到一张本地索引无法匹配的面孔时,Stella 会将其写入:
/data/user/0/com.facebook.stella/files/NameTagsPending/
每张未识别的面孔都会生成一对以全新 UUID 命名的文件:
- 一个
.jpg——即裁剪并对齐后的面孔,是 SCRFD + KPSAligner 的输出;以及 - 一个
.emb——即 2048 个数字的 SFace 指纹。
该目录的模式为 0700,且重启后依然存在。写入仅发生在无匹配分支上;匹配到的人脸会触发通知,不在磁盘上留下任何痕迹。
我直接验证了该嵌入向量的结构:
File: NameTagsPending/1566ab46-[...].emb
Size: 8,192 bytes (2048 × float32, big-endian)
L2 norm: 0.999999 ← canonical L2-normalized face embedding
Min/max: −0.092110 / +0.098950
Mean: +0.000292
合在一起,(uuid.jpg, uuid.emb) 就是一张人脸的完整、可索引的生物特征记录——其形状与编码正是 person_profiles/objects.db 中的余弦索引所构建用来比对的。
NameTagsPending 这个名字最字面的解读是"等待命名的人脸"——以生物特征编码,等待一个标签。我只陈述这一结构性事实,让它自己说明问题:一张人脸图像及其指纹,以明文并排存储,模式为 0700,重启后依然存在,这恰恰就是当你打算在标签到来后回溯性地识别这些人脸时会构建的数据集。


通知界面已完全接通
Stella 定义了一个专用的 Android 通知渠道
NotificationChannel{
id = "nametags_recognition"
name = "NameTags recognition"
description = "Notifications for recognized NameTags connections"
importance = IMPORTANCE_HIGH (heads-up + sound + badge)
sound = system notification sound
}
通知模板硬编码在识别处理程序中。标题始终是“Person recognized”;正文始终是"Recognized " + name,其中name来自person表中的person_profiles/objects.db:
NotificationCompat.Builder(ctx, "nametags_recognition")
.setContentTitle("Person recognized")
.setContentText("Recognized " + matched_name)
.setAutoCancel(true)
.setContentIntent(
PendingIntent.getActivity(
ctx,
matched_name.hashCode(),
Intent.ACTION_VIEW with
Uri "fb-viewapp://name_tags?face_id=" + face_id,
FLAG_IMMUTABLE | FLAG_UPDATE_CURRENT))
.build()
NotificationManagerCompat.notify(matched_name.hashCode(), notification)
该通知可点击:其 contentIntent 是形如 fb-viewapp://name_tags?face_id=<face_id> 的深度链接,这是 Meta 编写的 URL scheme,用于在 Stella 内打开个人资料页面。
一个坦诚的提醒:在 v273 中,我找不到那个目标页面。点击该通知会将 Stella 路由到其默认标签页,因为目标 Compose 页面在导航图中并不存在。通知会触发;但它指向的页面并未构建进这个版本。

APK 中存在一个面向用户的“Connections”入口。
Stella v273 包含一个 widget,在标题为“Connections”的区块标题下渲染一张卡片,文字为“See your connections” / “Remember the people you met and make new connections.”这两个字符串都是 APK 中硬编码的字面量,并非由服务器推送。
在未注册的出厂账户上,这张卡片在 Glasses 标签页中完全不会出现。它在测试期间变得可见。在正常使用中,用户不会看到它。

这一切加起来意味着什么
- 完整的端侧人脸识别技术栈:检测、对齐、嵌入向量、向量索引、存储、写入路径以及通知界面,全都存在于 Stella v273 中并已完成组装。
- 它是可用的。端到端运行后,它能识别出已知人脸并在通知中报出名字,同时将未知人脸(裁剪图 + 指纹)暂存到磁盘。
- 索引维度、嵌入向量形状和存储 schema 彼此一致,这是一个连贯的系统,而不是散落的死代码。
- 用户真正会接触到的部分:通知所打开的"Connections"卡片和个人资料页面,要么在该构建中缺失,要么埋得更深。
- 实时流水线所使用的数据库位于一个由 Meta 在服务端填充的同步命名空间中,与它已经在填充的其他命名空间并列,但我在自己的账户上并未观察到向人脸命名空间推送数据。
我并非在声称:Meta 如今正在为用户识别陌生人、注册数据正在流动,或者这其中任何一项已在生产环境中启用。
难以轻描淡写的是:构建、发布并打通如此多的装置,精细到 2048 维的人脸指纹和一条硬编码的"Person recognized"通知,这是一笔工程投入。这种能力不会偶然上线。它是否以及何时投入生产,要由 Meta 来回答。
这项研究与《WIRED》的报道同步发布。
来源:Hacker News 热门(buzzing.cc 中文翻译) · buchodi.com