148 lines
3.9 KiB
Dart
148 lines
3.9 KiB
Dart
|
|
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未配置或不可用)',
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|