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

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 支持拍摄视频,端上抽帧,云端拼接

测试

获取鉴权

  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)【初始化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);

门脸文字识别调用流程

第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;
   }
}
上一篇
iOS_SDK
下一篇
价签识别服务