#pragma once #include #include #include #include #include #include #include #include class Image_ReadChannel; namespace fs = std::filesystem; /// @brief 单张图片对信息(左图 + 右图配对) struct LoadImagePairInfo { std::string product_id; ///< 产品 ID std::string image_name; ///< 图片名称(不含目录) std::string original_channel; ///< 原始通道名称(从文件名提取) std::string channel_name; ///< 映射后的通道名称 std::string camera_side; ///< 相机方位:"left" | "right" | "merge" // 左图信息 std::string left_path; ///< 左图完整路径 cv::Mat left_img; ///< 左图数据 // 右图信息 std::string right_path; ///< 右图完整路径 cv::Mat right_img; ///< 右图数据 /// @brief 默认构造函数,初始化所有成员 LoadImagePairInfo() { Init(); } /// @brief 初始化成员变量,释放图片内存 void Init() { product_id = ""; image_name = ""; original_channel = ""; channel_name = ""; camera_side = ""; left_path = ""; right_path = ""; if (!left_img.empty()) left_img.release(); if (!right_img.empty()) right_img.release(); } /// @brief 拷贝另一个对象的数据(深拷贝) /// @param tem 源对象 void copy(const LoadImagePairInfo &tem) { this->product_id = tem.product_id; this->image_name = tem.image_name; this->original_channel = tem.original_channel; this->channel_name = tem.channel_name; this->camera_side = tem.camera_side; this->left_path = tem.left_path; this->right_path = tem.right_path; if (!tem.left_img.empty()) { this->left_img = tem.left_img.clone(); } if (!tem.right_img.empty()) { this->right_img = tem.right_img.clone(); } } /// @brief 打印图片对信息到控制台 /// @param str 前缀标识字符串 void print(const std::string &str) const { printf("%s>> product_id: %s, image: %s, channel: %s (original: %s, side: %s)\n", str.c_str(), product_id.c_str(), image_name.c_str(), channel_name.c_str(), original_channel.c_str(), camera_side.c_str()); printf(" Left: %s\n", left_path.c_str()); if (!left_img.empty()) { printf(" %dx%d, type: %d\n", left_img.cols, left_img.rows, left_img.type()); } printf(" Right: %s\n", right_path.c_str()); if (!right_img.empty()) { printf(" %dx%d, type: %d\n", right_img.cols, right_img.rows, right_img.type()); } printf("\n"); } /// @brief 获取图片对信息字符串 std::string GetInfo(const std::string &str) const { std::string info = str + ">> " + product_id + " | " + image_name + " | " + channel_name + " (orig: " + original_channel + ", side: " + camera_side + ")\n"; info += " Left: " + left_path + "\n"; info += " Right: " + right_path + "\n"; return info; } }; /// @brief 一个产品的所有图片对(产品 - 图片两层结构) struct LoadProductImages { std::string product_id; ///< 产品 ID int camera_Num; ///< 相机数量 std::vector images; ///< 图片对列表 /// @brief 默认构造函数,初始化所有成员 LoadProductImages() { Init(); } /// @brief 初始化成员变量,清空图片列表 void Init() { product_id = ""; images.clear(); images.shrink_to_fit(); camera_Num = 0; } /// @brief 拷贝另一个对象的数据(深拷贝) /// @param tem 源对象 void copy(const LoadProductImages &tem) { this->product_id = tem.product_id; this->camera_Num = tem.camera_Num; this->images.clear(); for (size_t i = 0; i < tem.images.size(); i++) { LoadImagePairInfo img; img.copy(tem.images[i]); this->images.push_back(img); } } void print(const std::string &str) { printf("╔═══════════════════════════════════════════════════════════\n"); printf("║ Product: %-50s camera_Num %d \n", product_id.c_str(), camera_Num); printf("║ Total Channel Pairs: %zu\n", images.size()); printf("╠═══════════════════════════════════════════════════════════\n"); for (size_t i = 0; i < images.size() && i < 5; i++) { printf("║ [%2zu] camera %-10s Channel: %-10s\n", i, images[i].camera_side.c_str(), images[i].channel_name.c_str()); printf("║ Left: %-30s\n", images[i].left_path.empty() ? "(none)" : images[i].left_path.c_str()); printf("║ Right: %-30s\n", images[i].right_path.empty() ? "(none)" : images[i].right_path.c_str()); } if (images.size() > 5) { printf("║ ... (%zu more image pairs)\n", images.size() - 5); } printf("╚═══════════════════════════════════════════════════════════\n\n"); } /// @brief 获取产品信息字符串 std::string GetInfo(const std::string &str) { return str + ">> Product: " + product_id + " |camera_Num" + std::to_string(camera_Num) + " | Image Pairs: " + std::to_string(images.size()) + "\n"; } }; /// @brief 加载配置 struct LoadConfig { std::vector extensions; ///< 支持的图片扩展名列表 bool recursive; ///< 是否递归搜索子目录 size_t max_products; ///< 最多加载的产品数量(0 表示不限制) /// @brief 默认构造函数,初始化所有成员 LoadConfig() { Init(); } /// @brief 初始化配置参数 void Init() { extensions.clear(); extensions.push_back("tif"); extensions.push_back("tiff"); extensions.push_back("jpg"); extensions.push_back("jpeg"); extensions.push_back("png"); extensions.push_back("bmp"); recursive = true; max_products = 0; } /// @brief 拷贝另一个对象的数据 /// @param tem 源对象 void copy(const LoadConfig &tem) { this->extensions = tem.extensions; this->recursive = tem.recursive; this->max_products = tem.max_products; } }; /// @brief 图片加载器类 /// @detail 线程安全说明: /// - LoadALLImg() 可安全地在多个线程中同时调用 /// - total_loaded_ 计数器使用原子操作,线程安全 /// - last_error_ 由互斥锁保护 /// - 注意:返回的结果向量由调用者自行管理 class LoadImage { public: /// @brief 构造函数 LoadImage(); /// @brief 析构函数 ~LoadImage(); long getcurTime(); /// @brief 初始化成员变量 void Init(); /// @brief 拷贝另一个对象的数据(深拷贝) /// @param tem 源对象 void copy(const LoadImage &tem); /// @brief 加载指定目录下的所有图片 /// @param root_dir 根目录路径 /// @param config 加载配置(可选,默认为默认配置) /// @param pImageReadChannel 通道名称映射对象(可选,用于通道过滤和名称映射) /// @param target_product_id 目标产品 ID(可选,为空时加载所有产品) /// @return 产品图片列表 std::vector LoadALLImg( const std::string &root_dir, const LoadConfig &config = LoadConfig(), void *pImageReadChannel = nullptr, const std::string &target_product_id = ""); /// @brief 获取最后错误信息 /// @return 错误信息字符串的引用 const std::string &getLastError() const; /// @brief 获取已加载图片总数 /// @return 图片总数 size_t getTotalLoaded() const; /// @brief 打印加载器信息到控制台 /// @param str 前缀标识字符串 void print(const std::string &str); /// @brief 获取加载器信息字符串 /// @param str 前缀标识字符串 /// @return 格式化后的信息字符串 std::string GetInfo(const std::string &str); /// @brief 解析图片路径,提取图片对信息 /// @param file_path 文件路径 /// @param out_info 输出图片对信息 /// @param product_id 产品 ID /// @param image_name 图片名称 /// @param channel_name 通道名称 /// @param is_left 是否为左图 /// @param load_data 是否加载图片数据(默认为 true) /// @return 解析是否成功 static bool parseImagePath( const fs::path &file_path, LoadImagePairInfo &out_info, const std::string &product_id, const std::string &image_name, const std::string &channel_name, bool is_left, bool load_data = true); /// @brief 从目录名提取产品 ID /// @param dir_name 目录名 /// @return 产品 ID 字符串 static std::string extractProductId(const std::string &dir_name); /// @brief 从文件名提取通道名称 /// @param filename 文件名 /// @return 通道名称字符串 static std::string extractChannelName(const std::string &filename); private: // 预编译的扩展名常量(小写,不含点号) static constexpr const char *EXTENSIONS[] = { "tif", "tiff", "jpg", "jpeg", "png", "bmp"}; static constexpr size_t EXTENSION_COUNT = 6; /// @brief 检查文件名是否匹配的扩展名 /// @param filename 文件名 /// @return 是否匹配 bool matchesExtension( const std::string &filename) const; /// @brief 加载图片文件 /// @param path 文件路径 /// @return OpenCV Mat 图片数据 cv::Mat loadImageFile(const fs::path &path) const; /// @brief 设置错误信息 /// @param error 错误信息字符串 void setLastError(const std::string &error); std::string last_error_; ///< 最后错误信息 std::atomic total_loaded_{0}; ///< 已加载图片总数(原子操作) mutable std::mutex mutex_; ///< 互斥锁,保护共享状态 };