#!/usr/bin/env python3 # -*- coding: utf-8 -*- """远程生图候选 → 像素化候选稿 v3(v1.3 五步流程第 4 步)。 v3 设计要点(对应四类问题:脏点/模糊/信息丢失/白边): - 档位翻倍: 普通怪 96 / 精英 128 / Boss 192(终值仍归战斗一屏样张) - 去底去影 = 「奶油描边屏障法」: 描边膨胀 3px 作屏障,从图缘一口吞掉底色+地面影 (影色相规则对同色系必误吞,禁用) - 白边 = 吃描边: 主色距离门 + 「底色↔主色线段」反锯齿混合带桥(限层深,防深入本体) - 教训: 不做第二遍低膨胀洪泛——JPEG 噪点在描边开 1-2px 缺口会洪灌本体 (duergar 甲面被吃空实证);屏障只跑一遍 dil=3 用法: python tools/pixelize_candidates.py <候选批次目录> # v3 批量像素化(现行行为) python tools/pixelize_candidates.py <目录> --fixer # +rust fixer 收网格+封闭灰袋清除 python tools/pixelize_candidates.py <目录> --standee-display [--standee-out ] # 九张站桩原稿分辨率稿(八敌 pixel-1+主角手修 2x 稿; # 内容紧裁原样存盘,零降采样——显示缩放归游戏侧按档) --fixer: rust pixelfixer(Retro-Diffusion, MIT, D:/code/pixel-art-fixer)对原图先做网格检测+重建(收网格),v3 去底后再跑封闭灰袋清除 (三闸=近底色∧中性灰∧组件不触图缘——防误伤盔甲与影);--fixer-exe 可换引擎路径。 """ import sys, os, subprocess, tempfile from collections import deque, Counter from PIL import Image, ImageFilter FIXER_EXE_DEFAULT = r"D:\code\pixel-art-fixer\rust\target\release\pixelfixer.exe" FIXER_EXE = FIXER_EXE_DEFAULT # --fixer-exe 可覆写(__main__ 解析) STANDEE_SLOT_H = 144 # 主角显示档高(中型档——art-bible §8.2 三档绝对高) # 八敌站桩显示档 = 三档绝对高(art-bible §8.2 显示档定值单点)——小型 96 / 中型 144 / 大型 188 # 归档 = 生物体型语义(character-design §5.2):小 < 中 < 大,禁按强度重排。 # 显示倍率 = 档高 / 原稿高,宽随源比例(高定宽随,NEAREST);入包件 = 原稿紧裁原样, # 禁任何降采样(2026-10-14 降档裁决作废——现行入包稿与原稿逐像素同)。 STANDEE_DISPLAY_H = { "goblin": 96, "giant_spider": 96, "duergar": 96, "hook_horror": 144, "bugbear": 144, "ogre_chief": 188, "drider": 188, "duergar_king": 188, } # 原稿高表(入包件逐件源高 == 此值——禁降采样纪律的机检锚;主角 = 手修 2x 稿紧裁后高) STANDEE_SRC_H = { "goblin": 90, "giant_spider": 81, "duergar": 126, "hook_horror": 94, "bugbear": 123, "ogre_chief": 189, "drider": 189, "duergar_king": 189, "hero": 92, } # 中间产物(pixel-1)的生成分辨率——只决定中间图有多细,**不决定最终体量**。 # 站桩入包件 = 原稿紧裁原样(STANDEE_SRC_H),显示体量归 STANDEE_DISPLAY_H。这里取得 # 比显示档越大,入包件细节越足。 # 注意:ogre_chief 取 192 是分辨率选择(要给它更高的中间精度),不是把它归为 Boss 档。 SIZES = {"goblin": 96, "giant_spider": 96, "hook_horror": 96, "bugbear": 128, "duergar": 128, "ogre_chief": 192, "drider": 192, "duergar_king": 192} BG_TOL = 48 CREAT_HUE = (28, 100) # 奶油/骨色相域 CREAT_S = (0.07, 0.55) # 屏障用饱和域 CREAT_V = 0.58 BARRIER_DILATE = 3 OUTLINE_TOL = 48 # 吃描边:与主色最大色距 EAT_S_MIN = 0.02 OUTLINE_PROBE = 6 # 主色采样:透明区膨胀半径 BRIDGE_DEPTH = 4 # 混合带桥最大层深 BRIDGE_SEG_TOL = 14 # 到底色↔主色线段的距离 BRIDGE_S_MAX = 0.10 BRIDGE_V_MIN = 0.70 QUANT_COLORS = 64 STRIP_BGS = [("brick", (201, 175, 131)), ("floor", (179, 154, 120)), ("bright", (232, 224, 208)), ("dark", (58, 58, 68))] def hsv(r, g, b): mx, mn = max(r, g, b), min(r, g, b) d = mx - mn h = 0 if d: if mx == r: h = (60 * (g - b) / d) % 360 elif mx == g: h = 60 * (b - r) / d + 120 else: h = 60 * (r - g) / d + 240 return h, (d / mx if mx else 0), mx / 255 def is_cream(c): h, s, v = hsv(*c) h0, h1 = CREAT_HUE return h0 <= h <= h1 and CREAT_S[0] <= s <= CREAT_S[1] and v >= CREAT_V def dist(a, b): return abs(a[0] - b[0]) + abs(a[1] - b[1]) + abs(a[2] - b[2]) def seg_dist(c, a, b): """c 到线段 a-b 的最小欧氏距离(反锯齿混合带判据)。""" ab = (b[0] - a[0], b[1] - a[1], b[2] - a[2]) ac = (c[0] - a[0], c[1] - a[1], c[2] - a[2]) ab2 = ab[0] ** 2 + ab[1] ** 2 + ab[2] ** 2 t = 0.0 if ab2: t = max(0.0, min(1.0, (ac[0] * ab[0] + ac[1] * ab[1] + ac[2] * ab[2]) / ab2)) p = (a[0] + ab[0] * t, a[1] + ab[1] * t, a[2] + ab[2] * t) return dist(c, p) def dilate(mask, w, hgt, n): for _ in range(n): nb = [row[:] for row in mask] for y in range(hgt): row = mask[y] for x in range(w): if not row[x]: if (x > 0 and row[x - 1]) or (x < w - 1 and row[x + 1]) \ or (y > 0 and mask[y - 1][x]) or (y < hgt - 1 and mask[y + 1][x]): nb[y][x] = True mask = nb return mask def barrier_flood(im): """优雅路径:奶油描边膨胀成屏障,从图缘吞掉底色+影。描边被影压暗开缺口时会漏灌。""" im = im.convert("RGB") w, hgt = im.size px = im.load() barrier = dilate([[is_cream(px[x, y]) for x in range(w)] for y in range(hgt)], w, hgt, BARRIER_DILATE) alpha = [[255] * w for _ in range(hgt)] seen = [[False] * w for _ in range(hgt)] q = deque() for x in range(w): q.append((x, 0)); q.append((x, hgt - 1)) for y in range(hgt): q.append((0, y)); q.append((w - 1, y)) while q: x, y = q.popleft() if x < 0 or y < 0 or x >= w or y >= hgt or seen[y][x]: continue seen[y][x] = True if barrier[y][x]: continue alpha[y][x] = 0 for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): q.append((x + dx, y + dy)) return im, w, hgt, alpha def bg_flood(im): """稳妥路径:底色门+梯度跟随洪泛去底,再按影色规则吞影(v1 逻辑,轮廓最稳)。""" im = im.convert("RGB") w, hgt = im.size px = im.load() corners = [px[x, y] for x, y in ((2, 2), (w - 3, 2), (2, hgt - 3), (w - 3, hgt - 3))] bg = Counter(corners).most_common(1)[0][0] alpha = [[255] * w for _ in range(hgt)] seen = [[False] * w for _ in range(hgt)] q = deque() for x in range(w): q.append((x, 0, bg)); q.append((x, hgt - 1, bg)) for y in range(hgt): q.append((0, y, bg)); q.append((w - 1, y, bg)) while q: x, y, ref = q.popleft() if x < 0 or y < 0 or x >= w or y >= hgt or seen[y][x]: continue seen[y][x] = True c = px[x, y] if dist(c, ref) > 14 and dist(c, bg) > BG_TOL: continue alpha[y][x] = 0 for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): q.append((x + dx, y + dy, bg if dist(c, bg) <= BG_TOL else c)) # 影色规则:纯灰/暗紫/暖暗,只吞底部 45% 区域 q = deque() for y in range(hgt): for x in range(w): if alpha[y][x] == 0 and y >= hgt * 0.55: q.append((x, y)) while q: x, y = q.popleft() for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): nx, ny = x + dx, y + dy if nx < 0 or ny < 0 or nx >= w or ny >= hgt or alpha[ny][nx] == 0: continue h, s, v = hsv(*px[nx, ny]) ok = s < 0.10 if not ok: for h0, h1, smax, vmax in ((255, 350, 0.35, 0.45), (15, 45, 0.32, 0.42)): in_hue = h0 <= h <= h1 if h0 < h1 else (h >= h0 or h <= h1) if in_hue and s < smax and v < vmax: ok = True break if ok: alpha[ny][nx] = 0 q.append((nx, ny)) return im, w, hgt, alpha def eat_outline(im, w, hgt, alpha): """吃描边:主色距离门 + 底色↔主色线段混合带桥(限层深)。""" px = im.load() corners = [px[x, y] for x, y in ((2, 2), (w - 3, 2), (2, hgt - 3), (w - 3, hgt - 3))] bg = Counter(corners).most_common(1)[0][0] near = [[False] * w for _ in range(hgt)] for y in range(hgt): for x in range(w): if alpha[y][x] == 0: for dy in range(-OUTLINE_PROBE, OUTLINE_PROBE + 1): for dx in range(-OUTLINE_PROBE, OUTLINE_PROBE + 1): nx, ny = x + dx, y + dy if 0 <= nx < w and 0 <= ny < hgt: near[ny][nx] = True cnt = Counter() for y in range(hgt): for x in range(w): if alpha[y][x] == 255 and near[y][x] and is_cream(px[x, y]): c = px[x, y] cnt[(c[0] // 8 * 8, c[1] // 8 * 8, c[2] // 8 * 8)] += 1 if not cnt: return base = cnt.most_common(1)[0][0] layer = [[-1] * w for _ in range(hgt)] dq = deque() for y in range(hgt): for x in range(w): if alpha[y][x] == 0: layer[y][x] = 0 dq.append((x, y)) while dq: x, y = dq.popleft() for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): nx, ny = x + dx, y + dy if 0 <= nx < w and 0 <= ny < hgt and layer[ny][nx] == -1: layer[ny][nx] = layer[y][x] + 1 dq.append((nx, ny)) q = deque() for y in range(hgt): for x in range(w): if alpha[y][x] == 0: q.append((x, y)) while q: x, y = q.popleft() for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): nx, ny = x + dx, y + dy if nx < 0 or ny < 0 or nx >= w or ny >= hgt or alpha[ny][nx] == 0: continue c = px[nx, ny] h, s, v = hsv(*c) h0, h1 = CREAT_HUE main = dist(c, base) <= OUTLINE_TOL and h0 <= h <= h1 and s >= EAT_S_MIN and v >= CREAT_V bridge = (layer[ny][nx] <= BRIDGE_DEPTH and s <= BRIDGE_S_MAX and v >= BRIDGE_V_MIN and seg_dist(c, base, bg) <= BRIDGE_SEG_TOL) if main or bridge: alpha[ny][nx] = 0 q.append((nx, ny)) def remove_bg(im): """双路择优:屏障法漏灌(描边被影压暗开缺口)时自动回落底色门路径。""" im1, w, hgt, a1 = barrier_flood(im) n1 = sum(row.count(255) for row in a1) im2, _, _, a2 = bg_flood(im) n2 = sum(row.count(255) for row in a2) if n1 < 0.6 * n2: # 屏障法保留面积崩塌 = 漏灌,回落稳妥路径 im, alpha = im2, a2 else: im, alpha = im1, a1 eat_outline(im, w, hgt, alpha) out = im.convert("RGBA") pa = out.load() for y in range(hgt): for x in range(w): if alpha[y][x] == 0: pa[x, y] = (0, 0, 0, 0) return out def crop_content(im, pad=2): bbox = im.getchannel("A").getbbox() if not bbox: return im l, t, r, b = bbox l, t = max(0, l - pad), max(0, t - pad) r, b = min(im.width, r + pad), min(im.height, b + pad) return im.crop((l, t, r, b)) def despeckle(spr, min_px=10): """删小于 min_px 的不透明孤立连通域(噪点)。""" w, hgt = spr.size pa = spr.load() seen = [[False] * w for _ in range(hgt)] for y0 in range(hgt): for x0 in range(w): if seen[y0][x0] or pa[x0, y0][3] == 0: continue comp = [] q = deque([(x0, y0)]) seen[y0][x0] = True while q: x, y = q.popleft() comp.append((x, y)) for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): nx, ny = x + dx, y + dy if 0 <= nx < w and 0 <= ny < hgt and not seen[ny][nx] and pa[nx, ny][3] != 0: seen[ny][nx] = True q.append((nx, ny)) if len(comp) < min_px: for x, y in comp: pa[x, y] = (0, 0, 0, 0) return spr def edge_inpaint(spr, passes=2): """边缘色内插:与透明相邻的不透明像素改色为其内部邻居均值(灭白边,不削轮廓)。""" w, hgt = spr.size for _ in range(passes): pa = spr.load() fixes = [] for y in range(hgt): for x in range(w): c = pa[x, y] if c[3] == 0: continue inner, boundary = [], False for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1), (1, 1), (1, -1), (-1, 1), (-1, -1)): nx, ny = x + dx, y + dy if not (0 <= nx < w and 0 <= ny < hgt): boundary = True continue nc = pa[nx, ny] if nc[3] == 0: boundary = True elif all(pa[nx + ddx, ny + ddy][3] != 0 for ddx, ddy in ((1, 0), (-1, 0), (0, 1), (0, -1)) if 0 <= nx + ddx < w and 0 <= ny + ddy < hgt): inner.append(nc) if boundary and inner: fixes.append((x, y, tuple(sum(ch[i] for ch in inner) // len(inner) for i in range(3)))) for x, y, c in fixes: r, g, b, a = pa[x, y] pa[x, y] = (c[0], c[1], c[2], a) return spr def pixelize(im, target): """BOX 预滤到 4x(α腐蚀2px灭混合带) → 量化 → NEAREST → 降噪点 → 边缘内插。""" src = crop_content(im) scale = target * 4 / max(src.size) pre = src.resize((max(1, round(src.width * scale)), max(1, round(src.height * scale))), Image.BOX) rgb = pre.convert("RGB") flat = rgb.quantize(QUANT_COLORS, method=Image.MEDIANCUT).convert("RGB") out = flat.resize((max(1, round(pre.width / 4)), max(1, round(pre.height / 4))), Image.NEAREST) a = pre.getchannel("A").filter(ImageFilter.MinFilter(3)).resize(out.size, Image.NEAREST).point(lambda v: 255 if v > 128 else 0) out.putalpha(a) despeckle(out) edge_inpaint(out) return out def strip(spr, path): cell = max(spr.size) + 8 sheet = Image.new("RGBA", (cell * 4, cell), (0, 0, 0, 255)) for i, (_, bg) in enumerate(STRIP_BGS): panel = Image.new("RGBA", (cell, cell), bg + (255,)) panel.alpha_composite(spr, ((cell - spr.width) // 2, (cell - spr.height) // 2)) sheet.paste(panel, (i * cell, 0)) sheet = sheet.resize((sheet.width * 2, sheet.height * 2), Image.NEAREST) sheet.save(path) def remove_closed_gray_bags(im): """封闭灰袋清除:本体轮廓内困住的底色/灰袋,图缘洪泛够不着。 三闸全过才除(防误伤盔甲与影): ①近底色(四角底色距离门) ②中性灰(低饱和) ③连通组件不触图缘(封闭——贴缘的地面影属开放区,洪泛已处理)。 """ im = im.convert("RGBA") w, hgt = im.size px = im.load() corners = [px[x, y][:3] for x, y in ((1, 1), (w - 2, 1), (1, hgt - 2), (w - 2, hgt - 2)) if px[x, y][3] > 0] if not corners: return im bg = Counter(tuple(c // 16 * 16 for c in cr) for cr in corners).most_common(1)[0][0] def is_bag(c): r, g, b, a = c if a == 0: return False if dist((r, g, b), bg) > BG_TOL: # 闸① 近底色 return False h, s, v = hsv(r, g, b) # 闸② 中性灰 return s < 0.10 # 闸③ 组件级:候选像素连通域任一成员触图缘 = 开放区,整域保留 comp_id = [[-1] * w for _ in range(hgt)] touches_edge = [] cid = 0 for y0 in range(hgt): for x0 in range(w): if comp_id[y0][x0] != -1 or not is_bag(px[x0, y0]): continue q = deque([(x0, y0)]) comp_id[y0][x0] = cid edge_hit = False members = [] while q: x, y = q.popleft() members.append((x, y)) if x in (0, w - 1) or y in (0, hgt - 1): edge_hit = True for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): nx, ny = x + dx, y + dy if 0 <= nx < w and 0 <= ny < hgt and comp_id[ny][nx] == -1 and is_bag(px[nx, ny]): comp_id[ny][nx] = cid q.append((nx, ny)) touches_edge.append(edge_hit) cid += 1 n = 0 for y in range(hgt): for x in range(w): i = comp_id[y][x] if i >= 0 and not touches_edge[i]: r, g, b, a = px[x, y] px[x, y] = (r, g, b, 0) n += 1 return im def fixer_grid_snap(src_path): """rust pixelfixer 收网格:网格检测+重建回原生分辨率(全量档,亚秒级)。失败回落原图。""" tmp_out = tempfile.mktemp(suffix=".png") try: r = subprocess.run([FIXER_EXE, "process", str(src_path), tmp_out, "full"], capture_output=True, timeout=120) if r.returncode == 0 and os.path.isfile(tmp_out): return Image.open(tmp_out).convert("RGB") print(" [fixer] 失败(code=%s),回落原图: %s" % (r.returncode, r.stderr.decode(errors="replace")[-120:])) except Exception as e: # 引擎缺位/超时——回落,管线不断 print(" [fixer] 异常回落原图: %s" % e) finally: if os.path.isfile(tmp_out): os.remove(tmp_out) return Image.open(src_path).convert("RGB") def _bbox_alpha(im): bbox = im.getchannel("A").getbbox() return bbox if bbox else (0, 0, im.width, im.height) def dom_block_resize(im, nw, nh): """块内众数投票降采样(48px 档专用,代拍板待追认):每目标格取源块内众数不透明色, 透明占比过半则置透明。零插值(输出色 ⊆ 源色板)——大比例缩比(≈0.25×)下比单点 NEAREST 干净(体量连贯/描边连续,09-29 goblin A/B 实证);NEAREST 仅用于无损域。""" w, hgt = im.size px = im.load() out = Image.new("RGBA", (nw, nh), (0, 0, 0, 0)) po = out.load() for oy in range(nh): y0, y1 = int(oy * hgt / nh), max(int(oy * hgt / nh) + 1, int((oy + 1) * hgt / nh)) for ox in range(nw): x0, x1 = int(ox * w / nw), max(int(ox * w / nw) + 1, int((ox + 1) * w / nw)) cnt = Counter() tot = 0 opaque = 0 for y in range(y0, y1): for x in range(x0, x1): c = px[x, y] cnt[c] += 1 tot += 1 if c[3] > 0: opaque += 1 if opaque * 2 < tot: po[ox, oy] = (0, 0, 0, 0) else: po[ox, oy] = max((c for c in cnt if c[3] > 0), key=lambda c: cnt[c]) return out def build_standee_display(batch_root, out_dir, extra_hero_states=("attack", "hit")): """出八敌 + 主角的站桩入包稿(原稿分辨率,零降采样)。源 = 八敌 pixel-1 + 主角手修 2x 稿。 口径: 1. 内容紧裁透明边后原样存盘——画布即内容,禁任何缩放与降采样(art-bible §8.2: 显示按三档绝对高在游戏侧缩放,入包件保留原稿分辨率)。 2. 逐件自证:紧裁后高度 == STANDEE_SRC_H 表值;九件计数 != 9 即停 (缺件即停——禁回退旧降采样稿顶替)。 """ os.makedirs(out_dir, exist_ok=True) enemy_srcs = {} for key in sorted(STANDEE_SRC_H.keys()): if key == "hero": continue p = os.path.join(batch_root, key, "pixel-1.png") if os.path.isfile(p): enemy_srcs[key] = Image.open(p).convert("RGBA") hero_p = os.path.join(batch_root, "hero-ref", "edit", "normal_2x.png") jobs = [(k, im, "character_enemy_%s.png" % k) for k, im in enemy_srcs.items()] if os.path.isfile(hero_p): jobs.append(("hero_normal", Image.open(hero_p).convert("RGBA"), "character_hero_portrait.png")) for st in extra_hero_states: sp = os.path.join(batch_root, "hero-ref", "edit", "%s_2x.png" % st) if os.path.isfile(sp): jobs.append(("hero_%s" % st, Image.open(sp).convert("RGBA"), "character_hero_%s.png" % st)) made = [] n_pieces = 0 # 九件 = 八敌 + 主角立绘(attack/hit 为立绘伴随态,不入计数) for key, im, out_name in jobs: l, t, r, b = _bbox_alpha(im) im = im.crop((l, t, r, b)) src_key = "hero" if key.startswith("hero") else key if im.height != STANDEE_SRC_H.get(src_key): print("FAIL 站桩原稿高失配 %s: 实得 %d, 表值 %s(禁降采样——查源稿)" % (key, im.height, STANDEE_SRC_H.get(src_key))) sys.exit(2) outp = os.path.join(out_dir, out_name) im.save(outp) print("standee %-14s %dx%d -> %s" % (key, im.width, im.height, outp)) made.append(outp) if out_name.startswith("character_enemy_") or out_name == "character_hero_portrait.png": n_pieces += 1 if n_pieces != len(STANDEE_SRC_H): print("FAIL 站桩产物计数 %d != %d(缺件即停——禁回退旧降采样稿顶替)" % (n_pieces, len(STANDEE_SRC_H))) sys.exit(2) return made def main(argv): root = argv[0] if argv and not argv[0].startswith("--") else "artwork/remote-gen-2026-09-26" args = argv[1:] if argv and not argv[0].startswith("--") else argv if "--standee-display" in args: out = args[args.index("--standee-out") + 1] if "--standee-out" in args \ else os.path.join("game", "assets", "art", "character") build_standee_display(root, out) return use_fixer = "--fixer" in args thumbs = [] for asset, target in sorted(SIZES.items()): d = os.path.join(root, asset) if not os.path.isdir(d): continue for f in sorted(os.listdir(d)): if not f.startswith("v") or "pixel" in f: continue n = f[1].split(".")[0] if len(f) > 1 and f[1].isdigit() else f if use_fixer: src = fixer_grid_snap(os.path.join(d, f)) else: src = Image.open(os.path.join(d, f)) rgba = remove_bg(src) if use_fixer: rgba = remove_closed_gray_bags(rgba) spr = pixelize(rgba, target) outp = os.path.join(d, "pixel-%s.png" % n) spr.save(outp) strip(spr, os.path.join(d, "pixel-%s-strip.png" % n)) thumbs.append((asset, target, spr)) print("%-14s %s %sx%s -> %s" % (asset, f, spr.width, spr.height, outp)) if thumbs: pad = 8 rows, row, rw = [], [], 0 for asset, target, t in thumbs: if rw + t.width + pad > 1400 and row: rows.append(row); row, rw = [], 0 row.append((asset, t)); rw += t.width + pad if row: rows.append(row) W = max(sum(t.width + pad for _, t in r) for r in rows) Htot = sum(max(t.height for _, t in r) + pad + 4 for r in rows) sheet = Image.new("RGBA", (W, Htot), (179, 154, 120, 255)) y = 0 for r in rows: x = 0 hh = max(t.height for _, t in r) for asset, t in r: sheet.alpha_composite(t, (x, y + (hh - t.height) // 2)) x += t.width + pad y += hh + pad + 4 sheet = sheet.resize((sheet.width * 2, sheet.height * 2), Image.NEAREST) sheet.save(os.path.join(root, "0-像素化预览.png")) print("总览 -> 0-像素化预览.png") if __name__ == "__main__": _argv = sys.argv[1:] if "--fixer-exe" in _argv: _i = _argv.index("--fixer-exe") FIXER_EXE = _argv[_i + 1] _argv = _argv[:_i] + _argv[_i + 2:] main(_argv)