import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter_tts/flutter_tts.dart'; import 'package:google_mlkit_object_detection/google_mlkit_object_detection.dart'; import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart'; import 'package:internal/common/camera.dart'; import 'package:internal/common/transform.dart'; /// Description: Home Page /// Time : 04/12/2023 Wednesday /// Author : liuyuqi.gov@msn.cn class HomeScreen extends StatefulWidget { const HomeScreen({Key? key}) : super(key: key); @override State createState() => _HomeScreenState(); } /// Description: Home Page State class _HomeScreenState extends State { CameraController? _cameraController; /// 检测器 final ObjectDetector _objectDetector = ObjectDetector( options: LocalObjectDetectorOptions( mode: DetectionMode.stream, multipleObjects: false, classifyObjects: false, modelPath: 'flutter_assets/files/0.tflite', ), ); final TextRecognizer _textDetector = TextRecognizer( script: TextRecognitionScript.latin, ); bool _canRender = true; bool _canSpace = true; final GlobalKey _cameraPreviewKey = GlobalKey(); List _data = []; String message = ""; FlutterTts tts = FlutterTts(); /// 初始化状态 @override void initState() { super.initState(); tts.awaitSpeakCompletion(true); /// 检测可用相机 availableCameras().then((cameras) { CameraDescription cameraDescription = cameras[0]; _cameraController = CameraController(cameraDescription, ResolutionPreset.high, enableAudio: false); // ResolutionPreset.high 质量较高的预设,不录音 _cameraController!.initialize().then((_) { _cameraController!.startImageStream((image) => processCameraImage(cameraDescription, image, onImage)); setState(() {}); }); }); } /// 回调函数,处理camera拍摄的图片 Future onImage(InputImage inputImage) async { if (!_canRender) return; _canRender = false; _data = []; bool xx = await shouldTURNAROUND(inputImage); if (!xx) await shouldGUIDELINE(inputImage); _canRender = true; } /// 是否需要旋转图片 /// [inputImage] 图片 Future shouldTURNAROUND(InputImage inputImage) async { final list = await _textDetector.processImage(inputImage); // 识别文字 Size? size = _cameraPreviewKey.currentContext?.size; if (size == null) { return false; } InputImageRotation? rotation = inputImage.inputImageData?.imageRotation; Size? absoluteSize = inputImage.inputImageData?.size; if (rotation == null || absoluteSize == null) { return false; } List data = []; for (final item in list.blocks) { final left = translateX(item.boundingBox.left, rotation, size, absoluteSize); final top = translateY(item.boundingBox.top, rotation, size, absoluteSize); final right = translateX(item.boundingBox.right, rotation, size, absoluteSize); final bottom = translateY(item.boundingBox.bottom, rotation, size, absoluteSize); final rect = Rect.fromLTRB(left, top, right, bottom); if (item.text.endsWith("T")) { data.add(rect); message = "TURN AROUND"; if (_canSpace) { _canSpace = false; // 语音播放:打开录音 tts.speak("TURN AROUND").then((_) => _canSpace = true); } _data.addAll(data); setState(() {}); return true; } } return false; } /// 是否需要引导 Future shouldGUIDELINE(InputImage inputImage) async { final list = await _objectDetector.processImage(inputImage); // 识别物体 Size? size = _cameraPreviewKey.currentContext?.size; if (size == null) { return; } InputImageRotation? rotation = inputImage.inputImageData?.imageRotation; Size? absoluteSize = inputImage.inputImageData?.size; if (rotation == null || absoluteSize == null) { return; } List data = []; for (final item in list) { final left = translateX(item.boundingBox.left, rotation, size, absoluteSize); final top = translateY(item.boundingBox.top, rotation, size, absoluteSize); final right = translateX(item.boundingBox.right, rotation, size, absoluteSize); final bottom = translateY(item.boundingBox.bottom, rotation, size, absoluteSize); final rect = Rect.fromLTRB(left, top, right, bottom); if (rect.height / rect.width >= 2) { data.add(rect); } break; } _data.addAll(data); execGUIDELINE(size); setState(() {}); } /// 执行指示 Future execGUIDELINE(Size size) async { final Rect rect = _data[0]; if (rect.center.dx < size.width / 2) { message = "LEFT"; if (_canSpace) { _canSpace = false; tts.speak("LEFT").then((_) => _canSpace = true); } setState(() {}); return; } if (rect.center.dx > size.width / 2) { message = "RIGHT"; if (_canSpace) { _canSpace = false; tts.speak("RIGHT").then((_) => _canSpace = true); } setState(() {}); return; } } @override Widget build(BuildContext context) { final xx = DateTime.now().millisecondsSinceEpoch; // if (xx >= 1676961078000) { // Navigator.of(context).pop(); // } return Scaffold( appBar: AppBar( title: const Text("SWIM"), ), body: Column( children: [ if (_cameraController != null) makeCameraPreview(), ], ), ); } Widget makeCameraPreview() { Rect? rect; if (_data.isNotEmpty) { rect = _data[0]; } return CameraPreview( _cameraController!, key: _cameraPreviewKey, child: Stack( children: [ ..._data.map((rect) { return AnimatedPositioned.fromRect( rect: rect, duration: const Duration(milliseconds: 100), child: Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration( border: Border.all(color: const Color(0xfff0f0f0), width: 2), borderRadius: BorderRadius.circular(8), ), child: Align( alignment: Alignment.topLeft, child: Container( padding: const EdgeInsets.symmetric(horizontal: 4), decoration: const BoxDecoration( borderRadius: BorderRadius.only(bottomRight: Radius.circular(8)), color: Color(0x99ffffff), ), child: Text( "${rect.center.dx.toStringAsFixed(2)},${rect.center.dy.toStringAsFixed(2)}", style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, color: Colors.indigo, ), ), ), ), ), ); }).toList(), Align( alignment: Alignment.topLeft, child: Container( padding: const EdgeInsets.symmetric(horizontal: 4), decoration: const BoxDecoration( borderRadius: BorderRadius.only(bottomRight: Radius.circular(8)), color: Color(0x99ffffff), ), child: Text( message, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Colors.pink, ), ), ), ), ], ), ); } }