Skip to content

Commit e64abd6

Browse files
committed
Added real-time detection using 'Camera-Controller.'
1 parent 69a1055 commit e64abd6

File tree

4 files changed

+119
-67
lines changed

4 files changed

+119
-67
lines changed

android/app/build.gradle

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ android {
3939
defaultConfig {
4040
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
4141
applicationId "com.umairadil.tensorflow_lite_flutter"
42-
minSdkVersion 19
42+
minSdkVersion 21
4343
targetSdkVersion 28
4444
versionCode flutterVersionCode.toInteger()
4545
versionName flutterVersionName
@@ -57,6 +57,11 @@ android {
5757
}
5858
}
5959
}
60+
61+
compileOptions {
62+
sourceCompatibility 1.8
63+
targetCompatibility 1.8
64+
}
6065
}
6166

6267
flutter {

lib/main.dart

Lines changed: 105 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import 'dart:io';
2-
1+
import 'package:camera/camera.dart';
2+
import 'package:flutter/foundation.dart';
33
import 'package:flutter/material.dart';
4-
import 'package:image_picker/image_picker.dart';
54
import 'package:tflite/tflite.dart';
65

76
void main() => runApp(MyApp());
@@ -10,11 +9,12 @@ class MyApp extends StatelessWidget {
109
@override
1110
Widget build(BuildContext context) {
1211
return MaterialApp(
13-
title: 'Flutter Demo',
12+
debugShowCheckedModeBanner: false,
13+
title: 'ML Room Colors',
1414
theme: ThemeData(
1515
primarySwatch: Colors.blue,
1616
),
17-
home: MyHomePage(title: 'Flutter Demo Home Page'),
17+
home: MyHomePage(title: 'Detect Room Color'),
1818
);
1919
}
2020
}
@@ -29,99 +29,138 @@ class MyHomePage extends StatefulWidget {
2929
}
3030

3131
class _MyHomePageState extends State<MyHomePage> {
32-
bool _loading;
32+
CameraController _camera;
33+
34+
bool _isDetecting = false;
35+
CameraLensDirection _direction = CameraLensDirection.back;
36+
Future<void> _initializeControllerFuture;
37+
38+
bool _modelLoaded;
3339
List _outputs;
34-
File _image;
40+
41+
Future<CameraDescription> _getCamera(CameraLensDirection dir) async {
42+
return await availableCameras().then(
43+
(List<CameraDescription> cameras) => cameras.firstWhere(
44+
(CameraDescription camera) => camera.lensDirection == dir,
45+
),
46+
);
47+
}
48+
49+
void _initializeCamera() async {
50+
log("_initializeCamera", "Initializing camera..");
51+
52+
_camera = CameraController(
53+
await _getCamera(_direction),
54+
defaultTargetPlatform == TargetPlatform.iOS
55+
? ResolutionPreset.low
56+
: ResolutionPreset.medium,
57+
);
58+
_initializeControllerFuture = _camera.initialize().then((value) {
59+
log("_initializeCamera", "Camera initialized, starting camera stream..");
60+
61+
_camera.startImageStream((CameraImage image) {
62+
if (!_modelLoaded) return;
63+
if (_isDetecting) return;
64+
_isDetecting = true;
65+
try {
66+
classifyImage(image);
67+
} catch (e) {
68+
print(e);
69+
}
70+
});
71+
});
72+
}
3573

3674
void initState() {
3775
super.initState();
38-
_loading = true;
76+
_initializeCamera();
77+
_modelLoaded = false;
3978

4079
loadModel().then((value) {
80+
4181
setState(() {
42-
_loading = false;
82+
_modelLoaded = true;
4383
});
4484
});
4585
}
4686

4787
//Load the Tflite model
4888
loadModel() async {
89+
log("loadModel", "Loading model..");
4990
await Tflite.loadModel(
5091
model: "assets/model_unquant.tflite",
5192
labels: "assets/labels.txt",
5293
);
5394
}
5495

55-
//Pick an image from the gallery
56-
pickImage() async {
57-
var image = await ImagePicker.pickImage(source: ImageSource.gallery);
58-
if (image == null) return null;
59-
setState(() {
60-
_loading = true;
61-
//Declare File _image in the class which is used to display the image on the screen.
62-
_image = image;
63-
});
64-
classifyImage(image);
65-
}
66-
67-
classifyImage(File image) async {
68-
var output = await Tflite.runModelOnImage(
69-
path: image.path,
70-
numResults: 2,
71-
threshold: 0.5,
96+
classifyImage(CameraImage image) async {
97+
await Tflite.runModelOnFrame(
98+
bytesList: image.planes.map((plane) {
99+
return plane.bytes;
100+
}).toList(),
101+
numResults: 3,
102+
threshold: 0.3,
72103
imageMean: 127.5,
73104
imageStd: 127.5,
74-
);
75-
setState(() {
76-
_loading = false;
77-
//Declare List _outputs in the class which will be used to show the classified classs name and confidence
78-
_outputs = output;
105+
//asynch: true,
106+
).then((value) {
107+
_isDetecting = false;
108+
109+
if (value.isNotEmpty) {
110+
log("classifyImage", "Results loaded. ${value.length}");
111+
value.forEach((element) {
112+
log("classifyImage", "$element");
113+
});
114+
}
115+
_outputs = value;
79116
});
80117
}
81118

82119
@override
83120
Widget build(BuildContext context) {
84121
return Scaffold(
85-
appBar: AppBar(
86-
title: Text(widget.title),
87-
),
88-
body: _loading
89-
? Container(
90-
alignment: Alignment.center,
91-
child: CircularProgressIndicator(),
92-
)
93-
: Container(
94-
width: MediaQuery.of(context).size.width,
95-
child: Column(
96-
crossAxisAlignment: CrossAxisAlignment.center,
97-
mainAxisAlignment: MainAxisAlignment.center,
98-
children: [
99-
_image == null ? Container() : Image.file(_image),
100-
SizedBox(
101-
height: 20,
102-
),
103-
_outputs != null
104-
? Text(
105-
"${_outputs[0]["label"]}",
106-
style: TextStyle(
107-
color: Colors.black,
108-
fontSize: 20.0,
109-
background: Paint()..color = Colors.white,
110-
),
111-
)
112-
: Text("Classification Failed")
113-
],
114-
),
115-
),
116-
floatingActionButton: FloatingActionButton(
117-
onPressed: pickImage,
118-
child: Icon(Icons.image),
119-
));
122+
appBar: AppBar(
123+
title: Text(widget.title),
124+
),
125+
body: FutureBuilder<void>(
126+
future: _initializeControllerFuture,
127+
builder: (context, snapshot) {
128+
if (snapshot.connectionState == ConnectionState.done) {
129+
// If the Future is complete, display the preview.
130+
return Stack(
131+
children: <Widget>[
132+
CameraPreview(_camera),
133+
Center(
134+
child: _outputs != null && _outputs.isNotEmpty
135+
? Text(
136+
"${_outputs[0]["label"]}",
137+
style: TextStyle(
138+
color: Colors.green,
139+
fontSize: 26.0,
140+
),
141+
)
142+
: Text("Classification Failed"),
143+
)
144+
],
145+
);
146+
} else {
147+
// Otherwise, display a loading indicator.
148+
return Center(child: CircularProgressIndicator());
149+
}
150+
},
151+
),
152+
);
120153
}
121154

122155
@override
123156
void dispose() {
124157
Tflite.close();
158+
_camera.dispose();
159+
log("dispose", "Clear resources.");
125160
super.dispose();
126161
}
162+
163+
void log(String methodName, String message) {
164+
debugPrint("{$methodName} {$message}");
165+
}
127166
}

pubspec.lock

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ packages:
2929
url: "https://pub.dartlang.org"
3030
source: hosted
3131
version: "1.0.5"
32+
camera:
33+
dependency: "direct main"
34+
description:
35+
name: camera
36+
url: "https://pub.dartlang.org"
37+
source: hosted
38+
version: "0.5.7+4"
3239
charcode:
3340
dependency: transitive
3441
description:

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ dependencies:
2626

2727
tflite: ^1.0.5
2828
image_picker: ^0.6.3+4
29+
camera: ^0.5.7+4
2930

3031
dev_dependencies:
3132
flutter_test:

0 commit comments

Comments
 (0)