开放能力
开发平台
行业应用
生态合作
开发与教学
资讯 社区 控制台
技术能力
语音技术
文字识别
人脸与人体
图像技术
语言与知识
视频技术
AR与VR
数据智能
场景方案
部署方案
行业应用
智能教育
智能医疗
智能零售
智能工业
企业服务
智能政务
信息服务
智能园区

Android_SDK

简介

本文介绍SDK的的功能使用,即下载包中的sdk module。

SDK为货架拼接云端非实时API和手机端实时拼接的封装,无任何额外功能。如果有和API文档不符的地方,以SDK为准。

支持Android Level 22及以上编译和使用。

Release Notes

时间 版本 说明
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 支持拍摄视频,端上抽帧,云端拼接

测试

获取鉴权

  1. 进入EasyDL零售版的百度智能云控制台应用列表页面,如下图所示:

1.png

  1. 如果还未创建应用,请点击「创建应用」按钮进行创建。创建应用后,参考鉴权参考文档,使用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)【加货架图:上传图片】
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个方法21

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步:对比图片
新图片(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);

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;
}

阈值的设置

手机端实时拼接支持设置:

  1. 最小IOU置信度
  2. 最大IOU置信度
  3. NMS置信度
  4. 商品检测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);

集成指南

添加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;

}
上一篇
iOS_SDK
下一篇
标准商品检测服务