PixelUMM 输出时好时坏时,先把“模型能力不行”这个假设往后放。同一张原图从不同入口上传,缩放库、压缩质量、EXIF 方向处理、透明通道合成只要有一处不同,送进模型的就已经不是同一张图。建议的判断顺序是:先把上传前的处理链路完整写下来,再用固定样本、每次只改一个变量的方式跑对比,最后才决定问题该归到模型还是归到链路。
如果同一批图片在不同入口、不同上传路径下表现差异明显,先按预处理链路排查:记录从原图到送模的每一步操作,固定样本后每次只改一个变量跑对比,观察差异是否随某个变量成组出现。如果同一输入、同一预处理参数多次调用仍然不一致,再把怀疑转向模型或调用侧——预处理规范再严格也解释不了这种情况。这套方法只用于定位差异来源,不能用来判断模型能力上限,也不要据此给出稳定性指标。
把图片上传前的每一步处理写下来
先不要动模型,把下面这些步骤按实际执行顺序列成清单,写清每一步用的库和参数。写不出取值的那一步,通常就是还没被控制住的变量。
- 读取与解码:PIL / ImageIO / OpenCV / ffmpeg / 前端 canvas,具体用哪个解码器;多帧图是否只取第一帧。
- 方向纠正:是否读取 EXIF Orientation 并旋转,旋转发生在缩放前还是缩放后,纠正后是否清除方向标记。
- 色彩与通道:是否统一转 RGB;RGBA 是合成到白底还是黑底,还是一起送进去;是否有色彩空间转换。
- 缩放:短边对齐还是长边对齐,具体目标像素值;用哪个缩放库和插值算法(PIL LANCZOS、cv2 INTER_AREA、canvas drawImage);是否分两步缩放,是否做等比 padding。
- 编码与压缩:输出 PNG / JPEG / WebP,quality 取值多少;是否存在二次压缩;是否有字节上限触发的重压。
- 传输:base64 编码、表单上传还是本地路径直读,编码环节是否改变字节。
记录时一行一个变量,避免“大概 1024 左右”这类描述。下面这张表可以直接抄成团队内的记录模板:
| 步骤 | 实际取值 | 由谁决定 | 本轮是否固定 |
|---|---|---|---|
| 方向纠正 | 读 EXIF / 不读 | 服务端脚本 | 是 |
| 缩放算法 | 例如 LANCZOS 或 INTER_AREA | 服务端脚本 | 是 |
| 目标尺寸 | 短边 768 / 长边 1024 | 前端或服务端 | 是 |
| 输出格式 | PNG / JPEG / WebP | 服务端脚本 | 是 |
| 压缩质量 | quality 取值 | 服务端脚本 | 是 |
写一个每次只改一个变量的对比脚本骨架
多个变量同时变化时,两轮结果不同也说明不了什么。把每个可能影响结果的处理做成开关,一次只打开一个。下面是一份通用骨架,具体实现替换成你实际使用的缩放库和编码器即可。
# preprocess_variants.py —— 通用骨架,按自己的技术栈替换实现细节
import json, pathlib
# 每个变体只在一个字段上与 base 不同
VARIANTS = {
"base": {"long_side": 1024, "fmt": "PNG", "quality": None, "exif": "keep", "bg": "none"},
"v_resize": {"long_side": 768, "fmt": "PNG", "quality": None, "exif": "keep", "bg": "none"},
"v_fmt": {"long_side": 1024, "fmt": "JPEG", "quality": 90, "exif": "keep", "bg": "none"},
"v_quality": {"long_side": 1024, "fmt": "JPEG", "quality": 70, "exif": "keep", "bg": "none"},
"v_exif": {"long_side": 1024, "fmt": "PNG", "quality": None, "exif": "apply", "bg": "none"},
"v_bg": {"long_side": 1024, "fmt": "PNG", "quality": None, "exif": "keep", "bg": "white"},
}
SAMPLES = pathlib.Path("samples.txt") # 每行一个原图路径,整轮实验不改动
def preprocess(path, cfg):
# 按 cfg 完成方向纠正、缩放、编码,返回 (bytes, meta)
# meta 里至少写入:原图尺寸、纠正后尺寸、最终尺寸、格式、quality、是否合成背景
...
def run(variant, sample, run_idx, outdir):
cfg = VARIANTS[variant]
data, meta = preprocess(sample, cfg)
stem = f"{pathlib.Path(sample).stem}__{variant}__run{run_idx}"
(outdir / f"{stem}.bin").write_bytes(data) # 送入模型的真实字节
(outdir / f"{stem}.json").write_text(json.dumps(meta, ensure_ascii=False, indent=2))
return stem
for variant in VARIANTS:
for line in SAMPLES.read_text().splitlines():
if not line.strip():
continue
for i in range(1, 4): # 每个样本每个变体跑 3 次
run(variant, line.strip(), i, pathlib.Path("out"))
样本清单建议覆盖要验证的几类情况:大图、小图、带 EXIF 旋转的图、带透明通道的 PNG、超长截图。清单一旦确定,整轮实验不要增删,否则前后两轮没法比。输出命名用“样本id__变体名__runN”一一对应,不要用时间戳当文件名,否则回看时对不上是哪张图。
固定输入只改预处理,跑两轮看输出差异
把样本清单锁死,两轮之间只改预处理配置,不改样本,也不改调用参数。建议这样安排:
- 第一轮:只跑 base 变体,每张样本连续调用 3 次,记录 3 次输出。
- 第二轮:只换一个变体(例如 v_resize),样本和调用次数与第一轮完全相同。
- 每轮记录字段:变体名、完整参数、运行序号、输出文件名、人工可用性标注、差异描述。
判断时先看组内一致性。同一个变体、同一张样本的 3 次调用如果输出之间也有差异,说明除了预处理还有别的因素在动,这类情况单独标出来,不要混进预处理对比。差异描述字段建议固定成几个词,方便横向比较:裁剪或截断、方向颠倒、颜色偏移、细小文字糊、整体可用。
| 样本ID | 变体 | 关键参数 | 运行序号 | 输出文件 | 可用性 | 差异描述 |
|---|---|---|---|---|---|---|
| s01 | base | PNG / 1024 / 不纠向 | 1 | s01__base__run1 | 可用 | 无 |
| s01 | v_resize | PNG / 768 / 不纠向 | 1 | s01__v_resize__run1 | 不可用 | 细小文字糊 |
| s02 | v_exif | PNG / 1024 / 纠向 | 1 | s02__v_exif__run1 | 不可用 | 方向颠倒 |
把差异归类到尺寸、格式、方向还是压缩
跑完之后看差异是“跟着某个变量成组出现”,还是“在同一个变量内部随机出现”。只有前者才说明是预处理带来的问题。
| 变量类别 | 建议样本量 | 典型差异表现 | 观察要点 |
|---|---|---|---|
| 尺寸 | 5-10 张,大小两档都要有 | 细节丢失、文字断行位置变化、整体内容被裁 | 同一张图缩到两个尺寸,差异是否稳定复现 |
| 格式 | 5-10 张,含透明通道与彩色照片 | 边缘块状、透明区变黑底或白底、颜色发闷 | 只换容器格式,其他参数不动 |
| 方向 | 3-5 张明确带旋转标记的图 | 输出描述左右颠倒或上下颠倒 | 对比纠向前后的同一张图,不要混入其他改动 |
| 压缩 | 5-10 张,quality 高低两档 | 小字糊、边缘噪点被当成内容 | 高低两档都跑,看差异是否只在低质量档出现 |
如果这四类变量都控制住了,同一输入多次调用仍然表现不一,那就不属于预处理链路能解释的范围了。把原图、处理后的字节、对应输出一并交给负责模型接入的人,让那边继续排查调用侧和模型侧的因素。
固化一套下发前的预处理规范
把实验里确认过的取值写成条目,作为图片下发前的固定规则:
- 方向:解码后立即按 EXIF 纠正,纠正后清除方向标记,后续环节不再旋转。
- 尺寸:统一按短边或长边策略缩放,写明目标像素值、缩放库和插值算法。
- 格式与压缩:统一输出格式与 quality 取值;带透明通道的图先按指定背景色合成。
- 单次编码:同一张图在链路里只编码一次,避免重复压缩带来的叠加损失。
- 版本化:预处理脚本带版本号,版本号随图片一起记录,便于回看某批次用的是哪套规则。
检查方式可以放在下发前抽检:抽取若干张图,比对脚本输出的元数据记录与规范条目是否一致,把图片和处理参数一起留档。不满足规范时的动作建议是明确的:不直接送入模型,退回上游重新生成;确实无法重新生成时,标记为“非规范输入”,与其他样本分开统计,不要混在一起比较结果。