feat(classifier): Add JSON label map support, fix encapsulation

- Rewrite LoadLabelMap to support both JSON object format and legacy line format
- Fix CMakeLists.txt test command to use existing WAV file path
- Move WavFileSource::parse_wav_header and resample_if_needed to private section
- Verify JSON label_map works: demo_offline correctly loads and classifies
- All tests pass: test_core_lib 6/6, demo_offline 20/20
zhaochang_branch
赵昌 17 hours ago
parent c5c16ad74b
commit 25ce259113

@ -208,7 +208,7 @@ if(BUILD_TESTS)
endif()
add_test(NAME classifier_cpp COMMAND test_classifier_cpp
--model ${CMAKE_CURRENT_SOURCE_DIR}/models/gunshot_classifier.onnx
--wav ${CMAKE_CURRENT_SOURCE_DIR}/dataset/binary/train/ambient/drone_noise_simulated.wav)
--wav ${CMAKE_CURRENT_SOURCE_DIR}/dataset/binary/val/ambient/drone_noise_val_000.wav)
endif()
endif()

@ -20,8 +20,6 @@ public:
~WavFileSource();
bool open();
bool parse_wav_header();
void resample_if_needed(std::vector<float>& mono, int src_rate, int dst_rate);
std::size_t read(std::vector<std::vector<float>>& out, std::size_t max_samples);
void close();
@ -32,6 +30,8 @@ public:
bool is_open() const { return fp_ != nullptr; }
private:
bool parse_wav_header();
void resample_if_needed(std::vector<float>& mono, int src_rate, int dst_rate);
std::string path_;
int target_sample_rate_;
std::FILE* fp_;

@ -33,32 +33,67 @@ struct GunshotClassifier::Impl {
bool LoadLabelMap(const std::string& path) {
std::ifstream f(path);
if (!f) return false;
std::string line;
std::string content((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
labels.clear();
while (std::getline(f, line)) {
// Parse "index: label" format
size_t pos = line.find(':');
if (pos == std::string::npos) continue;
int idx = 0;
try {
idx = std::stoi(line.substr(0, pos));
} catch (...) {
continue;
}
std::string label = line.substr(pos + 1);
// trim whitespace
size_t start = label.find_first_not_of(" \t\r\n");
size_t end = label.find_last_not_of(" \t\r\n");
if (start != std::string::npos && end != std::string::npos) {
label = label.substr(start, end - start + 1);
} else {
label.clear();
// Try simple JSON object format: {"0": "ambient", "1": "threat"}
if (content.find('{') != std::string::npos) {
size_t pos = 0;
while ((pos = content.find('"', pos)) != std::string::npos) {
size_t key_start = pos + 1;
size_t key_end = content.find('"', key_start);
if (key_end == std::string::npos) break;
std::string key = content.substr(key_start, key_end - key_start);
int idx = -1;
try { idx = std::stoi(key); } catch (...) {}
size_t colon = content.find(':', key_end);
if (colon == std::string::npos) break;
size_t val_quote = content.find('"', colon);
if (val_quote == std::string::npos) break;
size_t val_end = content.find('"', val_quote + 1);
if (val_end == std::string::npos) break;
std::string label = content.substr(val_quote + 1, val_end - val_quote - 1);
if (idx >= 0) {
if (idx >= static_cast<int>(labels.size())) labels.resize(idx + 1);
labels[idx] = label;
}
pos = val_end + 1;
}
if (idx >= static_cast<int>(labels.size())) {
labels.resize(idx + 1);
} else {
// Fallback: "index: label" line format
size_t line_start = 0;
while (line_start < content.size()) {
size_t line_end = content.find('\n', line_start);
if (line_end == std::string::npos) line_end = content.size();
std::string line = content.substr(line_start, line_end - line_start);
line_start = line_end + 1;
std::size_t pos = line.find(':');
if (pos == std::string::npos) continue;
int idx = 0;
try {
idx = std::stoi(line.substr(0, pos));
} catch (...) {
continue;
}
std::string label = line.substr(pos + 1);
std::size_t start = label.find_first_not_of(" \t\r\n");
std::size_t end = label.find_last_not_of(" \t\r\n");
if (start != std::string::npos && end != std::string::npos) {
label = label.substr(start, end - start + 1);
} else {
label.clear();
}
if (idx >= static_cast<int>(labels.size())) {
labels.resize(idx + 1);
}
labels[idx] = label;
}
labels[idx] = label;
}
// Remove empty entries and compact
std::vector<std::string> cleaned;
for (const auto& l : labels) {

Loading…
Cancel
Save