Android-SDK
简介
此文档介绍飞桨开源模型SDK如何进行开发集成
系统支持
Android 版本:支持 Android 5.0(API 21) 及以上
硬件:支持 arm64-v8a 和 armeabi-v7a,暂不支持模拟器
预测图像时运行内存不能过小,一般大于模型资源文件夹大小的3倍。
快速开始
文件结构说明
.EasyEdge-Android-SDK
├── app
│ ├── src/main
│ │ ├── assets
│ │ │ ├── demo # demo配置文件夹
│ │ │ │ └── conf.json
│ │ │ ├── infer # 模型资源文件夹
│ │ │ │ ├── model
│ │ │ │ ├── params
│ │ │ │ ├── label_list.txt
│ │ │ │ └── infer_cfg.json
│ │ ├── java/com.baidu.ai.edge/demo
│ │ │ ├── infertest # 通用ARM精简版测试
│ │ │ │ ├── TestInferClassifyTask.java # 图像分类
│ │ │ │ ├── TestInferDetectionTask.java # 物体检测
│ │ │ │ ├── TestInferSegmentTask.java # 实例分割
│ │ │ │ ├── TestInferPoseTask.java # 姿态估计
│ │ │ │ ├── TestInferOcrTask.java # OCR
│ │ │ │ └── MainActivity.java # 精简版启动 Activity
│ │ │ ├── MainActivity.java # Demo APP 启动 Activity
│ │ │ ├── CameraActivity.java # 摄像头UI逻辑
│ │ │ └── ...
│ │ └── ...
│ ├── libs
│ │ ├── armeabi-v7a # v7a的依赖库
│ │ ├── arm64-v8a # v8a的依赖库
│ │ └── easyedge-sdk.jar # jar文件
│ └── ...
├── camera_ui # UI模块,包含相机逻辑
├── README.md
└── ... # 其他 gradle 等工程文件
测试精简版
精简版忽略摄像头等UI逻辑,可兼容如无摄像头的开发板测试。
支持以下硬件环境的精简版测试:
- 通用ARM:图像分类、物体检测、实例分割、姿态估计、文字识别
示例代码位于 app 模块下 infertest 目录,修改 app/src/main/AndroidManifest.xml 中的启动 Activity 开启测试。
<!-- 以通用ARM为例 -->
<activity android:name=".infertest.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Demo APP 检测模型运行示例 | 精简版检测模型运行示例 |
---|---|
![]() |
![]() |
SDK使用说明
本节介绍如何将 SDK 接入开发者的项目中使用。
集成指南
步骤一:依赖库集成 步骤二:添加必要权限 步骤三:混淆配置(可选)
依赖库集成
A. 项目中未集成其他 jar 包和 so 文件:
// 1. 复制 app/libs 至项目的 app/libs 目录
// 2. 参考 app/build.gradle 配置 NDK 可用架构和 so 依赖库目录
android {
...
defaultConfig {
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
B. 项目中已集成其他 jar 包,未集成 so 文件:
// 1. 复制 app/libs/easyedge-sdk.jar 与其他 jar 包同目录
// 2. 复制 app/libs 下 armeabi-v7a 和 arm64-v8a 目录至 app/src/main/jniLibs 目录下
// 3. 参考 app/build.gradle 配置 NDK 可用架构
android {
...
defaultConfig {
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
}
C. 项目中已集成其他 jar 包和 so 文件:
// 1. 复制 app/libs/easyedge-sdk.jar 与其他 jar 包同目录
// 2. 融合 app/libs 下 armeabi-v7a 和 arm64-v8a 下的 so 文件与其他同架构 so 文件同目录
// 3. 参考 app/build.gradle 配置 NDK 可用架构
android {
...
defaultConfig {
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a' // 只支持 v7a 和 v8a 两种架构,有其他架构需删除
}
}
}
添加权限
参考 app/src/main/AndroidManifest.xml 中配置的权限。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
混淆规则(可选)
请不要混淆 jar 包文件,参考 app/proguard-rules.pro 配置。
-keep class com.baidu.ai.edge.core.*.*{ *; }
调用流程示例
以通用ARM的图像分类预测流程为例,详细说明请参考后续章节:
try {
// step 1: 准备配置类
InferConfig config = new InferConfig(context.getAssets(), "infer");
// step 2: 准备预测 Manager
InferManager manager = new InferManager(context, config, "");
// step 3: 准备待预测的图像,必须为 Bitmap.Config.ARGB_8888 格式,一般为默认格式
Bitmap image = getFromSomeWhere();
// step 4: 预测图像
List<ClassificationResultModel> results = manager.classify(image, 0.3f);
// step 5: 解析结果
for (ClassificationResultModel resultModel : results) {
Log.i(TAG, "labelIndex=" + resultModel.getLabelIndex()
+ ", labelName=" + resultModel.getLabel()
+ ", confidence=" + resultModel.getConfidence());
}
// step 6: 释放资源。预测完毕请及时释放资源
manager.destroy();
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
初始化
准备配置类 芯片与配置类对应关系:
- 通用ARM:InferConfig
// 示例
// 参数二为芯片对应的模型资源文件夹名称
InferConfig config = new InferConfig(context.getAssets(), "infer");
准备预测 Manager 芯片与 Manager 对应关系:
- 通用ARM:InferManager
// 示例
// 参数二为配置类对象
// 参数三保持空字符串即可
InferManager manager = new InferManager(context, config, "");
注意
- 同一时刻只能有且唯一有效的 Manager,若要新建一个 Manager,之前创建的 Manager 需先调用 destroy() 销毁;
- Manager 的任何方法都不能在 UI 线程调用;
- Manager 的任何成员变量及方法由于线程同步问题,都必须在同一个线程中执行;
预测图像
本节介绍各种模型类型的预测函数及结果解析。
注意 预测函数可以多次调用,但必须在同一个线程中,不支持并发 预测函数中的 confidence 非必需,默认使用模型推荐值。填 0 可返回所有结果 待预测的图像必须为 Bitmap.Config.ARGB_8888 格式的 Bitmap
图像分类
// 预测函数
List<ClassificationResultModel> classify(Bitmap bitmap) throws BaseException;
List<ClassificationResultModel> classify(Bitmap bitmap, float confidence) throws BaseException;
// 返回结果
ClassificationResultModel
- label: 分类标签,定义在label_list.txt中
- labelIndex: 分类标签对应的序号
- confidence: 置信度,0-1
物体检测
// 预测函数
List<DetectionResultModel> detect(Bitmap bitmap) throws BaseException;
List<DetectionResultModel> detect(Bitmap bitmap, float confidence) throws BaseException;
// 返回结果
DetectionResultModel
- label: 标签,定义在label_list.txt中
- confidence: 置信度,0-1
- bounds: Rect,包含左上角和右下角坐标,指示物体在图像中的位置
实例分割
// 预测函数
List<SegmentationResultModel> segment(Bitmap bitmap) throws BaseException;
List<SegmentationResultModel> segment(Bitmap bitmap, float confidence) throws BaseException;
// 返回结果
SegmentationResultModel
- label: 标签,定义在label_list.txt中
- confidence: 置信度,0-1
- lableIndex: 标签对应的序号
- box: Rect,指示物体在图像中的位置
- mask: byte[],表示原图大小的0,1掩码,绘制1的像素即可得到当前对象区域
- maskLEcode: mask的游程编码
关于 maskLEcode 的解析方式可参考 http demo
姿态估计
// 预测函数
List<PoseResultModel> pose(Bitmap bitmap) throws BaseException;
// 返回结果
PoseResultModel
- label: 标签,定义在label_list.txt中
- confidence: 置信度,0-1
- points: Pair<Point, Point>, 2个点构成一条线
文字识别
// 预测函数
List<OcrResultModel> ocr(Bitmap bitmap) throws BaseException;
List<OcrResultModel> ocr(Bitmap bitmap, float confidence) throws BaseException;
// 返回结果
OcrResultModel
- label: 识别出的文字
- confidence: 置信度,0-1
- points: List<Point>, 文字所在区域的点位
模型替换说明
模型资源文件位于 app/src/main/assets 目录下,开发者可替换运行其他模型:
- infer/model:模型网络结构文件,对应Paddle1.x的
__model__
,Paddle2.x的model.pdmodel
- infer/params:模型网络参数文件,对应Paddle1.x的
__params__
,Paddle2.x的model.pdiparams
- infer/label_list.txt:label文件
- infer/infer_cfg.json:模型推理的预处理、后处理配置文件,具体见下
- demo/conf.json:【非必需】Demo App 配置文件,建议配置 modelName,缺少不影响运行
infer_cfg.json说明
其中,非标记【必须】的可不填
{
"version": 1,
"model_info": {
"best_threshold": 0.3, // 默认0.3
"model_kind": 1, // 【必须】 1-分类,2-检测,6-实例分割,14-语义分割,401-人脸,402-姿态
},
"pre_process": { // 【必须】
// 归一化, 预处理会把图像 (origin_img - mean) * scale
"skip_norm": false, // 默认为false, 如果设置为true,不做mean scale处理
"mean": [123, 123, 123], // 【必须】
"scale": [0.017, 0.017, 0.017], // 【必须】
"color_format": "RGB", // BGR 【必须】
"channel_order": "CHW", // HWC
// 大小相关
"resize": [300, 300], // w, h 【必须】
"rescale_mode": "keep_size", // 默认keep_size, keep_ratio, keep_ratio2, keep_raw_size, warp_affine
"max_size": 1366, // keep_ratio 用。如果没有提供,则用 resize[0]
"target_size": 800, // keep_ratio 用。如果没有提供,则用 resize[1]
"raw_size_range": [100, 10000], // keep_raw_size 用
"warp_affine_keep_res": // warp_affine模式使用,默认为false
"center_crop_size": [224, 224], // w, h, 如果需要做center_crop,则提供,否则,无需提供该字段
"padding": false,
"padding_mode": "padding_align32", // 【非必须】默认padding_align32, 其他可指定:padding_fill_size
"padding_fill_size": [416, 416], // 【非必须】仅padding_fill_size模式下需要提供, [fill_size_w, fill_size_h], 这里padding fill对齐paddle detection实现,在bottom和right方向实现补齐
"padding_fill_value": [114, 114, 114] // 【非必须】仅padding_fill_size模式下需要提供
// 其他
"letterbox": true,
"ocr_rec_resize": [320, 48], // 【非必须】仅ocr需提供
"ocr_rec_batch_num": 6, // 【非必须】仅ocr需提供,默认为1
}
}
预处理的顺序如下(没有的流程自动略过):
- 灰度图 -> rgb图变换
- resize 尺寸变换
- center_crop
- rgb/bgr变换
- padding_fill_size
- letterbox(画个厚边框,填上黑色)
- chw/hwc变换
- 归一化:mean, scale
- padding_align32
rescale_mode说明:
- keep_size: 将图片缩放到resize指定的大小
- keep_ratio: 将图片按比例缩放,长边不超过max_size,短边不超过target_size
- keep_raw_size: 保持原图尺寸,但必须在raw_size_range之间
- warp_affine: 仿射变换,可以设置warp_affine_keep_res指定是否keep_res,在keep_res为false场景下,宽高通过resize字段指定
错误码
错误码 | 错误描述 | 详细描述及解决方法 |
---|---|---|
1001 | assets 目录下用户指定的配置文件不存在 | SDK可以使用assets目录下config.json作为配置文件。如果传入的config.json不在assets目录下,则有此报错 |
1002 | 用户传入的配置文件作为json解析格式不准确,如缺少某些字段 | 正常情况下,demo中的config.json不要修改 |
19xx | Sdk内部错误 | 请与百度人员联系 |
2001 | XxxxMANAGER 只允许一个实例 | 如已有XxxxMANAGER对象,请调用destory方法 |
2002 | XxxxMANAGER 已经调用过destory方法 | 在一个已经调用destory方法的DETECT_MANAGER对象上,不允许再调用任何方法 |
2003 | 传入的assets下模型文件路径为null | XxxxConfig.getModelFileAssetPath() 返回为null。由setModelFileAssetPath(null)导致 |
2011 | libedge-xxxx.so 加载失败 | System.loadLibrary("edge-xxxx"); libedge-xxxx.so 没有在apk中。CPU架构仅支持armeabi-v7a arm-v8a |
2012 | JNI内存错误 | heap的内存不够 |
2103 | license过期 | license失效或者系统时间有异常 |
2601 | assets 目录下模型文件打开失败 | 请根据报错信息检查模型文件是否存在 |
2611 | 检测图片时,传递至引擎的图片二进制与长宽不符合 | 具体见报错信息 |
27xx | Sdk内部错误 | 请与百度人员联系 |
28xx | 引擎内部错误 | 请与百度人员联系 |
29xx | Sdk内部错误 | 请与百度人员联系 |
3000 | so加载错误 | 请确认所有so文件存在于apk中 |
3001 | 模型加载错误 | 请确认模型放置于能被加载到的合法路径中,并确保config.json配置正确 |
3002 | 模型卸载错误 | 请与百度人员联系 |
3003 | 调用模型错误 | 在模型未加载正确或者so库未加载正确的情况下调用了分类接口 |
50xx | 在线模式调用异常 | 请与百度人员联系 |
报错日志收集
通常 Logcat 可以看见日志及崩溃信息,若设备无法获取日志信息,可使用 Demo 中的 xCrash 工具:
// 1. 引入 app/build.gradle 的 xCrash 依赖
android {
...
dependencies {
implementation 'com.iqiyi.xcrash:xcrash-android-lib:2.4.5' // 可以保存崩溃信息,默认未引入
...
}
}
// 2. 启用日志收集。日志将保存在 /sdcard/<包名>/xCrash
// app/src/main/java/com.baidu.ai.edge/demo/MyApplication.java
protected void attachBaseContext(Context context) {
// 日志保存位置
String basePath = Environment.getExternalStorageDirectory().toString() + "/" + context.getPackageName();
// 启用
XCrash.InitParameters params = new XCrash.InitParameters();
params.setAppVersion(BaseManager.VERSION);
params.setLogDir(basePath + "/xCrash");
XCrash.init(this, params);
}