Android_SDK
简介
本文介绍SDK的的功能使用,即下载包中的sdk module。
SDK为货架拼接云端非实时API和手机端实时拼接的封装,无任何额外功能。如果有和API文档不符的地方,以SDK为准。
支持Android Level 22及以上编译和使用。
Release Notes
时间 | 版本 | 说明 |
---|---|---|
2022.12.22 | 5.0.0 | 更名为门店拜访SDK,新增门脸文字识别功能、防窜拍功能 |
2021.12.22 | 4.1.0 | 新增手机端实时拼接模糊图像检测功能 |
2021.08.20 | 4.0.0 | 新增手机端实时拼接功能 |
2021.03.09 | 3.0.1 | 新增光线和手机方向检测功能 |
2020.12.30 | 3.0.0 | 新增支持拍摄图片,云端拼接功能 |
2020.11.12 | 2.0.0 | 新增支持排面统计占比 |
2019.08.30 | 1.0.0 | 支持拍摄视频,端上抽帧,云端拼接 |
测试
获取鉴权
- 进入EasyDL零售版的百度智能云控制台应用列表页面,如下图所示:
- 如果还未创建应用,请点击「创建应用」按钮进行创建。创建应用后,参考鉴权参考文档,使用API Key(AK)和Secret Key(SK)获取access_token
1{
2 "ak": "Mz0zhObvEZ6lnG1K3renXXXX", // API Key的值
3 "sk": "188fRHYvLPmlPrNCDpBnkhL3ydXXXXX", // Secret Key的值
4 "apiUrl": "https://aip.baidubce.com/rpc/2.0/ai_custom_retail/v1/detection/XXXX" // 定制商品检测服务API
5}
正常情况下,启动的app及其功能和扫描二维码一致
demo的请求和结果会放在/sdcard/com.baidu.ai.easydl.montage中
测试云端拼接mini demo
测试app通过后,可以修改app/src/main/AndroidManifest.xml 内的启动app,修改为 "com.baidu.ai.easydl.minidemo.MiniActivity"
MiniActivity中有3个task,测试时需要填入 Appkey, AppSecret, ApiUrl信息
- ApiTestAsyncTask ,测试简单流程。
- QueryAsyncTask , 测试查询列表。
- RequestTestAsyncTask,测试assets/request下的图片输入。这个目录可以从SD卡中/sdcard/com.baidu.ai.easydl.montage/X/request复制。
手机端实时拼接调用流程
第1步:初始化
1)【获取实例】
2)【初始化API】
第2步:对比图片
新图片(now)在参与实时拼接前需先与上一张参与拼接的图片(last)进行对比,如果now与last的对比特征合法则可以成功拼接,否则无法得到理想实时拼接结果
第3步:实时拼接
SDK会寻找now.jpg并进行实时拼接得到新结果
第4步:上传云端,得到结果
SDK 调用
手机端实时拼接各流程通过 MobileStitchAPI 调用,具体使用和返回参数见下
初始化
MobileStitchAPI不支持多线程,且仅有一个实例以保证实时拼接过程中的正确文件操作。
获取实例
1/**
2 * 获取实例
3 *
4 * @param appKey 网页上的应用的appkey
5 * @param secretKey 网页上的应用的appSecret
6 * @param apiUrl 商品检测服务API
7 */
8public static MobileStitchAPI getInstance(String appKey, String secretKey, String apiUrl);
9
10/**
11 * 获取实例
12 *
13 * @param appKey 网页上的应用的appkey
14 * @param secretKey 网页上的应用的appSecret
15 * @param apiUrl 商品检测服务API
16 * @param numConcurrency 同时调用商品检测API的并发数
17 */
18public static MobileStitchAPI getInstance(String appKey, String secretKey, String apiUrl, int numConcurrency);
初始化API
1/**
2 * 初始化API
3 *
4 * @param workDirPath 保存实时拼接过程产生的各类文件的路径
5 */
6public void init(String workDirPath);
对比图片
1/**
2 * 对比当前图片与上一张参与拼接的图片
3 *
4 * @param currentImgBitmap 当前要参与对比的图片
5 * @param firstFrame 是否是第一帧
6 */
7public void compareImages(Bitmap currentImgBitmap, boolean firstFrame);
- 第一帧的定义取决于上一张参与拼接的图片(last)是否已经被对比过。假设有图片A和B,先用A与last对比,且last是初次被对比,此时firstFrame应为true,再用B与last对比,此时firstFrame应为false。
- 异步回调:MobileStitchAPIListener.onImagesCompared(CompareResult)
CompareResult
1// 当前图片相对上一张参与拼接的图片的方位,参考MobileStitchAPI.DIRECTION_{LEFT|UP|RIGHT|DOWN|UNKNOWN}
2public int getDirection();
3
4// 对比结果中的方位是否合法,非法的方位将无法完成拼接
5public boolean isDirectionValid();
6
7/**
8 * 是否需要判断方向,如当拍摄完图片过近时,direction可能由于两图过于相似而不可靠,这种情况不需要判断方向,即该值=false
9 * 一般direction不可靠时,该值=false
10 * 对比的两张图是第一次对比时,该值=false
11 * 当该值=true时,请在调用实时拼接API前确认方法是否合法
12 */
13public boolean needCheckDirection();
14
15// 两张图片重叠部分的点位
16public List<PointF> getPoints();
对比结果的合法性判断参考
1switch (compareResult.getOverlapStatus()) {
2 case CompareResult.OVERLAP_CORRECT:
3 if (compareResult.needCheckDirection() && !compareResult.isDirectionValid()) {
4 // 非法,当前参与对比的图片方位不正确,无法拼接
5 } else {
6 // 合法
7 }
8 break;
9 case CompareResult.OVERLAP_TOO_FAR:
10 // 非法,两张图重叠度过低
11 break;
12 case CompareResult.OVERLAP_TOO_CLOSE:
13 // 非法,两张图重叠度过高
14 break;
15}
实时拼接
1/**
2 * 拼接图片
3 *
4 * @param compareResult 对比图片回调返回的结果
5 */
6public void stitchImage(CompareResult compareResult);
- 建议调用拼接前参考【对比结果的合法性判断】,用不合法的对比结果进行实时拼接将无法获得正确输出
- 将要参与拼接的图片必须命名为“now.jpg”(也可使用MobileStitchAPI.IMAGE_NAME_NOW),并保存在初始化API时的 workDirPath 目录下,否则实时拼接无法正常工作。成功拼接后"now.jpg"会被SDK重新命名为{index}.jpg,其中{index}代表图片序号。
- 为获得更快的拼接效率,建议减小参与拼接的图片尺寸;为了保证拼接效果,缩放后的图片尺寸应不小于宽648和高864
-
异步回调:
MobileStitchResult
1// 获取缩略拼接图路径
2public String getThumbnailPath();
3
4// 获取完整拼接图路径
5public String getFullImgPath();
6
7// 获取最近一张参与拼接的图片的序号
8public int getLatestPhotoIndex();
9
10// 拼接是否成功
11public boolean isSuccess();
保存最佳尺寸的图片以提高商品检测精度
SDK默认使用以上保存的一系列{index}.jpg
调用商品检测API并取得结果,由于建议减小该系列图片尺寸以获得更优的拼接效率,但同时更小尺寸的图片对商品检测精度有一定影响,因此为提高精度,建议同时保存最佳尺寸的图片用于上传云端。
1// 1.原图
2Bitmap bitmap = getFromSomewhere();
3
4// 2.计算最佳缩放系数
5float scaleFactor = calculateScaleFactor(bitmap);
6
7// 3.缩放获得最佳尺寸的图片
8Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,
9 (int) (bitmap.getWidth() * scaleFactor),
10 (int) (bitmap.getHeight() * scaleFactor),
11 true);
12
13// 4.保存
14String fullImgFilepath = workDirPath + "/"
15 + MobileStitchAPI.DIR_NAME_FULL_IMAGE + "/"
16 + MobileStitchAPI.IMAGE_NAME_NOW;
17ImageUtil.saveBitmap(scaledBitmap, fullImgFilepath);
18
19/**
20 * 计算最佳缩放系数
21 */
22private float calculateScaleFactor(Bitmap originalBitmap) {
23 int longerSide;
24 int shorterSide;
25 if (originalBitmap.getWidth() > originalBitmap.getHeight()) {
26 longerSide = originalBitmap.getWidth();
27 shorterSide = originalBitmap.getHeight();
28 } else {
29 longerSide = originalBitmap.getHeight();
30 shorterSide = originalBitmap.getWidth();
31 }
32 return Math.min(1333f / longerSide, 800f / shorterSide);
33}
撤销拼接结果
SDK支持撤销最后一次拼接结果,请自行编码删除最后一张参与拼接的图片,再调用MobileStitchAPI.notifyLatestPhotoDeletion()通知SDK,参考:
1// 最后一张参与拼接的图片路径,workDirPath为初始化时工作目录路径
2// latestPhotoIndex为最后一张参与拼接的图片序号,可在 MobileStitchAPIListener 以下回调时赋值
3// 1. onAPIPrepared() - 参数takenPhotoSize
4// 2. onStitchCompleted() - 参数result.getLatestPhotoIndex()
5// 3. onDeletionConfirmed() - 参数latestPhotoIndex
6String filepath = workDirPath + "/" + latestPhotoIndex + ".jpg";
7
8// 删除图片
9FileUtil.deleteFile(filepath);
10
11// 通知SDK
12// SDK确认删除后回调 MobileStitchAPIListener.onDeletionConfirmed(int, int[])
13mobileStitchAPI.notifyLatestPhotoDeletion();
上传云端,得到结果
1/**
2 * 上传云端检测,并获得结果
3 */
4public void mergeDetectResults();
-
异步回调:
MergeResult
1// 获取商品检测并去重后的结果
2public String getCorrectedSKUJson();
MobileStitchAPIListener
手机端实时拼接通过 MobileStitchAPIListener 异步回调各函数结果,监听器可通过:
- mobileStitchAPI.registerListener()注册
- mobileStitchAPI.unRegisterListener()注销
1/**
2 * API准备好时的回调
3 *
4 * @param takenPhotoSize 工作目录下已拍摄的图像数量
5 * @param latestPhotoPos 最新拍摄图片的坐标
6 */
7void onAPIPrepared(int takenPhotoSize, int[] latestPhotoPos);
8
9/**
10 * 图片对比完成
11 *
12 * @param compareResult 对比结果
13 */
14void onImagesCompared(CompareResult compareResult);
15
16/**
17 * 调用拼接接口后,缩略图生成后的回调
18 *
19 * @param thumbnailName 缩略图在工作目录下的文件名
20 */
21void onStitchThumbnailGenerated(String thumbnailName);
22
23/**
24 * 调用拼接接口后,完整拼接图片生成后的回调
25 *
26 * @param fullImageName 完成拼接图片在工作目录下的文件名
27 */
28void onStitchFullImageGenerated(String fullImageName);
29
30/**
31 * 拼接完成回调
32 *
33 * @param mobileStitchResult 拼接结果
34 */
35void onStitchCompleted(MobileStitchResult mobileStitchResult);
36
37/**
38 * 删除确认回调
39 *
40 * @param latestPhotoIndex -1=操作失败,否则返回删除后,最新拍摄图片的下标;如删除了3.jpg,将返回2
41 * @param latestPhotoPos 最新拍摄图片的坐标
42 */
43void onDeletionConfirmed(int latestPhotoIndex, int[] latestPhotoPos);
44
45/**
46 * 检测图片进度更新回调
47 *
48 * @param leftCount 剩余要处理图片的数量
49 */
50void onDetectProgressUpdated(int leftCount);
51
52/**
53 * 商品检测并去重处理完成的回调
54 *
55 * @param mergeResult 检测并去重结果
56 */
57void onDetectedResultsMerged(MergeResult mergeResult);
模糊图像检测
手机端实时拼接已接入AI模型以支持模糊图像检测,需引入以下依赖库及模型文件:
- libedge-infer.so:模糊图像检测引擎库
- easyedge-sdk.jar:模糊图像检测引擎库
- sdk/src/main/assets/infer/:模糊图像检测模型所在文件夹
以下为调用示例,也可参考 app/src/main/java/com/baidu/ai/easydl/montage/page/photo/mobilestitch/MobileStitchViewPresenter.java 类中对 FuzzyModelProxy 的使用:
1/* 初始化 */
2FuzzyModelProxy fuzzyModelStateListener = new FuzzyModelProxy.ModelStateListener() {
3 @Override
4 public void onInitialized(Exception exception) {
5 if (exception != null) {
6 // 模糊模型初始化失败
7 } else {
8 // 模糊模型初始化成功
9 }
10 }
11
12 @Override
13 public void onDestroyed() {
14 // 模糊模型销毁回调
15 }
16};
17FuzzyModelProxy fuzzyModelProxy = new FuzzyModelProxy(mContext, fuzzyModelStateListener);
18fuzzyModelProxy.initModel();
19
20/* 调用示例 */
21if (fuzzyModelProxy.modelEngineActivate()) {
22 Bitmap bitmap = bitmapFromSomewhere();
23 fuzzyModelProxy.infer(bitmap, new FuzzyModelProxy.ModelInferListener() {
24 @Override
25 public void onCompleted(boolean fuzzy) {
26 if (!fuzzy) {
27 // 图像非模糊
28 } else {
29 // 图像模糊
30 }
31 }
32
33 @Override
34 public void onException(Exception exception) {
35 // 图像检测失败
36 }
37 }
38} else {
39 // 模糊图像推理引擎异常
40}
41
42/* 销毁 */
43if (fuzzyModelProxy != null) {
44 fuzzyModelProxy.destroyModelEngine();
45 fuzzyModelStateListener = null;
46}
阈值的设置
手机端实时拼接支持设置:
- 最小IOU置信度
- 最大IOU置信度
- NMS置信度
- 商品检测API最大重试次数
1/**
2 * 设置最低iou置信度,需在init()后调用
3 *
4 * @param threshold 在0-1范围内有效
5 */
6public void setMinIouThreshold(float threshold);
7
8/**
9 * 设置最高iou置信度,需在init()后调用
10 *
11 * @param threshold 在0-1范围内有效
12 */
13public void setMaxIouThreshold(float threshold);
14
15/**
16 * 设置NMS置信度,需在init()后调用
17 *
18 * @param threshold 在0-1范围内有效
19 */
20public void setNmsIouThreshold(float threshold);
21
22/**
23 * 设置商品检测API最大重试次数
24 *
25 * @param maxRetryTimes 最大重试次数,<=0无效
26 */
27public void setMaxRetryTimes(int maxRetryTimes);
云端非实时拼接调用流程
第1步, 创建任务,上传图片
1)【创建任务:开始拼接整个流程】
2)【加货架图:上传图片】
3)【开始任务:启动货架拼接离线任务】
第2步,不定时查询结果,一般10分钟后有结果参数
【查询结果:查询任务运行状态或者结果】
其它可选操作:
【终止任务:终止正在进行或者等待的任务】
【任务列表:查询所有状态的任务列表】
SDK 调用
根据调用流程, SDK有两种调用方式:
- StitchApi,api的封装
- StitchTask,StitchApi的封装,避免taskId的传递。一个task对应一个StitchTask
返回参数以及其他信息详见文档货架拼接API调用方法。
StitchApi
初始化
1/**
2* 初始化
3* @param appKey 网页上的应用的appkey
4* @param secretKey 网页上的应用的appSecret
5*/
6public StitchApi(String appKey, String secretKey) {
7 super(appKey, secretKey);
8}
9
10/**
11* 初始化
12* @param appKey 网页上的应用的appkey
13* @param secretKey 网页上的应用的appSecret
14* @param connection 自定义HTTP连接
15*/
16public StitchApi(String appKey, String secretKey, ISdkConnection connection) {
17 super(appKey, secretKey, connection);
18}
创建任务
1public CreateStitchResponse create(CreateStitchRequest request);
2
3// CreateStitchRequest 及 CreateStitchResponse 参数同API文档
同步上传图片
1public CommonStitchResponse upload(UploadImageRequest request);
2
3// UploadImageRequest 及 CreateStitchResponse 参数同API文档
4
5设置图片的话,以下2个方法2选1
6
7public void setImageFile(String imageFile);
8
9public void setImageInputStream(InputStream inputStream)
异步上传图片
1public void uploadAsync(UploadImageRequest request,
2 IApiResponseListener<CommonStitchResponse> listener)
3
4// UploadImageRequest 参数同API文档
5
6
7// IApiResponseListener<CommonStitchResponse> 接口:
8onSdkResponse(CommonStitchResponse response, String userDefinedRequestId)
9// 其中userDefinedRequestId是在UploadImageRequest 里面设置的
10
11// 使用 clearAysncQueue()可以清空未开始的任务
开始任务
1CommonStitchResponse start(CommonStitchRequest request)
查询结果
1public QueryStitchReponse query(CommonStitchRequest request)
任务列表
1public ListStitchResponse list(ListStitchRequest request)
StitchTask
一个任务新建一个StitchTask 调用方式同 StitchApi, 参数中不必设置taskId
AbstractApiRequest
目前Request类的基类 。
1// 设置自定义请求Id, 调用异步接口的回调使用
2public void setUserDefinedRequestId(String userDefinedRequestId)
3
4// 设置是否添加debug日志
5public void setEnableDebug(boolean enableDebug)
CommonStitchResponse 及 AbstractApiResponse
1// 获取任务状态
2public String getTaskStatus();
3
4// 获取logId
5public String getLogId();
6
7// 获取服务端返回的原始json
8public JSONObject getOriginalJson();
9
10// 获取请求
11public AbstractApiRequest getRequest();
门脸文字识别调用流程
第1步:初始化
1)【获取实例】
2)【初始化API】
第2步:门脸图片上传云端,获取门脸文字识别结果
1)【门脸文字识别】
2)【释放资源】
SDK 调用
门脸文字识别流程通过 DetectionDoorAPI 调用,具体使用和返回参数见下
初始化
DetectionDoorAPI不支持多线程,且仅有一个实例。
获取实例
1/**
2 * 获取实例
3 * @param appKey 网页上的应用的appkey
4 * @param secretKey 网页上的应用的appSecret
5 */
6public static DetectionDoorAPI getInstance(String appKey, String secretKey);
初始化API
1/**
2 * 初始化API
3 * 建议传参getApplicationContext
4 * 初始化(文件夹/定位)
5 */
6public void init(Context context);
1// 注册门脸文字识别监听
2public void registerListener(DoorAPIListener listener);
3
4public interface DoorAPIListener {
5 /**
6 * 识别成功, 返回门脸文字识别结果
7 * @param responseJson
8 */
9 void onDetectSuccess(String responseJson);
10 /**
11 * 识别异常
12 * @param Exception e
13 */
14 void onException(Exception e);
15}
16
17// 销毁监听
18public void unRegisterListener();
门脸图片上传云端,获取门店检测结果
门脸文字识别
1/**
2 * 开始门脸文字识别
3 * @param bitmap
4 */
5public void detectDoorImage(Bitmap bitmap);
释放资源
1// 释放资源
2public void destroy();
模糊图像检测
门脸文字识别已接入AI模型以支持模糊图像检测,需引入以下依赖库及模型文件:
- libedge-infer.so:模糊图像检测引擎库
- easyedge-sdk.jar:模糊图像检测引擎库
- sdk/src/main/assets/infer/:模糊图像检测模型所在文件夹
调用示例可参考【手机端实时拼接调用流程-模糊图像检测】,也可参考【门脸文字识别调用流程】 app/src/main/java/com/baidu/ai/easydl/montage/page/door/IDoorViewPresenter.java类中对 FuzzyModelProxy 的使用。
集成指南
添加NDK编译架构
SDK依赖OpenCV库,需添加NDK编译选项,支持常用的两个架构,可参考app/build.gradle配置
1ndk {
2 abiFilters "arm64-v8a", "armeabi-v7a"
3}
集成拍照逻辑
查看com.baidu.ai.easydl.montage.page.photo.take
包,里面均为摄像拍照逻辑。
拍照参数设置
1package com.baidu.ai.easydl.montage.page.photo;
2
3public interface IPhotoParam {
4
5 /**
6 * 两张图片的hash算法
7 */
8 String IMAGE_COMPARE_HASH_METHOD = "pHash"; // pHash,dHash,ahash
9
10 /**
11 * 两张图片的hash比较值
12 */
13 float IMAGE_COMPARE_HASH_CONFIDENCE_THRESHOLD = 0.75f;
14
15 /**
16 * 相机的Sensor的旋转误差值, 取值为0-180,大于180表示忽略
17 */
18 int SENSOR_ORIENTATION_EVENT_DELTA = 20;
19
20 /**
21 * 传感器的SensorY的旋转误差值, 取值为0-180,大于180表示忽略
22 */
23 double SENSOR_ORIENTATION_SENSOR_Y_DELTA = Math.PI / 6;
24
25 /**
26 * 拍照建议的最低亮度值
27 */
28 double SENSOR_LIGHT_LUMEN_MIN = 100;
29
30 /**
31 * 拍照建议的最高亮度值
32 */
33 double SENSOR_LIGHT_LUMEN_MAX = 500;
34 /**
35 * 40%图片的透明度
36 */
37 float IMAGE_SLIDE_TRANSPARENT_ALPHA = 0.5f;
38
39 /**
40 * 每行货架最多的照片数量,服务端支持最大60
41 */
42 int SLOT_MAX_PHOTO_NUM = 60;
43
44 // 下面的参数,请不要修改
45 float IMAGE_SLIDE_CROP_RATIO = 0.4f;
46
47 boolean IMAGE_COMPARE_HASH_DEBUG_SAVE_IMAGES = false;
48
49}
防止图片窜拍开关参数设置
1package com.baidu.ai.utils;
2
3public class CheckImageConfig {
4 /**
5 * 是否开启防窜拍
6 * 默认开启
7 */
8 private volatile boolean pirateImageCheck = true;
9
10 public void setPirateImageCheck(boolean pirateImageCheck) {
11 this.pirateImageCheck = pirateImageCheck;
12 }
13
14 public boolean getPirateImageCheck() {
15 return pirateImageCheck;
16 }
17}