#pragma once #include #include #include #include #include #include #include #include #include namespace r1_vision { struct Detection { bool valid = false; std::string class_name; double timestamp = 0.0; double confidence = 0.0; double u = 0.0; double v = 0.0; }; inline bool ExtractNumber(const std::string& json, const std::string& key, double& value) { const std::string search = "\"" + key + "\""; const size_t p = json.find(search); if (p == std::string::npos) return false; const size_t colon = json.find(':', p); if (colon == std::string::npos) return false; try { value = std::stod(json.substr(colon + 1)); return true; } catch (...) { return false; } } inline bool ExtractString(const std::string& json, const std::string& key, std::string& value) { const std::string search = "\"" + key + "\""; const size_t p = json.find(search); if (p == std::string::npos) return false; const size_t colon = json.find(':', p); if (colon == std::string::npos) return false; const size_t q1 = json.find('"', colon + 1); if (q1 == std::string::npos) return false; const size_t q2 = json.find('"', q1 + 1); if (q2 == std::string::npos) return false; value = json.substr(q1 + 1, q2 - q1 - 1); return true; } inline bool ReadDetection(const std::string& path, Detection& d) { std::ifstream f(path); if (!f) return false; std::stringstream buffer; buffer << f.rdbuf(); const std::string json = buffer.str(); if (json.empty()) return false; if (!ExtractNumber(json, "timestamp", d.timestamp)) return false; if (!ExtractNumber(json, "confidence", d.confidence)) return false; if (!ExtractNumber(json, "u", d.u)) return false; if (!ExtractNumber(json, "v", d.v)) return false; ExtractString(json, "class", d.class_name); d.valid = true; return true; } inline bool IsFresh(const Detection& d, double max_age_sec = 0.5) { const double now = std::chrono::duration( std::chrono::system_clock::now().time_since_epoch()).count(); return std::isfinite(d.timestamp) && std::abs(now - d.timestamp) <= max_age_sec; } inline bool GetStableBottleCenter( const std::string& path, double& u, double& v, int samples = 8, double min_confidence = 0.7, double timeout_sec = 5.0) { std::vector us; std::vector vs; const auto start = std::chrono::steady_clock::now(); while (static_cast(us.size()) < samples) { Detection d; if (ReadDetection(path, d) && d.valid && (d.class_name.empty() || d.class_name == "bottle") && d.confidence >= min_confidence && IsFresh(d)) { us.push_back(d.u); vs.push_back(d.v); } const double elapsed = std::chrono::duration( std::chrono::steady_clock::now() - start).count(); if (elapsed > timeout_sec) return false; std::this_thread::sleep_for(std::chrono::milliseconds(25)); } std::sort(us.begin(), us.end()); std::sort(vs.begin(), vs.end()); const auto median = [](const std::vector& x) { const size_t n = x.size(); if (n & 1u) return x[n / 2]; return 0.5 * (x[n / 2 - 1] + x[n / 2]); }; u = median(us); v = median(vs); return true; } } // namespace r1_vision