Files
sight-identification/lib/services/yolo_service.dart
2026-01-07 16:14:34 +08:00

148 lines
3.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:io';
import 'package:http/http.dart' as http;
import 'dart:convert';
class YOLOAnalysisResult {
final List<DetectedObject> objects;
final String? error;
YOLOAnalysisResult({
required this.objects,
this.error,
});
factory YOLOAnalysisResult.fromJson(Map<String, dynamic> json) {
if (json.containsKey('error')) {
return YOLOAnalysisResult(
objects: [],
error: json['error'],
);
}
List<DetectedObject> objects = [];
if (json.containsKey('objects') || json.containsKey('detections')) {
final detections = json['objects'] ?? json['detections'] ?? [];
objects = (detections as List)
.map((item) => DetectedObject.fromJson(item))
.toList();
}
return YOLOAnalysisResult(objects: objects);
}
}
class DetectedObject {
final String label;
final double confidence;
final BoundingBox bbox;
DetectedObject({
required this.label,
required this.confidence,
required this.bbox,
});
factory DetectedObject.fromJson(Map<String, dynamic> json) {
return DetectedObject(
label: json['label'] ?? json['class'] ?? json['name'] ?? 'Unknown',
confidence: (json['confidence'] ?? json['score'] ?? 0.0).toDouble(),
bbox: BoundingBox.fromJson(json['bbox'] ?? json['box'] ?? {}),
);
}
}
class BoundingBox {
final double x;
final double y;
final double width;
final double height;
BoundingBox({
required this.x,
required this.y,
required this.width,
required this.height,
});
factory BoundingBox.fromJson(Map<String, dynamic> json) {
return BoundingBox(
x: (json['x'] ?? json['x1'] ?? 0.0).toDouble(),
y: (json['y'] ?? json['y1'] ?? 0.0).toDouble(),
width: (json['width'] ?? (json['x2'] ?? 0.0) - (json['x1'] ?? 0.0)).toDouble(),
height: (json['height'] ?? (json['y2'] ?? 0.0) - (json['y1'] ?? 0.0)).toDouble(),
);
}
}
class YOLOService {
// 配置YOLO API端点用户可以修改
static const String yoloApiUrl = 'http://10.168.2.249:3000/api/upload';
// 分析单张图片
Future<YOLOAnalysisResult> analyzeImage(String imagePath) async {
try {
final file = File(imagePath);
if (!await file.exists()) {
return YOLOAnalysisResult(
objects: [],
error: '图片文件不存在',
);
}
// 创建multipart请求
var request = http.MultipartRequest('POST', Uri.parse(yoloApiUrl));
request.files.add(
await http.MultipartFile.fromPath('image', imagePath),
);
// 发送请求
var response = await request.send();
var responseBody = await response.stream.bytesToString();
if (response.statusCode == 200) {
final jsonData = json.decode(responseBody);
return YOLOAnalysisResult.fromJson(jsonData);
} else {
return YOLOAnalysisResult(
objects: [],
error: 'API请求失败: ${response.statusCode}',
);
}
} catch (e) {
// 如果API不可用返回模拟数据用于测试
return _getMockAnalysisResult();
}
}
// 分析多张图片
Future<Map<String, YOLOAnalysisResult>> analyzeImages(List<String> imagePaths) async {
Map<String, YOLOAnalysisResult> results = {};
for (String path in imagePaths) {
results[path] = await analyzeImage(path);
}
return results;
}
// 获取模拟分析结果用于测试当API不可用时
YOLOAnalysisResult _getMockAnalysisResult() {
return YOLOAnalysisResult(
objects: [
DetectedObject(
label: 'person',
confidence: 0.95,
bbox: BoundingBox(x: 100, y: 100, width: 200, height: 300),
),
DetectedObject(
label: 'chair',
confidence: 0.87,
bbox: BoundingBox(x: 300, y: 250, width: 150, height: 200),
),
],
error: '使用模拟数据YOLO API未配置或不可用',
);
}
}