做 Native 广告竞价的时候,遇到的第一个麻烦往往不是出价策略,不是 CTR 模型,而是图片尺寸。

bid request 里 imp.native.request 带着主图、icon、logo 的各种宽高要求。每个媒体、App、版位都不一样——有的给精确 w / h,有的给 wmin / hmin,有的只给比例,有的什么都不给但 SSP 渲染模板暗含了尺寸约束。同一个广告位,不同渲染模板可能要求完全不同的图片规格。这还只是一个 exchange,当你对接了十几个 exchange、几百个 publisher、上千个 placement 之后,尺寸矩阵会膨胀到让人不想看的程度。

最直觉的反应是:把所有可能尺寸提前裁好。进一步想,也可能考虑在竞价时实时裁图。

两条路看起来都成立,但仔细拆下去,各有各的坑。这篇文章想讨论的是这两条路的问题在哪,以及是不是存在一个不同的方向——把问题从图片处理重新框定为素材可投放性管理。

穷举尺寸:直觉上成立,工程上不可持续

把广告主的一张主图裁成所有可能的尺寸,听起来是最稳妥的方案。流量方要什么就给什么,不会因为尺寸不匹配丢掉展示机会。

但数字不会骗人。

假设 Native 图片要覆盖的宽度从 100px 到 1200px,高度从 100px 到 1200px,再加上常见宽高比——1:1、1.91:1、16:9、4:3、4:5、9:16——光是主图就可能需要几十个变体。这还只是一个广告主的一张图。广告主通常在一个 campaign 里有多张主图,一个 DSP 同时跑着几百到几千个 campaign。每个 campaign 有主图、icon、logo 三种资产。变体总数增长极快。

存储和计算成本可能还能忍,真正疼的是审核。

大多数 exchange 要求创意图片提前提交审核。不同 exchange 的审核系统是独立的——在 Google 审核通过不代表在 Xandr 也通过。如果对接了 10 个 exchange,每个主图只生成 20 个比例变体,那就是 200 张待审核图片,每个广告主,每个 campaign。审核周期取决于 exchange,从分钟级到天级都有可能。广告主问"为什么我的广告还没上量",答案可能是"还有 160 张图在等审核"。

还有更新问题。广告主换了一张主图——可能只是改了 logo 位置或换了个背景色——所有变体要重新生成,重新提交,重新等审核。旧的已审核变体全部作废。这个维护成本不是一次性的,伴随 campaign 的整个生命周期。

更关键的是:大部分变体用不上。统计一下实际 bid request 的尺寸分布,你会发现根据经验,常见 Native 流量里少数几种主流宽高比往往已经覆盖了大部分请求,而大量长尾尺寸的出现频率很低。为这些低频尺寸生成额外变体,投入的审核和维护成本远超过增量收入。

穷举尺寸本质上是一个"怕丢机会"驱动的决策,但边际收益在递减,而边际成本恒定甚至递增——审核、存储、更新维护,一张图也不少。

实时裁图:把图片处理问题变成了运维问题

既然穷举不划算,那在竞价时动态裁图呢?逻辑很直:收到 bid request → 解析尺寸要求 → 从原图裁出所需尺寸 → 返回新 URL。

CPU 够、有图像处理库、CDN 支持动态上传,这条链路能跑通。技术上不是做不到。但把图片处理塞进 bid handler,会引入三个更难解决的问题。

审核失控。 bid handler 裁出来的是一张全新图片。裁到了什么、有没有裁掉 logo 或文字、有没有把人物从脖子处截断、视觉效果是否合格——审核系统毫不知情。许多 exchange 要求素材提前提交并通过审核才能参与竞价。竞价时临时裁剪生成的 URL 不在已审核清单里,exchange 可能在 serve 阶段拒绝。一旦被拒,这次 bid 的 CPU 时间、网络 I/O、出价机会全部浪费。如果某 exchange 对审核拒绝有累积惩罚机制,频繁被拒还可能牵连其他正常素材的竞价表现。

排查失控。 广告主投诉"我的广告在某某 App 上展示变形了"。回头查日志:当时 bid request 要求的尺寸是多少?裁图时的 crop 参数是多少?原图是哪个版本?裁出来的图落地到哪个 CDN 节点?这些问题在实时裁图链路里非常难回答,因为裁图是一次性的、无状态的。日志可能记录了请求尺寸,但不会记录裁图效果——你无法知道那张图实际长什么样。排查一次问题需要在请求日志、裁图服务日志、CDN 日志之间来回跳,链路长、信息散。

缓存失控。 同一个素材、同一个尺寸,如果每次都生成新 URL,CDN 缓存命中率为零,每次展示都要回源,出图延迟不稳定。如果复用 URL,需要在 bid handler 里引入去重逻辑——"这个尺寸裁过吗"、"裁出来的 URL 是什么"——这本质上是把裁图服务的状态管理耦合进了竞价链路。bid handler 应该是无状态的、延迟可预测的,加一个分布式状态查询进来,延迟预算就可能被击穿。

说到延迟预算:有人可能会想,如果工程资源够多,是不是可以建独立的裁图服务、做裁图日志持久化、搭自动审核 pipeline——把运维问题也工程化掉?这当然不是完全不可能。但需要算一笔账:引入这套额外基础设施,要覆盖的恰恰是长尾尺寸(主流尺寸用预生成变体就解决了),增量收入能否覆盖增量系统复杂度,并不显然。对于资源有限的团队,这笔账更难算平。

实时裁图的问题不在技术可行性。它真正的代价是把图片处理变成了运维问题,而运维问题的特点是:失败的代价不发生在开发环境,发生在线上,且难以复现、排查周期长。

换个角度:协议和平台都没要求这样做

踩完这两个方向的坑之后,回头看 OpenRTB Native 协议,会发现一个之前可能被忽略的事实。

OpenRTB Native Ads 1.2 的 Image Request Object 定义了 type(图片资产类型)、w / h(建议宽高)、wmin / hmin(最小宽高)、mimes(支持 MIME 类型)。协议要求 DSP 的响应满足 required assets 的类型、尺寸和 MIME 约束,但它不规定 DSP 以什么方式满足这些约束,更不要求 DSP 为每一种尺寸预先准备图片。

注意 wmin/hmin 的语义——"最小请求宽高"。这个字段至少说明协议允许表达一个范围,而不是只表达像素级精确尺寸。只要素材宽高不小于最小值,后续是否能 downscale、padding 或有限裁剪,取决于 exchange、publisher 渲染方式和双方约定。精确 w/h 的请求更应该谨慎处理:它可以在某些平台实践中按宽高比匹配,但不能默认所有 SSP 都接受这种弹性。

协议给了弹性空间,问题在于 DSP 是否愿意用这个空间,而不是把自己逼到"像素级别完美匹配"的角落。

再看看平台公开资料。Google Authorized Buyers 的 native creative 规范明确说明,publisher 可以在不改变宽高比的情况下缩小图片,允许在一个方向上做有限的对称裁剪;尺寸不完全匹配时可以通过 padding 避免扭曲。Xandr 的 Smart Image Adjustments 文档也描述了类似逻辑——图片与 placement 尺寸不完全匹配时,平台可以通过加白边来填充,同时限制缩放范围,原始图片尺寸决定了最终可适配范围。

这些平台文档当然不是 IAB 强制标准,也不能泛化成所有 SSP 的行为。但它们至少说明:部分头部平台自己承担了一部分适配责任,并没有要求 DSP 为所有尺寸穷举图片。 更稳妥的做法是把平台适配能力当成 partner 维度的能力配置,而不是写成全局假设。

一个可行方向:管素材的可投放性,而不是管图片处理

把问题重新框定一下,思路会清晰很多。

不要问"怎么为每种尺寸生成对应的图片",而是问"怎么判断一个素材能不能投放给这个 bid request"。前者是图片处理问题,后者是匹配问题。匹配可以在亚毫秒级完成,图片处理不行。把处理放在竞价前,把匹配放在竞价时,两者不混在一起。

从这个角度,素材系统的设计可以沿三个层面来想。

第一个层面:区分资产语义。

Native 图片不是只有"图片"一种东西。OpenRTB Native 协议定义了多个 image asset type:主图(main image)、icon、logo、app icon 等。不同语义的图片有不同的用途和约束。主图通常是大尺寸、高信息量的图片,需要做多比例适配。icon 和 logo 是小尺寸、品牌标识类图片,不适合从主图裁剪出来——把一张 1200x1200 的主图缩到 128x128 当 icon,品牌标识可能缩成一团无法辨认。反过来,把 icon 放大当主图,马赛克是必然的。

这一层看起来简单,但如果素材库一开始只按"图片"来管理,后面要往回补语义标签,改造成本不低。

第二个层面:管理有限的比例变体。

不要为所有像素尺寸生成变体,而是选一组比例族群。常见候选包括 1:1、1.91:1、16:9、4:3、4:5、9:16。具体选哪些、选几个,应该来自流量数据分析——统计 bid request 中 img.wimg.himg.wminimg.hmin 的分布,找到覆盖主要流量的比例。这不是一次性决策,可以周期性调整。长尾比例可以先 no-bid,再根据收入机会决定是否补充新变体。

对主图按这些比例预生成变体。对 icon 和 logo 单独管理,不参与主图的裁剪流程。其他 exchange 或 publisher 自定义的 image asset 类型,按同样逻辑评估是否需要独立变体。

第三个层面:竞价时只做匹配。

bid handler 的逻辑是一条直线:解析 request → 按 asset type 筛选 → 检查 MIME 是否允许 → 检查尺寸是否在变体的可服务范围内 → 检查该 exchange 上的审核状态 → 匹配上就返回 native response,匹配不上就 no-bid。

可服务范围的判断不需要复杂逻辑:请求是精确 w/h,就看是否有对应尺寸或相近尺寸的变体;请求是 wmin/hmin,就看变体尺寸是否不小于最小值;请求只给了比例,就看是否有对应宽高比的变体。所有判断都是整数比较,不需要浮点运算,不需要图像库。

素材索引的核心字段大概是:creative_id、asset_type、asset_id、ratio_bucket、url、width、height、mime、file_size、audit_status_by_exchange。bid handler 做的是索引查找,复杂度 O(1)。

边界:什么能做、什么不能做、什么时候该放手

以上思路不是万能的。有几条边界值得诚实地说清楚。

什么时候需要裁剪变体。 只有主图需要生成多比例变体。icon、logo 等品牌标识类资产单独管理,不裁剪。视频缩略图等特殊类型按实际需求处理。

缩放的方向性。 缩小通常可以承受,明显放大不可接受。请求 wmin=600, hmin=315,有一个 1200x628 的变体,服务它。反过来,请求 1200x628,只有 600x314,不要放大后竞价。缩小的质量损失在渲染端通常看不出来,放大的模糊是第一眼就能感知的。Google 文档明确强调缩放不能改变宽高比,Xandr 也说明自动调整存在缩放范围限制。这些都指向同一个原则:素材尺寸应该不小于请求尺寸。

平台能力的边界。 Google Authorized Buyers 和 Xandr 明确支持 padding 或有限自动调整,但不是所有 SSP 都有这个能力。在依赖平台自动处理之前,确认 partner contract 或平台文档有明确承诺。把平台能力当作 bonus 而不是 guarantee。

什么时候果断 no-bid。 required asset 缺失、MIME 不支持、最低分辨率不满足、精确尺寸请求无匹配变体、比例差异过大裁剪会破坏主体内容、素材在该 exchange 未审核或审核过期或已被拒、平台要求重新审核而新 URL 尚未通过——这些情况下不该强行出价。

no-bid 损失的是一次展示机会,错误出价损失的不仅是一次 bid 的资源,还包括:可能的 exchange 过滤、低 CTR 对后续竞价的影响、品牌方投诉、以及排查人力成本。更重要的是,no-bid 保留了未来的匹配机会——素材库扩充或比例变体补充时,同一套匹配逻辑直接生效。而错误出价如果留下审核拒绝记录或低质量评分,修复的成本远高于从一开始就不出价。

关于焦点和安全区。 这是整个讨论里最不确定的一块。即使按正确比例裁图,也可能裁掉关键元素——人物被截断、logo 贴边、文字被腰斩。理想解法是人脸检测、主体检测、显著性检测做自动焦点识别,结合人工确认,为每个比例变体生成预览图,标记安全区。对包含文字的图片需要更保守——文字裁掉一半的图片通常直接不可用。但现实中流程完善度参差不齐,没有通用解法。只能说,缩小比裁剪安全,裁剪比硬适配安全,什么都不做比瞎做强。

结尾

上面这套思路的核心很简单:把 creative asset 问题从"怎么处理图片"重新框定为"怎么判断可投放性"。图片处理放在竞价前,竞价时只做匹配,匹配不上不强求。

但有几个问题目前还没有满意的答案。

流量数据驱动比例选择的思路,前提是有足够 bid request 做统计分析。流量规模小的 DSP,统计分布可能不稳定,冷启动阶段用行业常见比例是不是更实际?

不同 SSP 对图片尺寸不匹配的处理策略差异很大——有的 padding、有的拒绝、有的静默缩放、有的先接受再审核拒绝。系统化管理这个差异矩阵的方式,目前还没有看到特别成熟的方案。

焦点的自动化检测和人工确认之间的平衡,在资源有限的团队里怎么做才能不成为瓶颈——这块似乎更多依赖经验判断而非可复制的方法。

如果你在做类似的事情,有不一样的尝试或其他思路,我很有兴趣听。

参考资料