Skip to content

Commit a19c14a

Browse files
committed
segmentation via yolov8 done
1 parent 602e372 commit a19c14a

File tree

7 files changed

+96
-48
lines changed

7 files changed

+96
-48
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.1.3
2+
* Release of segmentation feature via YOLOv8.
3+
* Updated example code.
4+
* Updated README.
15
## 1.1.2
26
* GPU delegation error has been fixed.
37
* Coordinate representation of the box in documentation was fixed.

README.md

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# flutter_vision
22

3-
A Flutter plugin for managing [Yolov5, Yolov8](https://github.com/ultralytics/ultralytics) and [Tesseract v5](https://tesseract-ocr.github.io/tessdoc/) accessing with TensorFlow Lite 2.x. Support object detection and OCR on Android. iOS not updated, working in progress.
3+
A Flutter plugin for managing [Yolov5, Yolov8](https://github.com/ultralytics/ultralytics) and [Tesseract v5](https://tesseract-ocr.github.io/tessdoc/) accessing with TensorFlow Lite 2.x. Support object detection, segmentation and OCR on Android. iOS not updated, working in progress.
44

55
# Installation
66
Add flutter_vision as a dependency in your pubspec.yaml file.
@@ -42,7 +42,7 @@ import 'package:flutter_vision/flutter_vision.dart';
4242
```
4343

4444
4. Load the model and labels:
45-
`modelVersion`: yolov5 or yolov8
45+
`modelVersion`: yolov5 or yolov8 or yolov8seg
4646
```dart
4747
await vision.loadYoloModel(
4848
labels: 'assets/labelss.txt',
@@ -68,7 +68,7 @@ final result = await vision.yoloOnFrame(
6868
```
6969

7070
### For static image
71-
5. Make your first detection:
71+
5. Make your first detection or segmentation:
7272

7373
```dart
7474
final result = await vision.yoloOnImage(
@@ -143,13 +143,24 @@ await vision.loadTesseractModel(
143143
await vision.closeTesseractModel();
144144
```
145145
# About results
146-
## For Yolo
146+
## For Yolo v5 or v8 in detection task
147+
result is a `List<Map<String,dynamic>>` where Map have the following keys:
148+
149+
``` dart
150+
Map<String, dynamic>:{
151+
"box": [x1:left, y1:top, x2:right, y2:bottom, class_confidence]
152+
"tag": String: detected class
153+
}
154+
```
155+
156+
## For YoloV8 in segmentation task
147157
result is a `List<Map<String,dynamic>>` where Map have the following keys:
148158

149159
``` dart
150160
Map<String, dynamic>:{
151161
"box": [x1:left, y1:top, x2:right, y2:bottom, class_confidence]
152162
"tag": String: detected class
163+
"polygons": List<Map<String, double>>: [{x:coordx, y:coordy}]
153164
}
154165
```
155166

@@ -165,5 +176,17 @@ result is a `List<Map<String,dynamic>>` where Map have the following keys:
165176

166177
# Example
167178
![Screenshot_2022-04-08-23-59-05-652_com vladih dni_scanner_example](https://user-images.githubusercontent.com/32783435/164163922-2eb7c8a3-8415-491f-883e-12cc87512efe.jpg)
168-
![Screenshot_2022-04-08-23-59-42-594_com vladih dni_scanner_example](https://user-images.githubusercontent.com/32783435/164163927-b290e46b-2af8-4b2b-a6a4-88cf4075f388.jpg)
169-
![Screenshot_2022-04-09-00-00-53-316_com vladih dni_scanner_example](https://user-images.githubusercontent.com/32783435/164163929-4b22310a-e6f6-4453-886b-7c7b622892de.jpg)
179+
<img src="https://github.com/vladiH/flutter_vision/assets/32783435/8fbbb9da-062e-4089-b1f6-1d4b2795664c" alt="Home" width="250" height="555">
180+
<img src="https://github.com/vladiH/flutter_vision/assets/32783435/51098356-8ed7-4c72-bce0-cf64d56976cd" alt="Detection" width="250" height="555">
181+
<img src="https://github.com/vladiH/flutter_vision/assets/32783435/0368ae2f-89ad-4a1a-a4c3-0548f0948421" alt="Segmentation" width="250" height="555">
182+
183+
184+
# <div align="center">Contact</div>
185+
186+
For flutter_vision bug reports and feature requests please visit [GitHub Issues](https://github.com/vladiH/flutter_vision/issues)
187+
188+
<br>
189+
<div align="center">
190+
<a href="https://linktr.ee/randpoint" style="text-decoration:none;">
191+
<img src="https://github.com/vladiH/flutter_vision/assets/32783435/75d2c677-65b5-4b9d-b332-d9d08602f024" width="3%" alt="" /></a>
192+
</div>

android/src/main/java/com/vladih/computer_vision/flutter_vision/models/Yolo.java

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.File;
2323
import java.io.FileInputStream;
2424
import java.io.InputStreamReader;
25+
import java.lang.reflect.Array;
2526
import java.nio.ByteBuffer;
2627
import java.nio.MappedByteBuffer;
2728
import java.nio.channels.FileChannel;
@@ -118,8 +119,8 @@ public void initialize_model() throws Exception {
118119
}
119120
this.interpreter.allocateTensors();
120121
this.labels = load_labels(asset_manager, label_path);
121-
int[] shape = interpreter.getOutputTensor(0).shape();
122-
this.output = new float[shape[0]][shape[1]][shape[2]];
122+
int[] shape = interpreter.getOutputTensor(0).shape();//3dimension
123+
this.output = (float [][][]) Array.newInstance(float.class, shape);
123124
} catch (Exception e) {
124125
throw e;
125126
} finally {
@@ -161,10 +162,11 @@ public List<Map<String, Object>> detect_task(ByteBuffer byteBuffer,
161162
float iou_threshold,
162163
float conf_threshold, float class_threshold) throws Exception {
163164
try {
164-
int[] shape = this.interpreter.getInputTensor(0).shape();
165+
int[] input_shape = this.interpreter.getInputTensor(0).shape();
165166
this.interpreter.run(byteBuffer, this.output);
166-
List<float[]> boxes = filter_box(this.output, iou_threshold, conf_threshold, class_threshold, shape[1], shape[2]);
167-
boxes = restore_size(boxes, shape[1], shape[2], source_width, source_height);
167+
List<float[]> boxes = filter_box(this.output, iou_threshold, conf_threshold,
168+
class_threshold, input_shape[1], input_shape[2]);
169+
boxes = restore_size(boxes, input_shape[1], input_shape[2], source_width, source_height);
168170
return out(boxes, this.labels);
169171
} catch (Exception e) {
170172
throw e;
@@ -183,6 +185,8 @@ protected List<float[]> filter_box(float[][][] model_outputs, float iou_threshol
183185
int dimension = model_outputs[0][0].length;
184186
int rows = model_outputs[0].length;
185187
float x1, y1, x2, y2, conf;
188+
int max_index = 0;
189+
float max = 0f;
186190
for (int i = 0; i < rows; i++) {
187191
//convert xywh to xyxy
188192
x1 = (model_outputs[0][i][0] - model_outputs[0][i][2] / 2f) * input_width;
@@ -191,16 +195,18 @@ protected List<float[]> filter_box(float[][][] model_outputs, float iou_threshol
191195
y2 = (model_outputs[0][i][1] + model_outputs[0][i][3] / 2f) * input_height;
192196
conf = model_outputs[0][i][conf_index];
193197
if (conf < conf_threshold) continue;
194-
float max = 0;
195-
int max_index = 0;
196-
for (int j = class_index; j < dimension; j++) {
197-
if (model_outputs[0][i][j] < class_threshold) continue;
198-
if (max < model_outputs[0][i][j]) {
199-
max = model_outputs[0][i][j];
198+
199+
max_index = class_index;
200+
max = model_outputs[0][i][max_index];
201+
202+
for (int j = class_index + 1; j < dimension; j++) {
203+
float current = model_outputs[0][i][j];
204+
if (current > max) {
205+
max = current;
200206
max_index = j;
201207
}
202208
}
203-
if (max > 0) {
209+
if (max > class_threshold){
204210
float[] tmp = new float[6];
205211
tmp[0] = x1;
206212
tmp[1] = y1;
@@ -213,7 +219,7 @@ protected List<float[]> filter_box(float[][][] model_outputs, float iou_threshol
213219
}
214220
if (pre_box.isEmpty()) return new ArrayList<>();
215221
//for reverse orden, insteand of using .reversed method
216-
Comparator<float[]> compareValues = (v1, v2) -> Float.compare(v1[1], v2[1]);
222+
Comparator<float[]> compareValues = (v1, v2) -> Float.compare(v2[4], v1[4]);
217223
//Collections.sort(pre_box,compareValues.reversed());
218224
Collections.sort(pre_box, compareValues);
219225
return nms(pre_box, iou_threshold);

android/src/main/java/com/vladih/computer_vision/flutter_vision/models/Yolov8.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ public List<Map<String, Object>> detect_task(ByteBuffer byteBuffer,
5959
float conf_threshold,
6060
float class_threshold) throws Exception {
6161
try {
62-
int[] shape = this.interpreter.getInputTensor(0).shape();
62+
int[] input_shape = this.interpreter.getInputTensor(0).shape();
6363
this.interpreter.run(byteBuffer, this.output);
6464
//INFO: output from detection model is not normalized
65-
List<float[]> boxes = filter_box(this.output, iou_threshold, conf_threshold, class_threshold, shape[1], shape[2]);
66-
boxes = restore_size(boxes, shape[1], shape[2], source_width, source_height);
65+
List<float[]> boxes = filter_box(this.output, iou_threshold, conf_threshold,
66+
class_threshold, input_shape[1], input_shape[2]);
67+
boxes = restore_size(boxes, input_shape[1], input_shape[2], source_width, source_height);
6768
return out(boxes, this.labels);
6869
} catch (Exception e) {
6970
throw e;
@@ -81,15 +82,16 @@ protected List<float[]> filter_box(float[][][] model_outputs, float iou_threshol
8182
int class_index = 4;
8283
int dimension = model_outputs[0][0].length;
8384
int rows = model_outputs[0].length;
84-
85+
int max_index = 0;
86+
float max = 0f;
8587
for (int i = 0; i < dimension; i++) {
8688
float x1 = (model_outputs[0][0][i] - model_outputs[0][2][i] / 2f);
8789
float y1 = (model_outputs[0][1][i] - model_outputs[0][3][i] / 2f);
8890
float x2 = (model_outputs[0][0][i] + model_outputs[0][2][i] / 2f);
8991
float y2 = (model_outputs[0][1][i] + model_outputs[0][3][i] / 2f);
9092

91-
int max_index = class_index;
92-
float max = model_outputs[0][max_index][i];
93+
max_index = class_index;
94+
max = model_outputs[0][max_index][i];
9395

9496
for (int j = class_index + 1; j < rows; j++) {
9597
float current = model_outputs[0][j][i];
@@ -112,7 +114,7 @@ protected List<float[]> filter_box(float[][][] model_outputs, float iou_threshol
112114
}
113115
if (pre_box.isEmpty()) return new ArrayList<>();
114116
//for reverse orden, insteand of using .reversed method
115-
Comparator<float[]> compareValues = (v1, v2) -> Float.compare(v1[1], v2[1]);
117+
Comparator<float[]> compareValues = (v1, v2) -> Float.compare(v2[4], v1[4]);
116118
//Collections.sort(pre_box,compareValues.reversed());
117119
Collections.sort(pre_box, compareValues);
118120
return nms(pre_box, iou_threshold);

android/src/main/java/com/vladih/computer_vision/flutter_vision/models/Yolov8Seg.java

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ public List<Map<String, Object>> detect_task(ByteBuffer byteBuffer,
9090
List<int[]> seg_boxes_mask = new ArrayList<>();
9191
for (float[] mask_weight : seg_boxes) {
9292
seg_boxes_mask.add(compute_mask(mask_weight,
93-
masks[0], (float) 0.5,
93+
masks[0], (float) 0.3,
9494
output1_shape[1], output1_shape[2]));
9595
}
9696
masks = null;
@@ -160,9 +160,9 @@ private List<List<Map<String, Double>>> restore_seg_mask_size(List<float[]> boxe
160160
min(mask_height, Math.max(boxes.get(i)[3] * mask_height / source_height, 0))
161161
);
162162
// utils.getScreenshotBmp(crop, tag+"1");
163-
List<List<Map<String, Double>>> crop_polygon = get_polygons_from_bitmap(crop, mask_height,
163+
List<Map<String, Double>> crop_polygon = get_polygons_from_bitmap(crop, mask_height,
164164
mask_width, source_height, source_width);
165-
polygons.add(crop_polygon.get(0));
165+
polygons.add(crop_polygon);
166166
}
167167
return polygons;
168168
} catch (Exception e) {
@@ -173,7 +173,7 @@ private List<List<Map<String, Double>>> restore_seg_mask_size(List<float[]> boxe
173173
}
174174
}
175175

176-
public static List<List<Map<String, Double>>> get_polygons_from_bitmap(Bitmap mask,
176+
public static List<Map<String, Double>> get_polygons_from_bitmap(Bitmap mask,
177177
int mask_height,
178178
int mask_width,
179179
int source_height,
@@ -182,27 +182,39 @@ public static List<List<Map<String, Double>>> get_polygons_from_bitmap(Bitmap ma
182182
List<MatOfPoint> contours = new ArrayList<>();
183183
Imgproc.findContours(maskMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
184184

185-
List<List<Point>> polygons = new ArrayList<>();
185+
MatOfPoint largestContour = null;
186+
double largestArea = 0;
187+
186188
for (MatOfPoint contour : contours) {
187-
List<Point> polygon = new ArrayList<>();
188-
for (Point point : contour.toList()) {
189-
polygon.add(point);
189+
double area = Imgproc.contourArea(contour);
190+
if (area > largestArea) {
191+
largestArea = area;
192+
largestContour = contour;
190193
}
191-
polygons.add(polygon);
192194
}
193-
List<List<Map<String, Double>>> converted_polygons = new ArrayList<>();
195+
List<Point> polygon = new ArrayList<>(largestContour.toList());
196+
// List<List<Point>> polygons = new ArrayList<>();
197+
// for (MatOfPoint contour : contours) {
198+
// List<Point> polygon = new ArrayList<>();
199+
// for (Point point : contour.toList()) {
200+
// polygon.add(point);
201+
// }
202+
// polygons.add(polygon);
203+
// }
204+
// List<List<Map<String, Double>>> converted_polygons = new ArrayList<>();
194205

195-
for (List<Point> polygon : polygons) {
206+
// for (List<Point> polygon : polygons) {
196207
List<Map<String, Double>> convertedPolygon = new ArrayList<>();
197208
for (Point point : polygon) {
198209
Map<String, Double> pointMap = new HashMap<>();
199210
pointMap.put("x", point.x * source_width / mask_width);
200211
pointMap.put("y", point.y * source_height / mask_height);
201212
convertedPolygon.add(pointMap);
202213
}
203-
converted_polygons.add(convertedPolygon);
204-
}
205-
return converted_polygons;
214+
// converted_polygons.add(convertedPolygon);
215+
// }
216+
// return converted_polygons;
217+
return convertedPolygon;
206218
}
207219

208220
private boolean has_multiple_output() {
@@ -221,16 +233,17 @@ protected List<float[]> filter_box(float[][][] model_outputs, float iou_threshol
221233
int rows = model_outputs[0].length;
222234
int index_mask = rows - 32;
223235
float[] mask_weight = new float[32];
224-
236+
int max_index = 0;
237+
float max = 0f;
225238
for (int i = 0; i < dimension; i++) {
226239
// Convertir xywh a xyxy y ajustar por el ancho y alto de entrada
227240
float x1 = (model_outputs[0][0][i] - model_outputs[0][2][i] / 2f) * input_width;
228241
float y1 = (model_outputs[0][1][i] - model_outputs[0][3][i] / 2f) * input_height;
229242
float x2 = (model_outputs[0][0][i] + model_outputs[0][2][i] / 2f) * input_width;
230243
float y2 = (model_outputs[0][1][i] + model_outputs[0][3][i] / 2f) * input_height;
231244

232-
int max_index = class_index;
233-
float max = model_outputs[0][max_index][i];
245+
max_index = class_index;
246+
max = model_outputs[0][max_index][i];
234247

235248
for (int j = class_index + 1; j < index_mask; j++) {
236249
float current = model_outputs[0][j][i];
@@ -256,7 +269,7 @@ protected List<float[]> filter_box(float[][][] model_outputs, float iou_threshol
256269
}
257270
if (pre_box.isEmpty()) return new ArrayList<>();
258271
//for reverse orden, insteand of using .reversed method
259-
Comparator<float[]> compareValues = (v1, v2) -> Float.compare(v1[1], v2[1]);
272+
Comparator<float[]> compareValues = (v1, v2) -> Float.compare(v2[4], v1[4]);
260273
//Collections.sort(pre_box,compareValues.reversed());
261274
Collections.sort(pre_box, compareValues);
262275
return nms(pre_box, iou_threshold);

example/lib/main.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class _YoloVideoState extends State<YoloVideo> {
186186

187187
init() async {
188188
cameras = await availableCameras();
189-
controller = CameraController(cameras[0], ResolutionPreset.low);
189+
controller = CameraController(cameras[0], ResolutionPreset.medium);
190190
controller.initialize().then((value) {
191191
loadYoloModel().then((value) {
192192
setState(() {
@@ -559,7 +559,7 @@ class _YoloImageV8State extends State<YoloImageV8> {
559559
Future<void> loadYoloModel() async {
560560
await widget.vision.loadYoloModel(
561561
labels: 'assets/labels.txt',
562-
modelPath: 'assets/yolov8n-seg.tflite',
562+
modelPath: 'assets/yolov8n.tflite',
563563
modelVersion: "yolov8",
564564
quantization: false,
565565
numThreads: 2,

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: flutter_vision
2-
description: A Flutter plugin for managing Yolov5, Yolov8 and Tesseract v5 accessing with TensorFlow Lite 2.x. Support object detection and OCR on Android. iOS not updated, working in progress.
2+
description: A Flutter plugin for managing Yolov5, Yolov8 and Tesseract v5 accessing with TensorFlow Lite 2.x. Support object detection, segmentation and OCR on Android. iOS not updated, working in progress.
33

4-
version: 1.1.2
4+
version: 1.1.3
55

66
homepage: https://github.com/vladiH/flutter_vision
77

0 commit comments

Comments
 (0)