vlambda博客
学习文章列表

如何基于Flutter和Paddle Lite实现实时目标检测

很早之前接触到了飞桨(PaddlePaddle)以及PaddleDetection工具,被他们的简单易用吸引,同时,这些工具极大降低了训练模型的门槛并减少了所需时间,非常适合新手入门。在很多实际应用场景也有不俗的表现。


在端侧部署方面,Paddle Lite是飞桨产品栈中用于端侧高性能轻量化AI应用部署的推理引擎,给了移动端等场景更多可能。这款引擎允许我们在很多硬件平台上实现轻量化的高效预测,进行一次预测耗时较短,也不需要太多的计算资源。


那么如果我们想开发一款既能在本地进行预测又能在Android和iOS上面有一致体验的App的话,Flutter无疑是一个好选择。其作为开源移动UI框架已然成为跨平台移动开发一大趋势,在开发时可以保留状态进行热重载,内置许多令人眼前一亮的组件和漂亮的动画,同时还能保证性能达到和原生应用一样。也正因为这样,不少公司开始把自己的应用向Flutter迁移,有许多我们耳熟能详的App其实已经是基于Flutter开发。假如你已经对安卓原生开发十分熟悉的话,不妨去试试。


这次我们就基于Flutter来开发一个实时目标检测程序,这也得益于Flutter支持访问iOS和Android上的原生系统功能和系统SDK。


https://github.com/KernelErr/realtime-object-detector


其中内置了水果识别模型,下载下来就能直接编译体验。约定:

Flutter端:Flutter项目主目录。

Android端:项目的Android子目录,原生安卓。


开发环境


我们在开发的时候环境如下:

  • Flutter version 1.12.13+hotfix.8

  • Dart version 2.7.0

  • Android Studio (version 3.6)

  • Android toolchain - develop for Android devices (Android SDK version 29.0.3)


Flutter在更新的同时加入了越来越多新的特性,网上一些基于老版本的实现方法已经不太实用,我们需要进行一些修改。

准备模型


Paddle Lite需要通过opt工具生成其支持的轻量化模型,如果你手上已经有PaddleDetection训练出来的模型,那么你需要先在PaddleDetection导出模型,然后通过opt工具进行转换。


如果你有其他框架训练出来的模型,如caffe、tensorflow、onnx等,可以利用X2Paddle来转换。


假设我们已经得到了两个文件:

  • model.nb - 基于Yolov3 Tiny训练且已经通过opt优化好的模型

  • label - 模型预测一一对应的标签


如何在Flutter中支持Paddle Lite?


我们只需要通过Android Studio创建一个新的Flutter项目,这里我们假设名字是realtime_od。


准备Paddle Lite的预测库和模型文件


由于我们使用的是安卓原生代码,所以我们需要在Android端进行开发,而不是Flutter端。我们在Paddle Lite提供的预编译预测库里面下载需要的预编译库,放到Android端的相应文件夹内,和原生安卓的目录类似。之后我们继续在android文件夹内放置模型文件,在realtime_od/android/app/src/main/下面新建assets文件夹,并分别把模型和标签放到models和labels子文件夹内。这时候你的目录结果应该是这样:

如何基于Flutter和Paddle Lite实现实时目标检测

我们使用口罩模型作为样例,模型位置是:models/mask/model.nb,标签位置是:labels/mask_label_list。因此你需要在MainActivity里面赋值:

如何基于Flutter和Paddle Lite实现实时目标检测


禁用压缩


在生成APK的时候,我们的模型会被压缩,所以我们需要修改build.gradle配置文件来禁用assets文件夹的压缩。

如何基于Flutter和Paddle Lite实现实时目标检测


提供原生安卓支持


如果为了Flutter的支持,给Paddle Lite专门写一套Dart调用代码是工作巨大的,所以我们不妨直接基于官方的Demo进行修改。在Android端,我们直接使用了官方Demo中的代码,并在MainActivity内注册了Channel。

如何基于Flutter和Paddle Lite实现实时目标检测

由于Flutter中加入了进程安全机制,我们使用了一个MethodResultWrapper保证在主进程里面返回result。新版Flutter中你需要使用configureFlutterEngine而不是onCreate来注册组件。


使用实时影像


让我们来给Flutter提供来自摄像头的实时影像!添加一下Flutter的camera插件,Dart 已经有很多现成的包给我们使用:

如何基于Flutter和Paddle Lite实现实时目标检测

同时需要确保项目的最低Android SDK版本在21以上。在官方提供的Demo中,图片输入使用的是Bitmap图片,但是我们从插件得到的格式是android.graphics.ImageFormat.YUV_420_888,在Predictor类的最下面我们进行了相应的转换,转换代码的使用已经联系原作者获得授权。我们在其中使用了RenderScript进行高效计算,避免延迟过高。


显示实时图像并标注


大量的工作都花在了Android端上面,下面让我们来Flutter端做些工作。


在main.dart和object_detector.dart里面你可以发现我们调用Android端提供的方法,即loadModel以及detectObject。同时在DrawObjects类里面提供了标注目标的功能,代码相对比较简单,利用得到的预测结果进行画图。


Let’s run it!


这里使用的是群友提供的口罩模型,label文件里面只有两行,分别是戴口罩和未带口罩。我们在Android 9设备上面用PaddlePaddle官方示例图片测试一下。

如何基于Flutter和Paddle Lite实现实时目标检测

从日志上面可以看出Paddle Lite预测的时间是接近700 ms。

如何基于Flutter和Paddle Lite实现实时目标检测
 
更改模型和优化方案


如何使用其他模型


我们是参考群友的解决方案(参考链接里面给出)适配的YOLO v3,主要的修改在Predictor内的模型输入以及MainActivity的初始化。因为官方使用的是其他模型,输入的Shape和我们不一样,我们的是320。


如果你需要使用其他模型,请同步修改输入处的:

如何基于Flutter和Paddle Lite实现实时目标检测

以及输出处的:


标注函数处也需要做相应修改,修改main.dart:



怎么更快


实际上我们的模型还不够快,选择合适的模型,可以把预测时间缩短到更短。具体还是看自己的需要,Paddle Lite支持许多主流的模型,大家可以进行选用。


Trouble Shooting


记录的问题包括Flutter开发过程中遇到的和Paddle Lite使用中遇到的:


1. Methods marked with @UiThread must be executed on the main thread.


这是因为Flutter引入了进程安全,不能直接在子进程里面返回result,需要在主进程里面返回,网上现在有很多解决办法,我们的也是来自GitHub。


2. 错误: 不兼容的类型: MainActivity无法转换为FlutterEngine


很可能你看的教程是旧版本,请直接参考官方文档写原生安卓。我们在原生安卓开发的时候指定了v2。


3. Paddle Lite出现库错误


一开始以为是官方的问题,但是自己手动编译一次库就能解决。我已经内置了arm64的无问题的库。


4. 其他问题


官方文档 -> GitHub -> 搜索引擎,一般都能解决。

推荐阅读