123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- import 'dart:io';
- import 'dart:math' as math;
- import 'package:camera/camera.dart';
- import 'package:flutter/material.dart';
- import 'package:focus_detector/focus_detector.dart';
- import 'package:image_size_getter/file_input.dart';
- import 'package:image_size_getter/image_size_getter.dart';
- import 'package:native_vision/edge_detection.dart';
- import 'package:native_vision/polygon_anim.dart';
- import 'package:path_provider/path_provider.dart';
- import 'dart:ui' as ui;
- import '../native/doc_scanner.dart';
- class _DocIndicator extends CustomPainter {
- // final Paint _paint = Paint()..color = Colors.yellow.withOpacity(0.5);
- final Paint _polygonPaint = Paint()
- ..color = Colors.blue.withOpacity(0.4)
- // ..style = PaintingStyle.stroke
- ..strokeWidth = 2.0;
- late List<Offset> _coords;
- _DocIndicator(List<Offset> coords) {
- _coords = coords;
- }
- @override
- void paint(Canvas canvas, ui.Size size) {
- // canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), _paint);
- Path path = Path();
- path.addPolygon(_coords, true);
- path.close();
- canvas.drawPath(path, _polygonPaint);
- }
- @override
- bool shouldRepaint(covariant CustomPainter oldDelegate) {
- return true;
- }
- }
- class DocPreview extends StatefulWidget {
- final double displayW, displayH;
- const DocPreview({Key? key, required this.displayW, required this.displayH})
- : super(key: key);
- @override
- State<StatefulWidget> createState() {
- return DocPreviewState();
- }
- }
- class DocPreviewState extends State<DocPreview>
- with SingleTickerProviderStateMixin {
- bool detecting = false;
- final _coordsNotifier = ValueNotifier<List<Offset>>(List.empty());
- late ROI previewROI;
- CameraController? _cameraController;
- final CameraLensDirection _direction = CameraLensDirection.back;
- late DocScanner _docScan;
- late PolygonAnimController _polygonAnim;
- Future<CameraDescription> _getCamera(CameraLensDirection dir) async {
- final avaibleCameras = await availableCameras();
- return avaibleCameras[0];
- // return await availableCameras().then(
- // (List<CameraDescription> cameras) => cameras.lastWhere(
- // (CameraDescription camera) => camera.lensDirection == dir,
- // ),
- // );
- }
- ROI _calculateRoi(ui.Size sizeInPixel) {
- var screenSize = MediaQuery.of(context).size;
- final screenH = math.max(screenSize.height, screenSize.width);
- final screenW = math.min(screenSize.height, screenSize.width);
- final previewH = math.max(sizeInPixel.height, sizeInPixel.width);
- final previewW = math.min(sizeInPixel.height, sizeInPixel.width);
- final screenRatio = screenH / screenW;
- final previewRatio = previewH / previewW;
- // final maxHeight =
- // screenRatio > previewRatio ? screenH : screenW / previewW * previewH;
- // final maxWidth =
- // screenRatio > previewRatio ? screenH / previewH * previewW : screenW;
- final maxWidth = widget.displayW;
- final maxHeight = maxWidth * previewRatio;
- final scale = maxHeight / previewH;
- final top = (maxHeight - widget.displayH) / 2 / scale;
- final left = (maxWidth - widget.displayW) / 2 / scale;
- final cropH = widget.displayH / scale;
- final cropW = widget.displayW / scale;
- return ROI.create(left.toInt(), top.toInt(), cropW.toInt(), cropH.toInt(),
- maxWidth, maxHeight);
- }
- Future<Map<String, dynamic>?> takePic() async {
- try {
- final file = await _cameraController?.takePicture();
- if (file != null) {
- final size = ImageSizeGetter.getSize(FileInput(File(file.path)));
- final roi = _calculateRoi(
- ui.Size(size.width.toDouble(), size.height.toDouble()));
- final tmpDir = (await getTemporaryDirectory()).path;
- final outPath =
- "$tmpDir/roi_${DateTime.now().millisecondsSinceEpoch}.jpg";
- final highPreciseDocScan = DocScanner();
- await highPreciseDocScan.init(0.85);
- final ret =
- await highPreciseDocScan.singleScan(file.path, outPath, roi);
- await highPreciseDocScan.release();
- if (ret == null) return null;
- // return {
- // "EdgeDetectionResult": EdgeDetectionResult(
- // topLeft: Offset(roi.left / imgW, roi.top / imgH),
- // topRight: Offset((roi.left + roi.width) / imgW, roi.top / imgH),
- // bottomRight: Offset(
- // (roi.left + roi.width) / imgW, (roi.top + roi.height) / imgH),
- // bottomLeft: Offset(roi.left / imgW, (roi.top + roi.height) / imgH),
- // ),
- // "ImgPath": file.path,
- // };
- return {
- "EdgeDetectionResult": ret,
- "ImgPath": outPath,
- };
- }
- } catch (e, st) {
- debugPrintStack(stackTrace: st);
- }
- return null;
- }
- _initializeCamera() async {
- if (_cameraController != null && _cameraController!.value.isInitialized) {
- return;
- }
- try {
- _cameraController = CameraController(
- await _getCamera(_direction), ResolutionPreset.photo);
- await _cameraController?.initialize();
- if (!mounted) {
- return;
- }
- final previewSize = _cameraController!.value.previewSize;
- previewROI =
- _calculateRoi(ui.Size(previewSize!.width, previewSize.height));
- await _cameraController?.startImageStream((CameraImage image) async {
- // Stopwatch stopwatch = Stopwatch()..start();
- if (detecting || !mounted) return;
- detecting = true;
- final coords = await _docScan.scanWithCameraImage(
- image,
- previewROI.left.toInt(),
- previewROI.top.toInt(),
- previewROI.width.toInt(),
- previewROI.height.toInt(),
- widget.displayW.toInt(),
- widget.displayH.toInt());
- if (mounted && coords != null) {
- _polygonAnim.startAnim(coords);
- }
- detecting = false;
- // print('doSomething() executed in ${stopwatch.elapsed}');
- });
- setState(() {});
- } catch (e) {
- if (e is CameraException) {
- debugPrint(e.description);
- return;
- }
- debugPrint(e.toString());
- }
- }
- void onCoordsChange(List<Offset> coords) {
- _coordsNotifier.value = coords;
- }
- @override
- void initState() {
- super.initState();
- _initializeCamera();
- _polygonAnim = PolygonAnimController(
- len: 4, vsync: this, duration: const Duration(milliseconds: 100));
- _polygonAnim.onCoordsChange = onCoordsChange;
- _docScan = DocScanner();
- _docScan.init(0.65);
- }
- @override
- void dispose() {
- _polygonAnim.dispose();
- _docScan.release();
- _coordsNotifier.dispose();
- super.dispose();
- }
- @override
- Widget build(BuildContext context) {
- return FocusDetector(
- onFocusLost: () {
- _cameraController?.dispose();
- _cameraController = null;
- },
- onFocusGained: () {
- _initializeCamera();
- },
- child:
- _cameraController == null || !_cameraController!.value.isInitialized
- ? Container()
- : Stack(children: [
- SizedBox(
- width: widget.displayW,
- height: widget.displayH,
- child: ClipRRect(
- child: OverflowBox(
- maxHeight: previewROI.maxHeight,
- maxWidth: previewROI.maxWidth,
- child: CameraPreview(
- _cameraController!,
- ),
- ),
- ),
- ),
- ValueListenableBuilder(
- valueListenable: _coordsNotifier,
- builder: (_, List<Offset> coords, __) {
- if (coords.length != 4) return Container();
- return CustomPaint(
- size: ui.Size(widget.displayW, widget.displayH),
- painter: _DocIndicator(coords),
- );
- })
- ]),
- );
- }
- }
|