Dataset: add READMEs, organize scripts, update .gitignore
- Add README.md to all 16 dataset directories with script references - Centralize generation scripts under scripts/ with full README - Update .gitignore to cover all data directories, keep only metadata - Remove redundant CCPD/.gitignore (parent covers same rules) - Remove stale scripts/test/ (254MB pipeline residue) - Fix label parser reference: scripts are authoritative source of truth
This commit is contained in:
1 parent
7867c3bfaf
commit
a64690477a
43 files changed
+2691
-205
No files matched your search
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
CCPD_Random_Blue5K_Green5K_EXP_CUT → ICE-COR 角点推理 → 中心外扩 → 透视变换
|
||||
==================================================================================
|
||||
输出: CCPD_Random_Blue5K_Green5K_EXP_CUT_TRANS/ (192×64, 文件名追加 _trans)
|
||||
|
||||
用法:
|
||||
python build_exp_cut_trans.py --full
|
||||
python build_exp_cut_trans.py --n 100
|
||||
python build_exp_cut_trans.py --dry-run
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
PROJ_ROOT = SCRIPT_DIR.parents[1]
|
||||
ICE_COR_DIR = PROJ_ROOT / "ICE_NNv2" / "ICE-COR"
|
||||
MODEL_PATH = ICE_COR_DIR / "output" / "1_CCPD_19K_EXP_CUT" / "best.pth"
|
||||
SRC_DIR = PROJ_ROOT / "ICE_Dataset" / "CCPD_Random_Blue5K_Green5K_EXP_CUT"
|
||||
DST_DIR = PROJ_ROOT / "ICE_Dataset" / "CCPD_Random_Blue5K_Green5K_EXP_CUT_TRANS"
|
||||
|
||||
OUT_W, OUT_H = 192, 64
|
||||
PAD_X = 0.08
|
||||
PAD_Y = 0.05
|
||||
|
||||
if str(ICE_COR_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(ICE_COR_DIR))
|
||||
from corner_rectify import load_corner_model, predict_plate_corners, order_corners
|
||||
|
||||
# ---- Unicode-safe I/O ----
|
||||
def _imread(path: str):
|
||||
with open(path, 'rb') as f:
|
||||
data = f.read()
|
||||
return cv2.imdecode(np.frombuffer(data, np.uint8), cv2.IMREAD_COLOR)
|
||||
|
||||
def _imwrite(path: str, img: np.ndarray, params=None):
|
||||
ext = os.path.splitext(path)[1] or '.jpg'
|
||||
ok, buf = cv2.imencode(ext, img, params or [])
|
||||
if ok:
|
||||
with open(path, 'wb') as f:
|
||||
f.write(buf.tobytes())
|
||||
return ok
|
||||
|
||||
# ---- Single image ----
|
||||
def process_one(model, device, img_path: Path, out_img_path: Path, draw: bool = False):
|
||||
img = _imread(str(img_path))
|
||||
if img is None:
|
||||
return "skip"
|
||||
H, W = img.shape[:2]
|
||||
try:
|
||||
corners = predict_plate_corners(img, model, device)
|
||||
if corners is None:
|
||||
return "skip"
|
||||
except Exception:
|
||||
return "skip"
|
||||
try:
|
||||
ordered = order_corners(corners)
|
||||
except Exception:
|
||||
return "skip"
|
||||
|
||||
# center expand
|
||||
cx = ordered.mean(axis=0)
|
||||
expanded = ordered + (ordered - cx) * np.array([PAD_X, PAD_Y])
|
||||
expanded[:, 0] = np.clip(expanded[:, 0], 0, W - 1)
|
||||
expanded[:, 1] = np.clip(expanded[:, 1], 0, H - 1)
|
||||
|
||||
src = expanded.astype(np.float32)
|
||||
dst = np.float32([[0, 0], [OUT_W-1, 0], [OUT_W-1, OUT_H-1], [0, OUT_H-1]])
|
||||
M = cv2.getPerspectiveTransform(src, dst)
|
||||
warped = cv2.warpPerspective(img, M, (OUT_W, OUT_H),
|
||||
flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
|
||||
|
||||
if draw:
|
||||
for k, (px, py) in enumerate(expanded):
|
||||
cv2.circle(warped, tuple(dst[k].astype(int)), 3, (0, 255, 0), -1)
|
||||
cv2.polylines(warped, [dst.astype(np.int32)], isClosed=True, color=(0, 255, 0), thickness=1)
|
||||
|
||||
out_stem = img_path.stem + "_trans"
|
||||
out_img_path.with_stem(out_stem).parent.mkdir(parents=True, exist_ok=True)
|
||||
_imwrite(str(out_img_path.with_stem(out_stem)), warped, [cv2.IMWRITE_JPEG_QUALITY, 92])
|
||||
return "ok"
|
||||
|
||||
# ---- Main ----
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
g = parser.add_mutually_exclusive_group(required=True)
|
||||
g.add_argument('--full', action='store_true')
|
||||
g.add_argument('--n', type=int)
|
||||
g.add_argument('--dry-run', action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
print(f"Model: {MODEL_PATH}")
|
||||
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
||||
model, device = load_corner_model(str(MODEL_PATH), device)
|
||||
model.eval()
|
||||
print(f"Device: {device}\n")
|
||||
|
||||
classes = ['blue', 'green']
|
||||
draw = bool(args.dry_run)
|
||||
|
||||
for cls in classes:
|
||||
src_cls = SRC_DIR / cls
|
||||
dst_cls = DST_DIR / cls
|
||||
dst_cls.mkdir(parents=True, exist_ok=True)
|
||||
images = sorted(src_cls.glob('*.jpg'))
|
||||
if not images:
|
||||
print(f"[WARN] {cls}: no images")
|
||||
continue
|
||||
|
||||
n = 2 if args.dry_run else (args.n or len(images))
|
||||
images = images[:n]
|
||||
print(f"[{cls}] {len(images)} images...", flush=True)
|
||||
|
||||
t0 = time.perf_counter()
|
||||
ok = skip = 0
|
||||
for img_path in images:
|
||||
out = dst_cls / img_path.name
|
||||
r = process_one(model, device, img_path, out, draw=draw)
|
||||
if r == "ok": ok += 1
|
||||
else: skip += 1
|
||||
elapsed = time.perf_counter() - t0
|
||||
total = ok + skip
|
||||
print(f" → ok:{ok}/{total} skip:{skip} ({elapsed:.1f}s, {total/elapsed:.1f} img/s)")
|
||||
print(f"\nDone. Output: {DST_DIR}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in new issue
Block a user