1. Anuncie Aqui ! Entre em contato fdantas@4each.com.br

[Flutter] Flutter - image to text is blocking UI

Discussão em 'Mobile' iniciado por Stack, Outubro 17, 2024 às 10:53.

  1. Stack

    Stack Membro Participativo

    I am trying to extract text from the image. So, I use google_mlkit_text_recognition and camera API. The problem is for some low-end devices the UI is not responding properly, and seems laggy which is not a good user experience. I can't use a separate Isolate to recognize the text as google_mlkit_text_recognition must be run on the main isolate. so I am doing the work in an async function but still, the UI is lagging in low-end models with high-resolution camera configuration. If I set the resolution to .medium, it works fine but the MLKit detection performance is bad. I am seeking some ideas to fine-tune this one. I am sharing my code below.


    import 'dart:async';
    import 'dart:io';

    import 'package:camera/camera.dart';
    import 'package:flutter/material.dart';
    import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
    import 'package:permission_handler/permission_handler.dart';
    import 'package:smartdevice/ui/scan_serial_number/serial_number_validator.dart';

    class CameraView extends StatefulWidget {
    Function(String)? onCapture;
    CameraView({super.key, this.onCapture});

    @override
    State<CameraView> createState() => _CameraViewState();
    }

    // Add the WidgetsBindingObserver mixin
    class _CameraViewState extends State<CameraView> with WidgetsBindingObserver {
    bool _isPermissionGranted = false;
    final _textRecognizer = TextRecognizer();
    bool _isProcessingImage = false;
    late final Future<void> _future;
    var result = '';
    Timer? _timer;

    // Add this controller to be able to control de camera
    CameraController? _cameraController;

    @override
    void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);

    _future = _requestCameraPermission();
    }

    // We should stop the camera once this widget is disposed
    @override
    void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _stopCamera();
    _timer?.cancel();
    _textRecognizer.close();
    super.dispose();
    }

    // Starts and stops the camera according to the lifecycle of the app
    @override
    void didChangeAppLifecycleState(AppLifecycleState state) {
    if (_cameraController == null || !_cameraController!.value.isInitialized) {
    return;
    }

    if (state == AppLifecycleState.inactive) {
    _stopCamera();
    } else if (state == AppLifecycleState.resumed &&
    _cameraController != null &&
    _cameraController!.value.isInitialized) {
    _startCamera();
    }
    }

    @override
    Widget build(BuildContext context) {
    final screenSize = MediaQuery.of(context).size;
    return FutureBuilder(
    future: _future,
    builder: (context, snapshot) {
    return Stack(
    children: [
    // Show the camera feed behind everything
    if (_isPermissionGranted)
    FutureBuilder<List<CameraDescription>>(
    future: availableCameras(),
    builder: (context, snapshot) {
    if (snapshot.hasData) {
    _initCameraController(snapshot.data!);
    if (_cameraController != null) {
    return SizedBox(
    width: screenSize.width,
    height: screenSize.height,
    child: CameraPreview(_cameraController!),
    );
    } else {
    return const CircularProgressIndicator();
    }
    } else {
    return const CircularProgressIndicator();
    }
    },
    ),
    ],
    );
    },
    );
    }

    Future<void> _requestCameraPermission() async {
    final status = await Permission.camera.request();
    _isPermissionGranted = status == PermissionStatus.granted;
    }

    void _initCameraController(List<CameraDescription> cameras) {
    if (_cameraController != null) {
    return;
    }
    // Select the first rear camera.
    CameraDescription? camera;
    for (var i = 0; i < cameras.length; i++) {
    final CameraDescription current = cameras;
    if (current.lensDirection == CameraLensDirection.back) {
    camera = current;
    break;
    }
    }

    if (camera != null) {
    _cameraSelected(camera);
    }
    }

    void _startCamera() {
    if (_cameraController != null) {
    _cameraSelected(_cameraController!.description);
    }
    }

    void _stopCamera() {
    if (_cameraController != null) {
    _cameraController?.dispose();
    }
    _timer?.cancel();
    }

    Future<void> _listenToCameraStream() async {
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
    if (_cameraController != null && !_isProcessingImage) {
    await _scanImage();
    }
    });
    }

    Future<void> _cameraSelected(CameraDescription camera) async {
    _cameraController = CameraController(
    camera,
    ResolutionPreset.max,
    enableAudio: false,
    fps: 30,
    );

    await _cameraController!.initialize();

    if (!mounted) {
    return;
    }
    setState(() {});
    _listenToCameraStream();
    }

    Future<void> _scanImage() async {
    if (_cameraController == null) return;
    _isProcessingImage = true;
    print('## Processing started.');

    try {
    final pictureFile = await _cameraController!.takePicture();

    final file = File(pictureFile.path);

    final inputImage = InputImage.fromFile(file);
    final recognizedText = await _textRecognizer.processImage(inputImage);

    List<String> texts = recognizedText.text.split('\n');

    for (final text in texts) {
    print('## $text');
    if (text.isValidSerialNumber) {
    print('## Found valid serial number $text');
    }
    }
    } catch (e) {
    print('## Error taking photos - ${e.toString()}');
    }
    print('## Processing ended.');
    _isProcessingImage = false;
    }
    }


    publish_to: 'none'
    version: 0.5.0

    environment:
    sdk: 3.5.3

    dependencies:
    bloc: 8.1.4
    equatable: 2.0.5
    flutter:
    sdk: flutter
    flutter_bloc: 8.1.6
    go_router: 14.2.0
    domain:
    path: domain
    data:
    path: data
    get_it: 7.7.0
    injectable: 2.4.2
    cached_network_image: 3.3.1
    lottie: 3.1.2
    flutter_markdown: 0.7.3
    url_launcher: 6.3.0
    flutter_localizations:
    sdk: flutter
    intl: 0.19.0
    youtube_player_iframe: 5.1.3
    device_info_plus: 9.1.2
    camera: 0.11.0
    permission_handler: ^11.3.1
    google_mlkit_text_recognition: ^0.13.1
    image: ^4.2.0

    dev_dependencies:
    test: 1.25.7 # workaround to have the dependency in the sub packages
    flutter_test:
    sdk: flutter
    flutter_lints: 4.0.0
    mocktail: 1.0.4
    injectable_generator: 2.6.1
    # Tool for generating type-safe routes with go_router
    go_router_builder: 2.7.1
    build_runner: 2.4.11
    melos: 6.1.0
    bloc_test: 9.1.7


    module:
    androidX: true
    androidPackage: com.liebherr.hau.smartdevice
    iosBundleIdentifier: com.liebherr.hau.smartdevice

    Continue reading...

Compartilhe esta Página