Flutter数字墨水识别插件learning_digital_ink_recognition的使用

发布于 1周前 作者 nodeper 来自 Flutter

Flutter数字墨水识别插件learning_digital_ink_recognition的使用

概述

ML Kit的数字墨水识别使我们能够在Flutter中轻松实现手写文本和草图的识别。这项技术可以用于识别多种语言的手写字符,并且支持离线操作。

开始使用

添加依赖

在你的Flutter项目中添加learning_digital_ink_recognition依赖:

$ flutter pub add learning_digital_ink_recognition

或者在pubspec.yaml文件中添加:

dependencies:
  learning_digital_ink_recognition: ^0.0.1

然后运行:

flutter pub get

初始化

在使用之前,需要初始化识别对象并确保模型已下载。

import 'package:learning_digital_ink_recognition/learning_digital_ink_recognition.dart';

DigitalInkRecognition recognition = DigitalInkRecognition(model: 'en-US');

// 确保模型已经下载
bool isDownloaded = await DigitalInkModelManager.isDownloaded('en-US');
if (!isDownloaded) {
  await DigitalInkModelManager.download('en-US');
}

await recognition.start(writingArea: Size(width, height));

使用示例

以下是一个完整的示例应用程序,展示了如何使用该插件进行手写识别。

主程序

import 'package:flutter/material.dart';
import 'package:learning_digital_ink_recognition/learning_digital_ink_recognition.dart';
import 'package:provider/provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.lightBlue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
        primaryTextTheme: TextTheme(
          headline6: TextStyle(color: Colors.white),
        ),
      ),
      home: ChangeNotifierProvider(
        create: (_) => DigitalInkRecognitionState(),
        child: DigitalInkRecognitionPage(),
      ),
    );
  }
}

页面状态管理

class DigitalInkRecognitionPage extends StatefulWidget {
  @override
  _DigitalInkRecognitionPageState createState() => _DigitalInKRecognitionPageState();
}

class _DigitalInkRecognitionPageState extends State<DigitalInkRecognitionPage> {
  final String _model = 'en-US';
  DigitalInkRecognitionState get state => Provider.of(context, listen: false);
  late DigitalInkRecognition _recognition;
  double get _width => MediaQuery.of(context).size.width;
  double _height = 360;

  @override
  void initState() {
    _recognition = DigitalInkRecognition(model: _model);
    super.initState();
  }

  @override
  void dispose() {
    _recognition.dispose();
    super.dispose();
  }

  Future<void> _init() async {
    await _recognition.start(writingArea: Size(_width, _height));
    await _checkModel();
  }

  Future<void> _reset() async {
    state.reset();
    await _recognition.start(writingArea: Size(_width, _height));
  }

  Future<void> _checkModel() async {
    bool isDownloaded = await DigitalInkModelManager.isDownloaded(_model);
    if (!isDownloaded) {
      await DigitalInkModelManager.download(_model);
    }
  }

  Future<void> _actionDown(Offset point) async {
    state.startWriting(point);
    await _recognition.actionDown(point);
  }

  Future<void> _actionMove(Offset point) async {
    state.writePoint(point);
    await _recognition.actionMove(point);
  }

  Future<void> _actionUp() async {
    state.stopWriting();
    await _recognition.actionUp();
  }

  Future<void> _startRecognition() async {
    if (state.isNotProcessing) {
      state.startProcessing();
      await _checkModel();
      state.data = await _recognition.process();
      state.stopProcessing();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text('ML Digital Ink Recognition'),
      ),
      body: Column(
        children: [
          Builder(
            builder: (_) {
              _init();
              return GestureDetector(
                onScaleStart: (details) async => await _actionDown(details.localFocalPoint),
                onScaleUpdate: (details) async => await _actionMove(details.localFocalPoint),
                onScaleEnd: (details) async => await _actionUp(),
                child: Consumer<DigitalInkRecognitionState>(
                  builder: (_, state, __) => CustomPaint(
                    painter: DigitalInkPainter(writings: state.writings),
                    child: Container(
                      width: _width,
                      height: _height,
                    ),
                  ),
                ),
              );
            },
          ),
          SizedBox(height: 20),
          ElevatedButton(
            onPressed: _startRecognition,
            child: Text('Start Recognition'),
          ),
          SizedBox(height: 5),
          ElevatedButton(
            onPressed: _reset,
            child: Text('Reset Canvas'),
          ),
          SizedBox(height: 15),
          Center(
            child: Consumer<DigitalInkRecognitionState>(builder: (_, state, __) {
              if (state.isNotProcessing && state.isNotEmpty) {
                return Center(
                  child: Container(
                    padding: EdgeInsets.symmetric(horizontal: 18),
                    child: Text(
                      state.toCompleteString(),
                      textAlign: TextAlign.center,
                      style: TextStyle(fontSize: 18),
                    ),
                  ),
                );
              }
              if (state.isProcessing) {
                return Center(
                  child: Container(
                    width: 36,
                    height: 36,
                    color: Colors.transparent,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  ),
                );
              }
              return Container();
            }),
          ),
          Expanded(child: Container()),
        ],
      ),
    );
  }
}

状态管理类

class DigitalInkRecognitionState extends ChangeNotifier {
  List<List<Offset>> _writings = [];
  List<RecognitionCandidate> _data = [];
  bool isProcessing = false;

  List<List<Offset>> get writings => _writings;
  List<RecognitionCandidate> get data => _data;
  bool get isNotProcessing => !isProcessing;
  bool get isEmpty => _data.isEmpty;
  bool get isNotEmpty => _data.isNotEmpty;

  List<Offset> _writing = [];

  void reset() {
    _writings = [];
    notifyListeners();
  }

  void startWriting(Offset point) {
    _writing = [point];
    _writings.add(_writing);
    notifyListeners();
  }

  void writePoint(Offset point) {
    if (_writings.isNotEmpty) {
      _writings[_writings.length - 1].add(point);
      notifyListeners();
    }
  }

  void stopWriting() {
    _writing = [];
    notifyListeners();
  }

  void startProcessing() {
    isProcessing = true;
    notifyListeners();
  }

  void stopProcessing() {
    isProcessing = false;
    notifyListeners();
  }

  set data(List<RecognitionCandidate> data) {
    _data = data;
    notifyListeners();
  }

  @override
  String toString() {
    return isNotEmpty ? _data.first.text : '';
  }

  String toCompleteString() {
    return isNotEmpty ? _data.map((c) => c.text).toList().join(', ') : '';
  }
}

结论

通过以上代码,你可以创建一个简单的手写识别应用。用户可以在屏幕上书写文字或绘制图形,应用会自动识别并显示结果。更多细节和完整示例可以参考官方示例项目


更多关于Flutter数字墨水识别插件learning_digital_ink_recognition的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数字墨水识别插件learning_digital_ink_recognition的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter项目中集成并使用learning_digital_ink_recognition插件的示例代码。这个插件允许你进行数字墨水的识别,通常用于手写识别等功能。

首先,确保你的Flutter项目已经创建好,并且你已经在pubspec.yaml文件中添加了learning_digital_ink_recognition依赖:

dependencies:
  flutter:
    sdk: flutter
  learning_digital_ink_recognition: ^最新版本号  # 替换为实际发布的最新版本号

然后运行flutter pub get来获取依赖。

接下来是如何在你的Flutter应用中使用learning_digital_ink_recognition插件的示例代码。

1. 导入插件

在你的Dart文件中(例如main.dart),导入插件:

import 'package:flutter/material.dart';
import 'package:learning_digital_ink_recognition/learning_digital_ink_recognition.dart';

2. 创建UI界面

创建一个简单的UI界面来绘制和识别数字墨水。

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('数字墨水识别示例'),
        ),
        body: InkRecognitionScreen(),
      ),
    );
  }
}

class InkRecognitionScreen extends StatefulWidget {
  @override
  _InkRecognitionScreenState createState() => _InkRecognitionScreenState();
}

class _InkRecognitionScreenState extends State<InkRecognitionScreen> {
  final List<Offset> _points = [];
  final GlobalKey _globalKey = GlobalKey();
  String _recognitionResult = '';

  void _handlePanUpdate(DragUpdateDetails details) {
    setState(() {
      _points.add(details.globalPosition);
    });
  }

  void _handlePanEnd(DragEndDetails details) async {
    setState(() {
      _points.add(null);  // 添加null表示路径结束
    });

    // 获取识别结果
    final result = await LearningDigitalInkRecognition.recognize(_points);
    setState(() {
      _recognitionResult = result.text ?? '无法识别';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        GestureDetector(
          key: _globalKey,
          onPanUpdate: _handlePanUpdate,
          onPanEnd: _handlePanEnd,
          behavior: HitTestBehavior.translucent,
          child: CustomPaint(
            size: Size(double.infinity, double.infinity),
            painter: InkPainter(_points),
          ),
        ),
        SizedBox(height: 20),
        Text('识别结果: $_recognitionResult'),
      ],
    );
  }
}

class InkPainter extends CustomPainter {
  final List<Offset> points;

  InkPainter(this.points);

  @override
  void paint(Canvas canvas, Size size) {
    final Paint paint = Paint()
      ..color = Colors.black
      ..strokeWidth = 4.0
      ..style = PaintingStyle.stroke;

    if (points.isEmpty || points.last == null) return;

    for (int i = 0; i < points.length - 1; i++) {
      canvas.drawLine(points[i]!, points[i + 1]!, paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}

3. 运行应用

现在你可以运行你的Flutter应用,使用手指或触控笔在屏幕上书写,插件将尝试识别你书写的文字并显示识别结果。

注意事项

  1. 插件版本:确保你使用的是最新版本的learning_digital_ink_recognition插件,因为插件的API和功能可能会随着版本更新而变化。
  2. 错误处理:在实际应用中,你应该添加更多的错误处理逻辑,以处理可能的识别失败或异常。
  3. 性能优化:对于复杂的绘制和识别任务,你可能需要考虑性能优化,例如限制绘制的帧率或优化识别算法。

这个示例代码提供了一个基本框架,你可以根据需要进行扩展和修改。

回到顶部