3bba21d83b
一次调用返回(fault_status, fault_type)元组,简化诊断逻辑
190 lines
6.1 KiB
Python
190 lines
6.1 KiB
Python
"""
|
||
故障诊断模块
|
||
基于检测结果和温度特征进行故障诊断
|
||
"""
|
||
|
||
import json
|
||
from typing import Dict, List, Optional, Union
|
||
from pathlib import Path
|
||
import numpy as np
|
||
|
||
from .extract_temp import TempFeatureExtractor
|
||
from .detect import InfraredDetector
|
||
|
||
|
||
class FaultDiagnoser:
|
||
"""故障诊断器"""
|
||
|
||
def __init__(self,
|
||
model_path: Optional[str] = None,
|
||
temp_threshold: Optional[Dict] = None):
|
||
"""
|
||
初始化诊断器
|
||
|
||
Args:
|
||
model_path: YOLO模型路径
|
||
temp_threshold: 温度阈值配置
|
||
"""
|
||
self.detector = InfraredDetector(model_path) if model_path else None
|
||
self.extractor = TempFeatureExtractor(model_path)
|
||
self.temp_threshold = temp_threshold or {}
|
||
|
||
def diagnose(self, image: Union[str, np.ndarray],
|
||
device: Optional[str] = None) -> Dict:
|
||
"""
|
||
对图像进行故障诊断
|
||
|
||
Args:
|
||
image: 图像路径或数组
|
||
device: 运行设备,例如 '0' 表示GPU 0,'cpu' 表示CPU
|
||
|
||
Returns:
|
||
诊断结果
|
||
"""
|
||
result = {
|
||
'image': str(image) if isinstance(image, str) else 'array',
|
||
'detections': [],
|
||
'faults': [],
|
||
'summary': {}
|
||
}
|
||
|
||
if self.detector and self.detector.model:
|
||
detections = self.detector.detect(image, device=device)
|
||
result['detections'] = detections
|
||
|
||
import cv2
|
||
if isinstance(image, str):
|
||
img = cv2.imread(image, cv2.IMREAD_GRAYSCALE)
|
||
else:
|
||
img = image
|
||
|
||
for det in detections:
|
||
bbox = det['bbox']
|
||
x1, y1, x2, y2 = map(int, bbox)
|
||
|
||
roi = img[y1:y2, x1:x2]
|
||
temp_features = self.extractor.extract_from_roi(roi)
|
||
|
||
fault_status, fault_type = self._analyze_fault(temp_features)
|
||
temp_features['fault_status'] = fault_status
|
||
temp_features['fault_type'] = fault_type
|
||
|
||
result['faults'].append(temp_features)
|
||
else:
|
||
raise ValueError("Detector not initialized with model")
|
||
|
||
result['summary'] = self._generate_summary(result['faults'])
|
||
return result
|
||
|
||
def _analyze_fault(self, temp_features: Dict) -> tuple:
|
||
"""
|
||
分析温度特征,判断故障状态和类型
|
||
|
||
Args:
|
||
temp_features: 温度特征
|
||
|
||
Returns:
|
||
(fault_status, fault_type) 元组
|
||
"""
|
||
max_temp = temp_features.get('temp_max', 0)
|
||
std_temp = temp_features.get('temp_std', 0)
|
||
temp_range = max_temp - temp_features.get('temp_mean', 0)
|
||
|
||
max_threshold = self.temp_threshold.get('max_temp', 200)
|
||
std_threshold = self.temp_threshold.get('std_temp', 50)
|
||
|
||
# 判断状态:两项都不超标才为 normal
|
||
if max_temp <= max_threshold and std_temp <= std_threshold:
|
||
return ('normal', 'none')
|
||
|
||
# 分类故障类型
|
||
if std_temp > 30:
|
||
return ('abnormal', 'hotspot')
|
||
elif temp_range > 50:
|
||
return ('abnormal', 'local_overheating')
|
||
elif max_temp > 180:
|
||
return ('abnormal', 'overheat')
|
||
else:
|
||
return ('abnormal', 'suspect')
|
||
|
||
def _generate_summary(self, faults: List[Dict]) -> Dict:
|
||
"""
|
||
生成诊断摘要
|
||
|
||
Args:
|
||
faults: 故障列表
|
||
|
||
Returns:
|
||
摘要信息
|
||
"""
|
||
if not faults:
|
||
return {'status': 'no_detection', 'detection_count': 0}
|
||
|
||
abnormal_count = sum(1 for f in faults if f.get('fault_status') == 'abnormal')
|
||
fault_types = [f.get('fault_type', 'unknown') for f in faults]
|
||
|
||
return {
|
||
'status': 'fault_detected' if abnormal_count > 0 else 'normal',
|
||
'detection_count': len(faults),
|
||
'abnormal_count': abnormal_count,
|
||
'fault_types': list(set(fault_types))
|
||
}
|
||
|
||
def batch_diagnose(self, image_dir: Union[str, Path],
|
||
output_path: Union[str, Path],
|
||
device: Optional[str] = None) -> None:
|
||
"""
|
||
批量诊断
|
||
|
||
Args:
|
||
image_dir: 图像目录
|
||
output_path: 输出JSON路径
|
||
device: 运行设备,例如 '0' 表示GPU 0,'cpu' 表示CPU
|
||
"""
|
||
image_dir = Path(image_dir)
|
||
results = []
|
||
|
||
for img_path in sorted(image_dir.glob('*')):
|
||
if img_path.suffix.lower() not in ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']:
|
||
continue
|
||
|
||
try:
|
||
result = self.diagnose(str(img_path), device=device)
|
||
result['image'] = img_path.name
|
||
results.append(result)
|
||
except Exception as e:
|
||
print(f"Error processing {img_path.name}: {e}")
|
||
continue
|
||
|
||
with open(output_path, 'w', encoding='utf-8') as f:
|
||
json.dump(results, f, indent=2, ensure_ascii=False)
|
||
|
||
print(f"Batch diagnosis complete. {len(results)} images processed.")
|
||
print(f"Results saved to: {output_path}")
|
||
|
||
|
||
def load_threshold_config(config_path: str) -> Dict:
|
||
"""加载温度阈值配置"""
|
||
with open(config_path, 'r', encoding='utf-8') as f:
|
||
return json.load(f)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
import argparse
|
||
|
||
parser = argparse.ArgumentParser(description='Fault Diagnosis')
|
||
parser.add_argument('--model', type=str, required=True, help='YOLO model path')
|
||
parser.add_argument('--image-dir', type=str, required=True, help='Image directory')
|
||
parser.add_argument('--output', type=str, required=True, help='Output JSON path')
|
||
parser.add_argument('--threshold', type=str, help='Threshold config JSON')
|
||
parser.add_argument('--device', type=str, default=None, help='Device (e.g., "0" for GPU, "cpu" for CPU)')
|
||
|
||
args = parser.parse_args()
|
||
|
||
threshold = None
|
||
if args.threshold:
|
||
threshold = load_threshold_config(args.threshold)
|
||
|
||
diagnoser = FaultDiagnoser(args.model, threshold)
|
||
diagnoser.batch_diagnose(args.image_dir, args.output, device=args.device)
|