Skip to content

Commit 3bb7882

Browse files
committed
Update subject segmentation painter
1 parent 7036418 commit 3bb7882

File tree

10 files changed

+121
-99
lines changed

10 files changed

+121
-99
lines changed

packages/example/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ android {
4242

4343
defaultConfig {
4444
applicationId "com.google.ml.kit.flutter.example"
45-
minSdkVersion 21
45+
minSdkVersion 24
4646
targetSdkVersion 33
4747
versionCode flutterVersionCode.toInteger()
4848
versionName flutterVersionName
658 KB
Loading
1.01 MB
Loading

packages/example/lib/main.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import 'dart:io';
22

33
import 'package:flutter/material.dart';
4-
import 'package:google_ml_kit_example/vision_detector_views/subject_segmenter_view.dart';
5-
64
import 'nlp_detector_views/entity_extraction_view.dart';
75
import 'nlp_detector_views/language_identifier_view.dart';
86
import 'nlp_detector_views/language_translator_view.dart';
@@ -16,6 +14,7 @@ import 'vision_detector_views/label_detector_view.dart';
1614
import 'vision_detector_views/object_detector_view.dart';
1715
import 'vision_detector_views/pose_detector_view.dart';
1816
import 'vision_detector_views/selfie_segmenter_view.dart';
17+
import 'vision_detector_views/subject_segmenter_view.dart';
1918
import 'vision_detector_views/text_detector_view.dart';
2019

2120
Future<void> main() async {

packages/example/lib/vision_detector_views/painters/subject_segmentation_painter.dart

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,44 @@ class SubjectSegmentationPainter extends CustomPainter {
1919

2020
@override
2121
void paint(Canvas canvas, Size size) {
22-
final width = mask.width;
23-
final height = mask.height;
24-
final confidences = mask.confidences;
22+
final int width = mask.width;
23+
final int height = mask.height;
24+
final List<Subject> subjects = mask.subjects;
2525

2626
final paint = Paint()..style = PaintingStyle.fill;
2727

28-
for (int y = 0; y < height; y++) {
29-
for (int x = 0; x < width; x++) {
30-
final int tx = translateX(
31-
x.toDouble(),
32-
size,
33-
Size(mask.width.toDouble(), mask.height.toDouble()),
34-
rotation,
35-
cameraLensDirection,
36-
).round();
37-
final int ty = translateY(
38-
y.toDouble(),
39-
size,
40-
Size(mask.width.toDouble(), mask.height.toDouble()),
41-
rotation,
42-
cameraLensDirection,
43-
).round();
44-
45-
final double opacity = confidences[(y * width) + x] * 0.5;
46-
paint.color = color.withOpacity(opacity);
47-
canvas.drawCircle(Offset(tx.toDouble(), ty.toDouble()), 2, paint);
28+
for (final Subject subject in subjects) {
29+
final int startX = subject.startX;
30+
final int startY = subject.startY;
31+
final int subjectWidth = subject.subjectWidth;
32+
final int subjectHeight = subject.subjectHeight;
33+
final List<double> confidences = subject.confidences;
34+
35+
for (int y = 0; y < subjectHeight; y++) {
36+
for (int x = 0; y < subjectWidth; x++) {
37+
final int absoluteX = startX;
38+
final int absoluteY = startY;
39+
40+
final int tx = translateX(
41+
absoluteX.toDouble(),
42+
size,
43+
Size(width.toDouble(), height.toDouble()),
44+
rotation,
45+
cameraLensDirection)
46+
.round();
47+
48+
final int ty = translateY(
49+
absoluteY.toDouble(),
50+
size,
51+
Size(width.toDouble(), height.toDouble()),
52+
rotation,
53+
cameraLensDirection)
54+
.round();
55+
56+
final double opacity = confidences[(y * subjectWidth) + x] * 0.5;
57+
paint.color = color.withOpacity(opacity);
58+
canvas.drawCircle(Offset(tx.toDouble(), ty.toDouble()), 2, paint);
59+
}
4860
}
4961
}
5062
}

packages/example/lib/vision_detector_views/subject_segmenter_view.dart

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class _SubjectSegmenterViewState extends State<SubjectSegmenterView> {
1616
bool _isBusy = false;
1717
CustomPaint? _customPaint;
1818
String? _text;
19-
var _cameraLensDirection = CameraLensDirection.front;
19+
var _cameraLensDirection = CameraLensDirection.back;
2020

2121
@override
2222
void dispose() async {
@@ -44,10 +44,9 @@ class _SubjectSegmenterViewState extends State<SubjectSegmenterView> {
4444
setState(() {
4545
_text = '';
4646
});
47-
final mask = await _segmenter.processImage(inputImage);
47+
final SubjectSegmenterMask mask = await _segmenter.processImage(inputImage);
4848
if (inputImage.metadata?.size != null &&
49-
inputImage.metadata?.rotation != null &&
50-
mask != null) {
49+
inputImage.metadata?.rotation != null) {
5150
final painter = SubjectSegmentationPainter(
5251
mask,
5352
inputImage.metadata!.size,
@@ -57,8 +56,8 @@ class _SubjectSegmenterViewState extends State<SubjectSegmenterView> {
5756
_customPaint = CustomPaint(painter: painter);
5857
} else {
5958
// TODO: set _customPaint to draw on top of image
60-
_text =
61-
'There is a mask with ${(mask?.confidences ?? []).where((element) => element > 0.8).length} elements';
59+
_text = 'There is a mask with ${mask.subjects.length} subjects';
60+
6261
_customPaint = null;
6362
}
6463
_isBusy = false;

packages/google_mlkit_subject_segmentation/android/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ android {
3535
}
3636

3737
defaultConfig {
38-
minSdkVersion 21
38+
minSdkVersion 24
3939
}
4040
}
4141

4242
dependencies {
4343
implementation 'com.google.android.gms:play-services-mlkit-subject-segmentation:16.0.0-beta1'
44-
implementation files('/Users/bensonarafat/development/flutter/bin/cache/artifacts/engine/android-arm/flutter.jar')
44+
//implementation files('/Users/bensonarafat/development/flutter/bin/cache/artifacts/engine/android-arm/flutter.jar')
4545
}

packages/google_mlkit_subject_segmentation/android/src/main/java/com/google_mlkit_subject_segmentation/GoogleMlKitSubjectSegmentationPlugin.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
public class GoogleMlKitSubjectSegmentationPlugin implements FlutterPlugin {
99
private MethodChannel channel;
10-
private static final String channelName = "google_mlkit_subject_segmenter";
10+
private static final String channelName = "google_mlkit_subject_segmentation";
1111

1212
@Override
1313
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {

packages/google_mlkit_subject_segmentation/android/src/main/java/com/google_mlkit_subject_segmentation/SubjectSegmenterProcess.java

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
import com.google.mlkit.vision.segmentation.subject.SubjectSegmentation;
1010
import com.google.mlkit.vision.segmentation.subject.SubjectSegmenter;
1111

12+
import java.util.ArrayList;
13+
import java.util.List;
1214
import java.nio.FloatBuffer;
1315
import java.util.HashMap;
14-
import java.util.List;
1516
import java.util.Map;
1617

18+
import io.flutter.Log;
1719
import io.flutter.plugin.common.MethodCall;
1820
import io.flutter.plugin.common.MethodChannel;
1921

@@ -26,6 +28,8 @@ public class SubjectSegmenterProcess implements MethodChannel.MethodCallHandler
2628

2729
private final Context context;
2830

31+
private static final String TAG = "Logger";
32+
2933
private int imageWidth;
3034
private int imageHeight;
3135

@@ -37,8 +41,6 @@ public SubjectSegmenterProcess(Context context) {
3741
@Override
3842
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
3943
String method = call.method;
40-
41-
4244
switch (method) {
4345
case START:
4446
handleDetection(call, result);
@@ -54,17 +56,9 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result
5456
}
5557

5658
private SubjectSegmenter initialize(MethodCall call) {
57-
Boolean enableForegroundConfidenceMask = call.argument("enableForegroundConfidenceMask");
58-
Boolean enableForegroundBitmap = call.argument("enableForegroundBitmap");
59-
SubjectSegmenterOptions.Builder builder = new SubjectSegmenterOptions.Builder();
60-
61-
if(Boolean.TRUE.equals(enableForegroundConfidenceMask)){
62-
builder.enableForegroundConfidenceMask();
63-
}
64-
if(Boolean.TRUE.equals(enableForegroundBitmap)){
65-
builder.enableForegroundBitmap();
66-
}
67-
59+
SubjectSegmenterOptions.Builder builder = new SubjectSegmenterOptions.Builder()
60+
.enableMultipleSubjects(new SubjectSegmenterOptions.SubjectResultOptions.Builder()
61+
.enableConfidenceMask().build());
6862
SubjectSegmenterOptions options = builder.build();
6963
return SubjectSegmentation.getClient(options);
7064
}
@@ -84,27 +78,35 @@ private void handleDetection(MethodCall call, MethodChannel.Result result){
8478

8579
subjectSegmenter.process(inputImage)
8680
.addOnSuccessListener( subjectSegmentationResult -> {
87-
Map<String, Object> map = new HashMap<>();
88-
map.put("maxWidth", imageWidth);
89-
map.put("maxHeight", imageHeight);
90-
List<Subject> subjects = subjectSegmentationResult.getSubjects();
91-
final float[] confidences = new float[imageWidth * imageHeight];
92-
for (int k =0; k < subjects.size(); k++){
93-
Subject subject = subjects.get(k);
94-
FloatBuffer mask = subject.getConfidenceMask();
95-
for(int j = 0; j < subject.getHeight(); j++){
96-
for (int i = 0; j < subject.getWidth(); i++){
97-
if (mask.get() > 0.5f) {
98-
confidences[ (subject.getStartY() + j) * imageWidth + subject.getStartX() + i] = mask.get();
99-
}
100-
}
101-
}
102-
}
103-
map.put("confidences", confidences);
104-
result.success(map);
81+
List<Map<String, Object>> subjectsData = new ArrayList<>();
82+
for(Subject subject : subjectSegmentationResult.getSubjects()){
83+
Map<String, Object> subjectData = getStringObjectMap(subject);
84+
subjectsData.add(subjectData);
85+
}
86+
Map<String, Object> map = new HashMap<>();
87+
map.put("subjects", subjectsData);
88+
map.put("width", imageWidth);
89+
map.put("height", imageHeight);
90+
result.success(map);
10591
}).addOnFailureListener( e -> result.error("Subject segmentation failed!", e.getMessage(), e) );
10692
}
10793

94+
@NonNull
95+
private static Map<String, Object> getStringObjectMap(Subject subject) {
96+
Map<String, Object> subjectData = new HashMap<>();
97+
subjectData.put("startX", subject.getStartX());
98+
subjectData.put("startY", subject.getStartY());
99+
subjectData.put("width", subject.getWidth());
100+
subjectData.put("height", subject.getHeight());
101+
102+
FloatBuffer confidenceMask = subject.getConfidenceMask();
103+
assert confidenceMask != null;
104+
float[] confidences = new float[confidenceMask.remaining()];
105+
confidenceMask.get(confidences);
106+
subjectData.put("confidences", confidences);
107+
return subjectData;
108+
}
109+
108110
private void closeDetector(MethodCall call) {
109111
String id = call.argument("id");
110112
SubjectSegmenter subjectSegmenter = instances.get(id);

packages/google_mlkit_subject_segmentation/lib/src/subject_segmenter.dart

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,72 +6,82 @@ class SubjectSegmenter {
66
static const MethodChannel _channel =
77
MethodChannel('google_mlkit_subject_segmentation');
88

9-
///
10-
/// TODO: Comment here
11-
///
12-
final bool enableForegroundConfidenceMask;
13-
14-
///
15-
/// TODO: comment here
16-
///
17-
final bool enableForegroundBitmap;
18-
19-
SubjectSegmenter({
20-
this.enableForegroundConfidenceMask = true,
21-
this.enableForegroundBitmap = false,
22-
});
23-
249
/// Instance id.
2510
final id = DateTime.now().microsecondsSinceEpoch.toString();
2611

2712
/// Processes the given [InputImage] for segmentation.
2813
/// Returns the segmentation mask in the given image or nil if there was an error.
2914
Future<SubjectSegmenterMask> processImage(InputImage inputImage) async {
3015
final results = await _channel
31-
.invokeMethod('vision#startSubjectSegmentation', <String, dynamic>{
16+
.invokeMethod('vision#startSubjectSegmenter', <String, dynamic>{
3217
'id': id,
3318
'imageData': inputImage.toJson(),
34-
'enableForegroundConfidenceMask': enableForegroundBitmap,
35-
'enableForegroundBitmap': enableForegroundBitmap,
3619
});
37-
3820
SubjectSegmenterMask masks = SubjectSegmenterMask.fromJson(results);
3921
return masks;
4022
}
4123

4224
/// Closes the detector and releases its resources.
4325
Future<void> close() =>
44-
_channel.invokeMethod('vision#closeSubjectSegmentation', {'id': id});
26+
_channel.invokeMethod('vision#closeSubjectSegmenter', {'id': id});
4527
}
4628

4729
class SubjectSegmenterMask {
48-
/// The width of the mask.
4930
final int width;
5031

51-
/// The height of the mask.
5232
final int height;
5333

54-
/// The confidence of the pixel in the mask being in the foreground.
55-
final List<double> confidences;
34+
final List<Subject> subjects;
5635

5736
/// Constructir to create a instance of [SubjectSegmenterMask].
5837
SubjectSegmenterMask({
5938
required this.width,
6039
required this.height,
61-
required this.confidences,
40+
required this.subjects,
6241
});
6342

6443
/// Returns an instance of [SubjectSegmenterMask] from json
65-
factory SubjectSegmenterMask.fromJson(Map<String, dynamic> json) {
66-
final values = json['confidences'];
67-
final List<double> confidences = [];
68-
for (final item in values) {
69-
confidences.add(double.parse(item.toString()));
70-
}
44+
factory SubjectSegmenterMask.fromJson(Map<dynamic, dynamic> json) {
45+
List<dynamic> list = json['subjects'];
46+
List<Subject> subjects = list.map((e) => Subject.fromJson(e)).toList();
7147
return SubjectSegmenterMask(
7248
width: json['width'] as int,
7349
height: json['height'] as int,
74-
confidences: confidences,
50+
subjects: subjects,
7551
);
7652
}
7753
}
54+
55+
class Subject {
56+
final int startX;
57+
final int startY;
58+
final int subjectWidth;
59+
final int subjectHeight;
60+
final List<double> confidences;
61+
62+
Subject(
63+
{required this.startX,
64+
required this.startY,
65+
required this.subjectWidth,
66+
required this.subjectHeight,
67+
required this.confidences});
68+
69+
factory Subject.fromJson(Map<dynamic, dynamic> json) {
70+
return Subject(
71+
startX: json['startX'] as int,
72+
startY: json['startY'] as int,
73+
subjectWidth: json['width'] as int,
74+
subjectHeight: json['height'] as int,
75+
confidences: json['confidences']);
76+
}
77+
78+
Map<dynamic, dynamic> toJson() {
79+
return {
80+
"startX": startX,
81+
"startY": startY,
82+
"subjectWidth": subjectWidth,
83+
"subjectHeight": subjectHeight,
84+
"confidences": confidences,
85+
};
86+
}
87+
}

0 commit comments

Comments
 (0)