feat: add device parameter support for GPU/CPU switching in inference API

- InfraredDetector.detect() 和 detect_and_draw() 新增 device 参数
- TempFeatureExtractor.detect_and_extract() 新增 device 参数
- FaultDiagnoser.diagnose() 和 batch_diagnose() 新增 device 参数
- batch_extract_features() 新增 device 参数
- 命令行 --device 选项支持指定推理设备
- detect_and_draw() 返回值改为 (image, n_detections) 避免重复检测
This commit is contained in:
SkyLi
2026-03-27 09:10:57 +08:00
parent 9a5f6fe473
commit febd9eea8d
3 changed files with 32 additions and 17 deletions
+12 -7
View File
@@ -34,13 +34,15 @@ class InfraredDetector:
print(f"Model loaded from: {model_path}") print(f"Model loaded from: {model_path}")
def detect(self, image: Union[str, np.ndarray], def detect(self, image: Union[str, np.ndarray],
conf: Optional[float] = None) -> List[Dict]: conf: Optional[float] = None,
device: Optional[str] = None) -> List[Dict]:
""" """
对图像进行目标检测 对图像进行目标检测
Args: Args:
image: 图像路径或图像数组 image: 图像路径或图像数组
conf: 置信度阈值(覆盖默认值) conf: 置信度阈值(覆盖默认值)
device: 运行设备,例如 '0' 表示GPU 0,'cpu' 表示CPU
Returns: Returns:
检测结果列表 检测结果列表
@@ -49,7 +51,7 @@ class InfraredDetector:
raise ValueError("Model not loaded. Please load a model first.") raise ValueError("Model not loaded. Please load a model first.")
threshold = conf if conf is not None else self.conf threshold = conf if conf is not None else self.conf
results = self.model(image, conf=threshold) results = self.model(image, conf=threshold, device=device)
detections = [] detections = []
for result in results: for result in results:
@@ -75,7 +77,8 @@ class InfraredDetector:
def detect_and_draw(self, image: Union[str, np.ndarray], def detect_and_draw(self, image: Union[str, np.ndarray],
output_path: Optional[str] = None, output_path: Optional[str] = None,
conf: Optional[float] = None) -> np.ndarray: conf: Optional[float] = None,
device: Optional[str] = None) -> np.ndarray:
""" """
检测并绘制结果 检测并绘制结果
@@ -83,6 +86,7 @@ class InfraredDetector:
image: 图像路径或图像数组 image: 图像路径或图像数组
output_path: 输出图像路径 output_path: 输出图像路径
conf: 置信度阈值 conf: 置信度阈值
device: 运行设备,例如 '0' 表示GPU 0,'cpu' 表示CPU
Returns: Returns:
绘制了检测结果的图像 绘制了检测结果的图像
@@ -92,7 +96,7 @@ class InfraredDetector:
else: else:
img = image.copy() img = image.copy()
detections = self.detect(img, conf) detections = self.detect(img, conf, device=device)
for det in detections: for det in detections:
x1, y1, x2, y2 = map(int, det['bbox']) x1, y1, x2, y2 = map(int, det['bbox'])
@@ -110,7 +114,7 @@ class InfraredDetector:
cv2.imwrite(output_path, img) cv2.imwrite(output_path, img)
print(f"Result saved to: {output_path}") print(f"Result saved to: {output_path}")
return img return img, len(detections)
def train_yolo_model(data_yaml: str, def train_yolo_model(data_yaml: str,
@@ -185,6 +189,7 @@ if __name__ == '__main__':
detect_parser.add_argument('--image', type=str, required=True, help='Image path') detect_parser.add_argument('--image', type=str, required=True, help='Image path')
detect_parser.add_argument('--output', type=str, help='Output image path') detect_parser.add_argument('--output', type=str, help='Output image path')
detect_parser.add_argument('--conf', type=float, default=0.25, help='Confidence threshold') detect_parser.add_argument('--conf', type=float, default=0.25, help='Confidence threshold')
detect_parser.add_argument('--device', type=str, default=None, help='Device (e.g., "0" for GPU, "cpu" for CPU)')
# Train command # Train command
train_parser = subparsers.add_parser('train', help='Train YOLO model') train_parser = subparsers.add_parser('train', help='Train YOLO model')
@@ -198,8 +203,8 @@ if __name__ == '__main__':
if args.command == 'detect': if args.command == 'detect':
detector = InfraredDetector(args.model, args.conf) detector = InfraredDetector(args.model, args.conf)
img = detector.detect_and_draw(args.image, args.output) img, n_detections = detector.detect_and_draw(args.image, args.output, device=args.device)
print(f"Detected {len(detector.detect(args.image))} objects") print(f"Detected {n_detections} objects")
elif args.command == 'train': elif args.command == 'train':
best_path = train_yolo_model( best_path = train_yolo_model(
+10 -5
View File
@@ -29,12 +29,14 @@ class FaultDiagnoser:
self.extractor = TempFeatureExtractor(model_path) self.extractor = TempFeatureExtractor(model_path)
self.temp_threshold = temp_threshold or {} self.temp_threshold = temp_threshold or {}
def diagnose(self, image: Union[str, np.ndarray]) -> Dict: def diagnose(self, image: Union[str, np.ndarray],
device: Optional[str] = None) -> Dict:
""" """
对图像进行故障诊断 对图像进行故障诊断
Args: Args:
image: 图像路径或数组 image: 图像路径或数组
device: 运行设备,例如 '0' 表示GPU 0,'cpu' 表示CPU
Returns: Returns:
诊断结果 诊断结果
@@ -47,7 +49,7 @@ class FaultDiagnoser:
} }
if self.detector and self.detector.model: if self.detector and self.detector.model:
detections = self.detector.detect(image) detections = self.detector.detect(image, device=device)
result['detections'] = detections result['detections'] = detections
import cv2 import cv2
@@ -148,13 +150,15 @@ class FaultDiagnoser:
} }
def batch_diagnose(self, image_dir: Union[str, Path], def batch_diagnose(self, image_dir: Union[str, Path],
output_path: Union[str, Path]) -> None: output_path: Union[str, Path],
device: Optional[str] = None) -> None:
""" """
批量诊断 批量诊断
Args: Args:
image_dir: 图像目录 image_dir: 图像目录
output_path: 输出JSON路径 output_path: 输出JSON路径
device: 运行设备,例如 '0' 表示GPU 0,'cpu' 表示CPU
""" """
image_dir = Path(image_dir) image_dir = Path(image_dir)
results = [] results = []
@@ -164,7 +168,7 @@ class FaultDiagnoser:
continue continue
try: try:
result = self.diagnose(str(img_path)) result = self.diagnose(str(img_path), device=device)
result['image'] = img_path.name result['image'] = img_path.name
results.append(result) results.append(result)
except Exception as e: except Exception as e:
@@ -192,6 +196,7 @@ if __name__ == '__main__':
parser.add_argument('--image-dir', type=str, required=True, help='Image directory') 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('--output', type=str, required=True, help='Output JSON path')
parser.add_argument('--threshold', type=str, help='Threshold config JSON') 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() args = parser.parse_args()
@@ -200,4 +205,4 @@ if __name__ == '__main__':
threshold = load_threshold_config(args.threshold) threshold = load_threshold_config(args.threshold)
diagnoser = FaultDiagnoser(args.model, threshold) diagnoser = FaultDiagnoser(args.model, threshold)
diagnoser.batch_diagnose(args.image_dir, args.output) diagnoser.batch_diagnose(args.image_dir, args.output, device=args.device)
+10 -5
View File
@@ -33,13 +33,15 @@ class TempFeatureExtractor:
self.model = YOLO(model_path) self.model = YOLO(model_path)
def detect_and_extract(self, image: np.ndarray, def detect_and_extract(self, image: np.ndarray,
conf: float = 0.25) -> List[Dict]: conf: float = 0.25,
device: Optional[str] = None) -> List[Dict]:
""" """
对图像进行检测并提取ROI温度特征 对图像进行检测并提取ROI温度特征
Args: Args:
image: 输入红外图像 image: 输入红外图像
conf: 检测置信度阈值 conf: 检测置信度阈值
device: 运行设备,例如 '0' 表示GPU 0,'cpu' 表示CPU
Returns: Returns:
检测结果列表,每个元素包含检测框和温度特征 检测结果列表,每个元素包含检测框和温度特征
@@ -47,7 +49,7 @@ class TempFeatureExtractor:
if self.model is None: if self.model is None:
raise ValueError("Model not loaded. Please provide a valid model path.") raise ValueError("Model not loaded. Please provide a valid model path.")
results = self.model(image, conf=conf) results = self.model(image, conf=conf, device=device)
detections = [] detections = []
for result in results: for result in results:
@@ -135,7 +137,8 @@ def load_infrared_image(image_path: str) -> np.ndarray:
def batch_extract_features(image_dir: Union[str, Path], def batch_extract_features(image_dir: Union[str, Path],
output_path: Union[str, Path], output_path: Union[str, Path],
model_path: Optional[str] = None, model_path: Optional[str] = None,
conf: float = 0.25) -> None: conf: float = 0.25,
device: Optional[str] = None) -> None:
""" """
批量处理图像并提取温度特征 批量处理图像并提取温度特征
@@ -144,6 +147,7 @@ def batch_extract_features(image_dir: Union[str, Path],
output_path: 输出JSON文件路径 output_path: 输出JSON文件路径
model_path: YOLO模型路径 model_path: YOLO模型路径
conf: 检测置信度阈值 conf: 检测置信度阈值
device: 运行设备,例如 '0' 表示GPU 0,'cpu' 表示CPU
""" """
image_dir = Path(image_dir) image_dir = Path(image_dir)
extractor = TempFeatureExtractor(model_path) extractor = TempFeatureExtractor(model_path)
@@ -155,7 +159,7 @@ def batch_extract_features(image_dir: Union[str, Path],
try: try:
image = load_infrared_image(str(img_path)) image = load_infrared_image(str(img_path))
detections = extractor.detect_and_extract(image, conf=conf) detections = extractor.detect_and_extract(image, conf=conf, device=device)
all_results.append({ all_results.append({
'image': img_path.name, 'image': img_path.name,
@@ -179,6 +183,7 @@ if __name__ == '__main__':
parser.add_argument('--output', type=str, required=True, help='Output JSON path') parser.add_argument('--output', type=str, required=True, help='Output JSON path')
parser.add_argument('--model', type=str, default=None, help='YOLO model path') parser.add_argument('--model', type=str, default=None, help='YOLO model path')
parser.add_argument('--conf', type=float, default=0.25, help='Detection confidence threshold') parser.add_argument('--conf', type=float, default=0.25, help='Detection confidence threshold')
parser.add_argument('--device', type=str, default=None, help='Device (e.g., "0" for GPU, "cpu" for CPU)')
args = parser.parse_args() args = parser.parse_args()
batch_extract_features(args.image_dir, args.output, args.model, args.conf) batch_extract_features(args.image_dir, args.output, args.model, args.conf, args.device)