Mage-Flow 接进已有的出图流程 / 先确认输入输出格式能对得上

文章导读
把 Mage-Flow 接进一套已经跑得通的出图流程,第一步要解决的是接口两端对不对得上:上游交出来的是不是 NCHW 的浮点张量、通道顺序是 RGB 还是 BGR、取值范围是 [0,1] 还是 [0,255]、下游要的是像素还是编码后的潜变量。其中任何一项不一致,下游节点要么直接抛出形状错误,要么勉强跑通却出偏色、错位的图。先花半小时把这些字段记清楚,通常比反复调整模型参数更有效。
📋 目录
  1. Ⅰ 确认已有流程需要的输入是文件路径、张量还是编码后的潜变量
  2. Ⅱ 写一个只做格式转换的适配函数并用一张图验证
  3. Ⅲ 检查尺寸与通道数在下游节点是否被重新解释
  4. Ⅳ 用同一张输入跑通端到端并对比改造前后结果
A A

把 Mage-Flow 接进一套已经跑得通的出图流程,第一步要解决的是接口两端对不对得上:上游交出来的是不是 NCHW 的浮点张量、通道顺序是 RGB 还是 BGR、取值范围是 [0,1] 还是 [0,255]、下游要的是像素还是编码后的潜变量。其中任何一项不一致,下游节点要么直接抛出形状错误,要么勉强跑通却出偏色、错位的图。先花半小时把这些字段记清楚,通常比反复调整模型参数更有效。

判断:接入卡点多数在接口边界,不在模型本身。建议先确认对接层(文件路径、张量、潜变量),把两端的形状、通道顺序、取值范围、dtype 记录成一张对照清单;再写一个只做格式转换的适配函数,把所有改动限制在转换层;最后用同一张固定输入跑端到端,逐段保存中间结果,与改造前比对。若两端语义本就不同(一边是像素、一边是潜变量),中间需要编解码环节,不能靠形状变换硬凑。

确认已有流程需要的输入是文件路径、张量还是编码后的潜变量

先确认对接发生在哪一层,这决定了后面要改什么。常见的三种情况差别很大:

  • 文件路径层:上游把结果写入磁盘,下游按路径读文件。此时主要核对位深、色彩配置和 EXIF 方向标记,张量形状往往不是问题。
  • 张量层:上游直接把内存里的数组交给下游。需要核对维度顺序(NCHW 还是 NHWC)、通道顺序、取值范围、dtype 四项。
  • 潜变量层:下游接收的是编码之后的潜变量,尺寸通常远小于像素图(按空间压缩倍数缩小),数值分布也偏向零附近对称。如果上游送来的是像素张量,形状和范围都不匹配,中间缺的是一个编码步骤,而不是简单变形。

记录方式建议固定成一个字典,两侧各填一份,字段不要随手增减。值是程序里实际打印出来的,不要凭印象填。

upstream = {
    "layer": "tensor",           # path / tensor / latent
    "shape": (1, 3, 1024, 1024),
    "dim_order": "NCHW",
    "channel_order": "RGB",      # 潜变量可填 n/a
    "value_range": (0.0, 1.0),
    "dtype": "float32",
    "batch_semantics": "dim0",
}

downstream = {
    "layer": "latent",
    "shape": (1, 4, 128, 128),
    "dim_order": "NCHW",
    "channel_order": "n/a",
    "value_range": (-1.0, 1.0),
    "dtype": "float16",
    "batch_semantics": "dim0",
}

两份记录摆在一起,差异点就是适配函数要处理的范围。如果 layer 字段本身就不一致,先别写转换代码,需要补上中间环节再谈。

写一个只做格式转换的适配函数并用一张图验证

适配函数只做三件事:维度排布、通道置换、取值范围归一化。缩放、裁剪、色彩空间换算这类会改变画面内容的操作,尽量放到流程原有位置,不要混进转换层,否则出问题时说不清是哪一步造成的。

def adapt_to_target(x, src_spec, dst_spec):
    """x: 上游张量;src_spec/dst_spec: 上一步填的两份记录。
    只做格式转换,不改画面内容。"""
    # 1. 入口形状检查:维度数、通道维位置、批维语义
    if len(x.shape) != len(dst_spec["shape"]):
        raise ValueError(f"维度数不一致: {tuple(x.shape)} vs {dst_spec['shape']}")
    if tuple(x.shape) == tuple(dst_spec["shape"]):
        pass  # 形状已对齐,跳过变形

    # 2. 维度顺序:NCHW / NHWC 之间用 transpose,不要用 reshape 硬凑
    if src_spec["dim_order"] != dst_spec["dim_order"]:
        x = transpose_to(x, dst_spec["dim_order"])

    # 3. 通道顺序:RGB / BGR 用索引置换,比逐通道反转更不容易写错
    if src_spec.get("channel_order") and dst_spec.get("channel_order"):
        if src_spec["channel_order"] != dst_spec["channel_order"]:
            x = x[:, CHANNEL_INDEX_PLACEHOLDER]  # 按目标顺序取通道

    # 4. 取值范围归一化:先减下限再按区间长度缩放,避免越界
    lo_s, hi_s = src_spec["value_range"]
    lo_d, hi_d = dst_spec["value_range"]
    x = (x - lo_s) / (hi_s - lo_s) * (hi_d - lo_d) + lo_d

    # 5. dtype 放在最后,避免中间计算掉精度
    x = x.to(dst_spec["dtype"])

    # 6. 出口再断言一次,形成双向检查
    assert x.shape[-3] == dst_spec["shape"][-3]
    return x

函数放在上游输出和下游入口之间的粘合位置,两边原有逻辑一行不动。transpose_to、通道索引、区间端点都按上一步的记录填。验证时不要用复杂的自然图,先拿一张纯色图或棋盘格图跑一遍:通道顺序错了,红色区域会变蓝;范围错了,纯色会变成灰或全黑;dtype 错了,容易出现条纹。纯色图能把这三类问题一次性暴露出来。

Mage-Flow 接进已有的出图流程 / 先确认输入输出格式能对得上

检查尺寸与通道数在下游节点是否被重新解释

「能跑通但画面不对」是最容易被忽略的一类。适配函数的输出符合预期,不代表下游节点按同样的理解在使用它。在下游入口第一个处理函数之前加一行探针,是最省事的定位手段:

def tap(name, x):
    print(f"[{name}] shape={tuple(x.shape)} dtype={x.dtype} "
          f"min={float(x.min()):.4f} max={float(x.max()):.4f} "
          f"mean={float(x.mean()):.4f}")

# 调用位置:适配函数出口、下游第一个节点入口、下游最后一个节点出口
tap("after_adapt", y)
tap("downstream_in", y)
tap("downstream_out", out)

把三处打印放在同一次运行里对照,能判断数值是在哪一段被改写的。常见的错位现象和对应检查项如下:

  • 颜色偏红或偏蓝:通道顺序在某一侧被当成另一种解释,核对 RGB / BGR 与通道索引。
  • 整体发灰、对比度偏低:范围被压缩,例如 0–255 的数被按 0–1 读入。
  • 全黑或全白:范围端点不匹配后被裁剪,或归一化时除错了区间长度。
  • 出现条纹、噪点:dtype 溢出或位宽不一致,检查 float16 / float32 / uint8 的转换时机。
  • 图像被拉伸或裁掉一块:下游按固定边长或整除倍数假设处理尺寸,核对下游对尺寸的取整方式。
  • 上下颠倒或左右镜像:坐标原点约定不同,检查下游是否有翻转操作。
  • 通道维被当成批维:维度语义错位,核对 NCHW 里哪一维是 batch。

这些现象多数能从前面的三次打印里直接看出线索:形状对但 mean 异常,多半是范围问题;形状不对但数值正常,多半是排布问题。

用同一张输入跑通端到端并对比改造前后结果

最后一步是确认适配函数没有在转换过程中悄悄改动画面内容。做法是控制变量,只换流程本身,不换输入。

  1. 固定一张输入图,尺寸选常见的正方形,下采样后的潜变量尺寸要能被整除,避免触发下游的补齐逻辑。
  2. 如果流程内部有随机采样,先固定随机种子,否则两次结果不可比。
  3. 在原流程和接入 Mage-Flow 后的流程里,各保存一份中间结果:上游输出、适配函数输出、下游第一层输入、下游最终输出。落盘时用同一套命名,方便并排看。
  4. 逐段比对,先比形状和 dtype,再比 min / max / mean,最后看图像本身。判定标准是:适配函数前后的形状、通道、范围应符合记录表;下游第一层输入若与原流程对应位置一致或在数值容差内,说明转换层没有引入变化。
  5. 如果差异只出现在下游内部,转换层通常可以先排除,转去检查下游参数;如果差异从适配函数出口就开始出现,回到第 2 节的骨架逐项核对。

改造前后不要求逐像素完全一致,模型内部计算顺序变化、dtype 精度不同都可能带来细微差别。判断的重点是差异有没有超出预期范围,以及差异从哪一段开始出现。把这一步的中间图留档,后续改动时可以直接复用同一套比对方式。