店铺里的商品图是不同时间、不同地方拍的:有的在暖黄灯下,有的在阴天窗边,有的背景很亮。放在一起,整个店铺看起来很乱。

这篇对比两种「统一风格」的方法:

  • 代码调色:每张图先校正白平衡和曝光,再套同一套「店铺风格」参数,只调颜色,不重画;
  • Nano Banana Pro:把 4 张图一次交给它,让它统一成一种风格输出四宫格(4 张图只花 1 次调用的钱)。

结论:商品图用代码调色。Nano Banana Pro 出来的效果更好看,但它是重画,连杯身上的标志形状都改了。

上:6 张随手拍的原图,光线各不相同;下:用同一套参数代码调色后上:6 张随手拍的原图,光线各不相同;下:用同一套参数代码调色后

说明:6 张照片是《用 ComfyUI 批量做商品白底图》里 AI 生成的「手机随手拍」演示素材。

方法一:代码批量调色

思路和《外卖和零食图怎么修得更有食欲》里的调色脚本一样,多了两步:把曝光统一到同一个值、把所有图裁成同样大小。店铺风格写在一个小文件里,以后每批新图都用它,整个店铺就是一个风格。

保存为 batch_grade.py:

"""批量调色、统一风格:python3 batch_grade.py 原图文件夹 输出文件夹 [风格.json]

每张图先「归零」,再套同一套店铺风格,商品本身不重画:
1. 白平衡:画面里最亮、颜色最淡的那部分(白色商品、白墙、白纸)校正成中性白
2. 曝光:把整张图的中间亮度拉到同一个值,暗的提亮、亮的压一点
3. 店铺风格(风格.json,所有图共用):暖一点或冷一点、对比度、饱和度
4. 统一尺寸:从中间裁成正方形,缩到同样大小
最后输出每张图调整前后的亮度和偏色,方便看统一了没有。
"""
import json, os, sys
import numpy as np
from PIL import Image, ImageCms

STYLE = {"亮度": 0.56, "暖色": 0.6, "对比度": 1.08, "饱和度": 1.08, "边长": 1000}
T = ImageCms.buildTransform(ImageCms.createProfile("sRGB"), ImageCms.createProfile("LAB"), "RGB", "LAB")


def stats(a):
    """Median lightness (0-100) and the colour cast (a, b) of the brightest, palest 10% of pixels."""
    lab = np.asarray(ImageCms.applyTransform(Image.fromarray(a.astype(np.uint8)), T))
    L = lab[..., 0].astype(float) * 100 / 255
    A, B = lab[..., 1].view(np.int8).astype(float), lab[..., 2].view(np.int8).astype(float)
    ref = L > np.percentile(L, 90)
    return float(np.median(L)), float(A[ref].mean()), float(B[ref].mean())


def white_balance(a):
    lum, spread = a.mean(axis=2), a.max(axis=2) - a.min(axis=2)
    ref = (lum > np.percentile(lum, 90)) & (spread < np.percentile(spread, 50))
    if ref.sum() < 500:
        ref = lum > np.percentile(lum, 95)
    gain = a[ref].mean(axis=0)
    return np.clip(a * (gain.mean() / gain), 0, 255)


def grade(img, s):
    a = white_balance(np.asarray(img.convert("RGB")).astype(float))
    med = np.median(a.mean(axis=2)) / 255
    a = 255 * (a / 255) ** (np.log(s["亮度"]) / np.log(max(med, 0.02)))  # move the midtone to the target
    a = np.clip((a - 128) * s["对比度"] + 128, 0, 255)
    a = a * np.array([1 + 0.03 * s["暖色"], 1 + 0.005 * s["暖色"], 1 - 0.03 * s["暖色"]])
    gray = a.mean(axis=2, keepdims=True)
    a = np.clip(gray + (a - gray) * s["饱和度"], 0, 255)
    im = Image.fromarray(a.astype(np.uint8))
    side = min(im.size)
    im = im.crop(((im.width - side) // 2, (im.height - side) // 2, (im.width + side) // 2, (im.height + side) // 2))
    return im.resize((s["边长"], s["边长"]), Image.LANCZOS)


if __name__ == "__main__":
    src, dst = sys.argv[1], sys.argv[2]
    style = {**STYLE, **(json.load(open(sys.argv[3], encoding="utf-8")) if len(sys.argv) > 3 else {})}
    os.makedirs(dst, exist_ok=True)
    rows = []
    for f in sorted(os.listdir(src)):
        if not f.lower().endswith((".jpg", ".jpeg", ".png", ".webp")):
            continue
        img = Image.open(os.path.join(src, f)).convert("RGB")
        out = grade(img, style)
        out.save(os.path.join(dst, os.path.splitext(f)[0] + ".jpg"), quality=92)
        rows.append((f, stats(np.asarray(img).astype(float)), stats(np.asarray(out).astype(float))))
    print(f"{'文件':14}{'亮度 前→后':>14}{'亮处偏色(a,b) 前→后':>26}")
    for f, (L0, a0, b0), (L1, a1, b1) in rows:
        print(f"{f:14}{L0:6.0f} → {L1:<5.0f}   ({a0:+.0f},{b0:+.0f}) → ({a1:+.0f},{b1:+.0f})")
    for i, name in ((0, "亮度"), (2, "偏黄蓝 b")):
        before, after = np.std([r[1][i] for r in rows]), np.std([r[2][i] for r in rows])
        print(f"{name}在 {len(rows)} 张之间的差异(标准差):{before:.1f} → {after:.1f}")

店铺风格存成 风格.json,想更暖、更亮就改这里:

{"亮度": 0.56, "暖色": 0.6, "对比度": 1.08, "饱和度": 1.08, "边长": 1000}

运行(不需要 ComfyUI,也不用联网):

uv run --with pillow --with numpy batch_grade.py 原图 调色后 风格.json

6 张约 2 秒(M1 Max)。脚本会输出每张图调整前后的数字:

文件 亮度 前→后 亮处偏色 b 前→后(正数偏黄)
马克杯(暖黄灯木桌) 44 → 62 +14 → +4
白鞋 55 → 64 +2 → +3
护手霜(床单) 62 → 60 0 → +3
耳机(亮白桌面) 83 → 60 +2 → +4
保温杯(阴天窗边) 62 → 61 −4 → 0
小熊(暖光沙发) 41 → 62 +29 → +18
6 张之间的差异(标准差) 14.0 → 1.4 11.2 → 5.9

亮度差异降到原来的十分之一,偏色差异减半。剩下的偏色主要来自小熊那张:画面里没有白色的东西,脚本找不到白平衡的参照,只能校正一部分。

两个局限:

  • 没有白色参照的照片校不准(小熊那张)。拍的时候放一张白纸进画面,问题就解决了;
  • 背景特别亮的照片会被整体压暗(耳机那张,亮度从 83 压到 60),黑色的充电盒细节变少了。这类图可以单独把「亮度」调高一点再跑。

方法二:Nano Banana Pro 一次统一 4 张

用《一张商品照片生成多角度图》里的 nb_edit.py,4 张原图一起输入,要求输出一张四宫格,每格是一张统一风格后的图(Pro,2K,一次调用约 0.134 美元,51 秒):

python3 nb_edit.py 统一风格.png "You are given 4 product photos taken by phone under different lighting. Output ONE 2x2 grid image (top-left = image 1, top-right = image 2, bottom-left = image 3, bottom-right = image 4) where all four photos are color-graded into one consistent, clean e-commerce style: neutral white balance, the same bright soft daylight exposure, gentle contrast, slightly warm. Only adjust color and light. Keep each photo's composition, background objects and especially the product itself (shape, color, material, logo, every detail) exactly as in its source photo. Do not add or remove anything. No text, no borders." 马克杯.png 护手霜.png 保温杯.png 小熊.png --model gemini-3-pro-image --size 2K

切开用同一篇里的 split_grid.py。

Nano Banana Pro 输出的四宫格:风格非常统一,光线柔和Nano Banana Pro 输出的四宫格:风格非常统一,光线柔和

乍一看效果比代码调色更好:光线柔和、像同一天在同一个影棚拍的。但仔细看:

1. 它重新构图了。 4 张都被放大、重新裁切过,马克杯和小熊都比原图大了一圈。指令里写了「保持构图」。

2. 商品被重画了。 放大保温杯:

左:原图杯身的标志;右:Nano Banana Pro 统一风格后,标志的形状变了左:原图杯身的标志;右:Nano Banana Pro 统一风格后,标志的形状变了

杯身的圆形标志里,图案的形状变了。真实商品上这就是品牌标志,被改成了另一个样子。

3. 白色商品被调黄了。 用《首饰、手表这类反光商品怎么用 AI 修图》里的 check_color.py 量商品本身的颜色:

商品 原图 b(正数偏黄) 代码调色后 Nano Banana Pro
白色马克杯 +14.7(被暖灯映黄) +9.5(往中性白校正) +19.9(更黄了)
白色护手霜 +6.6 +9.7 +12.6
墨绿保温杯 +1.6 +3.1 +4.7
小熊 +34.8 +35.1 +39.4

指令里写了「中性白平衡、略微偏暖」,它把「暖」用在了商品上,白色的杯子、护手霜都比原图更黄。代码调色则把被暖灯映黄的马克杯往中性白拉了回来(不过护手霜的「暖色」参数也让它略黄了一点,对白色商品可以把暖色调到 0)。

上排马克杯、下排小熊:原图 / 代码调色 / Nano Banana Pro上排马克杯、下排小熊:原图 / 代码调色 / Nano Banana Pro

怎么选

代码调色 Nano Banana Pro
商品本身 不重画,形状、标志不变 重画,标志会变,颜色会漂
构图 统一裁成正方形,不改内容 会重新构图
统一程度 亮度差异 14.0 → 1.4 视觉上更统一、更好看
速度、成本 6 张约 2 秒,免费,不联网 4 张 51 秒,约 0.134 美元
适合 商品主图、详情页、白底图——所有展示商品本身的图 店铺首页横幅、活动海报、氛围图——商品不是主角的图

所以:展示商品的图用代码调色,保证买家看到的就是实物;需要「好看」、商品不是主角的营销图,可以用 Nano Banana Pro,但用之前仍要和实物对一遍,并按规定标识 AI 生成。

拍的时候就统一

调色只能救一部分,最省事的统一方法是拍的时候就统一:

  • 固定一个拍摄位置:同一张桌子、同一个窗边、每天差不多的时间;
  • 每张都放一张白纸进画面,脚本就能校准白平衡;
  • 关掉顶灯,只用窗户的自然光,避免暖黄灯和日光混在一起;
  • 背景、构图有统一要求的,直接按《用 ComfyUI 批量做商品白底图》做成白底图,或按《商品换背景》放进同一套场景。

常见问题

能不能把代码调色和抠图换背景结合起来?

可以,而且推荐:先用本篇脚本统一颜色,再按《商品换背景》放进同一套场景,商品颜色一致、背景也一致。

店铺风格的参数怎么定?

挑一张你最满意的图,把它当标准,调 风格.json 里的亮度和暖色,直到其他图和它看起来一样。定下来以后就不要再改,新图都用这一套。

为什么不用 Photoshop 的批处理?

当然可以用。本篇的脚本是免费、不用安装大软件的做法,逻辑是一样的:先归零,再套同一个风格。