fd1a5f4050
- Add YOLO-based infrared thermal image detection system - Add dataset configuration (dataset.yaml) with 12 equipment classes - Add training, validation and detection scripts - Add temperature feature extraction and fault diagnosis modules - Update README and CLAUDE.md with correct dataset paths - Optimize .gitignore to track dataset.yaml while ignoring large data files
204 lines
6.1 KiB
Python
204 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]) -> Dict:
|
|
"""
|
|
对图像进行故障诊断
|
|
|
|
Args:
|
|
image: 图像路径或数组
|
|
|
|
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)
|
|
result['detections'] = detections
|
|
|
|
for det in detections:
|
|
bbox = det['bbox']
|
|
x1, y1, x2, y2 = map(int, bbox)
|
|
|
|
if isinstance(image, str):
|
|
import cv2
|
|
img = cv2.imread(image, cv2.IMREAD_GRAYSCALE)
|
|
else:
|
|
img = image
|
|
|
|
roi = img[y1:y2, x1:x2]
|
|
temp_features = self.extractor.extract_from_roi(roi)
|
|
|
|
fault_status = self._check_fault(temp_features)
|
|
temp_features['fault_status'] = fault_status
|
|
temp_features['fault_type'] = self._classify_fault(temp_features)
|
|
|
|
result['faults'].append({
|
|
'bbox': bbox,
|
|
'confidence': det['confidence'],
|
|
**temp_features
|
|
})
|
|
else:
|
|
raise ValueError("Detector not initialized with model")
|
|
|
|
result['summary'] = self._generate_summary(result['faults'])
|
|
return result
|
|
|
|
def _check_fault(self, temp_features: Dict) -> str:
|
|
"""
|
|
根据温度特征判断是否有故障
|
|
|
|
Args:
|
|
temp_features: 温度特征
|
|
|
|
Returns:
|
|
'normal' 或 'abnormal'
|
|
"""
|
|
max_temp = temp_features.get('temp_max', 0)
|
|
mean_temp = temp_features.get('temp_mean', 0)
|
|
std_temp = temp_features.get('temp_std', 0)
|
|
|
|
max_threshold = self.temp_threshold.get('max_temp', 200)
|
|
std_threshold = self.temp_threshold.get('std_temp', 50)
|
|
|
|
if max_temp > max_threshold or std_temp > std_threshold:
|
|
return 'abnormal'
|
|
return 'normal'
|
|
|
|
def _classify_fault(self, temp_features: Dict) -> str:
|
|
"""
|
|
故障分类
|
|
|
|
Args:
|
|
temp_features: 温度特征
|
|
|
|
Returns:
|
|
故障类型
|
|
"""
|
|
max_temp = temp_features.get('temp_max', 0)
|
|
mean_temp = temp_features.get('temp_mean', 0)
|
|
std_temp = temp_features.get('temp_std', 0)
|
|
|
|
temp_range = max_temp - mean_temp
|
|
|
|
if std_temp > 30:
|
|
return 'hotspot'
|
|
elif temp_range > 50:
|
|
return 'local_overheating'
|
|
elif max_temp > 180:
|
|
return 'overheat'
|
|
else:
|
|
return 'suspect'
|
|
|
|
def _generate_summary(self, faults: List[Dict]) -> Dict:
|
|
"""
|
|
生成诊断摘要
|
|
|
|
Args:
|
|
faults: 故障列表
|
|
|
|
Returns:
|
|
摘要信息
|
|
"""
|
|
if not faults:
|
|
return {'status': 'no_detection', 'fault_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',
|
|
'fault_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]) -> None:
|
|
"""
|
|
批量诊断
|
|
|
|
Args:
|
|
image_dir: 图像目录
|
|
output_path: 输出JSON路径
|
|
"""
|
|
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))
|
|
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')
|
|
|
|
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)
|