解决缺少'normalize_data_format'

博客指出某种错误是由Keras版本问题导致,解决办法是点击指定文件,添加代码‘from keras.backend.common import normalize_data_format’,并将‘self.data_format = conv_utils.normalize_data_format(data_format)’修改为‘self.data_format = normalize_data_format(data_format)’。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考:https://blog.youkuaiyun.com/Markfieraloha/article/details/84071386在这里插入图片描述
出现此种错误的原因是,keras版本问题所带来的,这个问题可以先点击箭头所指文件,进入文件之后添加from keras.backend.common import normalize_data_format,
并将self.data_format = conv_utils.normalize_data_format(data_format)
修改为self.data_format = normalize_data_format(data_format)在这里插入图片描述

import os import cv2 import logging import platform import sys import numpy as np import ctypes # 显式导入ctypes模块 from ctypes import POINTER, byref, sizeof, memset, c_void_p, c_ubyte, Structure, cast # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler(sys.stdout), logging.FileHandler("camera_operation.log") ] ) # 添加海康SDK路径 - 根据实际安装位置调整 sdk_paths = [ "D:\\海康\\MVS\\Development\\Samples\\Python\\BasicDemo", "C:\\Program Files\\MVS\\Development\\Samples\\Python\\BasicDemo" ] for path in sdk_paths: if os.path.exists(path) and path not in sys.path: sys.path.append(path) logging.info(f"已添加SDK路径: {path}") # 尝试导入海康SDK模块 try: from MvCameraControl_class import MvCamera from MvErrorDefine_const import * from CameraParams_header import * # 显式导入SDK结构体 from CameraParams_header import MV_CC_DEVICE_INFO, MV_DISPLAY_FRAME_INFO, MV_FRAME_OUT_INFO_EX, MVCC_FLOATVALUE, MV_TRIGGER_MODE logging.info("成功导入海康SDK模块") except ImportError as e: logging.error(f"导入海康SDK模块失败: {str(e)}") # 定义基本错误码作为后备 MV_OK = 0 MV_E_PARAMETER = 0x80000001 MV_E_SAVE_IMAGE = 0x80000002 MV_E_NO_DATA = 0x80000003 MV_E_CAMERA_NOT_FOUND = 0x80000004 MV_E_SDK_EXCEPTION = 0x80000005 # 定义缺失的SDK结构体 class MV_CC_DEVICE_INFO(Structure): _fields_ = [] class MV_DISPLAY_FRAME_INFO(Structure): _fields_ = [] class MV_FRAME_OUT_INFO_EX(Structure): _fields_ = [("nWidth", ctypes.c_uint), ("nHeight", ctypes.c_uint), ("nFrameLen", ctypes.c_uint)] class MVCC_FLOATVALUE(Structure): _fields_ = [("fCurValue", ctypes.c_float)] class MV_TRIGGER_MODE(Structure): _fields_ = [("nTriggerMode", ctypes.c_uint)] # 路径处理工具函数 def validate_and_normalize_path(file_path): """验证和规范化文件路径""" if not file_path or not isinstance(file_path, str) or file_path.strip() == "": logging.error("文件路径不能为空") return None illegal_chars = r'<>:"/\|?*' if any(char in file_path for char in illegal_chars): logging.error(f"路径包含非法字符: {file_path}") return None if not os.path.isabs(file_path): file_path = os.path.abspath(file_path) file_path = os.path.normpath(file_path) if platform.system() == "Windows" and len(file_path) > 240: if not file_path.startswith("\\\\?\\"): file_path = "\\\\?\\" + file_path return file_path def ensure_directory_exists(file_path): """确保文件目录存在并有写入权限""" try: dir_path = os.path.dirname(file_path) if dir_path and not os.path.exists(dir_path): os.makedirs(dir_path, exist_ok=True) logging.info(f"已创建目录: {dir_path}") test_file = os.path.join(dir_path, "permission_test.tmp") with open(test_file, 'w') as f: f.write("test") os.remove(test_file) return True except PermissionError: logging.error(f"无权限写入目录: {dir_path}") return False except OSError as e: logging.error(f"创建目录失败: {dir_path}, 错误: {str(e)}") return False class CameraOperation: def __init__(self, cam, deviceList, nSelCamIndex): """ 初始化相机操作类 :param cam: 相机对象 :param deviceList: 设备列表 :param nSelCamIndex: 选择的相机索引 """ self.cam = cam self.deviceList = deviceList self.nSelCamIndex = nSelCamIndex self.current_frame = None self.is_connected = False self.is_grabbing = False # 相机参数 self.exposure_time = 0.0 self.gain = 0.0 self.frame_rate = 0.0 # 确保错误码可用 self.MV_OK = MV_OK if 'MV_OK' in globals() else 0 self.MV_E_SDK_EXCEPTION = MV_E_SDK_EXCEPTION if 'MV_E_SDK_EXCEPTION' in globals() else 0x80000005 def Open_device(self): """打开设备""" try: logging.info(f"正在打开设备,索引: {self.nSelCamIndex}") # 安全地使用cast if not hasattr(ctypes, 'cast'): logging.error("ctypes.cast不可用") return self.MV_E_SDK_EXCEPTION # 获取设备信息指针 device_info_ptr = self.deviceList.pDeviceInfo[self.nSelCamIndex] if not device_info_ptr: logging.error("设备信息指针为空") return self.MV_E_CAMERA_NOT_FOUND # 转换为正确的指针类型 mvcc_dev_info = ctypes.cast(device_info_ptr, ctypes.POINTER(MV_CC_DEVICE_INFO)).contents # 打开设备 ret = self.cam.MV_CC_CreateHandle(mvcc_dev_info) if ret != self.MV_OK: logging.error(f"创建相机句柄失败: {ret}") return ret ret = self.cam.MV_CC_OpenDevice() if ret != self.MV_OK: logging.error(f"打开设备失败: {ret}") return ret self.is_connected = True logging.info("设备打开成功") return self.MV_OK except Exception as e: logging.exception(f"打开设备异常: {str(e)}") return self.MV_E_SDK_EXCEPTION def Start_grabbing(self, win_id=None): """开始采集图像""" try: logging.info("正在开始采集图像") # 设置显示窗口(如果提供) if win_id: stDisplayInfo = MV_DISPLAY_FRAME_INFO() stDisplayInfo.hWnd = win_id ret = self.cam.MV_CC_SetDisplayWindow(stDisplayInfo) if ret != self.MV_OK: logging.warning(f"设置显示窗口失败: {ret}") # 开始取流 ret = self.cam.MV_CC_StartGrabbing() if ret != self.MV_OK: logging.error(f"开始取流失败: {ret}") return ret self.is_grabbing = True logging.info("图像采集已开始") return self.MV_OK except Exception as e: logging.exception(f"开始采集异常: {str(e)}") return self.MV_E_SDK_EXCEPTION def Stop_grabbing(self): """停止采集图像""" try: logging.info("正在停止采集图像") ret = self.cam.MV_CC_StopGrabbing() if ret != self.MV_OK: logging.error(f"停止取流失败: {ret}") return ret self.is_grabbing = False logging.info("图像采集已停止") return self.MV_OK except Exception as e: logging.exception(f"停止采集异常: {str(e)}") return self.MV_E_SDK_EXCEPTION def Close_device(self): """关闭设备""" try: logging.info("正在关闭设备") if self.is_grabbing: self.Stop_grabbing() ret = self.cam.MV_CC_CloseDevice() if ret != self.MV_OK: logging.error(f"关闭设备失败: {ret}") ret = self.cam.MV_CC_DestroyHandle() if ret != self.MV_OK: logging.error(f"销毁句柄失败: {ret}") self.is_connected = False logging.info("设备已关闭") return self.MV_OK except Exception as e: logging.exception(f"关闭设备异常: {str(e)}") return self.MV_E_SDK_EXCEPTION # 其他方法(Set_trigger_mode, Trigger_once, Save_Image等)保持类似结构 # 确保所有方法都使用self.MV_OK和self.MV_E_SDK_EXCEPTION def Get_frame(self, timeout=1000): """获取一帧图像""" try: stFrameInfo = MV_FRAME_OUT_INFO_EX() memset(byref(stFrameInfo), 0, sizeof(stFrameInfo)) # 获取图像数据 data_size = stFrameInfo.nFrameLen if data_size == 0: data_size = 1024 * 1024 * 10 # 10MB默认缓冲区 pData = (c_ubyte * data_size)() ret = self.cam.MV_CC_GetImageBuffer(pData, stFrameInfo, timeout) if ret != self.MV_OK: logging.error(f"获取图像失败: {ret}") return None, None # 转换为OpenCV格式 nWidth = stFrameInfo.nWidth nHeight = stFrameInfo.nHeight frame_data = np.frombuffer(pData, dtype=np.uint8, count=stFrameInfo.nFrameLen) # 根据像素格式处理图像 if stFrameInfo.enPixelType == 17301505: # Mono8 frame = frame_data.reshape((nHeight, nWidth)) elif stFrameInfo.enPixelType == 17301514: # BayerRG8 frame = cv2.cvtColor(frame_data.reshape((nHeight, nWidth)), cv2.COLOR_BayerRG2RGB) else: # RGB frame = frame_data.reshape((nHeight, nWidth, 3)) self.current_frame = frame return frame, stFrameInfo except Exception as e: logging.exception(f"获取图像异常: {str(e)}") return None, None # 简化后的Save_Image方法 def Save_Image(self, file_path, save_format="bmp", quality=95): """保存当前帧到文件""" try: if self.current_frame is None: logging.warning("无当前帧可保存") return self.MV_E_NO_DATA file_path = validate_and_normalize_path(file_path) if not file_path: return self.MV_E_PARAMETER if not ensure_directory_exists(file_path): return self.MV_E_SAVE_IMAGE # 添加扩展名 if not os.path.splitext(file_path)[1]: file_path += f".{save_format}" # 保存图像 cv2.imwrite(file_path, self.current_frame) logging.info(f"图像保存成功: {file_path}") return self.MV_OK except Exception as e: logging.exception(f"保存图像异常: {str(e)}") return self.MV_E_SAVE_IMAGE 把你的解决方案放进原程序
07-07
确定把# E:\AI_System\core\config.py import os import json import logging import configparser from pathlib import Path from typing import Dict, Any, Optional, Union, List, Tuple from dotenv import load_dotenv import re class CoreConfig: """ 重构后的核心配置管理类 优化点: 1. 强类型验证和转换 2. 自动创建缺失目录 3. 配置变更追踪 4. 更安全的敏感数据处理 5. 更健壮的错误处理 """ def __init__(self, config_path: Optional[Union[str, Path]] = None, env_prefix: str = "APP_", default_config: Optional[Dict[str, Any]] = None): """ 初始化配置管理器 :param config_path: 配置文件路径(支持.json, .ini, .env) :param env_prefix: 环境变量前缀 :param default_config: 默认配置字典 """ # 初始化日志 self.logger = logging.getLogger('CoreConfig') self._setup_logger() # 设置实例变量 self.env_prefix = env_prefix self.config_path = self._normalize_path(config_path) self.config_data = default_config or {} self.config_modified = False self.original_config = {} # 自动设置基础目录 self.BASE_DIR = self._determine_base_dir() self._set_default_paths() self.log(f"📋 初始化配置管理器 | 环境前缀: {env_prefix} | 基础目录: {self.BASE_DIR}") # 加载配置 self.load_configuration() # 确保所有配置目录存在 self._ensure_directories() def _setup_logger(self): """配置日志记录器""" if not self.logger.handlers: handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) self.logger.addHandler(handler) self.logger.setLevel(logging.INFO) self.logger.propagate = False def _normalize_path(self, path: Optional[Union[str, Path]]) -> Optional[Path]: """将路径转换为Path对象并解析为绝对路径""" if path is None: return None if isinstance(path, str): path = Path(path) if not path.is_absolute(): # 尝试相对于基础目录解析 base_dir = self._determine_base_dir() return (base_dir / path).resolve() return path.resolve() def _determine_base_dir(self) -> Path: """确定项目基础目录""" # 1. 尝试从环境变量获取 base_dir_env = os.getenv(f"{self.env_prefix}BASE_DIR") if base_dir_env: return Path(base_dir_env).resolve() # 2. 尝试基于当前文件位置计算 try: return Path(__file__).parent.parent.resolve() except NameError: # 在交互式环境中运行时 return Path.cwd().resolve() # 3. 默认使用当前工作目录 return Path.cwd().resolve() def _set_default_paths(self): """设置默认路径配置""" # 基本路径 self.config_data.setdefault("BASE_DIR", str(self.BASE_DIR)) # 常用目录 self.config_data.setdefault("LOG_DIR", str(self.BASE_DIR / "logs")) self.config_data.setdefault("CONFIG_DIR", str(self.BASE_DIR / "config")) self.config_data.setdefault("CACHE_DIR", str(self.BASE_DIR / "cache")) self.config_data.setdefault("DATA_DIR", str(self.BASE_DIR / "data")) self.config_data.setdefault("MODEL_DIR", str(self.BASE_DIR / "models")) def log(self, message: str, level: str = "info"): """记录日志""" log_levels = { "debug": self.logger.debug, "info": self.logger.info, "warning": self.logger.warning, "error": self.logger.error } log_func = log_levels.get(level.lower(), self.logger.info) log_func(message) def load_configuration(self): """加载所有配置源""" # 记录原始配置用于变更检测 self.original_config = self.config_data.copy() # 1. 加载环境变量 self._load_environment_vars() # 2. 加载配置文件(如果存在) if self.config_path and self.config_path.exists(): self._load_config_file() elif self.config_path: self.log(f"⚠️ 配置文件不存在: {self.config_path}", "warning") # 3. 应用路径配置 self._apply_path_configurations() self.log(f"✅ 配置加载完成 | 条目数: {len(self.config_data)}") # 检测配置变更 self._detect_config_changes() def _apply_path_configurations(self): """应用路径配置并转换为Path对象""" # 更新基础目录(如果配置中有) if "BASE_DIR" in self.config_data: self.BASE_DIR = Path(self.config_data["BASE_DIR"]).resolve() # 确保所有路径配置都是绝对路径 path_keys = ["LOG_DIR", "CONFIG_DIR", "CACHE_DIR", "DATA_DIR", "MODEL_DIR"] for key in path_keys: if key in self.config_data: path_str = self.config_data[key] if not Path(path_str).is_absolute(): # 转换为相对于BASE_DIR的路径 self.config_data[key] = str(self.BASE_DIR / path_str) def _load_environment_vars(self): """加载环境变量""" self.log("🔍 加载环境变量...") # 从CONFIG_DIR加载.env文件(如果存在) env_file = Path(self.config_data.get("CONFIG_DIR", self.BASE_DIR / "config")) / ".env" if env_file.exists(): load_dotenv(env_file) self.log(f" - 加载环境变量文件: {env_file}") # 获取所有以指定前缀开头的环境变量 for key, value in os.environ.items(): if key.startswith(self.env_prefix): config_key = key[len(self.env_prefix):].lower() parsed_value = self._parse_value(value) self.config_data[config_key] = parsed_value self.log(f" - 加载环境变量: {config_key} = {self._mask_secret(config_key, parsed_value)}") def _load_config_file(self): """根据文件扩展名加载配置文件""" suffix = self.config_path.suffix.lower() self.log(f"📄 加载配置文件: {self.config_path} (类型: {suffix[1:]})") try: if suffix == '.json': self._load_json_config() elif suffix in ('.ini', '.cfg'): self._load_ini_config() elif suffix == '.env': # .env文件已由_load_environment_vars处理 self.log(" - .env文件已由环境变量加载器处理") else: self.log(f"❌ 不支持的配置文件类型: {suffix}", "error") except Exception as e: self.log(f"❌ 配置文件加载失败: {str(e)}", "error") self.log(f"⚠️ 使用默认配置继续运行", "warning") def _load_json_config(self): """加载JSON配置文件""" with open(self.config_path, 'r', encoding='utf-8') as f: json_data = json.load(f) self._merge_config(json_data) def _load_ini_config(self): """加载INI配置文件""" parser = configparser.ConfigParser() parser.read(self.config_path) config_dict = {} for section in parser.sections(): section_dict = {} for key, value in parser.items(section): section_dict[key] = self._parse_value(value) config_dict[section] = section_dict self._merge_config(config_dict) def _merge_config(self, new_config: Dict[str, Any]): """合并新配置到现有配置""" for key, value in new_config.items(): # 处理嵌套配置 if isinstance(value, dict) and key in self.config_data and isinstance(self.config_data[key], dict): # 深度合并字典 self._deep_merge_dict(self.config_data[key], value) else: self.config_data[key] = value self.log(f" - 加载配置项: {key} = {self._mask_secret(key, value)}") def _deep_merge_dict(self, base: Dict[str, Any], new: Dict[str, Any]): """深度合并两个字典""" for key, value in new.items(): if key in base and isinstance(base[key], dict) and isinstance(value, dict): self._deep_merge_dict(base[key], value) else: base[key] = value def _parse_value(self, value: str) -> Any: """智能解析字符串值为合适的Python类型""" # 处理空值 if value is None or value.strip() == "": return None # 尝试解析为布尔值 lower_val = value.lower() if lower_val in ('true', 'yes', 'on', '1'): return True if lower_val in ('false', 'no', 'off', '0'): return False # 尝试解析为整数 try: return int(value) except ValueError: pass # 尝试解析为浮点数 try: return float(value) except ValueError: pass # 尝试解析为列表(支持逗号分隔和JSON数组格式) if ',' in value: # 简单逗号分隔的列表 return [self._parse_value(item.strip()) for item in value.split(',')] if value.startswith('[') and value.endswith(']'): # JSON格式的数组 try: return json.loads(value) except json.JSONDecodeError: pass # 尝试解析为字典(JSON对象) if value.startswith('{') and value.endswith('}'): try: return json.loads(value) except json.JSONDecodeError: pass # 返回原始字符串 return value def _mask_secret(self, key: str, value: Any) -> str: """对敏感信息进行掩码处理""" # 检测敏感键名 sensitive_keys = ['secret', 'password', 'key', 'token', 'credential', 'auth'] if any(s in key.lower() for s in sensitive_keys): if isinstance(value, str) and value: # 保留部分可见字符用于调试 if len(value) <= 3: return '***' return value[:1] + '***' + value[-1:] return '******' if value else '空' if isinstance(value, list): return f"[列表, 长度: {len(value)}]" if isinstance(value, dict): return f"{{字典, 键数: {len(value)}}}" if isinstance(value, str) and len(value) > 50: return f"字符串[{len(value)}字符]" return str(value) def get(self, key: str, default: Any = None, required: bool = False) -> Any: """ 获取配置值,支持点分路径 (如: 'database.host') :param key: 配置键名 :param default: 默认值(如果键不存在) :param required: 是否为必需项,如果必需且不存在则抛出异常 :return: 配置值 """ keys = key.split('.') current = self.config_data for k in keys: if isinstance(current, dict) and k in current: current = current[k] else: if required: raise KeyError(f"必需的配置项缺失: {key}") return default return current def get_path(self, key: str, default: Optional[Union[str, Path]] = None) -> Path: """ 获取路径配置项,确保返回Path对象 :param key: 配置键名 :param default: 默认路径 :return: Path对象 """ path_str = self.get(key, default) if path_str is None: raise ValueError(f"路径配置项 {key} 未设置") path = Path(path_str) if not path.is_absolute(): # 转换为相对于BASE_DIR的路径 return self.BASE_DIR / path return path def set(self, key: str, value: Any, persist: bool = False): """ 设置配置值 :param key: 配置键名 :param value: 配置值 :param persist: 是否持久化到配置文件 """ keys = key.split('.') current = self.config_data # 遍历创建嵌套字典 for i, k in enumerate(keys[:-1]): if k not in current: current[k] = {} elif not isinstance(current[k], dict): # 如果当前值不是字典,则覆盖为字典 current[k] = {} current = current[k] # 设置最终值 last_key = keys[-1] current[last_key] = value self.config_modified = True self.log(f"⚙️ 设置配置: {key} = {self._mask_secret(last_key, value)}") # 持久化到文件 if persist and self.config_path: self.save_config() def save_config(self, path: Optional[Union[str, Path]] = None): """ 保存配置到文件 :param path: 可选的自定义保存路径 :return: 是否保存成功 """ save_path = self._normalize_path(path) if path else self.config_path if not save_path: self.log("❌ 保存失败: 未指定配置文件路径", "error") return False # 确保目录存在 save_path.parent.mkdir(parents=True, exist_ok=True) suffix = save_path.suffix.lower() try: if suffix == '.json': with open(save_path, 'w', encoding='utf-8') as f: json.dump(self.config_data, f, indent=2, ensure_ascii=False) elif suffix in ('.ini', '.cfg'): self._save_ini_config(save_path) else: self.log(f"❌ 不支持的文件格式: {suffix}", "error") return False self.log(f"💾 配置已保存到: {save_path}") self.config_modified = False # 更新原始配置 self.original_config = self.config_data.copy() return True except Exception as e: self.log(f"❌ 配置保存失败: {str(e)}", "error") return False def _save_ini_config(self, path: Path): """保存为INI格式配置文件""" parser = configparser.ConfigParser() # 添加DEFAULT节 parser.add_section('DEFAULT') # 递归处理嵌套字典 def add_section(data, section_name=None): if section_name: if not parser.has_section(section_name): parser.add_section(section_name) for key, value in data.items(): if isinstance(value, dict): # 递归处理嵌套字典 new_section = f"{section_name}.{key}" if section_name else key add_section(value, new_section) else: # 处理非字典值 if section_name: parser.set(section_name, key, str(value)) else: parser.set('DEFAULT', key, str(value)) add_section(self.config_data) with open(path, 'w', encoding='utf-8') as f: parser.write(f) def reload(self): """重新加载所有配置源""" self.log("🔄 重新加载配置...") # 保留当前配置作为基础 current_config = self.config_data.copy() self.config_data = {} self.load_configuration() # 合并当前配置(优先保留当前设置) self._deep_merge_dict(self.config_data, current_config) self.log("✅ 配置重新加载完成") def to_dict(self) -> Dict[str, Any]: """返回当前配置的完整字典(深度拷贝)""" return json.loads(json.dumps(self.config_data)) def _ensure_directories(self): """确保所有配置目录存在""" dir_keys = ["LOG_DIR", "CONFIG_DIR", "CACHE_DIR", "DATA_DIR", "MODEL_DIR"] for key in dir_keys: path_str = self.get(key) if path_str: path = Path(path_str) if not path.exists(): path.mkdir(parents=True, exist_ok=True) self.log(f"📁 创建目录: {path}") def _detect_config_changes(self): """检测配置变更并记录""" changed = [] added = [] removed = [] # 检测新增配置项 for key in self.config_data: if key not in self.original_config: added.append(key) # 检测删除的配置项 for key in self.original_config: if key not in self.config_data: removed.append(key) # 检测值变更 for key in self.config_data: if key in self.original_config and self.config_data[key] != self.original_config[key]: changed.append(key) # 记录变更 if added or removed or changed: self.log(f"🔄 配置变更检测: 新增 {len(added)} 项, 删除 {len(removed)} 项, 修改 {len(changed)} 项") if added: self.log(f" ➕ 新增配置: {', '.join(added)}") if removed: self.log(f" ➖ 删除配置: {', '.join(removed)}") if changed: self.log(f" ✏️ 修改配置: {', '.join(changed)}") def __getitem__(self, key: str) -> Any: """支持字典式访问""" return self.get(key) def __setitem__(self, key: str, value: Any): """支持字典式设置""" self.set(key, value) def __contains__(self, key: str) -> bool: """检查配置项是否存在""" return self.get(key) is not None def __str__(self) -> str: """返回配置的字符串表示(掩码敏感信息)""" masked_data = {} for key, value in self.config_data.items(): masked_data[key] = self._mask_secret(key, value) return json.dumps(masked_data, indent=2, ensure_ascii=False) # 创建全局系统配置实例 system_config = CoreConfig( config_path=Path(__file__).parent.parent / "config" / "system_config.json", env_prefix="AI_SYSTEM_" ) # 测试函数 def run_config_tests(): """运行配置管理器测试""" # 设置测试日志 logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) print("=" * 50) print("核心配置管理器测试") print("=" * 50) # 创建临时测试目录 import tempfile with tempfile.TemporaryDirectory() as temp_dir: test_dir = Path(temp_dir) # 创建测试配置文件 config_json = { "database": { "host": "localhost", "port": 5432, "username": "admin", "password": "db_secret" }, "logging": { "level": "INFO", "path": "app_logs" # 相对路径 }, "features": ["auth", "caching", "analytics"] } test_config_path = test_dir / "test_config.json" with open(test_config_path, "w") as f: json.dump(config_json, f) # 设置环境变量 os.environ["APP_API_KEY"] = "env_secret_key" os.environ["APP_DATABASE_PORT"] = "6432" # 覆盖配置文件中的端口 try: # 创建配置管理器实例 config = CoreConfig( config_path=test_config_path, env_prefix="APP_", default_config={ "app_name": "MyApp", "version": "1.0.0" } ) # 测试基础功能 print("\n=== 基础功能测试 ===") print(f"基础目录: {config.BASE_DIR}") print(f"日志目录: {config.get_path('LOG_DIR')}") print(f"数据库主机: {config.get('database.host')}") print(f"数据库端口: {config.get('database.port')} (应被环境变量覆盖)") print(f"API密钥: {config.get('api_key')} (来自环境变量)") print(f"应用名称: {config.get('app_name')} (来自默认配置)") print(f"日志级别: {config.get('logging.level')}") print(f"功能列表: {config.get('features')}") # 测试路径解析 print("\n=== 路径解析测试 ===") log_path = config.get_path("logging.path") print(f"日志路径 (绝对路径): {log_path}") print(f"日志路径是否存在: {log_path.exists()}") # 测试配置设置 print("\n=== 配置设置测试 ===") config.set("new_feature.enabled", True) config.set("database.password", "new_secret", persist=True) print(f"新功能状态: {config.get('new_feature.enabled')}") # 测试保存和重新加载 print("\n=== 保存与重载测试 ===") saved_path = test_dir / "saved_config.json" config.save_config(saved_path) print(f"配置已保存到: {saved_path}") # 修改配置并重新加载 config.set("app_name", "UpdatedApp") print(f"修改后应用名称: {config.get('app_name')}") config.reload() print(f"重新加载后应用名称: {config.get('app_name')} (应恢复原值)") # 测试敏感信息掩码 print("\n=== 敏感信息掩码测试 ===") print(f"数据库密码: {config.get('database.password')} (实际值)") print(f"掩码后表示: {config._mask_secret('database.password', config.get('database.password'))}") # 测试配置变更检测 print("\n=== 配置变更检测测试 ===") config.set("test_change", "value") config.set("app_name", "ChangedApp") config.reload() # 会检测变更 except Exception as e: print(f"❌ 测试失败: {str(e)}") import traceback traceback.print_exc() # 仅在直接运行时执行测试 if __name__ == "__main__": run_config_tests() 改成# E:\AI_System\core\config.py import os import json import logging from pathlib import Path from typing import Dict, Any, Optional, Union from dotenv import load_dotenv import threading import time import hashlib class CoreConfig: """核心配置管理器,支持多来源配置加载和热更新""" def __init__(self, config_path: Union[str, Path] = None, env_prefix: str = "", default_config: Dict = None): """ 初始化配置管理器 Args: config_path: 配置文件路径 env_prefix: 环境变量前缀 default_config: 默认配置字典 """ # 初始化参数 self.config_path = Path(config_path) if config_path else None self.env_prefix = env_prefix self.default_config = default_config or {} self._config = {} self._last_hash = None self._lock = threading.Lock() # 设置日志器 self.logger = logging.getLogger('CoreConfig') # 初始化配置 self._initialize() def _initialize(self): """初始化配置""" self.logger.info(f"📋 初始化配置管理器 | 环境前缀: {self.env_prefix} | 基础目录: {Path.cwd()}") # 加载环境变量 self._load_environment() # 加载配置文件 if self.config_path and self.config_path.exists(): self._load_config_file() else: self.logger.warning(f"⚠️ 配置文件不存在: {self.config_path}") # 合并默认配置 self._merge_defaults() # 创建必要目录 self._create_directories() def _load_environment(self): """加载环境变量""" self.logger.info("🔍 加载环境变量...") # 加载系统环境变量 for key, value in os.environ.items(): if key.startswith(self.env_prefix): config_key = key[len(self.env_prefix):].lower() self._config[config_key] = value # 加载 .env 文件 env_path = Path.cwd() / ".env" if env_path.exists(): load_dotenv(env_path) self.logger.info(f"✅ 已加载环境变量文件: {env_path}") def _load_config_file(self): """加载配置文件""" file_type = self.config_path.suffix.lower() self.logger.info(f"📄 加载配置文件: {self.config_path} (类型: {file_type[1:]})") try: if file_type == '.json': with open(self.config_path, 'r') as f: config_data = json.load(f) elif file_type == '.yaml' or file_type == '.yml': import yaml with open(self.config_path, 'r') as f: config_data = yaml.safe_load(f) elif file_type == '.ini': import configparser parser = configparser.ConfigParser() parser.read(self.config_path) config_data = {} for section in parser.sections(): config_data[section] = dict(parser.items(section)) else: raise ValueError(f"不支持的配置文件类型: {file_type}") # 合并配置 self._deep_merge(self._config, config_data) self._last_hash = self._calculate_hash() # 记录加载的配置项 self._log_loaded_items(config_data) except Exception as e: self.logger.error(f"❌ 加载配置文件失败: {str(e)}") def _log_loaded_items(self, config_data: Dict): """记录加载的配置项""" for key, value in config_data.items(): if isinstance(value, dict): self.logger.info(f" - 加载配置项: {key} = {{字典, 键数: {len(value)}}}") elif isinstance(value, list): self.logger.info(f" - 加载配置项: {key} = {{列表, 长度: {len(value)}}}") elif isinstance(value, str) and len(value) > 20: self.logger.info(f" - 加载配置项: {key} = {value[:10]}***{value[-10:]}") elif key.lower().endswith('key') or key.lower().endswith('secret'): self.logger.info(f" - 加载配置项: {key} = {value[:3]}***{value[-3:]}") else: self.logger.info(f" - 加载配置项: {key} = {value}") def _merge_defaults(self): """合并默认配置""" self.logger.info("🔄 合并默认配置...") self._deep_merge(self._config, self.default_config) self.logger.info(f"✅ 配置加载完成 | 条目数: {len(self._config)}") def _deep_merge(self, target: Dict, source: Dict): """深度合并字典""" for key, value in source.items(): if (key in target and isinstance(target[key], dict) and isinstance(value, dict)): self._deep_merge(target[key], value) else: target[key] = value def _calculate_hash(self) -> str: """计算配置哈希值""" return hashlib.md5(json.dumps(self._config, sort_keys=True).encode()).hexdigest() def _create_directories(self): """创建配置中指定的目录""" dir_keys = ['log_dir', 'cache_dir', 'model_dir', 'data_dir'] created_dirs = [] for key in dir_keys: if key in self._config and isinstance(self._config[key], str): path = Path(self._config[key]) if not path.exists(): path.mkdir(parents=True, exist_ok=True) created_dirs.append(str(path)) if created_dirs: self.logger.info(f"📁 创建目录: {', '.join(created_dirs)}") def check_for_updates(self) -> bool: """检查配置是否有更新""" if not self.config_path or not self.config_path.exists(): return False with open(self.config_path, 'rb') as f: current_hash = hashlib.md5(f.read()).hexdigest() if current_hash != self._last_hash: self.logger.info("🔄 检测到配置文件更改,重新加载配置...") self._load_config_file() self._create_directories() return True return False # 添加属性访问支持 def __getattr__(self, name: str) -> Any: """支持点操作符访问配置项""" if name in self._config: return self._config[name] # 支持嵌套属性访问 (例如 system_config.app.name) parts = name.split('.') current = self._config for part in parts: if part in current: current = current[part] else: raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") return current def __getitem__(self, key: str) -> Any: """支持字典式访问配置项""" return self._config[key] def get(self, key: str, default: Any = None) -> Any: """安全获取配置值""" parts = key.split('.') current = self._config for part in parts: if part in current: current = current[part] else: return default return current def set(self, key: str, value: Any): """设置配置值""" parts = key.split('.') current = self._config for i, part in enumerate(parts): if i == len(parts) - 1: current[part] = value else: if part not in current: current[part] = {} current = current[part] def save(self): """保存配置到文件""" if not self.config_path: self.logger.warning("⚠️ 没有配置文件路径,无法保存配置") return try: with open(self.config_path, 'w') as f: if self.config_path.suffix.lower() == '.json': json.dump(self._config, f, indent=2) elif self.config_path.suffix.lower() in ('.yaml', '.yml'): import yaml yaml.dump(self._config, f) elif self.config_path.suffix.lower() == '.ini': import configparser parser = configparser.ConfigParser() # 将字典转换为INI格式 # 注意:INI格式不支持嵌套结构,所以只保存顶级键值对 for key, value in self._config.items(): if isinstance(value, dict): parser.add_section(key) for subkey, subvalue in value.items(): parser.set(key, subkey, str(subvalue)) else: parser['DEFAULT'][key] = str(value) parser.write(f) self._last_hash = self._calculate_hash() self.logger.info(f"💾 配置已保存到: {self.config_path}") except Exception as e: self.logger.error(f"❌ 保存配置失败: {str(e)}") def __str__(self) -> str: """返回配置的字符串表示""" return json.dumps(self._config, indent=2) def to_dict(self) -> Dict: """返回配置字典""" return self._config.copy() 吗?
最新发布
08-13
“from torch.utils.data import Dataset, DataLoader from torch.utils.tensorboard import SummaryWriter from PIL import Image import os import torchvision import torch class MyData(Dataset): #针对第一种数据类型:文件名对应照片集的Label #不同数据集布局对于数据的提取需要微调 def __init__(self, data_folder, transform=True): self.data_folder = data_folder self.transform = transform self.filenames = [] self.labels = [] per_classes = os.listdir(self.data_folder) for per_class in per_classes: per_class_path = os.path.join(self.data_folder, per_class) per_images = os.listdir(per_class_path) label = str(per_class) for per_image in per_images: self.filenames.append(os.path.join(per_class_path, per_image)) self.labels.append(label) def __getitem__(self, index): img = Image.open(self.filenames[index]) label = self.labels[index] if self.transform is not None: img = self.proprecess(img) return img, label def __len__(self): return len(self.filenames) def proprecess(self, img): data_transform = torchvision.transforms.Compose([torchvision.transforms.Resize(256), torchvision.transforms.CenterCrop(224), torchvision.transforms.Pad(2,padding_mode='edge'), torchvision.transforms.ToTensor(), torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])]) img = data_transform(img) return img data_folder = "dataset/train" ants_dataset = MyData(data_folder, transform=True) data_loader = DataLoader(ants_dataset, batch_size=16, shuffle=True,drop_last=True) writer = SummaryWriter("New_logs") for epoch in range(2): for batch_idx, (data, label) in enumerate(data_loader): writer.add_images("epoch:{}".format(epoch), data, batch_idx) # #__getitem__支持以下调用: # img, label = ants_dataset[0] #相当于ants_dataset.__getitem__(0) # img.show() # print(label)”分析一下这段代码有没有错误的地方
06-22
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值