Flutter&Rust#06 - 图片二维码识别

902 阅读3分钟

匠心千刃 是张风捷特烈通过 Flutter 打造的 全平台 工具产品。基于 fx 应用框架和 tolyui 视图框架构建的软件应用。匠心千刃中对于需要大量计算的场景,将使用 Rust 语言来实现。


本文介绍一下匠心千刃中的二维码图片识别。二维码已经和我们日常生活密不可分,乘车出行、消费付款、登录认证、扫描录入、分享信息、特别是疫情三年的健康码,让二维码充斥着电子生活的方方面面。

对于移动端来说,通过设备本身的后置镜头,可以非常方便地捕捉二维码图像进行识别。但对于桌面端或移动端本地的图片来说,识别二维码并不太方便。本文将借助 Rust 的能力,基于 Flutter 构建全平台的,图片二维码识别功能。


1. 交互界面

交互界面如下,用户选择一张图片二维码,然后应用会自动识别二维码中存储的信息。如下所示:

[1] . 支持选择或拖拽拾取图片
[2] . 图片选择成功之后,解析图片二维码,展示其中的文字信息
[3] . 可以移除图片,继续选择其他图片识别


2. 核心识别能力

对图片二维码的识别能力将借助 Rust 的 能力。这里使用了 quircs

---->[rust/Cargo.toml]----
quircs = "=0.10.2"

rust 的代码实现也比较简单,通过 detection_qr 函数处理。输入一个文件路径,返回二维码中的字符串数据列表。

pub fn detection_qr(input: &str) -> Vec<String> {
    let img = image::open(input).unwrap();
    let img_gray = img.into_luma8();
    let mut decoder = quircs::Quirc::default();
    let codes = decoder.identify(img_gray.width() as usize, img_gray.height() as usize, &img_gray);
    let mut ret: Vec<String> = Vec::new();
    for code in codes {
        let code = code.expect("failed to extract qr code");
        let decoded = code.decode().expect("failed to decode qr code");
        let str = std::str::from_utf8(&decoded.payload).unwrap().to_string();
        ret.push(str);
    }
    ret
}

之所以是字符串数据列表,是因为一张图片中可能存在多个多个二维码。它们都可以被识别,如下所示:

通过 flutter_rust_bridge_codegen generate 命令,就可以生成对应的 Dart 桥接代码在 Flutter 中使用:


3. Flutter 视图层处理

需求的核心功能已经实现了,下面界面交互的界面处理就交给 Flutter 了。选择图片使用 file_picker 插件:触发下面的函数可以选择一张图片:

void _onTapSelect() async {
  FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.image);
  if (result != null && result.files.isNotEmpty) {
    String? path = result.files.first.path;
    if (path != null) {
      _onFileSelect(path);
    }
  }
}

另外,拖拽文件使用 desktop_drop 插件。将目标区域嵌套 DropTarget 组件,在 onDragDone 回调函数在可以感知对应文件的路径:

DropTarget(
  child: child,
  onDragEntered: (v) {},
  onDragExited: (v) {},
  onDragDone: (value) {
    if(value.files.isNotEmpty){
      String path = value.files.first.path;
      _onFileSelect(path);
    }
  },
);

无论是点击选择还是拖拽选择,最终的结果都是得到一个图片文件路径,这个路径将成为向 Rust 传输的数据。如下所示,通过 detectionQr 方法得到结果数据,触发重新渲染即可:

void _onFileSelect(String path) async {
  List<String> result = await detectionQr(input: path);
  filename = p.basename(path);
  _result = result.join('\n');
  setState(() {});
}

4.尾声

总的来看,过程还是非常简单的。Flutter/Dart 端像是原料的提供商,Rust 则像加工厂,将原料加工完毕返回给 Flutter 渲染。

随着匠心千刃的后续研发,会遇到更多 Dart 和 Rust 桥接的知识。未来匠心千刃也会作为一个工具软件,供用户随意使用。更多精彩内容,敬请期待~
更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。