#include "yaml_config_parser.hpp" #include "../common/logger.hpp" #include #include #include #include namespace pipeline { bool YamlConfigParser::parse(const std::string& config_file) { try { Logger::info("Parsing config file: " + config_file); yaml_config_ = YAML::LoadFile(config_file); // 解析各个配置部分 if (!parseInputConfig(yaml_config_["input"])) { Logger::error("Failed to parse input config"); return false; } if (!parseModelConfig(yaml_config_["inference"])) { Logger::error("Failed to parse model config"); return false; } if (!parseRenderConfig(yaml_config_["render"])) { Logger::error("Failed to parse render config"); return false; } if (!parseOutputConfig(yaml_config_["output"])) { Logger::error("Failed to parse output config"); return false; } if (!parseLogConfig(yaml_config_["log"])) { Logger::error("Failed to parse log config"); return false; } Logger::info("Successfully parsed all configurations"); return true; } catch (const YAML::Exception& e) { Logger::error("YAML parsing error: " + std::string(e.what())); return false; } catch (const std::exception& e) { Logger::error("Error in parse: " + std::string(e.what())); return false; } } bool YamlConfigParser::validate() { // 基本验证 if (config_.input.sources.empty()) { Logger::error("No input sources configured"); return false; } // 验证输入源名称唯一性 std::unordered_set source_names; for (const auto& source : config_.input.sources) { if (!source_names.insert(source.name).second) { Logger::error("Duplicate input source name: " + source.name); return false; } } // 验证输出目标名称唯一性 std::unordered_set target_names; for (const auto& target : config_.output.targets) { if (!target_names.insert(target.name).second) { Logger::error("Duplicate output target name: " + target.name); return false; } } // 收集所有有效的输出目标名称 std::unordered_set valid_output_names; for (const auto& target : config_.output.targets) { valid_output_names.insert(target.name); } // 验证输入源配置 for (auto& source : config_.input.sources) { if (source.type.empty() || source.name.empty()) { Logger::error("Input source missing type or name"); return false; } if (source.type == "rtsp" && source.url.empty()) { Logger::error("RTSP source missing URL"); return false; } if (source.buffer_size <= 0) { Logger::error("Invalid buffer size for source: " + source.name); return false; } // 如果没有指定输出目标,使用所有可用的输出目标 if (source.outputs.empty() && !valid_output_names.empty()) { Logger::info("No output targets specified for source '" + source.name + "', using all available targets"); source.outputs.insert(source.outputs.end(), valid_output_names.begin(), valid_output_names.end()); } // 验证输出目标映射 else if (!source.outputs.empty()) { for (const auto& output : source.outputs) { if (valid_output_names.find(output) == valid_output_names.end()) { Logger::error("Invalid output target '" + output + "' specified for input source '" + source.name + "'"); return false; } } } } // 验证推理配置 if (config_.inference.engine_path.empty()) { Logger::error("Model engine path not specified"); return false; } if (config_.inference.input_shape.empty()) { Logger::error("Model input shape not specified"); return false; } if (config_.inference.threshold.conf < 0.0f || config_.inference.threshold.conf > 1.0f) { Logger::error("Invalid confidence threshold (must be between 0.0 and 1.0)"); return false; } if (config_.inference.threshold.nms < 0.0f || config_.inference.threshold.nms > 1.0f) { Logger::error("Invalid NMS threshold (must be between 0.0 and 1.0)"); return false; } // 验证渲染配置 // 验证窗口配置 if (config_.render.window.width <= 0 || config_.render.window.height <= 0) { Logger::error("Invalid window dimensions"); return false; } if (config_.render.window.name.empty()) { Logger::error("Window name not specified"); return false; } // 验证默认样式 if (config_.render.default_style.transparency < 0.0f || config_.render.default_style.transparency > 1.0f) { Logger::error("Invalid default style transparency (must be between 0.0 and 1.0)"); return false; } if (config_.render.default_style.box_thickness <= 0 || config_.render.default_style.font_thickness <= 0) { Logger::error("Invalid default style thickness values"); return false; } if (config_.render.default_style.font_scale <= 0.0) { Logger::error("Invalid default style font scale"); return false; } // 验证类别样式 for (const auto& [class_name, style] : config_.render.class_styles) { if (class_name.empty()) { Logger::error("Empty class name in style configuration"); return false; } if (style.transparency < 0.0f || style.transparency > 1.0f) { Logger::error("Invalid transparency for class " + class_name); return false; } if (style.box_thickness <= 0 || style.font_thickness <= 0) { Logger::error("Invalid thickness values for class " + class_name); return false; } if (style.font_scale <= 0.0) { Logger::error("Invalid font scale for class " + class_name); return false; } } // 验证性能指标配置 if (config_.render.metrics.update_interval_ms <= 0) { Logger::error("Invalid metrics update interval"); return false; } // 验证输出配置 for (const auto& target : config_.output.targets) { if (target.type.empty() || target.name.empty()) { Logger::error("Output target missing type or name"); return false; } if (target.type == "video") { if (target.path.empty()) { Logger::error("Video output target missing path"); return false; } if (target.fps <= 0) { Logger::error("Invalid video output fps"); return false; } if (target.bitrate <= 0) { Logger::error("Invalid video output bitrate"); return false; } if (target.codec.empty()) { Logger::error("Video output codec not specified"); return false; } } } // 验证日志配置 if (config_.log.level.empty()) { Logger::error("Log level not specified"); return false; } if (config_.log.save_path.empty()) { Logger::error("Log save path not specified"); return false; } return true; } bool YamlConfigParser::parseInputConfig(const YAML::Node& node) { try { // 解析输入源 if (node["sources"]) { for (const auto& source : node["sources"]) { InputSourceConfig src_config; src_config.type = source["type"].as(); src_config.name = source["name"].as(); if (source["url"]) { src_config.url = source["url"].as(); } if (source["buffer_size"]) { src_config.buffer_size = source["buffer_size"].as(); } // 解析输出目标列表 if (source["output_targets"]) { src_config.outputs = source["output_targets"].as>(); } // 向后兼容:如果没有output_targets字段,检查旧的outputs字段 else if (source["outputs"]) { src_config.outputs = source["outputs"].as>(); } // 如果都没有指定,将在validate阶段添加所有可用的输出目标 config_.input.sources.push_back(src_config); } } // 解析批处理大小 if (node["max_batch_size"]) { config_.input.max_batch_size = node["max_batch_size"].as(); } return true; } catch (const YAML::Exception& e) { Logger::error("Error parsing input config: " + std::string(e.what())); return false; } } bool YamlConfigParser::parseModelConfig(const YAML::Node& node) { try { if (!node["model"]) { Logger::error("Model configuration section not found"); return false; } const auto& model = node["model"]; // 检查必需的字段 if (!model["onnx_path"]) { Logger::error("ONNX path not specified in model config"); return false; } if (!model["engine_path"]) { Logger::error("Engine path not specified in model config"); return false; } if (!model["input_shape"]) { Logger::error("Input shape not specified in model config"); return false; } // 读取配置 config_.inference.engine_path = model["engine_path"].as(); config_.inference.input_shape = model["input_shape"].as>(); config_.inference.precision = model["precision"] ? model["precision"].as() : "FP16"; // 读取可选的版本和标签配置 if (model["version"]) { config_.inference.version = model["version"].as(); } if (model["labels"]) { config_.inference.labels = model["labels"].as>(); } // 读取阈值配置 if (node["threshold"]) { config_.inference.threshold.conf = node["threshold"]["conf"] ? node["threshold"]["conf"].as() : 0.5f; config_.inference.threshold.nms = node["threshold"]["nms"] ? node["threshold"]["nms"].as() : 0.45f; } // 读取GPU设备ID config_.inference.gpu_id = node["gpu_id"] ? node["gpu_id"].as() : 0; // 打印加载的配置信息 Logger::info("Loaded model configuration:"); Logger::info(" Engine path: " + config_.inference.engine_path); Logger::info(" Input shape: " + std::to_string(config_.inference.input_shape[0]) + "," + std::to_string(config_.inference.input_shape[1]) + "," + std::to_string(config_.inference.input_shape[2])); Logger::info(" Precision: " + config_.inference.precision); Logger::info(" GPU ID: " + std::to_string(config_.inference.gpu_id)); Logger::info(" Confidence threshold: " + std::to_string(config_.inference.threshold.conf)); Logger::info(" NMS threshold: " + std::to_string(config_.inference.threshold.nms)); if (!config_.inference.version.empty()) { Logger::info(" Version: " + config_.inference.version); } if (!config_.inference.labels.empty()) { std::string labels_str = " Labels: "; for (const auto& label : config_.inference.labels) { labels_str += label + ", "; } labels_str = labels_str.substr(0, labels_str.length() - 2); // 移除最后的", " Logger::info(labels_str); } return true; } catch (const YAML::Exception& e) { Logger::error("Error parsing model config: " + std::string(e.what())); return false; } } bool YamlConfigParser::parseRenderConfig(const YAML::Node& node) { try { // 解析渲染启用状态 if (node["enable"]) { config_.render.enable = node["enable"].as(); } // 解析窗口配置 if (node["window"]) { const auto& window = node["window"]; if (window["name"]) { config_.render.window.name = window["name"].as(); } if (window["width"]) { config_.render.window.width = window["width"].as(); } if (window["height"]) { config_.render.window.height = window["height"].as(); } if (window["fullscreen"]) { config_.render.window.fullscreen = window["fullscreen"].as(); } } // 解析默认样式 if (node["default_style"]) { const auto& style = node["default_style"]; if (style["box_color"]) { auto color = style["box_color"].as>(); if (color.size() >= 3) { config_.render.default_style.box_color = cv::Scalar(color[0], color[1], color[2]); } } if (style["text_color"]) { auto color = style["text_color"].as>(); if (color.size() >= 3) { config_.render.default_style.text_color = cv::Scalar(color[0], color[1], color[2]); } } if (style["transparency"]) { config_.render.default_style.transparency = style["transparency"].as(); } if (style["box_thickness"]) { config_.render.default_style.box_thickness = style["box_thickness"].as(); } if (style["font_scale"]) { config_.render.default_style.font_scale = style["font_scale"].as(); } if (style["font_thickness"]) { config_.render.default_style.font_thickness = style["font_thickness"].as(); } } // 解析类别样式 if (node["class_styles"]) { for (const auto& class_style : node["class_styles"]) { std::string class_name = class_style.first.as(); const auto& style = class_style.second; RenderConfig::ClassStyle class_config; if (style["box_color"]) { auto color = style["box_color"].as>(); if (color.size() >= 3) { class_config.box_color = cv::Scalar(color[0], color[1], color[2]); } } if (style["text_color"]) { auto color = style["text_color"].as>(); if (color.size() >= 3) { class_config.text_color = cv::Scalar(color[0], color[1], color[2]); } } if (style["transparency"]) { class_config.transparency = style["transparency"].as(); } if (style["box_thickness"]) { class_config.box_thickness = style["box_thickness"].as(); } if (style["font_scale"]) { class_config.font_scale = style["font_scale"].as(); } if (style["font_thickness"]) { class_config.font_thickness = style["font_thickness"].as(); } config_.render.class_styles[class_name] = class_config; } } // 解析性能指标配置 if (node["metrics"]) { const auto& metrics = node["metrics"]; if (metrics["show_fps"]) { config_.render.metrics.show_fps = metrics["show_fps"].as(); } if (metrics["show_inference_time"]) { config_.render.metrics.show_inference_time = metrics["show_inference_time"].as(); } if (metrics["show_gpu_usage"]) { config_.render.metrics.show_gpu_usage = metrics["show_gpu_usage"].as(); } if (metrics["update_interval_ms"]) { config_.render.metrics.update_interval_ms = metrics["update_interval_ms"].as(); } } return true; } catch (const YAML::Exception& e) { Logger::error("Error parsing render config: " + std::string(e.what())); return false; } } bool YamlConfigParser::parseOutputConfig(const YAML::Node& node) { try { if (node["targets"]) { for (const auto& target : node["targets"]) { OutputTargetConfig target_config; target_config.type = target["type"].as(); target_config.name = target["name"].as(); if (target["path"]) { target_config.path = target["path"].as(); } if (target["fps"]) { target_config.fps = target["fps"].as(); } if (target["codec"]) { target_config.codec = target["codec"].as(); } if (target["bitrate"]) { target_config.bitrate = target["bitrate"].as(); } config_.output.targets.push_back(target_config); } } return true; } catch (const YAML::Exception& e) { return false; } } bool YamlConfigParser::parseLogConfig(const YAML::Node& node) { try { // 检查必需的字段 if (!node["level"]) { Logger::error("Log level not specified"); return false; } if (!node["save_path"]) { Logger::error("Log save path not specified"); return false; } // 读取配置 std::string level = node["level"].as(); // 验证日志级别是否有效 if (level.empty() || (level != "debug" && level != "info" && level != "warn" && level != "error")) { Logger::error("Invalid log level: " + level); return false; } config_.log.level = level; config_.log.save_path = node["save_path"].as(); return true; } catch (const YAML::Exception& e) { Logger::error("Error parsing log config: " + std::string(e.what())); return false; } } bool YamlConfigParser::parseRtspConfig(const YAML::Node& config, RtspReader::Config& rtsp_config) { try { auto rtsp_node = config["rtsp"]; if (!rtsp_node) { return false; } // 设置默认值 rtsp_config = RtspReader::Config(); // 读取配置 if (rtsp_node["buffer_size"]) { rtsp_config.buffer_size = rtsp_node["buffer_size"].as(); } if (rtsp_node["max_retry_count"]) { rtsp_config.max_retry_count = rtsp_node["max_retry_count"].as(); } if (rtsp_node["retry_interval_ms"]) { rtsp_config.retry_interval_ms = rtsp_node["retry_interval_ms"].as(); } if (rtsp_node["frame_timeout_ms"]) { rtsp_config.frame_timeout_ms = rtsp_node["frame_timeout_ms"].as(); } if (rtsp_node["target_fps"]) { rtsp_config.target_fps = rtsp_node["target_fps"].as(); } return true; } catch (const YAML::Exception& e) { Logger::error("Error parsing RTSP config: " + std::string(e.what())); return false; } } // 工厂函数实现 std::unique_ptr createConfigParser() { return std::make_unique(); } } // namespace pipeline