forked from zakaria/chicken-counting-sukawarna-det
103 lines
3.4 KiB
C++
103 lines
3.4 KiB
C++
#pragma once
|
|
|
|
#include <filesystem>
|
|
#include <iostream>
|
|
#include <regex>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
#include "chicken_counter/config.hpp"
|
|
|
|
namespace cc {
|
|
|
|
struct CameraDiscoveryResult {
|
|
std::unordered_map<std::string, std::string> found;
|
|
std::unordered_map<std::string, std::string> skipped;
|
|
};
|
|
|
|
inline std::string replace_glob_placeholder(const std::string& pattern, int num) {
|
|
std::string s = pattern;
|
|
std::string token = "{num}";
|
|
size_t pos = s.find(token);
|
|
if (pos != std::string::npos) {
|
|
s.replace(pos, token.size(), std::to_string(num));
|
|
}
|
|
return s;
|
|
}
|
|
|
|
inline std::vector<std::string> glob_filenames(const std::string& dir, const std::string& pattern) {
|
|
namespace fs = std::filesystem;
|
|
std::vector<std::string> matches;
|
|
std::string regex_str = "^";
|
|
for (char c : pattern) {
|
|
if (c == '*') regex_str += ".*";
|
|
else if (c == '?') regex_str += ".";
|
|
else if (c == '.' || c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}')
|
|
regex_str += std::string("\\") + c;
|
|
else regex_str += c;
|
|
}
|
|
regex_str += "$";
|
|
std::regex re(regex_str);
|
|
|
|
for (auto& entry : fs::directory_iterator(dir)) {
|
|
if (!entry.is_regular_file()) continue;
|
|
std::string fname = entry.path().filename().string();
|
|
if (std::regex_match(fname, re))
|
|
matches.push_back(entry.path().string());
|
|
}
|
|
std::sort(matches.begin(), matches.end());
|
|
return matches;
|
|
}
|
|
|
|
inline CameraDiscoveryResult discover_camera_videos(
|
|
const std::string& day_dir,
|
|
const BatchSettings& settings)
|
|
{
|
|
namespace fs = std::filesystem;
|
|
if (!fs::is_directory(day_dir))
|
|
throw std::runtime_error("Daily input folder does not exist: " + day_dir);
|
|
|
|
CameraDiscoveryResult result;
|
|
int total = static_cast<int>(settings.cameras.size());
|
|
|
|
using pair_t = std::pair<std::string, CameraPreset>;
|
|
std::vector<pair_t> sorted_cams(settings.cameras.begin(), settings.cameras.end());
|
|
std::sort(sorted_cams.begin(), sorted_cams.end(),
|
|
[](const pair_t& a, const pair_t& b) { return a.second.camera_num < b.second.camera_num; });
|
|
|
|
for (const auto& [camera_id, preset] : sorted_cams) {
|
|
std::string pattern = replace_glob_placeholder(settings.batch.camera_glob, preset.camera_num);
|
|
auto matches = glob_filenames(day_dir, pattern);
|
|
if (matches.empty()) {
|
|
result.skipped[camera_id] = "video_not_found";
|
|
continue;
|
|
}
|
|
if (matches.size() > 1) {
|
|
result.skipped[camera_id] = "multiple_matches";
|
|
continue;
|
|
}
|
|
result.found[camera_id] = matches[0];
|
|
}
|
|
|
|
if (result.found.empty()) {
|
|
std::string summary;
|
|
for (const auto& [id, reason] : result.skipped) summary += id + " (" + reason + "), ";
|
|
throw std::runtime_error("No camera videos found in " + day_dir + ". Skipped: " + summary);
|
|
}
|
|
|
|
int found_count = static_cast<int>(result.found.size());
|
|
if (!result.skipped.empty()) {
|
|
std::string summary;
|
|
for (const auto& [id, reason] : result.skipped) summary += id + " (" + reason + "), ";
|
|
std::cerr << "[batch] discovered " << found_count << "/" << total
|
|
<< " cameras; skipped: " << summary << "\n";
|
|
} else {
|
|
std::cerr << "[batch] discovered " << found_count << "/" << total << " cameras\n";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace cc
|