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:
kirl committed 2026-06-23 14:41:39 +08:00
1 parent 7867c3bfaf
commit a64690477a
43 files changed
+2691 -205

No files matched your search

+26 -15
View File
@@ -45,8 +45,10 @@ from plate_number import (
# ── 车牌类型 ──
PLATE_TYPES = [
('blue', '蓝牌', generate_plate_number_blue),
('green_car', '小新能源', lambda: generate_plate_number_blue(length=8)),
('yellow', '教练车', generate_plate_number_yellow_xue),
('green_truck','新能源', lambda: generate_plate_number_blue(length=8)),
('green_truck','大新能源', lambda: generate_plate_number_blue(length=8)),
('white', '警车', generate_plate_number_white),
('white_army', '军车', generate_plate_number_army),
('black', '港澳', generate_plate_number_black_gangao),
@@ -113,7 +115,8 @@ def adjust_plate(plate_bgr, bg_color, h_shift, s_scale, v_scale):
return plate_bgr.copy()
hue_ranges = {
'yellow': (15, 50), 'green_truck': (40, 85),
'yellow': (15, 50), 'green_truck': (40, 85), 'green_car': (40, 85),
'blue': (90, 130),
'white': (0, 180), 'white_army': (0, 180),
'black': (0, 180), 'black_shi': (0, 180),
}
@@ -182,7 +185,7 @@ def update_display(*_args):
(w + 10, 25), cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 255, 200), 1)
# 类型标签
cv2.putText(display, f"[{current_type_idx+1}/9] {label}",
cv2.putText(display, f"[{current_type_idx+1}/{len(PLATE_TYPES)}] {label}",
(10, display.shape[0] - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (180, 180, 180), 1)
@@ -206,7 +209,8 @@ def save_params():
'# Format: (h_shift, s_scale_percent, v_scale_percent)',
'',
'PLATE_COLOR_PROFILE = {']
for bg_color in ['yellow', 'green_truck', 'white', 'white_army', 'black', 'black_shi']:
for bg_color in ['yellow', 'green_truck', 'green_car', 'blue',
'white', 'white_army', 'black', 'black_shi']:
p = params.get(bg_color, {'h_shift': 0, 's_scale': 100, 'v_scale': 100})
lines.append(f" '{bg_color}': ({p['h_shift']}, {p['s_scale']}, {p['v_scale']}),")
lines.append('}')
@@ -219,19 +223,26 @@ def save_params():
def on_key(key):
global current_type_idx, synthetic_plate
# keys: 1-9, 0(=10), -(=11) — 切换车牌类型
idx = -1
if ord('1') <= key <= ord('9'):
idx = key - ord('1')
if idx < len(PLATE_TYPES):
current_type_idx = idx
# 切换类型时复位 trackbar 到该类型已保存的参数
bg_color = PLATE_TYPES[current_type_idx][0]
p = load_params(bg_color)
cv2.setTrackbarPos('H offset', 'Color Tuner', p['h_shift'] + 30)
cv2.setTrackbarPos('S scale', 'Color Tuner', p['s_scale'])
cv2.setTrackbarPos('V scale', 'Color Tuner', p['v_scale'])
# 生成新车牌
synthetic_plate, _, _ = generate_plate(current_type_idx)
update_display()
elif key == ord('0'):
idx = 9
elif key == ord('-'):
idx = 10
if 0 <= idx < len(PLATE_TYPES):
current_type_idx = idx
# 切换类型时复位 trackbar 到该类型已保存的参数
bg_color = PLATE_TYPES[current_type_idx][0]
p = load_params(bg_color)
cv2.setTrackbarPos('H offset', 'Color Tuner', p['h_shift'] + 30)
cv2.setTrackbarPos('S scale', 'Color Tuner', p['s_scale'])
cv2.setTrackbarPos('V scale', 'Color Tuner', p['v_scale'])
# 生成新车牌
synthetic_plate, _, _ = generate_plate(current_type_idx)
update_display()
elif key == ord('r') or key == ord('R'):
bg_color = PLATE_TYPES[current_type_idx][0]
params[bg_color] = {'h_shift': 0, 's_scale': 100, 'v_scale': 100}
@@ -3,8 +3,10 @@
# Format: (h_shift, s_scale_percent, v_scale_percent)
PLATE_COLOR_PROFILE = {
'yellow': (-2, 150, 100),
'green_truck': (0, 100, 100),
'yellow': (0, 150, 150),
'green_truck': (0, 100, 150),
'green_car': (-4, 69, 92),
'blue': (-7, 150, 150),
'white': (0, 100, 100),
'white_army': (0, 100, 100),
'black': (0, 100, 100),
+30 -31
View File
@@ -156,6 +156,16 @@ PLATE_TYPE_CONFIG = {
'generator': lambda: generate_plate_number_blue(length=8),
'description': '大型新能源'
},
'blue': {
'bg_color': 'blue',
'generator': generate_plate_number_blue,
'description': '蓝牌小型汽车'
},
'green': {
'bg_color': 'green_car',
'generator': lambda: generate_plate_number_blue(length=8),
'description': '小型新能源'
},
}
@@ -407,7 +417,7 @@ def match_plate_color(synthetic_plate: np.ndarray,
color_cast = white_ref / ref_mean
strength_map = {
'white': 1.0, 'white_army': 1.0,
'green_truck': 1.5,
'blue': 1.0, 'green_car': 1.5, 'green_truck': 1.5,
}
strength = strength_map.get(bg_color, 2.0)
@@ -492,7 +502,8 @@ def fix_plate_bg_color(plate_bgr: np.ndarray, bg_color: str) -> np.ndarray:
# 色相范围
hue_ranges = {
'yellow': (15, 50), 'green_truck': (40, 85),
'yellow': (15, 50), 'green_truck': (40, 85), 'green_car': (40, 85),
'blue': (90, 130),
'white': (0, 180), 'white_army': (0, 180),
'black': (0, 180), 'black_shi': (0, 180),
}
@@ -741,11 +752,10 @@ def match_image_quality(result: np.ndarray,
_worker_pipeline = None
def _worker_init(plate_type, fast):
def _worker_init(plate_type):
"""进程初始化:每个 worker 启动时调用一次(加载字体、底板缓存)"""
global _worker_pipeline
_worker_pipeline = PlateSwapPipeline(plate_type=plate_type)
_worker_pipeline._fast_mode = fast
def _worker_process_single(args):
@@ -791,9 +801,6 @@ class PlateSwapPipeline:
font_dir = str(_GENERATOR_DIR / 'font_model')
self.generator = MultiPlateGenerator(plate_model_dir, font_dir)
# 快速模式标志(多进程时由 _worker_process_single 设置)
self._fast_mode = False
def generate_plate_number(self) -> str:
"""生成随机特殊车牌号码"""
return self.number_generator()
@@ -864,7 +871,8 @@ class PlateSwapPipeline:
# 不同底板色用不同下限,防止白底变灰、绿牌变亮
clip_cfg = {
'white': (0.5, 1.1), 'white_army': (0.5, 1.1),
'green_truck': (0.4, 1.0),
'blue': (0.5, 1.1),
'green_truck': (0.4, 1.0), 'green_car': (0.4, 1.0),
'yellow': (0.4, 1.0),
'black': (0.3, 1.2), 'black_shi': (0.3, 1.2),
}
@@ -879,16 +887,15 @@ class PlateSwapPipeline:
matched_plate = (matched_plate.astype(np.float32) * effective_ratio)
matched_plate = np.clip(matched_plate, 0, 255).astype(np.uint8)
# ---- 8. 退化模拟(快速模式下跳过) ----
if not self._fast_mode:
# JPEG 极限压缩
matched_plate = jpeg_compress(matched_plate, quality=2)
# 降采样→升采样 模拟马赛克/像素化
h_p, w_p = matched_plate.shape[:2]
small = cv2.resize(matched_plate, (w_p // 4, h_p // 4), interpolation=cv2.INTER_LINEAR)
matched_plate = cv2.resize(small, (w_p, h_p), interpolation=cv2.INTER_NEAREST)
# 较强模糊
matched_plate = cv2.GaussianBlur(matched_plate, (5, 5), 1.5)
# ---- 8. 退化模拟 ----
# JPEG 极限压缩
matched_plate = jpeg_compress(matched_plate, quality=2)
# 降采样→升采样 模拟马赛克/像素化
h_p, w_p = matched_plate.shape[:2]
small = cv2.resize(matched_plate, (w_p // 4, h_p // 4), interpolation=cv2.INTER_LINEAR)
matched_plate = cv2.resize(small, (w_p, h_p), interpolation=cv2.INTER_NEAREST)
# 较强模糊
matched_plate = cv2.GaussianBlur(matched_plate, (5, 5), 1.5)
# ---- 9. 透视变换回贴 ----
warped, plate_mask = warp_plate_back(
@@ -903,7 +910,7 @@ class PlateSwapPipeline:
# ---- 12. 保存 ----
basename = Path(filename).stem
out_name = f"{basename}_{self.plate_type}_{new_plate_number}.jpg"
out_name = f"{basename}__{self.plate_type}__{new_plate_number}.jpg"
out_path = os.path.join(output_dir, out_name)
_imwrite_unicode(out_path, final, [cv2.IMWRITE_JPEG_QUALITY, 95])
@@ -927,8 +934,7 @@ class PlateSwapPipeline:
num_images: int = 500,
preview: bool = False,
seed: int = 42,
workers: int = 1,
fast: bool = False):
workers: int = 1):
"""
批量处理 CCPD 图片目录。
@@ -939,7 +945,6 @@ class PlateSwapPipeline:
preview: 是否保存调试中间结果
seed: 随机种子
workers: 并行进程数 (1=单进程, >1=多进程)
fast: 快速模式 (跳过退化模拟步骤)
"""
random.seed(seed)
np.random.seed(seed)
@@ -969,8 +974,6 @@ class PlateSwapPipeline:
print(f"目标生成数: {num_images}")
print(f"输出目录: {output_dir}")
print(f"并行进程: {workers}" + (" (多核加速)" if workers > 1 else ""))
if fast:
print(f"快速模式: 跳过退化模拟")
print("-" * 60)
import time
@@ -984,7 +987,7 @@ class PlateSwapPipeline:
else:
# 多进程模式
success, failed = self._process_parallel(
image_paths, output_dir, num_images, preview, workers, fast
image_paths, output_dir, num_images, preview, workers
)
elapsed = time.perf_counter() - t0
@@ -1013,7 +1016,7 @@ class PlateSwapPipeline:
return success, failed
def _process_parallel(self, image_paths, output_dir, num_images,
preview, workers, fast):
preview, workers):
"""多进程并行处理(worker 复用 Pipeline,仅初始化一次)"""
from multiprocessing import Pool, cpu_count
n_procs = min(workers, cpu_count())
@@ -1022,7 +1025,7 @@ class PlateSwapPipeline:
# 每个 worker 启动时加载一次 Pipeline(含字体、底板缓存)
with Pool(n_procs, initializer=_worker_init,
initargs=(self.plate_type, fast)) as pool:
initargs=(self.plate_type,)) as pool:
# 参数精简: (img_path, output_dir, preview_bool)
args = [
(p, output_dir, (preview and i < 10))
@@ -1074,9 +1077,6 @@ def main():
help='随机种子 (默认: 42)')
parser.add_argument('--workers', type=int, default=1,
help='并行进程数 (默认: 1, 建议设为CPU核心数)')
parser.add_argument('--fast', action='store_true',
help='快速模式:跳过退化模拟,速度提升约40%%')
args = parser.parse_args()
ccpd_dirs = [d.strip() for d in args.ccpd_dir.split(',') if d.strip()]
@@ -1088,7 +1088,6 @@ def main():
preview=args.preview,
seed=args.seed,
workers=args.workers,
fast=args.fast,
)