|
|
#pragma once
|
|
|
|
|
|
#include <string>
|
|
|
#include <vector>
|
|
|
#include <filesystem>
|
|
|
#include <algorithm>
|
|
|
#include <cctype>
|
|
|
#include <mutex>
|
|
|
#include <atomic>
|
|
|
#include <opencv2/opencv.hpp>
|
|
|
|
|
|
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<LoadImagePairInfo> 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<std::string> 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<LoadProductImages> 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<size_t> total_loaded_{0}; ///< 已加载图片总数(原子操作)
|
|
|
mutable std::mutex mutex_; ///< 互斥锁,保护共享状态
|
|
|
};
|