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
{
"ak": "Mz0zhObvEZ6lnG1K3renXXXX", // API Key的值
"sk": "188fRHYvLPmlPrNCDpBnkhL3ydXXXXX", // Secret Key的值
"apiUrl": "https://aip.baidubce.com/rpc/2.0/ai_custom_retail/v1/detection/XXXX" // 定制商品检测服务API
}
正常情况下,启动的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不支持多线程,且仅有一个实例以保证实时拼接过程中的正确文件操作。
获取实例
/**
* 获取实例
*
* @param appKey 网页上的应用的appkey
* @param secretKey 网页上的应用的appSecret
* @param apiUrl 商品检测服务API
*/
public static MobileStitchAPI getInstance(String appKey, String secretKey, String apiUrl);
/**
* 获取实例
*
* @param appKey 网页上的应用的appkey
* @param secretKey 网页上的应用的appSecret
* @param apiUrl 商品检测服务API
* @param numConcurrency 同时调用商品检测API的并发数
*/
public static MobileStitchAPI getInstance(String appKey, String secretKey, String apiUrl, int numConcurrency);
初始化API
/**
* 初始化API
*
* @param workDirPath 保存实时拼接过程产生的各类文件的路径
*/
public void init(String workDirPath);
对比图片
/**
* 对比当前图片与上一张参与拼接的图片
*
* @param currentImgBitmap 当前要参与对比的图片
* @param firstFrame 是否是第一帧
*/
public void compareImages(Bitmap currentImgBitmap, boolean firstFrame);
- 第一帧的定义取决于上一张参与拼接的图片(last)是否已经被对比过。假设有图片A和B,先用A与last对比,且last是初次被对比,此时firstFrame应为true,再用B与last对比,此时firstFrame应为false。
- 异步回调:MobileStitchAPIListener.onImagesCompared(CompareResult)
CompareResult
// 当前图片相对上一张参与拼接的图片的方位,参考MobileStitchAPI.DIRECTION_{LEFT|UP|RIGHT|DOWN|UNKNOWN}
public int getDirection();
// 对比结果中的方位是否合法,非法的方位将无法完成拼接
public boolean isDirectionValid();
/**
* 是否需要判断方向,如当拍摄完图片过近时,direction可能由于两图过于相似而不可靠,这种情况不需要判断方向,即该值=false
* 一般direction不可靠时,该值=false
* 对比的两张图是第一次对比时,该值=false
* 当该值=true时,请在调用实时拼接API前确认方法是否合法
*/
public boolean needCheckDirection();
// 两张图片重叠部分的点位
public List<PointF> getPoints();
对比结果的合法性判断参考
switch (compareResult.getOverlapStatus()) {
case CompareResult.OVERLAP_CORRECT:
if (compareResult.needCheckDirection() && !compareResult.isDirectionValid()) {
// 非法,当前参与对比的图片方位不正确,无法拼接
} else {
// 合法
}
break;
case CompareResult.OVERLAP_TOO_FAR:
// 非法,两张图重叠度过低
break;
case CompareResult.OVERLAP_TOO_CLOSE:
// 非法,两张图重叠度过高
break;
}
实时拼接
/**
* 拼接图片
*
* @param compareResult 对比图片回调返回的结果
*/
public void stitchImage(CompareResult compareResult);
- 建议调用拼接前参考【对比结果的合法性判断】,用不合法的对比结果进行实时拼接将无法获得正确输出
- 将要参与拼接的图片必须命名为“now.jpg”(也可使用MobileStitchAPI.IMAGE_NAME_NOW),并保存在初始化API时的 workDirPath 目录下,否则实时拼接无法正常工作。成功拼接后"now.jpg"会被SDK重新命名为{index}.jpg,其中{index}代表图片序号。
- 为获得更快的拼接效率,建议减小参与拼接的图片尺寸;为了保证拼接效果,缩放后的图片尺寸应不小于宽648和高864
-
异步回调:
MobileStitchResult
// 获取缩略拼接图路径
public String getThumbnailPath();
// 获取完整拼接图路径
public String getFullImgPath();
// 获取最近一张参与拼接的图片的序号
public int getLatestPhotoIndex();
// 拼接是否成功
public boolean isSuccess();
保存最佳尺寸的图片以提高商品检测精度
SDK默认使用以上保存的一系列{index}.jpg
调用商品检测API并取得结果,由于建议减小该系列图片尺寸以获得更优的拼接效率,但同时更小尺寸的图片对商品检测精度有一定影响,因此为提高精度,建议同时保存最佳尺寸的图片用于上传云端。
// 1.原图
Bitmap bitmap = getFromSomewhere();
// 2.计算最佳缩放系数
float scaleFactor = calculateScaleFactor(bitmap);
// 3.缩放获得最佳尺寸的图片
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap,
(int) (bitmap.getWidth() * scaleFactor),
(int) (bitmap.getHeight() * scaleFactor),
true);
// 4.保存
String fullImgFilepath = workDirPath + "/"
+ MobileStitchAPI.DIR_NAME_FULL_IMAGE + "/"
+ MobileStitchAPI.IMAGE_NAME_NOW;
ImageUtil.saveBitmap(scaledBitmap, fullImgFilepath);
/**
* 计算最佳缩放系数
*/
private float calculateScaleFactor(Bitmap originalBitmap) {
int longerSide;
int shorterSide;
if (originalBitmap.getWidth() > originalBitmap.getHeight()) {
longerSide = originalBitmap.getWidth();
shorterSide = originalBitmap.getHeight();
} else {
longerSide = originalBitmap.getHeight();
shorterSide = originalBitmap.getWidth();
}
return Math.min(1333f / longerSide, 800f / shorterSide);
}
撤销拼接结果
SDK支持撤销最后一次拼接结果,请自行编码删除最后一张参与拼接的图片,再调用MobileStitchAPI.notifyLatestPhotoDeletion()通知SDK,参考:
// 最后一张参与拼接的图片路径,workDirPath为初始化时工作目录路径
// latestPhotoIndex为最后一张参与拼接的图片序号,可在 MobileStitchAPIListener 以下回调时赋值
// 1. onAPIPrepared() - 参数takenPhotoSize
// 2. onStitchCompleted() - 参数result.getLatestPhotoIndex()
// 3. onDeletionConfirmed() - 参数latestPhotoIndex
String filepath = workDirPath + "/" + latestPhotoIndex + ".jpg";
// 删除图片
FileUtil.deleteFile(filepath);
// 通知SDK
// SDK确认删除后回调 MobileStitchAPIListener.onDeletionConfirmed(int, int[])
mobileStitchAPI.notifyLatestPhotoDeletion();
上传云端,得到结果
/**
* 上传云端检测,并获得结果
*/
public void mergeDetectResults();
-
异步回调:
MergeResult
// 获取商品检测并去重后的结果
public String getCorrectedSKUJson();
MobileStitchAPIListener
手机端实时拼接通过 MobileStitchAPIListener 异步回调各函数结果,监听器可通过:
- mobileStitchAPI.registerListener()注册
- mobileStitchAPI.unRegisterListener()注销
/**
* API准备好时的回调
*
* @param takenPhotoSize 工作目录下已拍摄的图像数量
* @param latestPhotoPos 最新拍摄图片的坐标
*/
void onAPIPrepared(int takenPhotoSize, int[] latestPhotoPos);
/**
* 图片对比完成
*
* @param compareResult 对比结果
*/
void onImagesCompared(CompareResult compareResult);
/**
* 调用拼接接口后,缩略图生成后的回调
*
* @param thumbnailName 缩略图在工作目录下的文件名
*/
void onStitchThumbnailGenerated(String thumbnailName);
/**
* 调用拼接接口后,完整拼接图片生成后的回调
*
* @param fullImageName 完成拼接图片在工作目录下的文件名
*/
void onStitchFullImageGenerated(String fullImageName);
/**
* 拼接完成回调
*
* @param mobileStitchResult 拼接结果
*/
void onStitchCompleted(MobileStitchResult mobileStitchResult);
/**
* 删除确认回调
*
* @param latestPhotoIndex -1=操作失败,否则返回删除后,最新拍摄图片的下标;如删除了3.jpg,将返回2
* @param latestPhotoPos 最新拍摄图片的坐标
*/
void onDeletionConfirmed(int latestPhotoIndex, int[] latestPhotoPos);
/**
* 检测图片进度更新回调
*
* @param leftCount 剩余要处理图片的数量
*/
void onDetectProgressUpdated(int leftCount);
/**
* 商品检测并去重处理完成的回调
*
* @param mergeResult 检测并去重结果
*/
void 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 的使用:
/* 初始化 */
FuzzyModelProxy fuzzyModelStateListener = new FuzzyModelProxy.ModelStateListener() {
@Override
public void onInitialized(Exception exception) {
if (exception != null) {
// 模糊模型初始化失败
} else {
// 模糊模型初始化成功
}
}
@Override
public void onDestroyed() {
// 模糊模型销毁回调
}
};
FuzzyModelProxy fuzzyModelProxy = new FuzzyModelProxy(mContext, fuzzyModelStateListener);
fuzzyModelProxy.initModel();
/* 调用示例 */
if (fuzzyModelProxy.modelEngineActivate()) {
Bitmap bitmap = bitmapFromSomewhere();
fuzzyModelProxy.infer(bitmap, new FuzzyModelProxy.ModelInferListener() {
@Override
public void onCompleted(boolean fuzzy) {
if (!fuzzy) {
// 图像非模糊
} else {
// 图像模糊
}
}
@Override
public void onException(Exception exception) {
// 图像检测失败
}
}
} else {
// 模糊图像推理引擎异常
}
/* 销毁 */
if (fuzzyModelProxy != null) {
fuzzyModelProxy.destroyModelEngine();
fuzzyModelStateListener = null;
}
阈值的设置
手机端实时拼接支持设置:
- 最小IOU置信度
- 最大IOU置信度
- NMS置信度
- 商品检测API最大重试次数
/**
* 设置最低iou置信度,需在init()后调用
*
* @param threshold 在0-1范围内有效
*/
public void setMinIouThreshold(float threshold);
/**
* 设置最高iou置信度,需在init()后调用
*
* @param threshold 在0-1范围内有效
*/
public void setMaxIouThreshold(float threshold);
/**
* 设置NMS置信度,需在init()后调用
*
* @param threshold 在0-1范围内有效
*/
public void setNmsIouThreshold(float threshold);
/**
* 设置商品检测API最大重试次数
*
* @param maxRetryTimes 最大重试次数,<=0无效
*/
public void setMaxRetryTimes(int maxRetryTimes);
云端非实时拼接调用流程
第1步, 创建任务,上传图片
1)【创建任务:开始拼接整个流程】
2)【加货架图:上传图片】
3)【开始任务:启动货架拼接离线任务】
第2步,不定时查询结果,一般10分钟后有结果参数
【查询结果:查询任务运行状态或者结果】
其它可选操作:
【终止任务:终止正在进行或者等待的任务】
【任务列表:查询所有状态的任务列表】
SDK 调用
根据调用流程, SDK有两种调用方式:
- StitchApi,api的封装
- StitchTask,StitchApi的封装,避免taskId的传递。一个task对应一个StitchTask
返回参数以及其他信息详见文档货架拼接API调用方法。
StitchApi
初始化
/**
* 初始化
* @param appKey 网页上的应用的appkey
* @param secretKey 网页上的应用的appSecret
*/
public StitchApi(String appKey, String secretKey) {
super(appKey, secretKey);
}
/**
* 初始化
* @param appKey 网页上的应用的appkey
* @param secretKey 网页上的应用的appSecret
* @param connection 自定义HTTP连接
*/
public StitchApi(String appKey, String secretKey, ISdkConnection connection) {
super(appKey, secretKey, connection);
}
创建任务
public CreateStitchResponse create(CreateStitchRequest request);
// CreateStitchRequest 及 CreateStitchResponse 参数同API文档
同步上传图片
public CommonStitchResponse upload(UploadImageRequest request);
// UploadImageRequest 及 CreateStitchResponse 参数同API文档
设置图片的话,以下2个方法2选1
public void setImageFile(String imageFile);
public void setImageInputStream(InputStream inputStream)
异步上传图片
public void uploadAsync(UploadImageRequest request,
IApiResponseListener<CommonStitchResponse> listener)
// UploadImageRequest 参数同API文档
// IApiResponseListener<CommonStitchResponse> 接口:
onSdkResponse(CommonStitchResponse response, String userDefinedRequestId)
// 其中userDefinedRequestId是在UploadImageRequest 里面设置的
// 使用 clearAysncQueue()可以清空未开始的任务
开始任务
CommonStitchResponse start(CommonStitchRequest request)
查询结果
public QueryStitchReponse query(CommonStitchRequest request)
任务列表
public ListStitchResponse list(ListStitchRequest request)
StitchTask
一个任务新建一个StitchTask 调用方式同 StitchApi, 参数中不必设置taskId
AbstractApiRequest
目前Request类的基类 。
// 设置自定义请求Id, 调用异步接口的回调使用
public void setUserDefinedRequestId(String userDefinedRequestId)
// 设置是否添加debug日志
public void setEnableDebug(boolean enableDebug)
CommonStitchResponse 及 AbstractApiResponse
// 获取任务状态
public String getTaskStatus();
// 获取logId
public String getLogId();
// 获取服务端返回的原始json
public JSONObject getOriginalJson();
// 获取请求
public AbstractApiRequest getRequest();
门脸文字识别调用流程
第1步:初始化
1)【获取实例】
2)【初始化API】
第2步:门脸图片上传云端,获取门脸文字识别结果
1)【门脸文字识别】
2)【释放资源】
SDK 调用
门脸文字识别流程通过 DetectionDoorAPI 调用,具体使用和返回参数见下
初始化
DetectionDoorAPI不支持多线程,且仅有一个实例。
获取实例
/**
* 获取实例
* @param appKey 网页上的应用的appkey
* @param secretKey 网页上的应用的appSecret
*/
public static DetectionDoorAPI getInstance(String appKey, String secretKey);
初始化API
/**
* 初始化API
* 建议传参getApplicationContext
* 初始化(文件夹/定位)
*/
public void init(Context context);
// 注册门脸文字识别监听
public void registerListener(DoorAPIListener listener);
public interface DoorAPIListener {
/**
* 识别成功, 返回门脸文字识别结果
* @param responseJson
*/
void onDetectSuccess(String responseJson);
/**
* 识别异常
* @param Exception e
*/
void onException(Exception e);
}
// 销毁监听
public void unRegisterListener();
门脸图片上传云端,获取门店检测结果
门脸文字识别
/**
* 开始门脸文字识别
* @param bitmap
*/
public void detectDoorImage(Bitmap bitmap);
释放资源
// 释放资源
public 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配置
ndk {
abiFilters "arm64-v8a", "armeabi-v7a"
}
集成拍照逻辑
查看com.baidu.ai.easydl.montage.page.photo.take
包,里面均为摄像拍照逻辑。
拍照参数设置
package com.baidu.ai.easydl.montage.page.photo;
public interface IPhotoParam {
/**
* 两张图片的hash算法
*/
String IMAGE_COMPARE_HASH_METHOD = "pHash"; // pHash,dHash,ahash
/**
* 两张图片的hash比较值
*/
float IMAGE_COMPARE_HASH_CONFIDENCE_THRESHOLD = 0.75f;
/**
* 相机的Sensor的旋转误差值, 取值为0-180,大于180表示忽略
*/
int SENSOR_ORIENTATION_EVENT_DELTA = 20;
/**
* 传感器的SensorY的旋转误差值, 取值为0-180,大于180表示忽略
*/
double SENSOR_ORIENTATION_SENSOR_Y_DELTA = Math.PI / 6;
/**
* 拍照建议的最低亮度值
*/
double SENSOR_LIGHT_LUMEN_MIN = 100;
/**
* 拍照建议的最高亮度值
*/
double SENSOR_LIGHT_LUMEN_MAX = 500;
/**
* 40%图片的透明度
*/
float IMAGE_SLIDE_TRANSPARENT_ALPHA = 0.5f;
/**
* 每行货架最多的照片数量,服务端支持最大60
*/
int SLOT_MAX_PHOTO_NUM = 60;
// 下面的参数,请不要修改
float IMAGE_SLIDE_CROP_RATIO = 0.4f;
boolean IMAGE_COMPARE_HASH_DEBUG_SAVE_IMAGES = false;
}
防止图片窜拍开关参数设置
package com.baidu.ai.utils;
public class CheckImageConfig {
/**
* 是否开启防窜拍
* 默认开启
*/
private volatile boolean pirateImageCheck = true;
public void setPirateImageCheck(boolean pirateImageCheck) {
this.pirateImageCheck = pirateImageCheck;
}
public boolean getPirateImageCheck() {
return pirateImageCheck;
}
}