2026 年 Google Cloud Vision API 实操:账单为什么是图片数的 3 倍,以及多页 PDF 的正确姿势
Cloud Vision 按 feature × 图片数计费,不是按图片数。一张图叠三个 feature 跑 100 万张就是 300 万 units。再加上多页 PDF 用错端点的常见错法,本来 $40 的活做成 $400。定价模型、tier 跳价和实操指南。
上面是文章摘要,下面进入正文深读。可以配合目录逐段阅读,不会丢掉上下文。
让我第一次真正去读 Cloud Vision 定价页的,是一个市集型产品的图片审核管线账单。计划看着很稳:用户上传商品图,每月 80 万张,跑 SAFE_SEARCH_DETECTION 做准入门禁,跑 LABEL_DETECTION 自动打标,再跑 OBJECT_LOCALIZATION(PM 觉得 bounding box 对图册裁剪有用)。我按 "80 万张 × $1.50 / 1,000" 粗算预算 $1,200。
实际账单是 $4,320。
如果你是从 "cloud vision api" 这类检索词过来的,Cloud Vision API 大概率已经在你的 OCR、图片审核、视觉打标的候选清单里了。这篇要讲的就是定价和架构里文档其实讲了、但很少有人在收到第一张账单之前真正吃透的几件事:feature × 图片的计费模型、WEB 和 OBJECT 这两个 feature 的额外溢价、多页 PDF 的常见错法,以及人脸识别那堵让每个项目第一天都失望一次的墙。
"一个 unit" 到底怎么算
Cloud Vision 定价页对计费单位的描述是清楚的,只是措辞容易被快速扫过:
"Pricing is calculated by the number of Feature units billed."
一个 feature unit = 一个 feature 应用到一张图。一次 annotate 请求在一张图上启用 3 个 feature,就计 3 个 units。免费额度是每个 feature 每月 1,000 units,按 feature 各自独立。
我的审核管线,每张图 3 个 feature(SAFE_SEARCH + LABEL + OBJECT_LOCALIZATION)。80 万张 × 3 unit = 240 万 units。扣掉 3,000 个免费 units(每个 feature 1,000),剩 2,397,000 个付费 units。按三个 feature 的价格分摊($1.50 + $1.50 + $2.25 / 1,000 units),账单算出来差不多就是 $4,320。线性的"按图片"数学给我 $1,200,是因为我下意识地以为每张图算一个 unit,不是三个。
规则一般化:任意一次 annotate 请求,N 个 feature 应用到 M 张图,计 N × M 个 units,每个 feature 的价格按它自己贡献的那 M 个 units 计算。
11 个标准 feature 与各自的价格
价格表是任何成本讨论的承重梁。下面是 2026 年标准价格,单位是每 1,000 units 价格,每个 feature 每月前 1,000 units 免费之后开始计费。
| Feature | 返回什么 | 1,001 – 5M | 5M+ |
|---|---|---|---|
| LABEL_DETECTION | 开放词表标签 | $1.50 | $1.00 |
| TEXT_DETECTION | 场景文本 OCR | $1.50 | $0.60 |
| DOCUMENT_TEXT_DETECTION | 文档/手写 OCR(含 block/paragraph/word/symbol 层级结构) | $1.50 | $0.60 |
| FACE_DETECTION | 人脸 landmark + 情绪 likelihood(不含身份) | $1.50 | $0.60 |
| LANDMARK_DETECTION | 地标识别 | $1.50 | $0.60 |
| LOGO_DETECTION | 品牌 logo | $1.50 | $0.60 |
| IMAGE_PROPERTIES | 主色调 | $1.50 | $0.60 |
| SAFE_SEARCH_DETECTION | adult / spoof / medical / violence / racy likelihood | $1.50 | $0.60 |
| OBJECT_LOCALIZATION | 对象检测 + bounding box | $2.25 | $1.50 |
| CROP_HINTS | 智能裁剪建议 | $0.60 | $0.30 |
| WEB_DETECTION | 反向以图搜图 + best-guess label | $3.50 | $2.00 |
另有两个特殊 feature 不在标准表里:
- PRODUCT_SEARCH 计费模型独立:按 product 入库一笔费用 + 按查询一笔费用,与标准 tier 无关。
- CELEBRITY_RECOGNITION 是白名单 feature,要单独申请 Google Cloud review。
标准表里最显眼的两个异常值:WEB_DETECTION($3.50,超过 $1.50 的 2 倍多)和 OBJECT_LOCALIZATION($2.25,1.5 倍)。当一段代码路径无脑启用"反正都有用的 feature 都开上"时,这两个会主导账单。我前面那个市集审核管线,OBJECT_LOCALIZATION 一个 feature 就吃掉 $4,320 里的 $1,800,而 PM 后来承认 bounding box 在 UI 上还没接上。
优化方法很机械:每次 annotate 请求里的每个 feature,都必须确认下游真的有消费方解析。下游不用了的 feature 立刻砍。
5M units 处的 tier 跳价
第二档价格在每个 feature 每月 5,000,000 units 处起跳。下沉幅度按 feature 区分:
- LABEL_DETECTION: $1.50 → $1.00(降 33%)
- TEXT_DETECTION、DOCUMENT_TEXT_DETECTION、FACE、LANDMARK、LOGO、IMAGE_PROPERTIES、SAFE_SEARCH: $1.50 → $0.60(降 60%)
- OBJECT_LOCALIZATION: $2.25 → $1.50(降 33%)
- WEB_DETECTION: $3.50 → $2.00(降 43%)
- CROP_HINTS: $0.60 → $0.30(降 50%)
5M 这个边界是按 feature 算的,不是池化。如果你的流量里 LABEL 跑 6M units、TEXT 跑 4M units,只有 LABEL 进入第二档,TEXT 还在第一档。这是上量后第二轮账单困惑的来源:团队看到总 units 过了 5M,以为整体跳了档,但每个 feature 有自己的计数器。
另一个含义是 挑对 feature 组合可以让所有 feature 在更长时间里都待在 5M 以下。如果你的应用能让 sentiment 类的 feature 全量跑,WEB_DETECTION 只跑挑出来的少数样本,那便宜的 feature 享受规模带来的成本曲线,贵的 feature 待在小分母上。
多页 PDF 的常见错法
我在三个不同的代码库里都看见过同一个错法:开发把多页 PDF 用工具栅格化成每页一张 PNG,然后循环对每页跑 TEXT_DETECTION + images:annotate。代码能跑。输出有两处不对,账单还没省下来。
正确的姿势是 DOCUMENT_TEXT_DETECTION + 异步 files 端点:
from google.cloud import vision_v1
client = vision_v1.ImageAnnotatorClient()
input_config = vision_v1.InputConfig(
gcs_source=vision_v1.GcsSource(uri="gs://my-bucket/inputs/contract.pdf"),
mime_type="application/pdf",
)
output_config = vision_v1.OutputConfig(
gcs_destination=vision_v1.GcsDestination(
uri="gs://my-bucket/outputs/contract-",
),
batch_size=100, # 每个输出 JSON 文件包含的页数
)
feature = vision_v1.Feature(type_=vision_v1.Feature.Type.DOCUMENT_TEXT_DETECTION)
request = vision_v1.AsyncAnnotateFileRequest(
features=[feature],
input_config=input_config,
output_config=output_config,
)
operation = client.async_batch_annotate_files(requests=[request])
print(f"已提交: {operation.operation.name}")
result = operation.result(timeout=600)
print(f"完成。输出在 gs://my-bucket/outputs/contract-*")
异步 files 端点单文档最多 2,000 页,PDF 输入走 GCS URI,提交后返回 long-running operation 名,结果异步写到你指定的 GCS 前缀,每 batch_size 页一个 JSON 文件。同步的 files:annotate 只支持 5 页以内。
用对端点能省下什么:
- 结构。 DOCUMENT_TEXT_DETECTION 返回 block / paragraph / word / symbol 的层级结构,每一层都带 bounding box。TEXT_DETECTION 返回的是为场景 OCR(路牌、招牌)设计的扁平文本注解,会丢段落和换行信息。
- 配额余量。 默认 1,800 req/min 的 project 配额,按页循环吃得很快。200 页的 PDF 拆 200 个同步请求,能瞬时挤占你这一分钟相当比例的预算。一个异步 file 请求只算一次。
- 运维简洁度。 不用为每页写重试逻辑,long-running operation 内部处理重试。
不省的是钱。计费还是按页 × feature,200 页的异步文档算 200 个 DOCUMENT_TEXT_DETECTION units,和 200 个同步按页请求一样。用对端点省的是工程时间,不是钱。
输入形态:base64、GCS 还是 URL
三种输入模式,各有锋利的边:
- 内嵌 base64。 字节嵌在请求 JSON 里。受 HTTP payload 上限约束(base64 膨胀后约 10MB)。意思是每次 annotate 请求,哪怕你用了 16 张图的 batch,实际能塞下多少更多是被字节数卡住,不是被 16 张这个数字。两张 5MB 的图片就把一个请求撑满。临时活能用,生产里很别扭。
- GCS URI(
gs://bucket/path)。单文件上限 20MB。Vision 在区域内从 GCS 拉字节(前提是 bucket 和 API 调用在兼容的区域),绕过 base64 膨胀,让 16 张图的 batch 名副其实。生产默认就该用这个。 - HTTP/HTTPS URL。 API 接受一个公网 URL,自己去抓图。Google 这边没有重试合约,也没有缓存保证,文档原文写得很直白:这个能力是给 demo 用的,不是生产输入。quickstart 可以用,正式上线就要切掉。
GCS 跨区域往 Vision 拉的成本在上量时不小。bucket 和 API 调用尽量同区域:bucket 的 location 在创建时设定,client 的 endpoint 单独设置。
配额、批量与 1,800 RPM 实际意味着什么
Vision API 默认 project 配额是 每分钟 1,800 个请求,同步 images:annotate 单批最多 16 张图,同步 files:annotate 最多 5 页。异步 file 端点每次提交算一个请求,与文档页数无关,这也是它在延迟不敏感场景仍然好用的一个原因。
几个生产设计上的含义:
- 同步请求最多按 16 张图打满 batch。 每个请求都算一次 RPM,所以 16x 批量 = 默认配额下有效吞吐到 28,800 张图/分钟。哪怕你不在乎延迟也值得做,因为它在省配额。
- 配额上调要在上线前申请,不要等到第一波 429。 Cloud Quotas 控制台处理 Vision 配额一般几小时到一天,偶尔更久。如果你的稳态预测高于 1,200 RPM,提前一周提单。
- 1,800 RPM 是 project 全量 Vision 端点共享的, 包括异步提交。瞬时丢 200 个异步 PDF 也吃这一分钟的配额。
人脸识别那堵墙
这个不是定价惊喜,是能力惊喜,每个把"人脸识别"写进需求文档的项目都会碰到。
Cloud Vision 的 FACE_DETECTION 返回的是做人脸 分析 需要的东西:bounding box、landmark(眼、鼻、嘴、耳的坐标),以及 joy、sorrow、anger、surprise、headwear、blur、under-exposure 等多个属性的 likelihood。它不返回身份。没有 SearchFacesByImage 那种端点,没有可以注册用户的 face collection,也没有可以跨调用比对的 embedding 向量。
这是 Google 的明确策略选择。AWS Rekognition 提供人脸识别 API,Google 不提供。如果你的需求确实是 1:1("这张和那张是不是同一个人")或 1:N("这张能匹配到注册库里的哪个用户"),Vision 就是错的服务。现实可选项:
- AWS Rekognition Face Compare 或 SearchFacesByImage
- Azure Face API(注意 Microsoft 这两年也在收紧公开访问)
- 自建模型:ArcFace、FaceNet 或 InsightFace,自己用 gRPC 服务起来
值得在需求评审上当场敲定。"检测人脸"和"识别人脸"是两个不同的产品,把这两个搞混是 Cloud Vision 项目第一天最常见的失望来源。
15 行能跑的 LABEL_DETECTION
最短能拿到一个有用响应的路径,Python 版:
from google.cloud import vision_v1
client = vision_v1.ImageAnnotatorClient()
image = vision_v1.Image(source=vision_v1.ImageSource(
image_uri="gs://my-bucket/photos/cat.jpg"
))
response = client.annotate_image({
"image": image,
"features": [
{"type_": vision_v1.Feature.Type.LABEL_DETECTION, "max_results": 10},
{"type_": vision_v1.Feature.Type.SAFE_SEARCH_DETECTION},
],
})
for label in response.label_annotations:
print(f"{label.score:.2f} {label.description}")
ss = response.safe_search_annotation
print(f"adult={ss.adult.name} racy={ss.racy.name} violence={ss.violence.name}")
三个值得首次集成时留意的点:
max_results是按 feature 设的。 LABEL_DETECTION 默认大约 10 条,要长尾标签就调高。- SafeSearch 返回的是 enum 化的 likelihood,不是数值。 五个值:
UNKNOWN、VERY_UNLIKELY、UNLIKELY、POSSIBLE、LIKELY、VERY_LIKELY。阈值按业务定,UGC fail-closed 门禁建议锚在POSSIBLE。 annotate_image是同步单图。 真要批量要用batch_annotate_images,单请求最多 16 张图,仍然只算一次配额。
上线 100 万张/天前要做的事
把一个 Cloud Vision 集成从原型推到生产规模的实操清单:
- 审一遍 feature 列表。 每次 annotate 请求里的每个 feature,下游必须有消费方解析。没人用的 feature 立刻砍:每多一个 feature 就是账单上的一个隐性乘数。
- 预算按 feature × 图片估,不要按图片估。 把图片量乘以每次调用的 feature 数得到 unit 预测。叠 3 个 feature,unit 数是图片数的 3 倍。
- PDF 处理路径在写循环之前先定下来。 多页 PDF 走 DOCUMENT_TEXT_DETECTION + files:asyncBatchAnnotate,不要按页循环 TEXT_DETECTION。
- 生产环境输入切到 GCS URI。 走出原型就把 base64 扔掉。33% 体积膨胀加 10MB payload 上限,上量后叠加得很难看。
- bucket 和 API 同区域 避免跨区域拉取费用和延迟。
- Cloud Billing budget alert 在 50% 和 90% 各设一档。 feature × 图片这个惊喜在第一个完整账单周期咬得最狠。
- 预计 RPM 高于 1,200 就提配额申请。 给峰值留余量。
- 需求文档里的"人脸识别"先确认是不是其实想说"人脸 检测"。 如果真要识别,Vision 就是错的服务,项目架构本身就走偏了。
如果你还在挑图像 API,我们目录里的 photography 类目 列出了在选 Cloud Vision 之前值得对比的替代品、补充品和相邻服务。
快速跳到对应段落
下一步
读完后可以继续回到工具目录,对比具体产品。
去看工具