Commit 8c6f2427 authored by Indika NK's avatar Indika NK

added face mask detector module

parent c3394e69
......@@ -35,7 +35,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.covidefender"
minSdkVersion 16
minSdkVersion 21
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
......@@ -48,6 +48,11 @@ android {
signingConfig signingConfigs.debug
}
}
aaptOptions {
noCompress 'tflite'
noCompress 'lite'
}
}
flutter {
......
with_mask
without_mask
\ No newline at end of file
import 'package:covidefender/pages/mask_detect/invoker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
......@@ -75,7 +76,10 @@ class I_guider extends StatelessWidget {
'',
style: TextStyle(fontWeight: FontWeight.bold),
),
onPressed: () {},
onPressed: () {
print('face mask detection');
Navigator.pushNamed(context, '/invoker');
},
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
......
import 'package:covidefender/pages/mask_detect/invoker.dart';
import 'package:flutter/material.dart';
......@@ -9,6 +10,7 @@ void main() => runApp(MaterialApp(
routes: {
'/':(context) => HomeScreen(),
'/guider':(context) => I_guider(),
'/invoker':(context) => invoker(),
},
......
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:oktoast/oktoast.dart';
import 'package:tflite/tflite.dart';
import 'camera_page.dart';
class MaskDetectingApp extends StatelessWidget {
const MaskDetectingApp({
@required List<CameraDescription> cameras,
}) : assert(cameras != null),
_cameras = cameras;
final List<CameraDescription> _cameras;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Face mask detecting',
theme: ThemeData(
brightness: Brightness.dark,
),
builder: (BuildContext context, Widget widget) => OKToast(
child: widget,
),
home: CameraPage(
cameras: _cameras,
),
);
}
}
import 'dart:math';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'overlay.dart' as ol;
import 'package:tflite/tflite.dart';
class CameraPage extends StatefulWidget {
const CameraPage({
@required List<CameraDescription> cameras,
}) : assert(cameras != null),
_cameras = cameras;
final List<CameraDescription> _cameras;
@override
_CameraPageState createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPage> with WidgetsBindingObserver {
CameraController _controller;
bool _isDetecting = false;
bool _rear = false;
List<dynamic> _recognitions;
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (_controller == null || !_controller.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
_controller?.dispose();
} else if (state == AppLifecycleState.resumed) {
if (_controller != null) {
_setupCamera();
}
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_controller?.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_setupCamera();
}
void _setupCamera() {
if (widget._cameras == null || widget._cameras.isEmpty) {
print('No camera is found');
} else {
_controller = CameraController(
widget._cameras[_rear ? 0 : 1],
ResolutionPreset.max,
);
_controller.initialize().then((_) {
if (_updateCamera()) {
_readFrames();
}
});
}
}
Future<void> _switchCameraLens() async {
_rear = !_rear;
await _controller?.dispose();
_setupCamera();
}
bool _updateCamera() {
if (!mounted) {
return false;
}
setState(() {});
return true;
}
void _updateRecognitions({
List<dynamic> recognitions,
}) {
setState(() {
_recognitions = recognitions;
});
}
void _readFrames() {
_controller.startImageStream(
(CameraImage img) {
if (!_isDetecting) {
_isDetecting = true;
Tflite.runModelOnFrame(
bytesList: img.planes.map((Plane plane) {
return plane.bytes;
}).toList(),
imageWidth: img.width,
imageHeight: img.height,
numResults: 2,
).then((List<dynamic> recognitions) {
_updateRecognitions(
recognitions: recognitions,
);
_isDetecting = false;
});
}
},
);
}
@override
Widget build(BuildContext context) {
if (_controller == null || !_controller.value.isInitialized) {
return Container();
}
final Size screen = MediaQuery.of(context).size;
final double screenH = max(screen.height, screen.width);
final double screenW = min(screen.height, screen.width);
final Size previewSize = _controller.value.previewSize;
final double previewH = max(previewSize.height, previewSize.width);
final double previewW = min(previewSize.height, previewSize.width);
final double screenRatio = screenH / screenW;
final double previewRatio = previewH / previewW;
return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
onPressed: () async => _switchCameraLens(),
child: Icon(_rear ? Icons.camera_front : Icons.camera_rear),
backgroundColor: Colors.green,
),
body: Stack(
children: <Widget>[
OverflowBox(
maxHeight: screenRatio > previewRatio
? screenH
: screenW / previewW * previewH,
maxWidth: screenRatio > previewRatio
? screenH / previewH * previewW
: screenW,
child: CameraPreview(_controller),
),
ol.Overlay(
results: _recognitions ?? <dynamic>[],
)
],
),
);
}
}
import 'package:camera/camera.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:tflite/tflite.dart';
import 'cameara_service.dart';
class invoker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Face Mask Detector'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset('lib/assets/images/wear.gif'),
),
SizedBox(height: 20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ElevatedButton(
onPressed: ()=> invokeCamera(),
child: Text('Detect Mask'),
),
],
),
],
),
);
}
Future<void> invokeCamera() async {
WidgetsFlutterBinding.ensureInitialized();
print(await Tflite.loadModel(
model: 'lib/assets/model.tflite', labels: "lib/assets/labels.txt"));
runApp(MaskDetectingApp(
cameras: await availableCameras(),
));
}
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'toast.dart';
import 'package:oktoast/oktoast.dart';
class Overlay extends StatefulWidget {
const Overlay({
@required List<dynamic> results,
this.threshold = 0.5,
}) : _results = results;
final List<dynamic> _results;
final double threshold;
@override
_OverlayState createState() => _OverlayState();
}
class _OverlayState extends State<Overlay> {
ToastFuture _toastFuture;
String _label;
double _confidence = 0;
set label(String value) => setState(() {
_label = value;
});
set confidence(double value) => setState(() {
_confidence = value;
});
String get label => _label;
double get confidence => _confidence;
Color _updateBorderColor(
BuildContext context,
List<dynamic> bits,
) {
if (bits == null) {
return Colors.transparent;
}
if (bits.length > 1) {
final String firstLabel = bits.first["label"] as String;
final double firstConfidence = bits.first["confidence"] as double;
final String secondLabel = bits.last["label"] as String;
final double secondConfidence = bits.last["confidence"] as double;
if (firstConfidence > secondConfidence) {
label = firstLabel;
confidence = firstConfidence;
} else {
label = secondLabel;
confidence = secondConfidence;
}
}
if (bits.length == 1) {
label = bits.first["label"] as String;
confidence = bits.first["confidence"] as double;
}
if (confidence < widget.threshold) {
scheduleMicrotask(() => _toastFuture ??= Toast.show(
context,
ToastType.error,
"Please keep the camera steady and keep a certain distance to prevent shaking.",
onDismiss: () {
_toastFuture = null;
}));
return Colors.transparent;
}
if (label == "without_mask") {
return Colors.red;
}
if (label == "with_mask") {
return Colors.greenAccent;
}
return Colors.transparent;
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
border: Border.fromBorderSide(BorderSide(
color: _updateBorderColor(
context,
widget._results,
),
width: 10,
)),
),
),
Container(
margin: const EdgeInsets.symmetric(
horizontal: 85,
),
child: Column(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).padding.top,
),
Align(
alignment: Alignment.center,
child: Text(
label == "with_mask"
? "Wearing mask ${(confidence * 100).toStringAsFixed(0)}%"
: "No mask ${(confidence * 100).toStringAsFixed(0)}%",
style: Theme.of(context).textTheme.caption.copyWith(
color: label == "with_mask"
? Colors.greenAccent
: Colors.red,
),
),
),
Align(
alignment: Alignment.center,
child: LinearProgressIndicator(
value: confidence,
valueColor: AlwaysStoppedAnimation<Color>(
label == "with_mask" ? Colors.greenAccent : Colors.red),
minHeight: 15,
),
),
],
),
),
],
);
}
}
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:oktoast/oktoast.dart';
class SlideFadeInToastAnimation extends BaseAnimationBuilder {
@override
Widget buildWidget(
BuildContext context,
Widget child,
AnimationController controller,
double percent,
) {
final double opacity = min(1.0, percent + 0.2);
final double offset = (1 + percent) * 11;
return Opacity(
opacity: opacity,
child: Transform.translate(
child: child,
offset: Offset(0, offset),
),
);
}
}
import 'package:flutter/material.dart';
import 'slide_fade_in_toast_animation.dart';
import 'package:oktoast/oktoast.dart';
class Toast {
static const Duration kToastDuration = Duration(seconds: 3);
static ToastFuture show(
BuildContext context,
ToastType type,
String text, {
VoidCallback onDismiss,
Duration duration = kToastDuration,
bool dismissEnable = true,
}) {
return showToastWidget(
_buildToast(context, type, text, dismissEnable),
context: context,
position: const ToastPosition(align: Alignment.topCenter),
duration: dismissEnable ? duration : const Duration(days: 1),
handleTouch: true,
onDismiss: onDismiss,
animationBuilder: SlideFadeInToastAnimation(),
);
}
static void dismissAll({bool animated}) {
dismissAllToast(showAnim: animated);
}
static Widget _buildOkButton(BuildContext context) {
return Row(
children: <Widget>[
const SizedBox(
width: 10.0,
),
GestureDetector(
onTap: () => dismissAllToast(showAnim: true),
child: Text(
"OK",
style: Theme.of(context).textTheme.overline.copyWith(
color: Colors.white,
),
),
)
],
);
}
static Widget _buildToast(
BuildContext context,
ToastType type,
String text,
bool dismissEnable,
) {
return SafeArea(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 10.0),
child: Material(
color: Colors.black,
borderRadius: const BorderRadius.all(Radius.circular(10.0)),
child: Container(
padding:
const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
child: Row(
children: <Widget>[
if (type == ToastType.success)
const Image(
image: AssetImage('lib/assets/toast_check_mark.png'),
width: 18.0,
height: 18.0,
)
else if (type == ToastType.error)
const Image(
image: AssetImage('lib/assets/toast_error.png'),
width: 18.0,
height: 18.0,
)
else
throw Exception("Wrong toast type!"),
const SizedBox(
width: 16.0,
),
Expanded(
child: Text(
text,
overflow: TextOverflow.clip,
style: Theme.of(context)
.textTheme
.bodyText1
.copyWith(color: Colors.white),
),
),
if (dismissEnable) _buildOkButton(context),
],
),
),
),
),
);
}
}
enum ToastType {
success,
error,
}
......@@ -24,6 +24,9 @@ dependencies:
flutter:
sdk: flutter
flutter_svg: ^0.22.0
camera: ^0.5.8+8
tflite: ^1.1.1
oktoast: ^2.3.2
......@@ -57,6 +60,9 @@ flutter:
- lib/assets/images/time80.png
- lib/assets/images/event80.png
- lib/assets/images/
- lib/assets/model.tflite
- lib/assets/labels.txt
- assets/
# To add assets to your application, add an assets section, like this:
# assets:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment