Adding isolate
This commit is contained in:
parent
ca9f74290c
commit
9c5587df03
|
@ -0,0 +1,22 @@
|
|||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'detection_model.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class DetectionModel {
|
||||
List<int> finalClasses;
|
||||
List<List<double>> finalBboxes;
|
||||
List<double> finalScores;
|
||||
|
||||
|
||||
DetectionModel({
|
||||
required this.finalClasses,
|
||||
required this.finalBboxes,
|
||||
required this.finalScores,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => _$DetectionModelToJson(this);
|
||||
|
||||
factory DetectionModel.fromJson(Map<String, dynamic> json) =>
|
||||
_$DetectionModelFromJson(json);
|
||||
}
|
|
@ -17,9 +17,9 @@ RecipeModel _$RecipeModelFromJson(Map<String, dynamic> json) => RecipeModel(
|
|||
instructions: (json['instructions'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList(),
|
||||
prepTime: json['prepTime'] as int?,
|
||||
cookTime: json['cookTime'] as int?,
|
||||
servings: json['servings'] as int?,
|
||||
prepTime: (json['prepTime'] as num?)?.toInt(),
|
||||
cookTime: (json['cookTime'] as num?)?.toInt(),
|
||||
servings: (json['servings'] as num?)?.toInt(),
|
||||
utensils: (json['utensils'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList(),
|
||||
|
|
|
@ -7,9 +7,9 @@ part of 'utensil_model.dart';
|
|||
// **************************************************************************
|
||||
|
||||
Utensil _$UtensilFromJson(Map<String, dynamic> json) => Utensil(
|
||||
id: json['id'] as int?,
|
||||
id: (json['id'] as num?)?.toInt(),
|
||||
name: json['name'] as String?,
|
||||
isSelected: json['isSelected'] as int?,
|
||||
isSelected: (json['isSelected'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UtensilToJson(Utensil instance) => <String, dynamic>{
|
||||
|
|
|
@ -13,7 +13,7 @@ BaseResponse<T> _$BaseResponseFromJson<T>(
|
|||
BaseResponse<T>(
|
||||
data: _$nullableGenericFromJson(json['data'], fromJsonT),
|
||||
)
|
||||
..code = json['code'] as int?
|
||||
..code = (json['code'] as num?)?.toInt()
|
||||
..message = json['message'] as String?;
|
||||
|
||||
Map<String, dynamic> _$BaseResponseToJson<T>(
|
||||
|
|
|
@ -65,9 +65,14 @@ class RecipeDetectionViewModel extends BaseViewModel {
|
|||
hideLoadingContainer();
|
||||
}
|
||||
|
||||
void _resetBounding(){
|
||||
classes.clear();
|
||||
bboxes.clear();
|
||||
scores.clear();
|
||||
}
|
||||
|
||||
Future<void> updatePostProcess() async {
|
||||
detectedIngredients.clear();
|
||||
print("MASUK 1");
|
||||
if (inferenceOutput.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
@ -76,8 +81,6 @@ class RecipeDetectionViewModel extends BaseViewModel {
|
|||
List<List<double>> newBboxes = [];
|
||||
List<double> newScores = [];
|
||||
|
||||
print("MASUK 2");
|
||||
|
||||
/// Wait this process with loading
|
||||
(newClasses, newBboxes, newScores) = await model.postprocess(
|
||||
inferenceOutput,
|
||||
|
@ -86,7 +89,6 @@ class RecipeDetectionViewModel extends BaseViewModel {
|
|||
confidenceThreshold: confidenceThreshold,
|
||||
iouThreshold: iouThreshold,
|
||||
);
|
||||
print("MASUK 3");
|
||||
|
||||
debugPrint('Detected ${newClasses} classes');
|
||||
debugPrint('Detected ${newBboxes.length} boxed');
|
||||
|
@ -132,17 +134,11 @@ class RecipeDetectionViewModel extends BaseViewModel {
|
|||
const CustomCameraWidget(compressionQuality: 80),
|
||||
),
|
||||
);
|
||||
print("MASUK 9");
|
||||
|
||||
|
||||
if (data != null) {
|
||||
print("MASUK 10");
|
||||
|
||||
imageFile.value = data;
|
||||
// _startTimer();
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
print("MASUK 11");
|
||||
|
||||
_resetBounding();
|
||||
_detectIngredients();
|
||||
}
|
||||
}
|
||||
|
@ -152,18 +148,14 @@ class RecipeDetectionViewModel extends BaseViewModel {
|
|||
}
|
||||
|
||||
void _detectIngredients() async {
|
||||
print("MASUK SINI 1");
|
||||
|
||||
// showLoadingDialog();
|
||||
|
||||
showLoadingDialog();
|
||||
final image = img.decodeImage(await imageFile.value!.readAsBytes())!;
|
||||
print("MASUK SINI 2");
|
||||
|
||||
imageHeight.value = image.height;
|
||||
imageWidth.value = image.width;
|
||||
inferenceOutput.value = model.infer(image);
|
||||
inferenceOutput.value = await model.inferenceImage(image);
|
||||
closeLoadingDialog();
|
||||
|
||||
print("MASUK SINI 3");
|
||||
updatePostProcess();
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:image/image.dart' as image_lib;
|
||||
import 'package:tflite_flutter/tflite_flutter.dart';
|
||||
|
||||
class IsolateInference {
|
||||
static const String _debugName = "TFLITE_INFERENCE";
|
||||
final ReceivePort _receivePort = ReceivePort();
|
||||
late Isolate _isolate;
|
||||
late SendPort _sendPort;
|
||||
|
||||
SendPort get sendPort => _sendPort;
|
||||
|
||||
Future<void> start() async {
|
||||
_isolate = await Isolate.spawn<SendPort>(entryPoint, _receivePort.sendPort,
|
||||
debugName: _debugName);
|
||||
_sendPort = await _receivePort.first;
|
||||
}
|
||||
|
||||
Future<void> close() async {
|
||||
_isolate.kill();
|
||||
_receivePort.close();
|
||||
}
|
||||
|
||||
static void entryPoint(SendPort sendPort) async {
|
||||
final port = ReceivePort();
|
||||
sendPort.send(port.sendPort);
|
||||
|
||||
await for (final InferenceModel isolateModel in port) {
|
||||
image_lib.Image? image;
|
||||
image = isolateModel.image;
|
||||
int inModelWidth = 640;
|
||||
int inModelHeight = 640;
|
||||
int numClasses = 6;
|
||||
|
||||
final imgResized = image_lib.copyResize(image!,
|
||||
width: inModelWidth, height: inModelHeight);
|
||||
final imgNormalized = List.generate(
|
||||
inModelHeight,
|
||||
(y) => List.generate(
|
||||
inModelWidth,
|
||||
(x) {
|
||||
final pixel = imgResized.getPixel(x, y);
|
||||
return [pixel.rNormalized, pixel.gNormalized, pixel.bNormalized];
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// output shape:
|
||||
// 1 : batch size
|
||||
// 4 + 6: left, top, right, bottom and probabilities for each class
|
||||
// 8400: num predictions
|
||||
final output = [
|
||||
List<List<double>>.filled(4 + numClasses, List<double>.filled(8400, 0))
|
||||
];
|
||||
int predictionTimeStart = DateTime.now().millisecondsSinceEpoch;
|
||||
Interpreter interpreter =
|
||||
Interpreter.fromAddress(isolateModel.interpreterAddress);
|
||||
|
||||
interpreter.run([imgNormalized], output);
|
||||
debugPrint(
|
||||
'Prediction time: ${DateTime.now().millisecondsSinceEpoch - predictionTimeStart} ms');
|
||||
|
||||
isolateModel.responsePort.send(output[0]);
|
||||
}
|
||||
}
|
||||
|
||||
static List<double> xywh2xyxy(List<double> bbox) {
|
||||
double halfWidth = bbox[2] / 2;
|
||||
double halfHeight = bbox[3] / 2;
|
||||
return [
|
||||
bbox[0] - halfWidth,
|
||||
bbox[1] - halfHeight,
|
||||
bbox[0] + halfWidth,
|
||||
bbox[1] + halfHeight,
|
||||
];
|
||||
}
|
||||
|
||||
/// Computes the intersection over union between two bounding boxes encoded with
|
||||
/// the xyxy format.
|
||||
static double computeIou(List<double> bbox1, List<double> bbox2) {
|
||||
assert(bbox1[0] < bbox1[2]);
|
||||
assert(bbox1[1] < bbox1[3]);
|
||||
assert(bbox2[0] < bbox2[2]);
|
||||
assert(bbox2[1] < bbox2[3]);
|
||||
|
||||
// Determine the coordinate of the intersection rectangle
|
||||
double xLeft = max(bbox1[0], bbox2[0]);
|
||||
double yTop = max(bbox1[1], bbox2[1]);
|
||||
double xRight = min(bbox1[2], bbox2[2]);
|
||||
double yBottom = min(bbox1[3], bbox2[3]);
|
||||
|
||||
if (xRight < xLeft || yBottom < yTop) {
|
||||
return 0;
|
||||
}
|
||||
double intersectionArea = (xRight - xLeft) * (yBottom - yTop);
|
||||
double bbox1Area = (bbox1[2] - bbox1[0]) * (bbox1[3] - bbox1[1]);
|
||||
double bbox2Area = (bbox2[2] - bbox2[0]) * (bbox2[3] - bbox2[1]);
|
||||
|
||||
double iou = intersectionArea / (bbox1Area + bbox2Area - intersectionArea);
|
||||
assert(iou >= 0 && iou <= 1);
|
||||
return iou;
|
||||
}
|
||||
}
|
||||
|
||||
class InferenceModel {
|
||||
image_lib.Image? image;
|
||||
int interpreterAddress;
|
||||
late SendPort responsePort;
|
||||
|
||||
InferenceModel(this.image, this.interpreterAddress);
|
||||
}
|
|
@ -74,6 +74,7 @@ import 'dart:math';
|
|||
sortedBestScores.removeAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
return (finalClasses, finalBboxes, finalScores);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import 'dart:isolate';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tflite_flutter/tflite_flutter.dart';
|
||||
import 'package:image/image.dart';
|
||||
|
||||
import 'isolate_inference.dart';
|
||||
import 'nms.dart';
|
||||
|
||||
class YoloModel {
|
||||
|
@ -10,6 +13,7 @@ class YoloModel {
|
|||
final int inHeight;
|
||||
final int numClasses;
|
||||
Interpreter? _interpreter;
|
||||
late final IsolateInference isolateInference;
|
||||
|
||||
YoloModel(
|
||||
this.modelPath,
|
||||
|
@ -20,11 +24,12 @@ class YoloModel {
|
|||
|
||||
Future<void> init() async {
|
||||
_interpreter = await Interpreter.fromAsset(modelPath);
|
||||
isolateInference = IsolateInference();
|
||||
await isolateInference.start();
|
||||
}
|
||||
|
||||
List<List<double>> infer(Image image) {
|
||||
assert(_interpreter != null, 'The model must be initialized');
|
||||
|
||||
final imgResized = copyResize(image, width: inWidth, height: inHeight);
|
||||
final imgNormalized = List.generate(
|
||||
inHeight,
|
||||
|
@ -51,6 +56,21 @@ class YoloModel {
|
|||
return output[0];
|
||||
}
|
||||
|
||||
Future<List<List<double>>> _inference(InferenceModel inferenceModel) async {
|
||||
ReceivePort responsePort = ReceivePort();
|
||||
isolateInference.sendPort
|
||||
.send(inferenceModel..responsePort = responsePort.sendPort);
|
||||
// get inference result.
|
||||
var results = await responsePort.first;
|
||||
return results;
|
||||
}
|
||||
|
||||
// inference still image
|
||||
Future<List<List<double>>> inferenceImage(Image image) async {
|
||||
var isolateModel = InferenceModel(image, _interpreter?.address ?? 0);
|
||||
return _inference(isolateModel);
|
||||
}
|
||||
|
||||
Future<(List<int>, List<List<double>>, List<double>)> postprocess(
|
||||
List<List<double>> unfilteredBboxes,
|
||||
int imageWidth,
|
||||
|
|
Loading…
Reference in New Issue