"""Generate a CSV report matching merged images to source stamp images. This script is intentionally standalone and does not import cmbot project code. Edit the three paths below, then run: python match_stamp.py It does not copy, move, rename, or delete any image files. """ from __future__ import print_function import csv from datetime import datetime from pathlib import Path OUTPUT_DIR = r"D:\chengma\cmbot\output\20260623_094529" STAMP_DIR = r"D:\chengma\印花和底图\已处理印花\卡通71(66大码200斤 KEKE已上)\横1" REPORT_PATH = r"D:\chengma\cmbot\match_stamp_report.csv" IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp"} FIELDNAMES = [ "status", "code", "merged_image", "stamp_image", "merged_folder", "message", ] def is_image(path): return path.is_file() and path.suffix.lower() in IMAGE_EXTENSIONS def extract_code_from_merged_name(path): """Extract stamp code from the final underscore suffix. Example: 1_TY037.png -> TY037. """ stem = path.stem if "_" not in stem: return "" code = stem.rsplit("_", 1)[1].strip() return code def iter_images(root): root = Path(root) if not root.exists(): return for path in sorted(root.rglob("*")): if is_image(path): yield path def build_stamp_index(stamp_dir): index = {} for path in iter_images(stamp_dir): code = path.stem.strip() if not code: continue index.setdefault(code, []).append(path.resolve()) return index def match_merged_image(path, stamp_index): code = extract_code_from_merged_name(path) if not code: return { "status": "bad_name", "code": "", "merged_image": str(path.resolve()), "stamp_image": "", "merged_folder": str(path.parent.resolve()), "message": "File name has no underscore suffix code", } candidates = stamp_index.get(code, []) if not candidates: return { "status": "missing", "code": code, "merged_image": str(path.resolve()), "stamp_image": "", "merged_folder": str(path.parent.resolve()), "message": "No stamp image found for code", } stamp_paths = " | ".join(str(p) for p in candidates) if len(candidates) > 1: return { "status": "duplicate", "code": code, "merged_image": str(path.resolve()), "stamp_image": stamp_paths, "merged_folder": str(path.parent.resolve()), "message": "Multiple stamp images found for code", } return { "status": "matched", "code": code, "merged_image": str(path.resolve()), "stamp_image": stamp_paths, "merged_folder": str(path.parent.resolve()), "message": "", } def write_csv(rows, report_path): report_path = Path(report_path) report_path.parent.mkdir(parents=True, exist_ok=True) with report_path.open("w", encoding="utf-8-sig", newline="") as f: writer = csv.DictWriter(f, fieldnames=FIELDNAMES) writer.writeheader() for row in rows: writer.writerow(row) def generate_report(output_dir, stamp_dir, report_path): output_dir = Path(output_dir) stamp_dir = Path(stamp_dir) stamp_index = build_stamp_index(stamp_dir) rows = [] for path in iter_images(output_dir): rows.append(match_merged_image(path, stamp_index)) write_csv(rows, report_path) return rows def summarize(rows): counts = {} for row in rows: status = row.get("status", "") counts[status] = counts.get(status, 0) + 1 return counts def main(): started = datetime.now() rows = generate_report(OUTPUT_DIR, STAMP_DIR, REPORT_PATH) counts = summarize(rows) print("Generated report: {}".format(Path(REPORT_PATH).resolve())) print("Merged images: {}".format(len(rows))) print("Matched: {}".format(counts.get("matched", 0))) print("Missing: {}".format(counts.get("missing", 0))) print("Duplicate: {}".format(counts.get("duplicate", 0))) print("Bad name: {}".format(counts.get("bad_name", 0))) print("Elapsed: {}".format(datetime.now() - started)) if __name__ == "__main__": main()