Skip to content

Commit 602e372

Browse files
committed
flutter segmentation scren done
1 parent 348a403 commit 602e372

File tree

1 file changed

+209
-1
lines changed

1 file changed

+209
-1
lines changed

example/lib/main.dart

Lines changed: 209 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'dart:async';
99
import 'package:flutter_vision/flutter_vision.dart';
1010
import 'package:image_picker/image_picker.dart';
1111

12-
enum Options { none, imagev5, imagev8, frame, tesseract, vision }
12+
enum Options { none, imagev5, imagev8, imagev8seg, frame, tesseract, vision }
1313

1414
late List<CameraDescription> cameras;
1515
main() async {
@@ -78,6 +78,18 @@ class _MyAppState extends State<MyApp> {
7878
});
7979
},
8080
),
81+
SpeedDialChild(
82+
child: const Icon(Icons.camera),
83+
backgroundColor: Colors.blue,
84+
foregroundColor: Colors.white,
85+
label: 'YoloV8seg on Image',
86+
labelStyle: const TextStyle(fontSize: 18.0),
87+
onTap: () {
88+
setState(() {
89+
option = Options.imagev8seg;
90+
});
91+
},
92+
),
8193
SpeedDialChild(
8294
child: const Icon(Icons.camera),
8395
backgroundColor: Colors.blue,
@@ -141,6 +153,9 @@ class _MyAppState extends State<MyApp> {
141153
if (option == Options.imagev8) {
142154
return YoloImageV8(vision: vision);
143155
}
156+
if (option == Options.imagev8seg) {
157+
return YoloImageV8Seg(vision: vision);
158+
}
144159
if (option == Options.tesseract) {
145160
return TesseractImage(vision: vision);
146161
}
@@ -622,6 +637,199 @@ class _YoloImageV8State extends State<YoloImageV8> {
622637
}
623638
}
624639

640+
class YoloImageV8Seg extends StatefulWidget {
641+
final FlutterVision vision;
642+
const YoloImageV8Seg({Key? key, required this.vision}) : super(key: key);
643+
644+
@override
645+
State<YoloImageV8Seg> createState() => _YoloImageV8SegState();
646+
}
647+
648+
class _YoloImageV8SegState extends State<YoloImageV8Seg> {
649+
late List<Map<String, dynamic>> yoloResults;
650+
File? imageFile;
651+
int imageHeight = 1;
652+
int imageWidth = 1;
653+
bool isLoaded = false;
654+
655+
@override
656+
void initState() {
657+
super.initState();
658+
loadYoloModel().then((value) {
659+
setState(() {
660+
yoloResults = [];
661+
isLoaded = true;
662+
});
663+
});
664+
}
665+
666+
@override
667+
void dispose() async {
668+
super.dispose();
669+
}
670+
671+
@override
672+
Widget build(BuildContext context) {
673+
final Size size = MediaQuery.of(context).size;
674+
if (!isLoaded) {
675+
return const Scaffold(
676+
body: Center(
677+
child: Text("Model not loaded, waiting for it"),
678+
),
679+
);
680+
}
681+
return Stack(
682+
fit: StackFit.expand,
683+
children: [
684+
imageFile != null ? Image.file(imageFile!) : const SizedBox(),
685+
Align(
686+
alignment: Alignment.bottomCenter,
687+
child: Row(
688+
mainAxisAlignment: MainAxisAlignment.center,
689+
children: [
690+
TextButton(
691+
onPressed: pickImage,
692+
child: const Text("Pick image"),
693+
),
694+
ElevatedButton(
695+
onPressed: yoloOnImage,
696+
child: const Text("Detect"),
697+
)
698+
],
699+
),
700+
),
701+
...displayBoxesAroundRecognizedObjects(size),
702+
],
703+
);
704+
}
705+
706+
Future<void> loadYoloModel() async {
707+
await widget.vision.loadYoloModel(
708+
labels: 'assets/labels.txt',
709+
modelPath: 'assets/yolov8n-seg.tflite',
710+
modelVersion: "yolov8seg",
711+
quantization: false,
712+
numThreads: 2,
713+
useGpu: true);
714+
setState(() {
715+
isLoaded = true;
716+
});
717+
}
718+
719+
Future<void> pickImage() async {
720+
final ImagePicker picker = ImagePicker();
721+
// Capture a photo
722+
final XFile? photo = await picker.pickImage(source: ImageSource.gallery);
723+
if (photo != null) {
724+
setState(() {
725+
imageFile = File(photo.path);
726+
});
727+
}
728+
}
729+
730+
yoloOnImage() async {
731+
yoloResults.clear();
732+
Uint8List byte = await imageFile!.readAsBytes();
733+
final image = await decodeImageFromList(byte);
734+
imageHeight = image.height;
735+
imageWidth = image.width;
736+
final result = await widget.vision.yoloOnImage(
737+
bytesList: byte,
738+
imageHeight: image.height,
739+
imageWidth: image.width,
740+
iouThreshold: 0.8,
741+
confThreshold: 0.4,
742+
classThreshold: 0.5);
743+
if (result.isNotEmpty) {
744+
setState(() {
745+
yoloResults = result;
746+
});
747+
}
748+
}
749+
750+
List<Widget> displayBoxesAroundRecognizedObjects(Size screen) {
751+
if (yoloResults.isEmpty) return [];
752+
753+
double factorX = screen.width / (imageWidth);
754+
double imgRatio = imageWidth / imageHeight;
755+
double newWidth = imageWidth * factorX;
756+
double newHeight = newWidth / imgRatio;
757+
double factorY = newHeight / (imageHeight);
758+
759+
double pady = (screen.height - newHeight) / 2;
760+
761+
Color colorPick = const Color.fromARGB(255, 50, 233, 30);
762+
return yoloResults.map((result) {
763+
return Stack(children: [
764+
Positioned(
765+
left: result["box"][0] * factorX,
766+
top: result["box"][1] * factorY + pady,
767+
width: (result["box"][2] - result["box"][0]) * factorX,
768+
height: (result["box"][3] - result["box"][1]) * factorY,
769+
child: Container(
770+
decoration: BoxDecoration(
771+
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
772+
border: Border.all(color: Colors.pink, width: 2.0),
773+
),
774+
child: Text(
775+
"${result['tag']} ${(result['box'][4] * 100).toStringAsFixed(0)}%",
776+
style: TextStyle(
777+
background: Paint()..color = colorPick,
778+
color: Colors.white,
779+
fontSize: 18.0,
780+
),
781+
),
782+
),
783+
),
784+
Positioned(
785+
left: result["box"][0] * factorX,
786+
top: result["box"][1] * factorY + pady,
787+
width: (result["box"][2] - result["box"][0]) * factorX,
788+
height: (result["box"][3] - result["box"][1]) * factorY,
789+
child: CustomPaint(
790+
painter: PolygonPainter(
791+
points: (result["polygons"] as List<dynamic>).map((e) {
792+
Map<String, double> xy = Map<String, double>.from(e);
793+
xy['x'] = (xy['x'] as double) * factorX;
794+
xy['y'] = (xy['y'] as double) * factorY;
795+
return xy;
796+
}).toList()),
797+
)),
798+
]);
799+
}).toList();
800+
}
801+
}
802+
803+
class PolygonPainter extends CustomPainter {
804+
final List<Map<String, double>> points;
805+
806+
PolygonPainter({required this.points});
807+
808+
@override
809+
void paint(Canvas canvas, Size size) {
810+
final paint = Paint()
811+
..color = const Color.fromARGB(129, 255, 2, 124)
812+
..strokeWidth = 2
813+
..style = PaintingStyle.fill;
814+
815+
final path = Path();
816+
if (points.isNotEmpty) {
817+
path.moveTo(points[0]['x']!, points[0]['y']!);
818+
for (var i = 1; i < points.length; i++) {
819+
path.lineTo(points[i]['x']!, points[i]['y']!);
820+
}
821+
path.close();
822+
}
823+
824+
canvas.drawPath(path, paint);
825+
}
826+
827+
@override
828+
bool shouldRepaint(CustomPainter oldDelegate) {
829+
return false;
830+
}
831+
}
832+
625833
class TesseractImage extends StatefulWidget {
626834
final FlutterVision vision;
627835
const TesseractImage({Key? key, required this.vision}) : super(key: key);

0 commit comments

Comments
 (0)