| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- """
- 集成脚本:card_seg_v2 分割+warp → yolo26s_card_no 检测编号 → 输出可视化+位置
- 环境:pytorch
- 输入:IN_DIR(平铺目录,默认 ~/顾工交接/wzj/_infer_in)
- 输出:OUT_DIR/vis_num(可视化图)+ OUT_DIR/labels_num(YOLO格式,含conf)
- 用法:
- export IN_DIR=/path/to/images
- export OUT_DIR=/path/to/output
- CUDA_VISIBLE_DEVICES=$UUID ~/miniconda3/envs/pytorch/bin/python flat_card_no.py
- """
- import os, sys, json, time, datetime
- from pathlib import Path
- sys.stdout.reconfigure(encoding="utf-8")
- sys.path.insert(0, "/home/martin/顾工交接/wzj")
- import cv2
- import numpy as np
- import config
- from modules.yolo_detector import CardDetector
- from modules.rectifier import CardRectifier
- IN_DIR = os.environ.get("IN_DIR", "/home/martin/顾工交接/wzj/_infer_in")
- OUT_DIR = os.environ.get("OUT_DIR", "/home/martin/顾工交接/wzj/_infer_out")
- VIS_DIR = os.path.join(OUT_DIR, "vis_num")
- LABEL_DIR = os.path.join(OUT_DIR, "labels_num")
- os.makedirs(VIS_DIR, exist_ok=True)
- os.makedirs(LABEL_DIR, exist_ok=True)
- # 模型路径(绝对路径)
- SEG_MODEL = "/home/martin/顾工交接/wzj/ultralytics/runs/segment/card_seg_pn_v2/weights/best.pt"
- CARD_NO_MODEL = "/home/martin/顾工交接/wzj/ultralytics/runs/detect/yolo26s_card_no/weights/best.pt"
- print("[INIT] 加载 card_seg_v2 分割模型 ...")
- _t = time.perf_counter()
- seg_detector = CardDetector(SEG_MODEL, conf=0.25)
- rectifier = CardRectifier(use_perspective=True)
- t_seg = time.perf_counter() - _t
- print(f"[INIT] 加载 card_no 检测模型 ...")
- from modules.ultralytics_compat import import_yolo
- YOLO = import_yolo()
- _t = time.perf_counter()
- card_no_model = YOLO(CARD_NO_MODEL, task="detect")
- t_load = time.perf_counter() - _t
- print(f"[INIT] card_seg_v2 {t_seg:.2f}s, card_no {t_load:.2f}s\n")
- imgs = []
- for ext in ("*.jpg", "*.jpeg", "*.png", "*.JPG", "*.JPEG", "*.PNG"):
- imgs += list(Path(IN_DIR).glob(ext))
- imgs = sorted(set(imgs))
- print(f"[RUN] {len(imgs)} 张图片\n")
- n_seg_ok, n_seg_fail, n_det_hit, n_det_multi, n_det_none = 0, 0, 0, 0, 0
- t_all = time.perf_counter()
- for i, imgp in enumerate(imgs, 1):
- name = imgp.name
- try:
- img_bgr = cv2.imread(str(imgp))
- if img_bgr is None:
- n_seg_fail += 1
- continue
- img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
- # ① card_seg_v2 分割 + mask4点 warp 摆正
- box, mask, orig_rgb = seg_detector.detect_box_mask(img_rgb)
- if box is None:
- crop_rgb = seg_detector.detect_and_crop(img_rgb)
- if crop_rgb is None:
- n_seg_fail += 1
- continue
- else:
- crop_rgb = rectifier.rectify(orig_rgb, mask=mask, box=box, angle=0)
- if crop_rgb is None or crop_rgb.size == 0:
- n_seg_fail += 1
- continue
- n_seg_ok += 1
- crop_bgr = cv2.cvtColor(crop_rgb, cv2.COLOR_RGB2BGR)
- # ② yolo26s_card_no 检测编号
- res = card_no_model.predict(crop_bgr, conf=0.25, imgsz=640, verbose=False)[0]
- h, w = crop_bgr.shape[:2]
- boxes = res.boxes
- n = 0 if boxes is None else len(boxes)
- if n == 0:
- n_det_none += 1
- elif n == 1:
- n_det_hit += 1
- else:
- n_det_multi += 1
- # ③ 可视化 + YOLO标签
- vis = crop_bgr.copy()
- label_lines = []
- if boxes is not None:
- xyxy = boxes.xyxy.cpu().numpy()
- confs = boxes.conf.cpu().numpy()
- clss = boxes.cls.cpu().numpy()
- for (x1, y1, x2, y2), conf, cls in zip(xyxy, confs, clss):
- cv2.rectangle(vis, (int(x1), int(y1)), (int(x2), int(y2)), (0, 255, 0), 2)
- cv2.putText(vis, f"card_no {conf:.2f}", (int(x1), max(0, int(y1) - 6)),
- cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)
- xc = (x1 + x2) / 2 / w
- yc = (y1 + y2) / 2 / h
- bw = (x2 - x1) / w
- bh = (y2 - y1) / h
- label_lines.append(f"{int(cls)} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f} {conf:.4f}")
- cv2.imwrite(os.path.join(VIS_DIR, name), vis)
- with open(os.path.join(LABEL_DIR, f"{os.path.splitext(name)[0]}.txt"), "w") as f:
- f.write("\n".join(label_lines))
- except Exception as e:
- n_seg_fail += 1
- print(f" [ERR] {name}: {e}")
- if i % 200 == 0 or i == len(imgs):
- el = time.perf_counter() - t_all
- rate = i / el if el > 0 else 0
- eta = (len(imgs) - i) / rate if rate > 0 else 0
- print(f" [{i}/{len(imgs)}] 分割ok={n_seg_ok} fail={n_seg_fail} "
- f"检出1框={n_det_hit} 多框={n_det_multi} 无={n_det_none} "
- f"速率={rate:.1f}/s 剩余≈{eta/60:.1f}min", flush=True)
- n = max(len(imgs), 1); t_all = time.perf_counter() - t_all
- print(f"\n[RESULT] {n} 张")
- print(f" 分割成功: {n_seg_ok}, 分割失败: {n_seg_fail}")
- print(f" card_no 检出1框: {n_det_hit}, 多框: {n_det_multi}, 无检出: {n_det_none}")
- print("=" * 50)
- print(f"⏱ flat_card_no (card_seg_v2 + yolo26s_card_no) | {n} 张")
- print(f" 加载 card_seg_v2 {t_seg:.2f}s, card_no {t_load:.2f}s")
- print(f" 总耗时 {t_all:.2f}s ({n/t_all:.1f}张/s)")
- print("=" * 50)
- with open(os.path.join(OUT_DIR, "timing.log"), "a", encoding="utf-8") as lf:
- lf.write(f"\n[{datetime.datetime.now():%Y-%m-%d %H:%M:%S}] === flat_card_no.py (card_seg_v2 + yolo26s_card_no) ===\n")
- lf.write(f" 样本 {n} 张\n")
- lf.write(f" 分割成功 {n_seg_ok}, 分割失败 {n_seg_fail}\n")
- lf.write(f" card_no 检出1框 {n_det_hit}, 多框 {n_det_multi}, 无检出 {n_det_none}\n")
- lf.write(f" 加载 card_seg_v2 {t_seg:.2f}s, card_no {t_load:.2f}s\n")
- lf.write(f" 总耗时 {t_all:.2f}s (均 {t_all/n*1000:.1f} ms/张, 吞吐 {n/t_all:.1f} 张/s)\n")
- print(f"✓ 可视化: {VIS_DIR} | 标签: {LABEL_DIR} | 计时→ timing.log")
|