import argparse import base64 import json import os import sys from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path from openai import OpenAI ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) os.chdir(ROOT) from config import api_key, base_url DEFAULT_PROMPT = "improve this infographics to make it look more visual coherent and appealing." def parse_args(): parser = argparse.ArgumentParser() parser.add_argument( "--input-root", default=None, help="chart_template_samples 下某次任务目录;默认取最新时间戳目录", ) parser.add_argument( "--output-dir", default=None, help="输出目录;默认 /gpt_image_2_improved", ) parser.add_argument("--model", default="gpt-image-2") parser.add_argument("--prompt", default=DEFAULT_PROMPT) parser.add_argument("--limit", type=int, default=0) parser.add_argument("--workers", type=int, default=1) parser.add_argument("--size", default="auto") parser.add_argument("--quality", default="auto") parser.add_argument("--overwrite", action="store_true") return parser.parse_args() def latest_sample_run(root: Path): runs = [ p for p in root.iterdir() if p.is_dir() and (p / "run_config.json").is_file() and p.name[:8].isdigit() ] if not runs: raise SystemExit(f"no timestamp run found under {root}") return sorted(runs, key=lambda p: p.name)[-1] def collect_pngs(input_root: Path, output_dir: Path, limit: int): pngs = [ p for p in input_root.rglob("*.png") if output_dir not in p.parents ] pngs = sorted(pngs) if limit > 0: pngs = pngs[:limit] return pngs def output_path_for(input_root: Path, output_dir: Path, image_path: Path): rel = image_path.relative_to(input_root) return output_dir / rel.parent / f"{image_path.stem}_gpt_image_2.png" def write_jsonl(path: Path, record: dict): path.parent.mkdir(parents=True, exist_ok=True) with open(path, "a", encoding="utf-8") as f: f.write(json.dumps(record, ensure_ascii=False) + "\n") def improve_one(client, image_path: Path, out_path: Path, args): out_path.parent.mkdir(parents=True, exist_ok=True) with open(image_path, "rb") as image_file: result = client.images.edit( model=args.model, image=image_file, prompt=args.prompt, size=args.size, quality=args.quality, n=1, ) image_bytes = base64.b64decode(result.data[0].b64_json) with open(out_path, "wb") as f: f.write(image_bytes) return { "input": str(image_path), "output": str(out_path), "model": args.model, "prompt": args.prompt, } def improve_job(job): args, image_path, out_path = job client = OpenAI(api_key=api_key, base_url=base_url) return improve_one(client, image_path, out_path, args) def main(): args = parse_args() input_root = Path(args.input_root) if args.input_root else latest_sample_run( ROOT / "output" / "chart_template_samples" ) output_dir = Path(args.output_dir) if args.output_dir else input_root / "gpt_image_2_improved" output_dir.mkdir(parents=True, exist_ok=True) images = collect_pngs(input_root, output_dir, args.limit) manifest = output_dir / "manifest.jsonl" if manifest.exists() and args.overwrite: manifest.unlink() jobs = [] for image_path in images: out_path = output_path_for(input_root, output_dir, image_path) if out_path.exists() and not args.overwrite: continue jobs.append((args, image_path, out_path)) print(f"input_root={input_root}", flush=True) print(f"output_dir={output_dir}", flush=True) print(f"images={len(images)} pending={len(jobs)} workers={args.workers}", flush=True) if args.workers > 1: with ThreadPoolExecutor(max_workers=args.workers) as executor: futures = [executor.submit(improve_job, job) for job in jobs] for index, future in enumerate(as_completed(futures), 1): record = future.result() write_jsonl(manifest, record) print(f"[{index}/{len(jobs)}] {record['output']}", flush=True) else: client = OpenAI(api_key=api_key, base_url=base_url) for index, (_args, image_path, out_path) in enumerate(jobs, 1): record = improve_one(client, image_path, out_path, args) write_jsonl(manifest, record) print(f"[{index}/{len(jobs)}] {record['output']}", flush=True) summary = { "input_root": str(input_root), "output_dir": str(output_dir), "model": args.model, "prompt": args.prompt, "total_images": len(images), "submitted_images": len(jobs), } with open(output_dir / "summary.json", "w", encoding="utf-8") as f: json.dump(summary, f, indent=2, ensure_ascii=False) if __name__ == "__main__": main()