Skip to content

Commit 9adbec0

Browse files
authored
Merge pull request #25 from vladiH/segmentation
Segmentation feature
2 parents c7ef5b7 + a19c14a commit 9adbec0

File tree

10 files changed

+648
-58
lines changed

10 files changed

+648
-58
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/FlutterVisionPlugin.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.vladih.computer_vision.flutter_vision.models.Yolo;
1212
import com.vladih.computer_vision.flutter_vision.models.Yolov8;
1313
import com.vladih.computer_vision.flutter_vision.models.Yolov5;
14+
import com.vladih.computer_vision.flutter_vision.models.Yolov8Seg;
1415
import com.vladih.computer_vision.flutter_vision.utils.utils;
1516

1617
import org.opencv.android.OpenCVLoader;
@@ -204,8 +205,21 @@ private void load_yolo_model(Map<String, Object> args) throws Exception {
204205
rotation);
205206
break;
206207
}
208+
209+
case "yolov8seg": {
210+
yolo_model = new Yolov8Seg(
211+
context,
212+
model,
213+
is_asset,
214+
num_threads,
215+
quantization,
216+
use_gpu,
217+
label_path,
218+
rotation);
219+
break;
220+
}
207221
default: {
208-
throw new Exception("Model version must be yolov5 or yolov8");
222+
throw new Exception("Model version must be yolov5, yolov8 or yolov8seg");
209223
}
210224
}
211225
yolo_model.initialize_model();

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: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@
55
import android.content.Context;
66
import android.content.res.AssetFileDescriptor;
77
import android.content.res.AssetManager;
8+
import android.graphics.Bitmap;
9+
import android.graphics.Color;
810
import android.util.Log;
911

12+
import com.vladih.computer_vision.flutter_vision.utils.utils;
13+
1014
import org.opencv.core.Core;
1115
import org.opencv.core.CvType;
1216
import org.opencv.core.Mat;
17+
import org.opencv.core.MatOfPoint;
18+
import org.opencv.core.Point;
1319
import org.opencv.imgproc.Imgproc;
1420
import org.tensorflow.lite.Interpreter;
1521
import org.tensorflow.lite.gpu.CompatibilityList;
@@ -20,6 +26,7 @@
2026

2127
import java.io.File;
2228
import java.io.FileInputStream;
29+
import java.lang.reflect.Array;
2330
import java.nio.ByteBuffer;
2431
import java.nio.MappedByteBuffer;
2532
import java.nio.channels.FileChannel;
@@ -29,9 +36,10 @@
2936
import java.util.HashMap;
3037
import java.util.List;
3138
import java.util.Map;
39+
import java.util.UUID;
3240
import java.util.Vector;
3341

34-
public class Yolov8 extends Yolo{
42+
public class Yolov8 extends Yolo {
3543
public Yolov8(Context context,
3644
String model_path,
3745
boolean is_assets,
@@ -44,63 +52,89 @@ public Yolov8(Context context,
4452
}
4553

4654
@Override
47-
protected List<float[]>filter_box(float [][][] model_outputs, float iou_threshold,
48-
float conf_threshold, float class_threshold, float input_width, float input_height){
55+
public List<Map<String, Object>> detect_task(ByteBuffer byteBuffer,
56+
int source_height,
57+
int source_width,
58+
float iou_threshold,
59+
float conf_threshold,
60+
float class_threshold) throws Exception {
61+
try {
62+
int[] input_shape = this.interpreter.getInputTensor(0).shape();
63+
this.interpreter.run(byteBuffer, this.output);
64+
//INFO: output from detection model is not normalized
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);
68+
return out(boxes, this.labels);
69+
} catch (Exception e) {
70+
throw e;
71+
} finally {
72+
byteBuffer.clear();
73+
}
74+
}
75+
76+
@Override
77+
protected List<float[]> filter_box(float[][][] model_outputs, float iou_threshold,
78+
float conf_threshold, float class_threshold, float input_width, float input_height) {
4979
try {
5080
//model_outputs = [1,box+class,detected_box]
5181
List<float[]> pre_box = new ArrayList<>();
5282
int class_index = 4;
5383
int dimension = model_outputs[0][0].length;
5484
int rows = model_outputs[0].length;
55-
float x1,y1,x2,y2;
56-
for(int i=0; i<dimension;i++){
57-
//convert xywh to xyxy
58-
x1 = (model_outputs[0][0][i]-model_outputs[0][2][i]/2f);
59-
y1 = (model_outputs[0][1][i]-model_outputs[0][3][i]/2f);
60-
x2 = (model_outputs[0][0][i]+model_outputs[0][2][i]/2f);
61-
y2 = (model_outputs[0][1][i]+model_outputs[0][3][i]/2f);
62-
float max = 0;
63-
int max_index = 0;
64-
for(int j=class_index;j<rows;j++){
65-
if (model_outputs[0][j][i]<class_threshold) continue;
66-
if (max<model_outputs[0][j][i]){
67-
max = model_outputs[0][j][i];
85+
int max_index = 0;
86+
float max = 0f;
87+
for (int i = 0; i < dimension; i++) {
88+
float x1 = (model_outputs[0][0][i] - model_outputs[0][2][i] / 2f);
89+
float y1 = (model_outputs[0][1][i] - model_outputs[0][3][i] / 2f);
90+
float x2 = (model_outputs[0][0][i] + model_outputs[0][2][i] / 2f);
91+
float y2 = (model_outputs[0][1][i] + model_outputs[0][3][i] / 2f);
92+
93+
max_index = class_index;
94+
max = model_outputs[0][max_index][i];
95+
96+
for (int j = class_index + 1; j < rows; j++) {
97+
float current = model_outputs[0][j][i];
98+
if (current > max) {
99+
max = current;
68100
max_index = j;
69101
}
70102
}
71-
if (max>0){
103+
104+
if (max > class_threshold) {
72105
float[] tmp = new float[6];
73-
tmp[0]=x1;
74-
tmp[1]=y1;
75-
tmp[2]=x2;
76-
tmp[3]=y2;
77-
tmp[4]=model_outputs[0][max_index][i];
78-
tmp[5]=(max_index-class_index)*1f;
106+
tmp[0] = x1;
107+
tmp[1] = y1;
108+
tmp[2] = x2;
109+
tmp[3] = y2;
110+
tmp[4] = max;
111+
tmp[5] = (max_index - class_index) * 1f;
79112
pre_box.add(tmp);
80113
}
81114
}
82115
if (pre_box.isEmpty()) return new ArrayList<>();
83116
//for reverse orden, insteand of using .reversed method
84-
Comparator<float []> compareValues = (v1, v2)->Float.compare(v1[1],v2[1]);
117+
Comparator<float[]> compareValues = (v1, v2) -> Float.compare(v2[4], v1[4]);
85118
//Collections.sort(pre_box,compareValues.reversed());
86-
Collections.sort(pre_box,compareValues);
119+
Collections.sort(pre_box, compareValues);
87120
return nms(pre_box, iou_threshold);
88-
}catch (Exception e){
89-
throw e;
121+
} catch (Exception e) {
122+
throw e;
90123
}
91124
}
125+
92126
@Override
93-
protected List<Map<String, Object>> out(List<float[]> yolo_result, Vector<String> labels){
127+
protected List<Map<String, Object>> out(List<float[]> yolo_result, Vector<String> labels) {
94128
try {
95129
List<Map<String, Object>> result = new ArrayList<>();
96-
for (float [] box: yolo_result) {
130+
for (float[] box : yolo_result) {
97131
Map<String, Object> output = new HashMap<>();
98-
output.put("box",new float[]{box[0], box[1], box[2], box[3], box[4]}); //x1,y1,x2,y2,conf_class
99-
output.put("tag",labels.get((int)box[5]));
132+
output.put("box", new float[]{box[0], box[1], box[2], box[3], box[4]}); //x1,y1,x2,y2,conf_class
133+
output.put("tag", labels.get((int) box[5]));
100134
result.add(output);
101135
}
102136
return result;
103-
}catch (Exception e){
137+
} catch (Exception e) {
104138
throw e;
105139
}
106140
}

0 commit comments

Comments
 (0)