# devdeploy **Repository Path**: hainingzhang/devdeploy ## Basic Information - **Project Name**: devdeploy - **Description**: devploy test - **Primary Language**: Python - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-02 - **Last Updated**: 2026-01-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # DevDeploy - 深度学习模型部署工具包 ## 项目简介 DevDeploy是一个专为深度学习模型部署设计的Python工具包,提供了统一的推理接口和多种实用功能。本项目支持图像分类、语义分割等任务,并提供了滑窗检测、批量处理等高级功能。 ## 项目架构 ``` devdeploy/ ├── inference/ # 推理模块 │ ├── base_inference.py # 推理基类 │ ├── inference.py # 推理工厂函数 │ ├── torch_inference.py # PyTorch推理实现 │ ├── onnx_inference.py # ONNX推理实现 │ └── testCls.py # 分类测试脚本 ├── src/ # 对外接口模块 │ ├── __init__.py # 模块初始化 │ ├── JinhengLib.py # 通用接口类 │ ├── imagequality/ # 图像质量检测模块 │ │ ├── __init__.py # 模块初始化 │ │ └── sliding_window_classifier.py # 滑窗分类器实现 │ ├── detectdark/ # 暗区检测模块 │ │ ├── __init__.py # 模块初始化 │ │ └── dark_detector.py # 暗区检测器实现 │ ├── detectblur/ # 模糊检测模块 │ │ ├── __init__.py # 模块初始化 │ │ └── blur_detector.py # 模糊检测器实现 │ ├── segmentationanalyzer/ # 分割模型分析模块 │ │ ├── __init__.py # 模块初始化 │ │ └── segmentation_analyzer.py # 分割模型分析器实现 │ └── classifier/ # 图像分类模块 │ ├── __init__.py # 模块初始化 │ └── classifier.py # 分类器实现 ├── test/ # 测试模块 │ ├── __init__.py # 测试模块初始化 │ └── test_sliding_window.py # 滑窗分类器功能测试 └── README.md # 项目说明文档 ``` ## 核心功能 ### 1. 统一推理接口 - 支持PyTorch模型(.pth)和ONNX模型(.onnx) - 自动设备选择(CPU/GPU) - 统一的输入输出格式 - 详细的模型信息输出(输入/输出名称、尺寸、类型等) #### 模型信息输出 当加载ONNX模型时,系统会自动输出以下详细信息: - **输入信息**: 输入名称、输入尺寸 - **输出信息**: 输出数量、每个输出的名称、尺寸和数据类型 - **配置信息**: 任务类型、图像尺寸、分类尺寸等 示例输出: ``` ONNX模型输入名: input ONNX模型输入尺寸: [1, 3, 224, 224] ONNX模型输出数量: 1 ONNX模型输出0名: output ONNX模型输出0尺寸: [1, 2] ONNX模型输出0类型: tensor(float) 任务类型: classification 分类任务resize尺寸: 224 ``` ### 2. 滑窗分类检测 - 224x224像素滑窗,步长224 - 自动模糊检测(类别0) - 边界框绘制和结果可视化 - 支持多种输入格式(路径、PIL Image、numpy数组、OpenCV BGR格式、单通道灰度图) - 自动处理OpenCV BGR到RGB的格式转换 ### 3. 暗区检测 - 基于分割模型的暗区检测 - 支持单张图片和批量检测 - 连通域分析,只返回最大暗区区域 - 动态面积阈值,根据图像尺寸自动调整 - 面积阈值过滤,判断是否存在黑边 - 自动提取暗区区域和边界框 - 结果可视化和保存 - 支持多种输入格式(路径、PIL Image、numpy数组、OpenCV BGR格式、单通道灰度图) - 自动处理OpenCV BGR到RGB的格式转换 ### 4. 模糊检测 - 基于分割模型的模糊检测 - 支持单张图片检测 - 返回1通道二值图,1表示模糊区域,0表示非模糊区域 - 自动resize回原图尺寸 - 支持多种输入格式(路径、PIL Image、numpy数组、OpenCV BGR格式、单通道灰度图) - 自动处理OpenCV BGR到RGB的格式转换 - 自动处理单通道灰度图到RGB的格式转换 - 提供模糊像素统计和比例计算 ### 5. 分割模型分析 - 基于分割模型的类别统计和分析功能 - 支持单张图片分析 - 统计各类别的像素数量和占比 - 重点统计类别1和类别2的分布情况 - **新增**:支持类别名称显示,在result.txt中显示类别名称占比 - 提供详细的分析结果可视化 - 支持多种输入格式(路径、PIL Image、numpy数组、OpenCV BGR格式、单通道灰度图) - 自动处理OpenCV BGR到RGB的格式转换 - 自动处理单通道灰度图到RGB的格式转换 ### 6. 图像分类 - 基于分类模型的图像分类功能 - 支持单张图片和批量分类推理 - 返回类别ID、类别名称、置信度和概率分布 - **智能类别获取**: 自动从模型的YAML配置文件中获取类别名称,无需手动指定 - 支持多种输入格式(路径、PIL Image、numpy数组、OpenCV BGR格式、单通道灰度图) - 自动处理OpenCV BGR到RGB的格式转换 - 自动处理单通道灰度图到RGB的格式转换 - 提供分类结果可视化和保存功能 ### 7. 批量处理 - 支持单张图片检测 - 外部遍历文件夹进行批量处理 - 多种图像格式支持 - 结果保存和导出 ### 8. 图像Mask生成 - 支持多种标注格式(LabelMe、COCO、自定义格式) - 根据标签顺序自动生成递增的mask值 - **新增**:支持标签合并配置,可将多个标签映射到同一个值 - JSON文件不存在时自动生成纯黑二值图 - 支持多边形、矩形、圆形等多种标注形状 - 批量处理整个目录的图像文件 - 自动组织输出目录结构:原图像保存到img/子目录,mask保存到label/子目录 ### 9. 接口设计 - 清晰的接口设计,只暴露用户需要的方法 - 实现与接口分离,便于维护和扩展 - 统一的对外接口,简化用户使用 ## Git配置说明 ### 解决每次push都需要输入用户名和密码的问题 项目已配置为使用SSH协议连接远程仓库,无需每次输入用户名和密码。 #### 方法1:使用SSH协议(推荐) 1. **检查SSH密钥是否存在** ```bash ls -la ~/.ssh/id_*.pub ``` 2. **如果没有SSH密钥,生成新的SSH密钥** ```bash ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # 按提示操作,可以直接回车使用默认路径 ``` 3. **查看SSH公钥** ```bash cat ~/.ssh/id_rsa.pub ``` 4. **将SSH公钥添加到Gitee** - 复制上面命令输出的公钥内容 - 登录Gitee → 设置 → SSH公钥 → 添加公钥 - 粘贴公钥内容并保存 5. **测试SSH连接** ```bash ssh -T git@gitee.com # 如果看到 "Hi hainingzhang! You've successfully authenticated..." 说明配置成功 ``` 6. **确认远程仓库使用SSH协议** ```bash git remote -v # 应该显示 git@gitee.com:hainingzhang/devdeploy.git # 如果显示 https://...,需要执行: git remote set-url origin git@gitee.com:hainingzhang/devdeploy.git ``` #### 方法2:使用Git凭证存储(备选) 如果不想使用SSH,可以配置Git凭证存储: ```bash # 配置Git凭证存储(Linux) git config --global credential.helper store # 或者使用缓存(15分钟内不需要重新输入) git config --global credential.helper 'cache --timeout=3600' ``` **注意**:使用凭证存储时,首次push仍需要输入用户名和密码,之后会自动保存。 ## 快速开始 ### 安装依赖 ```bash pip install torch torchvision onnx onnxruntime pillow opencv-python numpy scikit-image fastapi uvicorn ``` ### API服务启动 #### 分类器服务(动态启动) 分类器服务支持通过命令行参数动态指定模型路径和端口号来启动不同的服务实例。 ```bash # 基本用法 ./start_classifier_service.sh [container_name] # 示例:启动 resnet50_qiu 模型,端口 8796 ./start_classifier_service.sh ../weights/resnet50_qiu/fullmodel_best.onnx 8796 # 示例:启动 resnet50_fang 模型,端口 8797 ./start_classifier_service.sh ../weights/resnet50_fang/fullmodel_best.onnx 8797 # 示例:启动 resnet50_yuan 模型,端口 8798,自定义容器名 ./start_classifier_service.sh ../weights/resnet50_yuan/fullmodel_best.onnx 8798 classifier-yuan ``` **参数说明:** - `model_path`: 模型文件路径,weights目录已移动到项目上级目录(/data1/code/weights),因此路径应为 `../weights/...` - `port`: 服务端口号 - `container_name`: 可选,容器名称(默认为 `devdeploy-classifier-`) **服务管理命令:** ```bash # 查看运行中的服务 docker ps | grep devdeploy-classifier # 查看服务日志(替换端口号) docker-compose -f docker-compose.8796.yml -p classifier-8796 logs -f # 停止服务(替换端口号) docker-compose -f docker-compose.8796.yml -p classifier-8796 down # 重启服务(替换端口号) docker-compose -f docker-compose.8796.yml -p classifier-8796 restart # 删除容器 docker stop devdeploy-classifier-8796 docker rm devdeploy-classifier-8796 ``` #### CV API服务日志系统 CV API服务已配置完整的日志系统,所有运行时的输出都会同时保存到日志文件中,方便查看和调试。 **日志文件位置:** - 日志文件保存在项目根目录的 `./logs/` 目录下 - 日志文件名格式:`cv_api_port_{端口号}.log` - 例如:使用端口8775启动的服务,日志文件为 `./logs/cv_api_port_8775.log` **查看日志的方法:** 1. **实时查看日志(推荐)**: ```bash tail -f ./logs/cv_api_port_8775.log ``` 2. **查看最近100行日志**: ```bash tail -n 100 ./logs/cv_api_port_8775.log ``` 3. **查看所有日志**: ```bash cat ./logs/cv_api_port_8775.log ``` 4. **搜索日志内容**: ```bash grep "错误" ./logs/cv_api_port_8775.log ``` **日志内容包含:** - 服务启动信息 - 模型加载过程(分类器/分割器) - API请求处理日志 - 错误信息和堆栈跟踪 - 所有运行时输出 **注意事项:** - 日志文件会自动创建,无需手动创建目录 - 日志文件会持续追加,不会覆盖历史记录 - 每个端口号对应一个独立的日志文件,方便区分不同服务实例 #### 其他API服务 ```bash # 启动其他API服务 cd api python main.py # 或者使用uvicorn启动 uvicorn main:app --host 0.0.0.0 --port 8789 ``` ### API接口使用 #### 分类器接口 ##### 单张图片分类 **接口**: `POST /api/v1/classify/single` **Content-Type**: `application/json` **请求**: ```json { "image": "base64编码的图片字符串" } ``` **响应**: ```json { "success": true, "classifier_name": "default", "class_id": 1, "class_name": "1", "confidence": 0.9763887524604797, "probabilities": [0.0007, 0.9764, 0.0051, 0.0002, 0.0176], "message": null, "color_mask_base64": "data:image/png;base64,iVBORw0KGgo..." } ``` **字段说明**: - 请求: `image` (string) - base64编码的图片数据 - 响应: `success` (boolean) - 分类是否成功 - 响应: `classifier_name` (string) - 分类器名称,默认为"default" - 响应: `class_id` (integer) - 预测的类别ID - 响应: `class_name` (string) - 预测的类别名称 - 响应: `confidence` (float) - 预测置信度(0-1之间) - 响应: `probabilities` (array) - 所有类别的概率分布数组 - 响应: `message` (string, optional) - 附加消息,可能为null - 响应: `color_mask_base64` (string, optional) - base64编码的标注图片(包含数据URI前缀),图片上绘制了分类结果(类别名称、ID和置信度),如果绘制失败可能为null **其他接口**: - `GET /health` - 健康检查 - `GET /config` - 获取配置信息 #### 分割器接口 ##### 单张图片分割 **接口**: `POST /api/v1/segment/single` **Content-Type**: `application/json` **请求**: ```json { "image": "base64编码的图片字符串" } ``` **响应**: ```json { "success": true, "segmentation_mask_base64": "data:image/png;base64,iVBORw0KGgo...", "color_mask_base64": "data:image/png;base64,iVBORw0KGgo...", "num_classes": 3, "image_shape": [512, 512], "class_names": ["背景", "类别1", "类别2"], "message": null } ``` **字段说明**: - 请求: `image` (string) - base64编码的图片数据 - 响应: `success` (boolean) - 分割是否成功 - 响应: `segmentation_mask_base64` (string, optional) - base64编码的分割掩码图像(包含数据URI前缀),灰度掩码图 - 响应: `color_mask_base64` (string, optional) - base64编码的彩色掩码图像(包含数据URI前缀),彩色掩码与原图叠加后的图像,如果生成失败可能为null - 响应: `num_classes` (integer, optional) - 分割类别数量 - 响应: `image_shape` (array, optional) - 图像尺寸 [宽, 高] - 响应: `class_names` (array, optional) - 类别名称列表 - 响应: `message` (string, optional) - 附加消息,可能为null - 响应: `class_names` (array) - 类别名称列表 - 响应: `message` (string, optional) - 附加消息 **其他接口**: - `GET /health` - 健康检查 - `GET /config` - 获取配置信息 ·········································································· # 其他接口 #### 1. 获取配置信息 ```bash curl "http://localhost:8789/config" ``` #### 2. 文件夹分析(POST接口) ```bash # 使用默认配置 curl -X POST "http://localhost:8789/api/v1/analyze/folder" \ -H "Content-Type: application/json" \ -d '{}' # 自定义参数 curl -X POST "http://localhost:8789/api/v1/analyze/folder" \ -H "Content-Type: application/json" \ -d '{ "input_folder": "/path/to/input", "output_folder": "/path/to/output", "target_classes": [1, 2], "device": "cpu" }' ``` #### 3. 文件夹分析(GET接口) ```bash # 使用默认配置 curl "http://localhost:8789/api/v1/analyze/folder/simple" # 自定义参数 curl "http://localhost:8789/api/v1/analyze/folder/simple?input_folder=/path/to/input&output_folder=/path/to/output&target_classes=1,2&device=cpu" ``` ### 默认配置说明 当没有通过接口传入参数时,系统会自动使用`config.py`中的默认配置值: - **模型路径**: `/Users/zhanghaining/git/SteelAiLab/out_models/FCN_HrNET_zhn_zz/fullmodel_iter_best.onnx` - **输入文件夹**: `/Volumes/data1/JH/projects/jinxiang/dataset_ZZ/train/img` - **输出文件夹**: `/Volumes/data1/JH/projects/jinxiang/dataset_ZZ/train/result` - **目标类别**: `[1, 2]` - **推理设备**: `cpu` 你可以通过修改`api/config.py`文件来更改这些默认值。 ### 结果文件说明 #### result.txt文件格式 分析完成后,会在输出文件夹中生成`result.txt`文件,包含以下信息: ``` 金相组织分析汇总报告 ================================================== 分析时间: 2024-01-01 12:00:00 模型路径: /path/to/model.onnx 目标类别: [1, 2] 总文件数: 10 整体统计: 黑边 (ID:1) 平均占比: 15.30% 模糊区域 (ID:2) 平均占比: 8.45% 详细结果: image001 - 黑边: 12.50% 模糊区域: 6.20% image002 - 黑边: 18.10% 模糊区域: 10.70% ... ``` **说明:** - 类别名称会自动从模型的class_names配置中获取 - 如果没有配置类别名称,会显示为"类别ID"格式 - 整体统计显示所有图片的平均占比 - 详细结果显示每张图片的类别占比 ### 基本使用 #### 1. 单张图片分类 ```python from devdeploy.inference.inference import create_inference # 创建推理器 infer = create_inference('model.onnx', device='cpu') # 单张图片推理 result = infer.infer_single('image.jpg') print(f"类别: {result['class_name']}, 置信度: {result['confidence']:.3f}") ``` #### 2. 滑窗模糊检测 ```python from devdeploy.src import SlidingWindowClassifier, get_optimal_device # 自动选择最优设备 device = get_optimal_device() # 创建滑窗分类器 classifier = SlidingWindowClassifier('model.onnx', device=device) # 单张图片检测(使用skimage.io.imread) from skimage import io import numpy as np image_array = io.imread('image.jpg') result = classifier.detect_blur_single(image_array, 'output.jpg') # 使用OpenCV读取图像(自动BGR转RGB) import cv2 image_bgr = cv2.imread('image.jpg') result = classifier.detect_blur_single(image_bgr, 'output.jpg') # 批量检测示例(外部遍历) import os from skimage import io input_folder = 'input_folder' output_folder = 'output_folder' os.makedirs(output_folder, exist_ok=True) # 遍历文件夹中的每张图片 for image_file in os.listdir(input_folder): if image_file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif')): input_path = os.path.join(input_folder, image_file) output_path = os.path.join(output_folder, f"result_{image_file}") # 读取图像并检测 image_array = io.imread(input_path) result = classifier.detect_blur_single(image_array, output_path) print(f"{image_file}: 检测到 {result['total_blur_regions']} 个模糊区域") ``` #### 3. 暗区检测 ```python from devdeploy.src import DarkDetector, get_optimal_device # 自动选择最优设备 device = get_optimal_device() # 创建暗区检测器 detector = DarkDetector('segmentation_model.onnx', device=device, dark_class_id=1, area_threshold=400000) # 单张图片检测 result = detector.detect_dark_single('image.jpg', 'output_with_dark_regions.jpg') print(f"检测到 {result['total_dark_regions']} 个暗区区域") print(f"是否有黑边: {result['has_dark_edge']}") # 使用OpenCV读取图像(自动BGR转RGB) import cv2 image_bgr = cv2.imread('image.jpg') result = detector.detect_dark_single(image_bgr, 'output_with_dark_regions.jpg') print(f"检测到 {result['total_dark_regions']} 个暗区区域") print(f"是否有黑边: {result['has_dark_edge']}") # 批量检测 results = detector.detect_dark_batch('input_folder/') for img_name, result in results.items(): if 'error' not in result: print(f"{img_name}: 检测到 {result['total_dark_regions']} 个暗区区域") print(f"是否有黑边: {result['has_dark_edge']}") ``` #### 4. 模糊检测 ```python from devdeploy.src import BlurDetector, get_optimal_device import cv2 # 自动选择最优设备 device = get_optimal_device() # 创建模糊检测器 detector = BlurDetector('segmentation_model.onnx', device=device, blur_class_id=1) # 方法1:使用图像路径 result = detector.detect_blur_single('image.jpg', 'blur_mask.png') print(f"模糊像素数量: {result['num_blur_pixels']}") print(f"模糊比例: {result['blur_ratio']:.3f}") # 方法2:使用OpenCV读取图像(自动BGR转RGB) image_bgr = cv2.imread('image.jpg') result = detector.detect_blur_single(image_bgr, 'blur_mask.png') print(f"模糊像素数量: {result['num_blur_pixels']}") print(f"模糊比例: {result['blur_ratio']:.3f}") # 方法3:使用单通道灰度图(自动转换为RGB) import numpy as np from PIL import Image image_gray = Image.open('image.jpg').convert('L') image_single = np.array(image_gray) result = detector.detect_blur_single(image_single, 'blur_mask.png') print(f"模糊像素数量: {result['num_blur_pixels']}") print(f"模糊比例: {result['blur_ratio']:.3f}") ``` ## API 文档 ### get_optimal_device() 自动获取最优设备,优先选择CUDA,如果不可用则使用CPU。 **返回值:** - `str`: 设备名称 ('cuda' 或 'cpu') **示例:** ```python from devdeploy.src import get_optimal_device device = get_optimal_device() print(f"使用设备: {device}") ``` ### MaskGenerator 图像Mask生成器,用于根据JSON标注文件生成对应的mask图像。 #### 初始化参数 - `label_order` (List[str]): 标签顺序列表,用于生成递增的mask值 - 背景默认为0,不需要传入 - 传入的标签从1开始递增(如:dark=1, light=2, edge=3, defect=4) - `label_merge_config` (Dict[str, int]): 标签合并配置,指定标签到值的映射 - 如果提供,将覆盖label_order的递增逻辑 - 支持多个标签映射到同一个值(如:dark=1, mohu=1, light=2) #### 主要方法 ##### `process_single_image(image_path, output_path)` 处理单张图像,生成对应的mask **参数:** - `image_path` (str): 输入图像路径 - `output_path` (str): 输出mask路径 **返回值:** - `bool`: 处理是否成功 ##### `process_directory(input_dir, output_dir)` 批量处理目录中的所有图像 **参数:** - `input_dir` (str): 输入图像目录 - `output_dir` (str): 输出mask目录 **返回值:** - `Dict[str, int]`: 处理统计信息 #### 支持的标注格式 1. **LabelMe格式**: 包含`shapes`字段,支持多边形、矩形、圆形 2. **COCO格式**: 包含`annotations`字段,支持分割多边形 3. **自定义格式**: 包含`objects`字段,支持多边形和边界框 4. **其他格式**: 直接处理标注数据 ### SlidingWindowClassifier 滑窗分类器是项目的主要对外接口,提供以下功能: #### 初始化参数 - `model_path` (str): 模型文件路径 - `device` (str): 推理设备,默认'cpu' - `window_size` (int): 滑窗大小,默认224 - `stride` (int): 滑窗步长,默认224 - `blur_class_id` (int): 模糊类别ID,默认0 - `confidence_threshold` (float): 置信度阈值,默认0.5 #### 主要方法 ##### `detect_blur_single(image, output_path=None)` 检测单张图片中的模糊区域 **参数:** - `image`: 图像数据,可以是图像路径(str)、PIL Image对象、numpy数组、OpenCV BGR格式数组或单通道灰度图 - `output_path` (str, optional): 输出图片路径 **返回值:** - `Dict[str, Any]`: 检测结果,包含模糊区域坐标和置信度 ##### `get_model_info()` 获取模型信息 **返回值:** - `Dict[str, Any]`: 模型信息 ##### `validate_model()` 验证模型是否有效 **返回值:** - `bool`: 模型是否有效 ### DarkDetector 暗区检测器是基于分割模型的暗区检测接口,提供以下功能: #### 初始化参数 - `model_path` (str): 分割模型文件路径 - `device` (str): 推理设备,默认'cpu' - `dark_class_id` (int): 暗区类别ID,默认1 - `confidence_threshold` (float): 置信度阈值,默认0.5 - `area_threshold` (int): 面积阈值,默认400000像素点,大于此值认为有黑边 #### 主要方法 ##### `detect_dark_single(image, output_path=None)` 检测单张图片中的暗区区域 **参数:** - `image`: 图像数据,可以是图像路径(str)、PIL Image对象、numpy数组、OpenCV BGR格式数组或单通道灰度图 - `output_path` (str, optional): 输出图片路径,如果提供则保存带标注的图片 **返回值:** - `Dict[str, Any]`: 检测结果,包含暗区区域信息和黑边判断结果 - `dark_regions`: 暗区区域列表(只包含最大连通域) - `total_dark_regions`: 暗区区域数量 - `has_dark_edge`: 是否有黑边(bool类型) - `segmentation_mask`: 分割掩码 - `num_classes`: 类别数量 - `image_shape`: 图像形状 ##### `detect_dark_batch(input_dir)` 批量检测目录中的图片暗区 **参数:** - `input_dir` (str): 输入图片目录 **返回值:** - `Dict[str, Any]`: 批量检测结果,每张图片包含暗区区域信息和黑边判断结果 ##### `get_model_info()` 获取模型信息 **返回值:** - `Dict[str, Any]`: 模型信息 ##### `validate_model()` 验证模型是否有效 **返回值:** - `bool`: 模型是否有效 ##### `get_task_type()` 获取任务类型 **返回值:** - `str`: 任务类型 ### BlurDetector 模糊检测器是基于分割模型的模糊检测接口,提供以下功能: #### 初始化参数 - `model_path` (str): 分割模型文件路径 - `device` (str): 推理设备,默认'cpu' - `blur_class_id` (int): 模糊类别ID,默认1 - `confidence_threshold` (float): 置信度阈值,默认0.5 #### 主要方法 ##### `detect_blur_single(image, output_path=None)` 检测单张图片中的模糊区域 **参数:** - `image`: 图像数据,可以是图像路径(str)、PIL Image对象、numpy数组、OpenCV BGR格式数组或单通道灰度图 - `output_path` (str, optional): 输出二值图路径,如果提供则保存二值图 **返回值:** - `Dict[str, Any]`: 检测结果,包含模糊区域二值图 - `blur_mask`: 1通道二值图,1表示模糊区域,0表示非模糊区域 - `original_size`: 原始图像尺寸 - `mask_size`: 二值图尺寸 - `num_blur_pixels`: 模糊像素数量 - `blur_ratio`: 模糊像素比例 - `segmentation_mask`: 分割掩码 - `num_classes`: 类别数量 - `image_shape`: 图像形状 ##### `get_model_info()` 获取模型信息 **返回值:** - `Dict[str, Any]`: 模型信息 ##### `validate_model()` 验证模型是否有效 **返回值:** - `bool`: 模型是否有效 ##### `get_task_type()` 获取任务类型 **返回值:** - `str`: 任务类型 ### SegmentationAnalyzer 分割模型分析器是基于分割模型的类别统计和分析接口,提供以下功能: #### 初始化参数 - `model_path` (str): 分割模型文件路径 - `device` (str): 推理设备,默认'cpu' - `class_names` (List[str], optional): 类别名称列表,如果为None则使用默认名称 #### 主要方法 ##### `analyze_single(image, output_path=None)` 分析单张图片的类别分布 **参数:** - `image`: 图像数据,可以是图像路径(str)、PIL Image对象或numpy数组 - `output_path` (str, optional): 输出分析结果图片路径,如果提供则保存分析结果图片 **返回值:** - `Dict[str, Any]`: 分析结果,包含各类别统计信息 - `class_ratios`: 各类别占比字典 - `class_masks`: 各类别二值图字典 - `class_names`: 各类别名称字典(新增) - `image_shape`: 图像形状 - `segmentation_mask`: 分割掩码 ##### `get_model_info()` 获取模型信息 **返回值:** - `Dict[str, Any]`: 模型信息 ##### `validate_model()` 验证模型是否有效 **返回值:** - `bool`: 模型是否有效 ##### `get_task_type()` 获取任务类型 **返回值:** - `str`: 任务类型 ### Classifier 分类器是基于分类模型的图像分类接口,提供以下功能: #### 初始化参数 - `model_path` (str): 分类模型文件路径 - `device` (str): 推理设备,默认'cpu' - `class_names` (List[str], optional): 类别名称列表,如果为None则自动从模型的YAML配置文件中获取 #### 主要方法 ##### `classify_single(image, output_path=None)` 分类单张图片 **参数:** - `image`: 图像数据,可以是图像路径(str)、PIL Image对象、numpy数组、OpenCV BGR格式数组或单通道灰度图 - `output_path` (str, optional): 输出图片路径,如果提供则保存带标注的图片 **返回值:** - `Dict[str, Any]`: 分类结果,包含类别ID、类别名称、置信度等信息 - `class_id`: 预测的类别ID - `class_name`: 预测的类别名称 - `confidence`: 预测置信度 - `probabilities`: 所有类别的概率分布 - `image_shape`: 图像形状 - `success`: 分类是否成功 ##### `classify_batch(input_dir)` 批量分类目录中的图片 **参数:** - `input_dir` (str): 输入图片目录 **返回值:** - `Dict[str, Any]`: 批量分类结果,每张图片包含分类结果信息 ##### `get_model_info()` 获取模型信息 **返回值:** - `Dict[str, Any]`: 模型信息 ##### `validate_model()` 验证模型是否有效 **返回值:** - `bool`: 模型是否有效 ##### `get_task_type()` 获取任务类型 **返回值:** - `str`: 任务类型 ## 使用示例 ### 完整示例:图像分类 ```python from devdeploy.src import Classifier, get_optimal_device # 自动选择最优设备 device = get_optimal_device() # 初始化分类器(不传入class_names,会自动从模型配置中获取) classifier = Classifier( model_path='path/to/your/classification_model.onnx', device=device ) # 单张图片分类示例 result = classifier.classify_single('test_image.jpg', 'classification_result.jpg') print(f"分类结果: {result['class_name']} (ID: {result['class_id']})") print(f"置信度: {result['confidence']:.3f}") print(f"概率分布: {result['probabilities']}") # 批量分类 results = classifier.classify_batch('input_images/') for img_name, result in results.items(): if result['success']: print(f"图片: {img_name}") print(f"分类结果: {result['class_name']} (ID: {result['class_id']})") print(f"置信度: {result['confidence']:.3f}") else: print(f"图片: {img_name}, 分类失败: {result['error']}") ``` ### 完整示例:文件夹批量检测 ```python from devdeploy.src import SlidingWindowClassifier, get_optimal_device # 自动选择最优设备 device = get_optimal_device() # 初始化分类器 classifier = SlidingWindowClassifier( model_path='path/to/your/model.onnx', device=device, window_size=224, stride=224, blur_class_id=0 ) # 单张图片检测示例 from skimage import io import numpy as np image_array = io.imread('test_image.jpg') result = classifier.detect_blur_single(image_array, 'result.jpg') print(f"检测到 {result['total_blur_regions']} 个模糊区域") # 批量检测(外部遍历方式) import os from skimage import io input_folder = 'input_images/' output_folder = 'output_results/' os.makedirs(output_folder, exist_ok=True) # 遍历每张图片并检测 for image_file in os.listdir(input_folder): if image_file.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif')): input_path = os.path.join(input_folder, image_file) output_path = os.path.join(output_folder, f"result_{image_file}") # 读取图像并检测 image_array = io.imread(input_path) result = classifier.detect_blur_single(image_array, output_path) print(f"图片: {image_file}") print(f"检测到 {len(result['blur_regions'])} 个模糊区域") for i, region in enumerate(result['blur_regions']): print(f" 区域{i+1}: 坐标{region['bbox']}, 置信度{region['confidence']:.3f}") ``` ### 完整示例:暗区检测 ```python from devdeploy.src import DarkDetector, get_optimal_device # 自动选择最优设备 device = get_optimal_device() # 初始化暗区检测器 detector = DarkDetector( model_path='path/to/your/segmentation_model.onnx', device=device, dark_class_id=1, confidence_threshold=0.5, area_threshold=400000 ) # 单张图片检测示例 result = detector.detect_dark_single('test_image.jpg', 'dark_detection_result.jpg') print(f"检测到 {result['total_dark_regions']} 个暗区区域") print(f"是否有黑边: {result['has_dark_edge']}") # 批量检测 results = detector.detect_dark_batch('input_images/') for img_name, result in results.items(): if 'error' not in result: print(f"图片: {img_name}") print(f"检测到 {result['total_dark_regions']} 个暗区区域") print(f"是否有黑边: {result['has_dark_edge']}") for i, region in enumerate(result['dark_regions']): print(f" 暗区{i+1}: 坐标{region['bbox']}, 面积{region['area']}, 中心{region['center']}") ``` ### 完整示例:模糊检测 ```python from devdeploy.src import BlurDetector, get_optimal_device # 自动选择最优设备 device = get_optimal_device() # 初始化模糊检测器 detector = BlurDetector( model_path='path/to/your/segmentation_model.onnx', device=device, blur_class_id=1, confidence_threshold=0.5 ) # 单张图片检测示例 result = detector.detect_blur_single('test_image.jpg', 'blur_mask.png') print(f"模糊像素数量: {result['num_blur_pixels']}") print(f"模糊比例: {result['blur_ratio']:.3f}") print(f"二值图尺寸: {result['mask_size']}") ``` ``` #### 5. 图像分类 ```python from devdeploy.src import Classifier, get_optimal_device import cv2 # 自动选择最优设备 device = get_optimal_device() # 创建分类器(不传入class_names,会自动从模型配置中获取) classifier = Classifier('classification_model.onnx', device=device) # 方法1:使用图像路径 result = classifier.classify_single('image.jpg', 'classification_result.jpg') print(f"分类结果: {result['class_name']} (ID: {result['class_id']})") print(f"置信度: {result['confidence']:.3f}") # 方法2:使用OpenCV读取图像(自动BGR转RGB) image_bgr = cv2.imread('image.jpg') result = classifier.classify_single(image_bgr, 'classification_result.jpg') print(f"分类结果: {result['class_name']} (ID: {result['class_id']})") print(f"置信度: {result['confidence']:.3f}") # 方法3:使用单通道灰度图(自动转换为RGB) import numpy as np from PIL import Image image_gray = Image.open('image.jpg').convert('L') image_single = np.array(image_gray) result = classifier.classify_single(image_single, 'classification_result.jpg') print(f"分类结果: {result['class_name']} (ID: {result['class_id']})") print(f"置信度: {result['confidence']:.3f}") # 批量分类 results = classifier.classify_batch('input_folder/') for img_name, result in results.items(): if result['success']: print(f"{img_name}: {result['class_name']} (置信度: {result['confidence']:.3f})") else: print(f"{img_name}: 分类失败 - {result['error']}") ``` #### 6. 分割模型分析 ```python from devdeploy.src import SegmentationAnalyzer, get_optimal_device # 自动选择最优设备 device = get_optimal_device() # 初始化分割模型分析器 analyzer = SegmentationAnalyzer( model_path='path/to/your/segmentation_model.onnx', device=device, class_names=['背景', '黑边', '模糊区域'] ) # 单张图片分析示例 result = analyzer.analyze_single('test_image.jpg', 'analysis_result.jpg') print(f"图像尺寸: {result['class_statistics']['image_size']}") print(f"总像素数: {result['class_statistics']['total_pixels']:,}") # 重点统计类别1和类别2 class_1_stats = result['class_statistics']['class_1_stats'] class_2_stats = result['class_statistics']['class_2_stats'] print(f"类别1 ({class_1_stats['class_name']}): {class_1_stats['pixel_count']:,} 像素 ({class_1_stats['percentage']:.2f}%)") print(f"类别2 ({class_2_stats['class_name']}): {class_2_stats['pixel_count']:,} 像素 ({class_2_stats['percentage']:.2f}%)") # 类别1+2合计统计 combined_stats = result['class_statistics']['class_1_2_combined'] print(f"类别1+2合计: {combined_stats['total_pixels']:,} 像素 ({combined_stats['total_percentage']:.2f}%)") ``` #### 7. 图像Mask生成 ```python from utils.mask_generator import MaskGenerator # 方法1:使用标签顺序(递增映射) label_order = ["dark", "light", "edge", "defect"] generator1 = MaskGenerator(label_order) # 方法2:使用标签合并配置(自定义映射) merge_config = { "dark": 1, "mohu": 1, # 与dark合并,都映射到1 "light": 2, "edge": 3 } generator2 = MaskGenerator(label_merge_config=merge_config) # 单张图像处理 generator2.process_single_image('input.jpg', 'output_mask.png') # 批量处理 stats = generator2.process_directory('input_folder/', 'output_folder/') print(f"处理完成: 成功{stats['success']}个,失败{stats['failed']}个") ``` #### 8. 命令行使用Mask生成工具 ```bash # 使用标签顺序(递增映射) python utils/mask_generator.py \ --input_dir "/Volumes/data1/JH/dataset/datadark/dark_json" \ --output_dir "/Volumes/data1/JH/dataset/datadark/dark_result" \ --label_order "dark,light,edge,defect" # 使用标签合并配置(自定义映射) python utils/mask_generator.py \ --input_dir "/Volumes/data1/JH/dataset/datadark/dark_json" \ --output_dir "/Volumes/data1/JH/dataset/datadark/dark_result" \ --label_merge_config "dark:1,mohu:1,light:2,edge:3" python utils/visualize_labels.py \ --img_dir "/Volumes/data1/JH/dataset/datadark/dark_result/img" \ --label_dir "/Volumes/data1/JH/dataset/datadark/dark_result/label" \ --output_dir "/Volumes/data1/JH/dataset/datadark/dark_result/visual" ``` ### 运行测试 ```bash # 运行所有测试 python run_tests.py # 或者单独运行各个测试 python test/test_sliding_window.py # 滑窗分类器功能测试 python test/test_dark_detector.py # 暗区检测器功能测试 python test/test_segmentation_analyzer.py # 分割模型分析器功能测试 # 运行Mask生成示例 python utils/example_mask_generation.py ``` ## 接口设计说明 ### 接口设计 项目采用了接口与实现分离的设计模式: 1. **接口层** (`src/JinhengLib.py`): 定义了`SlidingWindowClassifier`接口类 2. **实现层** (`src/imagequality/`): 包含具体的实现类`SlidingWindowClassifierImpl` 3. **对外暴露**: 用户只需要导入`from devdeploy.src import SlidingWindowClassifier` ### 优势 - **简洁性**: 用户只需要关心核心方法,不需要了解内部实现细节 - **可维护性**: 实现细节被封装,便于后续修改和优化 - **可扩展性**: 可以轻松添加新的实现类或接口方法 - **一致性**: 统一的接口设计,降低学习成本 ## 注意事项 1. **模型要求**: 确保您的分类模型能够处理224x224的输入图像 2. **类别定义**: 默认假设类别0为模糊类别,可通过`blur_class_id`参数修改 3. **内存使用**: 大图片的滑窗处理可能消耗较多内存,建议在GPU上运行 4. **输出格式**: 边界框坐标格式为[x1, y1, x2, y2],其中(x1,y1)为左上角,(x2,y2)为右下角 ## 更新日志 ### v1.5.0 (当前版本) - ✅ 重构分类器服务启动方式 - ✅ 支持通过命令行参数动态指定模型路径和端口号 - ✅ 新增 `start_classifier_service.sh` 启动脚本 - ✅ 修改 `main_classifier.py` 支持环境变量配置 - ✅ 支持同时启动多个不同模型的分类器服务 - ✅ 简化配置管理,支持多实例部署 - ✅ 使用 docker-compose 管理服务,避免复杂的 docker 命令 - ✅ 自动生成独立的 docker-compose 配置文件(按端口号) - ✅ 将 docker-compose.yml 改为模板文件 ### v1.4.0 - ✅ 新增图像分类器(Classifier)模块 - ✅ 实现基于分类模型的图像分类功能 - ✅ 支持单张图片和批量分类推理 - ✅ 返回类别ID、类别名称、置信度和概率分布 - ✅ 支持多种输入格式(路径、PIL Image、numpy数组、OpenCV BGR格式、单通道灰度图) - ✅ 自动处理OpenCV BGR到RGB的格式转换 - ✅ 自动处理单通道灰度图到RGB的格式转换 - ✅ 提供分类结果可视化和保存功能 - ✅ 完善分类器API文档和使用示例 - ✅ 添加分类器测试脚本 ### v1.3.1 - ✅ 优化SegmentationAnalyzerService的可视化性能 - ✅ 简化_create_visualization函数,只保存overlap图像 - ✅ 大幅减少图像生成时间,提高批量处理效率 - ✅ 保持核心功能不变,仅优化可视化输出 ### v1.3.0 - ✅ 新增分割模型分析器(SegmentationAnalyzer)模块 - ✅ 实现分割模型类别统计和分析功能 - ✅ 重点统计类别1和类别2的像素占比 - ✅ 支持单张图片和批量分析 - ✅ 提供详细的分析结果可视化 - ✅ 支持多种输入格式(路径、PIL Image、numpy数组、OpenCV BGR格式、单通道灰度图) - ✅ 自动处理OpenCV BGR到RGB的格式转换 - ✅ 自动处理单通道灰度图到RGB的格式转换 - ✅ 完善分割模型分析器API文档和使用示例 - ✅ 添加分割模型分析器测试脚本 ### v1.2.4 - ✅ 为所有推理类添加析构函数,正确释放模型资源 - ✅ TorchInference析构函数:释放PyTorch模型,清理GPU缓存 - ✅ OnnxInference析构函数:关闭ONNX Runtime会话 - ✅ BaseInference析构函数:提供通用的资源清理 - ✅ 防止析构函数中抛出异常,确保程序稳定性 - ✅ 优化内存管理,避免内存泄漏 ### v1.2.3 - ✅ 新增OpenCV BGR格式支持,自动处理BGR到RGB的格式转换 - ✅ 新增单通道灰度图支持,自动处理单通道到RGB的格式转换 - ✅ 在base_inference.py中增强_preprocess_image_common方法,支持numpy数组输入(单通道和3通道) - ✅ 在blur_detector.py中完善OpenCV数组处理逻辑,支持多种numpy数组格式 - ✅ 添加OpenCV格式支持测试用例,验证不同格式的兼容性 - ✅ 更新API文档,说明OpenCV BGR格式和单通道图像支持 - ✅ 提供OpenCV和单通道图像使用示例和格式转换说明 ### v1.2.2 - ✅ 支持3类别分割模型(0=背景,1=黑边,2=模糊区域) - ✅ 优化_extract_dark_regions函数,只处理标签为1的黑边区域 - ✅ 增强detect_dark_single函数,返回完整的模型推理结果 - ✅ 更新可视化功能,支持3类别彩色显示 - ✅ 添加彩色和灰度分割掩码对比显示 - ✅ 完善颜色图例和类别标签显示 ### v1.2.1 - ✅ 优化暗区检测可视化功能 - ✅ 支持多类别(0-10)固定颜色显示 - ✅ 彩色分割掩码显示,便于区分不同类别 - ✅ 添加颜色图例,提升可视化效果 - ✅ 循环使用颜色调色板,支持超过10个区域 ### v1.2.0 - ✅ 新增暗区检测模块(detectdark) - ✅ 基于分割模型实现暗区检测功能 - ✅ 支持单张图片和批量暗区检测 - ✅ 自动提取暗区区域和边界框 - ✅ 结果可视化和保存功能 - ✅ 完善暗区检测API文档和使用示例 ### v1.1.0 - ✅ 重构项目结构,实现接口与实现分离 - ✅ 创建通用接口类,只暴露核心方法 - ✅ 将SlidingWindowClassifier移动到imagequality模块 - ✅ 优化导入路径,简化用户使用 - ✅ 完善文档和测试 ### v1.0.0 - ✅ 实现基础推理接口 - ✅ 添加滑窗分类检测功能 - ✅ 支持模糊区域边界框绘制 - ✅ 提供批量处理能力 ## 贡献指南 欢迎提交Issue和Pull Request来改进这个项目。在提交代码前,请确保: 1. 代码符合PEP 8规范 2. 添加适当的注释和文档 3. 通过所有测试用例 4. 遵循接口设计原则 ## 许可证 本项目采用MIT许可证,详见LICENSE文件。