#include "plate_swap.hpp" #include #include #include #include #include #include namespace fs = std::filesystem; using namespace std; using namespace ice; // ── 配置 ── struct Config { vector ccpd_dirs; string plate_type; string output_dir; 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; }; // ── 参数解析 ── Config parse_args(int argc, char* argv[]) { Config cfg; cfg.workers = thread::hardware_concurrency(); for (int i = 1; i < argc; i++) { string arg = argv[i]; if (arg == "--ccpd-dir" && i+1 < argc) { string dirs = argv[++i]; size_t pos = 0; while (pos < dirs.size()) { size_t comma = dirs.find(',', pos); string d = (comma == string::npos) ? dirs.substr(pos) : dirs.substr(pos, comma-pos); // trim while (!d.empty() && d.front() == ' ') d.erase(0,1); while (!d.empty() && d.back() == ' ') d.pop_back(); if (!d.empty()) cfg.ccpd_dirs.push_back(d); if (comma == string::npos) break; pos = comma + 1; } } else if (arg == "--plate-type" && i+1 < argc) cfg.plate_type = argv[++i]; else if (arg == "--output" && i+1 < argc) cfg.output_dir = argv[++i]; else if (arg == "--plate-pool" && i+1 < argc) cfg.plate_pool_dir = argv[++i]; 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]); } return cfg; } // ── 收集并预解析 CCPD 图片(过滤无效,预存角点) ── struct IndexedImage { string path; CCPDMeta meta; }; vector collect_and_index(const vector& dirs, mt19937& rng) { vector result; for (auto& d : dirs) { error_code ec; if (!fs::is_directory(d, ec)) { cerr << "[WARN] Dir not found: " << d << endl; continue; } for (auto& entry : fs::directory_iterator(d, ec)) { if (ec) break; if (!entry.is_regular_file()) continue; string ext = entry.path().extension().string(); for (auto& c : ext) c = (char)tolower(c); if (ext != ".jpg" && ext != ".jpeg" && ext != ".png") continue; string fname = entry.path().filename().string(); CCPDMeta meta = parse_ccpd_filename(fname); if (!meta.valid) continue; IndexedImage ii; ii.path = entry.path().string(); ii.meta = meta; result.push_back(ii); } } shuffle(result.begin(), result.end(), rng); return result; } // ── 主函数 ── int main(int argc, char* argv[]) { Config cfg = parse_args(argc, argv); if (cfg.ccpd_dirs.empty() || cfg.plate_type.empty()) { cerr << "Usage: plate_swap --ccpd-dir --plate-type [options]" << endl; cerr << " --plate-pool Plate pool directory (required)" << endl; cerr << " --num Number of images (default 500)" << endl; cerr << " --workers Thread count (default all cores)" << endl; cerr << " --fast Fast mode (skip degradation)" << endl; return 1; } cout << "=== ICE C++ Plate Swap ===" << endl; 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; return 1; } // Load plate pool auto t0 = chrono::steady_clock::now(); cout << "Loading plate pool..." << flush; PlatePool pool; string bin_path = cfg.plate_pool_dir + "/pool.bin"; if (fs::exists(bin_path)) pool.load_bin(bin_path); else pool.load(cfg.plate_pool_dir); size_t pool_count = pool.count(cfg.plate_type); auto t1 = chrono::steady_clock::now(); cout << " (" << pool_count << " available, " << chrono::duration(t1-t0).count() << "s)" << endl; if (pool_count == 0) { cerr << "[ERROR] No plates of type " << cfg.plate_type << " in pool" << endl; return 1; } // Collect & pre-parse CCPD (filter invalid, store corners) mt19937 rng(cfg.seed); cout << "Scanning & indexing CCPD..." << flush; vector images = collect_and_index(cfg.ccpd_dirs, rng); auto t2 = chrono::steady_clock::now(); cout << " (" << images.size() << " valid, " << chrono::duration(t2-t1).count() << "s)" << endl; if (images.empty()) { cerr << "[ERROR] No valid CCPD images found" << endl; return 1; } fs::create_directories(cfg.output_dir); 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; // Parallel processing with pre-parsed metadata atomic success{0}, failed{0}; atomic idx{0}; int total = min((int)images.size(), cfg.num_images); mutex cout_mtx; auto t_proc = chrono::steady_clock::now(); vector threads; for (unsigned t = 0; t < cfg.workers; t++) { threads.emplace_back([&](unsigned seed_offset) { mt19937 local_rng(cfg.seed + seed_offset * 10007); while (true) { int i = idx.fetch_add(1); if (i >= total) break; bool ok = process_single(images[i].path, swap_cfg, pool, local_rng, &images[i].meta); if (ok) { int s = success.fetch_add(1) + 1; if (s % 100 == 0 || s == total) { lock_guard lk(cout_mtx); cout << "\rProgress: " << s << "/" << total << flush; } } else { failed.fetch_add(1); } } }, t); } for (auto& th : threads) th.join(); auto t_done = chrono::steady_clock::now(); double elapsed = chrono::duration(t_done - t_proc).count(); cout << "\rDone! Success: " << success << ", Failed: " << failed << endl; cout << "Process time: " << elapsed << "s (" << (success/max(elapsed,0.01)) << " img/s)" << endl; cout << "Output: " << cfg.output_dir << endl; return 0; }