0

I'm also new to flutter and tesseract so might be a problem with my understanding.

I am wanting to make an app that takes an image from my gallery and can read the text from them.

I'm using Google ML Kit Text Recognition V2 for this, however I want to account for cases where text is not correctly facing and so need the OSD, specifically the orientation. I have been able to get this using the CLI and pytesseract with a simple python script but have been having trouble doing the same with flutter.

I'm pretty sure that the tessdata is in the correct directory. I've created a assets/tessdata/tessdata_config.json and added the necessary traineddata files into assets/tessdata directory and added both to the pubspec.yaml.

"main.dart"

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'dart:io';
import 'package:google_mlkit_text_recognition/google_mlkit_text_recognition.dart';
import 'package:flutter_application_cards/preprocess_img.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key, this.imgPicker});

  final ImagePicker? imgPicker;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FF photos',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: MyHomePage(
        title: 'FF photos',
        imagePicker: imgPicker ?? ImagePicker(),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title, required this.imagePicker});

  final String title;
  final ImagePicker imagePicker;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  XFile? _mediaFile;
  String _recognizedText = '';

  /// Picks an image from the selected source (Gallery or Camera)
  Future<void> _pickImage(ImageSource source) async {
    try {
      final XFile? pickedFile = await widget.imagePicker.pickImage(source: source);
      if (pickedFile != null) {
        setState(() {
          _mediaFile = pickedFile;
        });
        _processImage(File(pickedFile.path));
      }
    } catch (e) {
      print('Error picking image: $e');
    }
  }

  /// Processes the image and extracts text using Google ML Kit
  Future<void> _processImage(File imageFile) async {
    // Put preprocessing function in here
    await isImgBlurry(imageFile);
    final inputImage = InputImage.fromFile(imageFile);
    final textRecognizer = TextRecognizer();

    try {
      final RecognizedText recognizedText = await textRecognizer.processImage(inputImage);
      setState(() {
        _recognizedText = recognizedText.text;
      });
      print('Recognized Text:\n$_recognizedText');
    } catch (e) {
      print('Error recognizing text: $e');
    } finally {
      textRecognizer.close();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _mediaFile == null
                ? const Text('No image selected.')
                : Image.file(File(_mediaFile!.path), height: 250),
            const SizedBox(height: 20),
            ElevatedButton.icon(
              onPressed: () => _pickImage(ImageSource.gallery),
              icon: const Icon(Icons.photo_library),
              label: const Text('Pick from Gallery'),
            ),
            const SizedBox(height: 10),
            ElevatedButton.icon(
              onPressed: () => _pickImage(ImageSource.camera),
              icon: const Icon(Icons.camera_alt),
              label: const Text('Take Photo'),
            ),
            const SizedBox(height: 20),
            _recognizedText.isNotEmpty
                ? Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: Text(_recognizedText),
                  )
                : Container(),
          ],
        ),
      ),
    );
  }
}

"preprocess_img.dart"

import 'dart:io';

import 'package:opencv_core/opencv.dart';
import 'package:flutter_tesseract_ocr/flutter_tesseract_ocr.dart';

Future<void> isImgBlurry(File img, {double threshold = 100.0}) async {
  final test = imread(img.path);

  // Convert to grayscale
  final Mat grayImg = cvtColor(test, COLOR_BGR2GRAY);

  // Use laplacian method
  Mat lapImg = laplacian(grayImg, MatType.CV_64F);
  final double variance = lapImg.variance().val1;

  if (variance < threshold) {
    print("$variance, blurry");
  }
  else {
    print("Not blurry");
  }

  String osdData = await FlutterTesseractOcr.extractText(img.path, language: 'eng', args: {"psm": "0"});
  // psm 0 = Orientation and script detection (OSD) only.
  // String is empty
  print("Image osd:\n$osdData");
  
  int orientation = 0;
  int rotate = 0;
  String script = "N/A";

  RegExp orientationRegExp = RegExp(r'Orientation in degrees: (\d+)');
  RegExp rotateRegExp = RegExp(r'Rotate: (\d+)');
  RegExp scriptRegExp = RegExp(r'Script: (\w+)');

  Match? orientationMatch = orientationRegExp.firstMatch(osdData);
  Match? rotateMatch = rotateRegExp.firstMatch(osdData);
  Match? scriptMatch = scriptRegExp.firstMatch(osdData);

  if (orientationMatch != null) {
    orientation = int.parse(orientationMatch.group(1)!);
  }
  if (rotateMatch != null) {
    rotate = int.parse(rotateMatch.group(1)!);
  }
  if (scriptMatch != null) {
    script = scriptMatch.group(1)!;
  }

  // Output the extracted information
  print("Detected Orientation: $orientation degrees");
  print("Rotate by: $rotate degrees to correct");
  print("Detected Script: $script");
}

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.