212 lines
5.2 KiB
Dart
212 lines
5.2 KiB
Dart
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
import 'dart:ui';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'dart:async';
|
|
import 'package:flutter_vision/flutter_vision.dart';
|
|
import 'package:image_picker/image_picker.dart';
|
|
|
|
enum Options { none, imagev8 }
|
|
|
|
void main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
runApp(const MaterialApp(
|
|
home: MyApp(),
|
|
debugShowCheckedModeBanner: false,
|
|
));
|
|
}
|
|
|
|
class MyApp extends StatefulWidget {
|
|
const MyApp({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<MyApp> createState() => _MyAppState();
|
|
}
|
|
|
|
class _MyAppState extends State<MyApp> {
|
|
late FlutterVision vision;
|
|
Options option = Options.none;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
vision = FlutterVision();
|
|
}
|
|
|
|
@override
|
|
void dispose() async {
|
|
super.dispose();
|
|
await vision.closeYoloModel();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: task(option),
|
|
);
|
|
}
|
|
|
|
Widget task(Options option) {
|
|
return YoloImageV8(vision: vision);
|
|
|
|
}
|
|
}
|
|
|
|
class YoloImageV8 extends StatefulWidget {
|
|
final FlutterVision vision;
|
|
const YoloImageV8({Key? key, required this.vision}) : super(key: key);
|
|
|
|
@override
|
|
State<YoloImageV8> createState() => _YoloImageV8State();
|
|
}
|
|
|
|
class _YoloImageV8State extends State<YoloImageV8> {
|
|
late List<Map<String, dynamic>> yoloResults;
|
|
File? imageFile;
|
|
int imageHeight = 1;
|
|
int imageWidth = 1;
|
|
bool isLoaded = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
loadYoloModel().then((value) {
|
|
setState(() {
|
|
yoloResults = [];
|
|
isLoaded = true;
|
|
});
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() async {
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final Size size = MediaQuery.of(context).size;
|
|
if (!isLoaded) {
|
|
return const Scaffold(
|
|
body: Center(
|
|
child: Text("Model not loaded, waiting for it"),
|
|
),
|
|
);
|
|
}
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text("Yolo V8"),
|
|
),
|
|
body: Column(
|
|
children: [
|
|
Expanded(child: imageFile != null ? Image.file(imageFile!) : const SizedBox()),
|
|
Stack(
|
|
fit: StackFit.expand,
|
|
children: [
|
|
Align(
|
|
alignment: Alignment.bottomCenter,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
TextButton(
|
|
onPressed: pickImage,
|
|
child: const Text("ambil gmbr serah"),
|
|
),
|
|
// ElevatedButton(
|
|
// onPressed: yoloOnImage,
|
|
// child: const Text("Ya test"),
|
|
// )
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
...displayBoxesAroundRecognizedObjects(size),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> loadYoloModel() async {
|
|
await widget.vision.loadYoloModel(
|
|
labels: 'assets/labels.txt',
|
|
modelPath: 'assets/yolov8m_float16.tflite',
|
|
modelVersion: "yolov8",
|
|
quantization: false,
|
|
numThreads: 2,
|
|
useGpu: true,
|
|
);
|
|
setState(() {
|
|
isLoaded = true;
|
|
});
|
|
}
|
|
|
|
Future<void> pickImage() async {
|
|
final ImagePicker picker = ImagePicker();
|
|
final XFile? photo = await picker.pickImage(source: ImageSource.gallery);
|
|
if (photo != null) {
|
|
setState(() {
|
|
imageFile = File(photo.path);
|
|
yoloOnImage();
|
|
});
|
|
}
|
|
}
|
|
|
|
yoloOnImage() async {
|
|
yoloResults.clear();
|
|
Uint8List byte = await imageFile!.readAsBytes();
|
|
final image = await decodeImageFromList(byte);
|
|
imageHeight = image.height;
|
|
imageWidth = image.width;
|
|
final result = await widget.vision.yoloOnImage(
|
|
bytesList: byte,
|
|
imageHeight: image.height,
|
|
imageWidth: image.width,
|
|
iouThreshold: 0.8,
|
|
confThreshold: 0.2,
|
|
classThreshold: 0.3,
|
|
);
|
|
if (result.isNotEmpty) {
|
|
setState(() {
|
|
yoloResults = result;
|
|
});
|
|
}
|
|
}
|
|
|
|
List<Widget> displayBoxesAroundRecognizedObjects(Size screen) {
|
|
if (yoloResults.isEmpty) return [];
|
|
|
|
double factorX = screen.width / (imageWidth);
|
|
double imgRatio = imageWidth / imageHeight;
|
|
double newWidth = imageWidth * factorX;
|
|
double newHeight = newWidth / imgRatio;
|
|
double factorY = newHeight / (imageHeight);
|
|
|
|
double pady = (screen.height - newHeight) / 2;
|
|
|
|
Color colorPick = const Color.fromARGB(255, 50, 233, 30);
|
|
return yoloResults.map((result) {
|
|
return Positioned(
|
|
left: result["box"][0] * factorX,
|
|
top: result["box"][1] * factorY + pady,
|
|
width: (result["box"][2] - result["box"][0]) * factorX,
|
|
height: (result["box"][3] - result["box"][1]) * factorY,
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
|
|
border: Border.all(color: Colors.pink, width: 2.0),
|
|
),
|
|
child: Text(
|
|
"${result['tag']} ${(result['box'][4] * 100).toStringAsFixed(0)}%",
|
|
style: TextStyle(
|
|
background: Paint()..color = colorPick,
|
|
color: Colors.white,
|
|
fontSize: 12.0,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}).toList();
|
|
}
|
|
} |