Skip to content

Instantly share code, notes, and snippets.

@BoHellgren
Last active January 7, 2020 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BoHellgren/e2b34ce5e29b8cedd6401152dca6cbfa to your computer and use it in GitHub Desktop.
Save BoHellgren/e2b34ce5e29b8cedd6401152dca6cbfa to your computer and use it in GitHub Desktop.
Dog camera step 3
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:camera/camera.dart';
import 'package:soundpool/soundpool.dart';
import 'dart:ui' as ui;
import 'dart:typed_data';
import 'package:image/image.dart' as imglib;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CameraController _controller;
bool _cameraInitialized = false;
bool _isDetecting = false;
Soundpool _pool;
int _soundId;
CameraImage _savedImage;
Uint8List _snapShot;
bool _showSnapshot = false;
@override
void initState() {
super.initState();
SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]);
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
_initializeApp();
}
void _initializeApp() async {
_pool = Soundpool(streamType: StreamType.notification);
_soundId = await rootBundle
.load("assets/178186__snapper4298__camera-click-nikon.wav")
.then((ByteData soundData) {
return _pool.load(soundData);
});
List<CameraDescription> cameras = await availableCameras();
_controller = CameraController(cameras[0], ResolutionPreset.medium);
_controller.initialize().then((_) async {
_cameraInitialized = true;
// _isDetecting = false;
await _controller
.startImageStream((CameraImage image) => _processCameraImage(image));
setState(() {});
});
}
void _processCameraImage(CameraImage image) async {
if (_isDetecting) return;
_isDetecting = true;
// Detecting a dog will be done here.
setState(() {
_savedImage = image;
});
_isDetecting = false;
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTapDown: (TapDownDetails details) async {
double mediaHeight = MediaQuery.of(context).size.height;
if (details.localPosition.dy < mediaHeight * 0.8) return;
double mediaWidth = MediaQuery.of(context).size.width;
double xTap = details.localPosition.dx;
if (xTap < mediaWidth * 0.35) {
print('Left button tapped.');
} else if (xTap < mediaWidth * 0.65) {
// White button tapped.
if (_showSnapshot) {
// Stop showing snapshot if tapped while doing so
_showSnapshot = false;
return;
} else {
_pool.play(_soundId);
imglib.Image convertedImage = _convertCameraImage(_savedImage);
imglib.Image fullImage = imglib.copyResize(convertedImage,
height: mediaHeight.round());
_snapShot = imglib.encodePng(fullImage);
setState(() {_showSnapshot = true;});
Future.delayed(const Duration(seconds: 4), () {
setState(() {_showSnapshot = false;});
});
}
} else {
print('Right button tapped');
}
},
child: Container(
child: _cameraInitialized
? OverflowBox(
maxWidth: double.infinity,
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: _showSnapshot
? Stack(fit: StackFit.expand, children: <Widget>[
_snapShot != null
? Image.memory(_snapShot)
: Text('wait'),
CustomPaint(painter: ButtonsPainter(null)),
])
: Stack(fit: StackFit.expand, children: <Widget>[
CameraPreview(_controller),
CustomPaint(painter: ButtonsPainter(null)),
])))
: Text(
' Waiting for camera initialization',
style: TextStyle(fontSize: 20),
),
),
));
}
static imglib.Image _convertCameraImage(CameraImage image) {
int width = image.width;
int height = image.height;
// imglib -> Image package from https://pub.dartlang.org/packages/image
var img = imglib.Image(width, height); // Create Image buffer
const int hexFF = 0xFF000000;
final int uvyButtonStride = image.planes[1].bytesPerRow;
final int uvPixelStride = image.planes[1].bytesPerPixel;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
final int uvIndex =
uvPixelStride * (x / 2).floor() + uvyButtonStride * (y / 2).floor();
final int index = y * width + x;
final yp = image.planes[0].bytes[index];
final up = image.planes[1].bytes[uvIndex];
final vp = image.planes[2].bytes[uvIndex];
// Calculate pixel color
int r = (yp + vp * 1436 / 1024 - 179).round().clamp(0, 255);
int g = (yp - up * 46549 / 131072 + 44 - vp * 93604 / 131072 + 91)
.round()
.clamp(0, 255);
int b = (yp + up * 1814 / 1024 - 227).round().clamp(0, 255);
// color: 0x FF FF FF FF
// A B G R
img.data[index] = hexFF | (b << 16) | (g << 8) | r;
}
}
// Rotate 90 degrees to upright
var img1 = imglib.copyRotate(img, 90);
return img1;
}
}
class ButtonsPainter extends CustomPainter {
ui.Image buttonImage;
ButtonsPainter(this.buttonImage);
@override
void paint(Canvas canvas, Size size) {
var paint = Paint();
// First paint black field around buttons with low opacity
paint.color = Colors.black.withOpacity(0.1);
Rect rect =
Offset(0.0, size.height * 0.8) & Size(size.width, size.height * 0.2);
canvas.drawRect(rect, paint);
// Draw buttons at 10% from the bottom
final double yButton = size.height * 0.9;
paint.style = PaintingStyle.fill;
paint.color = Colors.grey;
final double canvasWidth = size.width;
double xButton;
var icon;
// Paint left button if no buttonImage supplied
if (buttonImage == null) {
xButton = canvasWidth * 0.3;
icon = Icons.photo_library;
canvas.drawCircle(Offset(xButton, yButton), 22.0, paint);
var builder = ui.ParagraphBuilder(ui.ParagraphStyle(
fontFamily: icon.fontFamily,
fontSize: 25.0,
))
..addText(String.fromCharCode(icon.codePoint));
var para = builder.build();
para.layout(const ui.ParagraphConstraints(width: 100.0));
canvas.drawParagraph(para, Offset(xButton - 12.5, yButton - 12.5));
}
//Paint middle button.
xButton = canvasWidth * 0.5;
canvas.drawCircle(Offset(xButton, yButton), 32.0, paint);
paint.color = Colors.white;
canvas.drawCircle(Offset(xButton, yButton), 28.0, paint);
// Paint right button.
xButton = canvasWidth * 0.7;
icon = Icons.info_outline;
paint.color = Colors.grey;
canvas.drawCircle(Offset(xButton, yButton), 22.0, paint);
var builder = ui.ParagraphBuilder(ui.ParagraphStyle(
fontFamily: icon.fontFamily,
fontSize: 25.0,
))
..addText(String.fromCharCode(icon.codePoint));
var para = builder.build();
para.layout(const ui.ParagraphConstraints(width: 100.0));
canvas.drawParagraph(para, Offset(xButton - 12.5, yButton - 12.5));
// Paint image on left button
if (buttonImage != null) {
xButton = canvasWidth * 0.3;
// First set up a round clipping area.
double radius = 22.0;
double l, t, r, b;
l = xButton - radius;
r = xButton + radius;
t = yButton - radius;
b = yButton + radius;
ui.Rect clippingRect = Rect.fromLTRB(l, t, r, b);
RRect clippingArea =
RRect.fromRectAndRadius(clippingRect, Radius.circular(radius));
canvas.clipRRect(clippingArea);
// Then draw the square button image over the round clipping area
double x, y = 0.0;
x = xButton - buttonImage.height / 2.0;
y = yButton - buttonImage.width / 2.0;
Offset buttonOffset = Offset(x, y);
canvas.drawImage(buttonImage, buttonOffset, Paint());
}
}
@override
bool shouldRepaint(ButtonsPainter oldDelegate) =>
oldDelegate.buttonImage != buttonImage;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment