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
@@ -39,4 +39,4 @@ Write-Host "可执行文件: $buildDir\Release\plate_swap.exe"
|
||||
# 使用示例
|
||||
Write-Host ""
|
||||
Write-Host "用法示例:"
|
||||
Write-Host " .\build\Release\plate_swap.exe --ccpd-dir ./CCPD/CCPD2019/ccpd_base --plate-type police --plate-pool ./output/plate_pool --num 500 --workers 32 --fast"
|
||||
Write-Host " .\build\Release\plate_swap.exe --ccpd-dir ./CCPD/CCPD2019/ccpd_base --plate-type police --plate-pool ./output/plate_pool --num 500 --workers 32"
|
||||
@@ -49,6 +49,8 @@ from plate_number import (
|
||||
|
||||
# 类型配置
|
||||
PLATE_TYPES = [
|
||||
('blue', 'blue', generate_plate_number_blue),
|
||||
('green', 'green_car', lambda: generate_plate_number_blue(length=8)),
|
||||
('police', 'white', generate_plate_number_white),
|
||||
('army', 'white_army', generate_plate_number_army),
|
||||
('gangao', 'black', generate_plate_number_black_gangao),
|
||||
|
||||
@@ -18,7 +18,6 @@ struct Config {
|
||||
string plate_pool_dir;
|
||||
int num_images = 500;
|
||||
unsigned workers = thread::hardware_concurrency();
|
||||
bool fast = false;
|
||||
int seed = 42;
|
||||
int h_shift = 0, s_scale = 100, v_scale = 100;
|
||||
};
|
||||
@@ -50,7 +49,6 @@ Config parse_args(int argc, char* argv[]) {
|
||||
else if (arg == "--num" && i+1 < argc) cfg.num_images = stoi(argv[++i]);
|
||||
else if (arg == "--workers" && i+1 < argc) cfg.workers = stoi(argv[++i]);
|
||||
else if (arg == "--seed" && i+1 < argc) cfg.seed = stoi(argv[++i]);
|
||||
else if (arg == "--fast") cfg.fast = true;
|
||||
else if (arg == "--h-shift" && i+1 < argc) cfg.h_shift = stoi(argv[++i]);
|
||||
else if (arg == "--s-scale" && i+1 < argc) cfg.s_scale = stoi(argv[++i]);
|
||||
else if (arg == "--v-scale" && i+1 < argc) cfg.v_scale = stoi(argv[++i]);
|
||||
@@ -103,7 +101,6 @@ int main(int argc, char* argv[]) {
|
||||
cerr << " --plate-pool <dir> Plate pool directory (required)" << endl;
|
||||
cerr << " --num <N> Number of images (default 500)" << endl;
|
||||
cerr << " --workers <N> Thread count (default all cores)" << endl;
|
||||
cerr << " --fast Fast mode (skip degradation)" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -111,7 +108,6 @@ int main(int argc, char* argv[]) {
|
||||
cout << "Type: " << cfg.plate_type << endl;
|
||||
cout << "Threads: " << cfg.workers << endl;
|
||||
cout << "Target: " << cfg.num_images << " images" << endl;
|
||||
cout << "Fast: " << (cfg.fast ? "yes" : "no") << endl;
|
||||
|
||||
if (cfg.plate_pool_dir.empty()) {
|
||||
cerr << "[ERROR] --plate-pool is required" << endl;
|
||||
@@ -153,7 +149,6 @@ int main(int argc, char* argv[]) {
|
||||
SwapConfig swap_cfg;
|
||||
swap_cfg.plate_type = cfg.plate_type;
|
||||
swap_cfg.output_dir = cfg.output_dir;
|
||||
swap_cfg.fast_mode = cfg.fast;
|
||||
swap_cfg.h_shift = cfg.h_shift;
|
||||
swap_cfg.s_scale = cfg.s_scale;
|
||||
swap_cfg.v_scale = cfg.v_scale;
|
||||
|
||||
@@ -184,6 +184,8 @@ static string bg_color_of(const string& plate_type) {
|
||||
if (plate_type == "lingguan_old" || plate_type == "lingguan_new") return "black";
|
||||
if (plate_type == "coach") return "yellow";
|
||||
if (plate_type == "new_energy") return "green_truck";
|
||||
if (plate_type == "blue") return "blue";
|
||||
if (plate_type == "green") return "green_car";
|
||||
return "white";
|
||||
}
|
||||
|
||||
@@ -322,7 +324,7 @@ Mat match_plate_color(const Mat& synthetic, const Mat& orig_roi,
|
||||
|
||||
// 白/绿牌
|
||||
Scalar color_cast(white_ref[0]/ref_mean, white_ref[1]/ref_mean, white_ref[2]/ref_mean);
|
||||
float strength = (bg_color == "green_truck") ? 1.5f : 1.0f;
|
||||
float strength = (bg_color == "green_truck" || bg_color == "green_car") ? 1.5f : 1.0f;
|
||||
Scalar amplified(1.0f + (color_cast[0]-1.0f)*strength, 1.0f+(color_cast[1]-1.0f)*strength, 1.0f+(color_cast[2]-1.0f)*strength);
|
||||
amplified[0] = std::clamp((float)amplified[0], 0.85f, 1.15f);
|
||||
amplified[1] = std::clamp((float)amplified[1], 0.85f, 1.15f);
|
||||
@@ -359,7 +361,8 @@ Mat fix_bg_color(const Mat& plate, const string& bg_color,
|
||||
|
||||
int h_lo = 0, h_hi = 180;
|
||||
if (bg_color == "yellow") { h_lo = 15; h_hi = 50; }
|
||||
else if (bg_color == "green_truck") { h_lo = 40; h_hi = 85; }
|
||||
else if (bg_color == "green_truck" || bg_color == "green_car") { h_lo = 40; h_hi = 85; }
|
||||
else if (bg_color == "blue") { h_lo = 90; h_hi = 130; }
|
||||
|
||||
Mat hsv;
|
||||
cvtColor(plate, hsv, COLOR_BGR2HSV);
|
||||
@@ -629,7 +632,7 @@ bool process_single(const string& img_path, const SwapConfig& cfg,
|
||||
if (current_w > 10 && abs(target_w - current_w) > 10) {
|
||||
float l_ratio = target_w / current_w;
|
||||
float min_c = 0.5f, max_c = 1.1f;
|
||||
if (bg_color == "green_truck" || bg_color == "yellow") { min_c = 0.4f; max_c = 1.0f; }
|
||||
if (bg_color == "green_truck" || bg_color == "green_car" || bg_color == "yellow") { min_c = 0.4f; max_c = 1.0f; }
|
||||
else if (bg_color == "black" || bg_color == "black_shi") { min_c = 0.3f; max_c = 1.2f; }
|
||||
l_ratio = std::clamp(l_ratio, min_c, max_c);
|
||||
|
||||
@@ -650,38 +653,16 @@ bool process_single(const string& img_path, const SwapConfig& cfg,
|
||||
}
|
||||
|
||||
// 8. Degradation
|
||||
if (!cfg.fast_mode)
|
||||
matched = degrade_plate(matched);
|
||||
matched = degrade_plate(matched);
|
||||
|
||||
// 9-11. Warp + blend + noise
|
||||
auto [warped, mask] = warp_plate_back(matched, meta.corners, Size(w_img, h_img));
|
||||
Mat blended = feather_blend(img_orig, warped, mask);
|
||||
Mat final = match_noise(blended, img_orig, mask);
|
||||
|
||||
// 12. Save with pinyin tags
|
||||
// 12. Save with original Chinese plate number (imwrite_unicode supports UTF-8)
|
||||
string stem = fp.stem().string();
|
||||
// Convert Chinese plate markers to ASCII tags
|
||||
string tag = plate_number;
|
||||
// Manual pinyin mapping for common plate characters (UTF-8 byte sequences)
|
||||
// 港=E6B8AF 澳=E6BEB3 警=E8ADA6 使=E4BDBF 领=E9A286 学=E5ADA6
|
||||
struct { const char* utf8; const char* py; } map[] = {
|
||||
{"\xE6\xB8\xAF", "GANG"}, {"\xE6\xBE\xB3", "AO"},
|
||||
{"\xE8\xAD\xA6", "JING"}, {"\xE4\xBD\xBF", "SHI"},
|
||||
{"\xE9\xA2\x86", "LING"}, {"\xE5\xAD\xA6", "XUE"},
|
||||
};
|
||||
for (auto& m : map) {
|
||||
size_t pos = 0;
|
||||
while ((pos = tag.find(m.utf8, pos)) != string::npos) {
|
||||
tag.replace(pos, 3, m.py);
|
||||
pos += strlen(m.py);
|
||||
}
|
||||
}
|
||||
// Remove any remaining non-ASCII
|
||||
string clean;
|
||||
for (char c : tag) {
|
||||
if ((unsigned char)c < 128) clean += c;
|
||||
}
|
||||
string out_name = stem + "__" + cfg.plate_type + "__" + clean + ".jpg";
|
||||
string out_name = stem + "__" + cfg.plate_type + "__" + plate_number + ".jpg";
|
||||
string ccpd_subdir = fp.parent_path().filename().string();
|
||||
fs::path out_dir = fs::path(cfg.output_dir) / ccpd_subdir / cfg.plate_type;
|
||||
fs::create_directories(out_dir);
|
||||
|
||||
@@ -34,7 +34,6 @@ struct SwapConfig {
|
||||
std::string plate_type;
|
||||
std::string output_dir;
|
||||
int h_shift = 0, s_scale = 100, v_scale = 100;
|
||||
bool fast_mode = false;
|
||||
};
|
||||
|
||||
// 单张处理 (pre-parsed meta 避免重复解析)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user