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

    Android_SDK

    简介

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

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

    支持Android Level 18以上编译和使用.

    Release Notes

    时间 版本 说明
    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);

    阈值的设置

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

    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
    下一篇
    API文档