升级异常的固件恢复方法
1. 概述
本文指导用户通过线下手动升级的方式,恢复升级异常的固件。
2. 获取升级固件
请务必从商米售前获取对应型号摄像头的升级固件 ,否则出现升级异常概不负责。
目前商米有的IPC摄像头有两款,分别是
- AI识客摄像机,英文名Face Sense Camera,简称FS,目前的型号是FM020。
- FM020线上环境固件 : 点击下载
- 智能看店摄像机,英文名Store Sense Camera,简称SS,目前的型号是FM010。
- FM010线上环境固件 : 点击下载
3. 升级规则
- 不同机型之间系统固件不能互相升级,例如FS和SS之间不能升级,所以请务必根据自己的机型来获取对应的正式环境固件。
- 固件本身有自己的签名机制和升级校验方法,所以请务必从商米处获取固件,切勿随便从第三方获取固件。
4. 详细流程
4.1 拷贝固件到TF卡
- 从摄像头的TF卡槽中取出TF卡;
- 将TF卡插入windows电脑,执行格式化操作,文件系统设置为exfat,卷标设置为SUNMI-XXXX(其中XXXX为摄像头MAC地址最后四位,摄像头MAC地址见机身背面标贴)。 注:卷标SUNMI-XXXX中的-为英文半角符号,非中文全角符号;XXXX中的字母为大写。
- 进入TF卡目录,将从商米处 (详见2. 获取升级固件)获取到的升级固件解压后拷贝到根目录下,并把固件重命名为up.bin。
4.2 升级固件
- 将带有固件的TF卡重新插入摄像头的TF卡槽中。
- 重新上电摄像头。
- 等待10秒左右,直到设备亮红灯,则说明正在固件升级 。
- 升级需要一些时间,再耐心等待1min左右,直到重新亮绿灯说明设备开始重启,当设备再次闪烁绿灯或者亮蓝灯说明设备已经重启完毕。
4.3 进行首配
- 重启后,长按设备上的Reset按键5秒以上进行恢复出厂设置。
- 恢复出厂重启后,使用正式环境的商米APP,按照《用户指南》手册中的软件配置指引完成摄像头的首次配置。
- 首配完成后,使用商米助手APP或登录正式环境的WEB服务网站https://store.sunmi.com 将设备固件更新到最新版本。
- 记得删除原来SD卡中的up.bin文件,避免不必要的意外升级。
IPC设备迁移
本章节详细介绍在Android平台如何快速方便添加IPC SDK
1、拓扑图
IPC设备和Android设备连接拓扑图如下:
1、Android设备有一套自有的用户管理模块,有唯一ID识别机制。Android设备在管理用户时,需要保留一张用户人脸图片。
2、IPC设备使用人脸图片作为识别预输入。
3、Android设备激活IPC设备后,通过局域网把用户人脸图片以及与用户唯一关联的ID传递给IPC设备
4、IPC设备从人脸图片提取人脸特征值,并把特征值与用户ID保存到数据库。
5、IPC设备监控到用户进入,查询注册的数据库,返回用户唯一ID的IPC设备。
6、Android设备获取到唯一ID值后,在用户管理模块根据该唯一ID进行查询,显示用户信息
2、IPC激活
IPC设备使用时,如果未激活,需要先激活IPC设备。激活步骤如下:
2.1 SDK初始化
APP_ID:激活与API调用校验使用的账号。
SECRET_KEY:API调用所需的签名密钥。
LICENSE:激活API所需的激活码。
IPCameraManager mIPCameraManager = IPCameraManager.getInstance(context);
mIPCameraManager.init(APP_ID, SECRET_KEY, LICENSE);
2.2 配网
IPC设备激活时需要连接到互联网。IPC连接互联网时,可以通过无线(wifi)或有线(以太网口)连接到路由器。无线连接时需要做配网设置。
有线接入:
有线接入只需要通过网线把IPC设备和集成SDK的Android设备接入到同一局域网即可,不需要其它设置。
无线接入:
无线配网方式相对要复杂点,步骤如下:
- 使用手机/PC的无线网卡扫描IPC的AP热点,一般AP热点的名称为SUNMI_XXXX,其中XXXX为MAC地址最后2个字节的16进制数字,MAC地址可以通过设备机身后背的标贴或者包装盒的标贴查到,AP热点本身是无加密的。
- 使用手机/PC的无线网卡连接IPC的AP热点,此时手机/PC就会获取到IPC分配的IP地址(按照设备发现描述的方法即可获取到),一般会是192.168.200.XXX,手机/PC的网关地址就是IPC的地址,一般会是192.168.200.1。
- 调用无线配置 API(见获取无线扫描AP列表 (无需签名校验)的描述)获取IPC扫描到的AP热点。
- 调用无线配置 API(见设置无线参数(无需签名校验)的描述)设置IPC要连接的无线网络(例如无线路由器的SSID和密码),使得IPC能够从网关处获取到IP地址。
- 如果网络是可以正常上网的话,IPC取到IP地址后很快就会亮蓝灯,此时表明IPC可以正常连接Internet了。
// 获取IPC扫描的AP热点
private void getWifiList() {
BasicConfig.getInstance(context).getApListWithoutAuth(sunmiDevice.getDeviceid(),
new RPCCallback‹RPCResponse‹IpcApBean››() {
@Override
public void onComplete(RPCResponse‹IpcApBean› result) {
if (result.code() == RPCErrorCode.SUCCESS) {
wifiListGetSuccess(result.data());
} else {
Log.i(TAG, "getApListWithoutAuth failed, errcode: " + result.code());
}
}
});
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (wifiList.size() == 0) {
setNoWifiVisible(View.VISIBLE);
}
}
}, TIMEOUT_GET_WIFI);
}
// 设置IPC连接的AP热点
private void setIpcWifi(String ssid, String psw) {
showLoadingDialog();
BasicConfig.getInstance(context).setWifiConfWithoutAuth(sunmiDevice.getDeviceid(),
ssid, psw, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
if (result.code() == RPCErrorCode.SUCCESS) {
hideLoadingDialog();
shortTip("配置成功,请等待设备联网,联网后指示灯会变成蓝色");
createWaitDialog();
} else {
hideLoadingDialog();
shortTip("配置失败");
}
}
@Override
public void onError(Throwable t) {
hideLoadingDialog();
shortTip("配置失败");
}
});
}
2.3 激活
通过 2.2配网 后,IPC通过无线连接到路由或者直接有线连接路由。
由于使用IPC的其它API时需要签名校验,所以在使用API之前需要激活IPC。激活只需要在第一次使用IPC时进行,后续都不再需要激活。
调用active接口既可以激活IPC设备。 代码如下:
//激活ipc设备,并设置回调接口
DeviceManage.getInstance(context).activate(ipcList.get(postion).getDeviceid(),
new RPCCallback‹RPCResponse›() {
//IPC返回相关错误码。
@Override
public void onComplete(RPCResponse result) {
if (result.code() == RPCErrorCode.SUCCESS || result.code() == RPCErrorCode.DEVICE_ACTIVATED) {
Log.i(TAG, "activate ipc success");
} else {
Log.i(TAG, "activate ipc failed");
}
}
//网络问题导致无法通信,会调用onError接口
@Override
public void onError(Throwable t) {
Log.i(TAG, "activate ipc failed");
}
});
2.4 画面调整
由于IPC的人脸识别对于人脸图像质量有一定要求,因此在使用IPC前需要调整IPC的画面,以达到最好的体验效果。画面调整包括镜头的调焦、对焦。步骤如下:
2.4.1 RTSP 播放
首先需要通过预览画面查看IPC设备镜头是对焦,画面是否清晰。IPC设备是通过RTSP协议传递视频流,所以开发一款RTSP播放器才能看到视频流。
调用VideoStream.getInstance(context).getLiveStream接口可以获取到RTSP播放流的地址。代码流程如下:
private void openMediaPlayer() {
new Thread(new Runnable() {
@Override
public void run() {
//获取播放流
VideoStream.getInstance(context).getLiveStream(mDevice.getDeviceid(),
new RPCCallback‹RPCResponse‹RPCResponse.LiveAddressBean››() {
@Override
public void onComplete(RPCResponse‹RPCResponse.LiveAddressBean› result) {
if (result.code() == RPCErrorCode.SUCCESS) {
Log.i(TAG, "live url: " + result.data().fhd_live_url);
if (mPlayer == null) {
//自定义的rtsp播放器
mPlayer = new SunmiPlayer(context);
mPlayer.setListener(new SunmiPlayerListener() {
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
iMediaPlayer.start();
}
});
mPlayer.setSurface(mVideoView.getHolder().getSurface());
//获取到ipc返回的播放流rtsp地址
String liveUrl = result.data().fhd_live_url.replaceFirst("^rtsp://", "rtsp://admin:admin@");
//自定义播放器开始播放rtsp视频流
mPlayer.setUp(liveUrl);
}
}
}
});
}
}).start();
}
2.4.2 调焦、对焦
private BasicConfig mBasicConfig;
public void init() {
...
mBasicConfig = BasicConfig.getInstance(context);
...
}
// 调焦
private void func1(){
...
mBasicConfig.setZoom(mDevice.getDeviceid(), zfBean.zoom, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
Log.i(TAG, "setZoom, code:" + result.code());
}
});
...
}
// 自动对焦
private void func2() {
...
mBasicConfig.autoFocus(mDevice.getDeviceid(), xRelative, yRelative, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
Log.d(TAG, "autoFocus, code:" + result.code());
if (result.code() == RPCErrorCode.SUCCESS) {
mBasicConfig.getZoomFocusConf(mDevice.getDeviceid(), new RPCCallback‹RPCResponse‹RPCResponse.ZoomFocusBean››() {
@Override
public void onComplete(RPCResponse‹RPCResponse.ZoomFocusBean› result) {
if (result.code() == RPCErrorCode.SUCCESS) {
zfBean = result.data();
mSbZoom.setProgress(zfBean.zoom);
}
}
});
}
}
});
...
}
// 自动对焦如果不够清晰,可以手动对焦进行微调
private void func3() {
...
mBasicConfig.manualFocus(mDevice.getDeviceid(), focus, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
if (result.code() == RPCErrorCode.SUCCESS) {
zfBean.focus = focus;
}
}
});
....
}
2.4.3 设置门线
设置门线主要是更精确地判断人流的方向(进门、出门、路过),提高人流统计的准确度。
调用接口PeopleFlowStats.getInstance(context).setDoorLine设置门线。代码如下:
private void func() {
...
PeopleFlowStats.getInstance(context).setDoorLine(mDevice.getDeviceid(), 0, lineStart[0],
lineStart[1], lineEnd[0], lineEnd[1], new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
if (result.code() == RPCErrorCode.SUCCESS) {
stopPlay();
finish();
startActivity(new Intent(context, MainActivity.class));
}
}
});
...
}
3、注册人脸
IPC激活好以后,向设备注册人脸信息。
Android设备端注册人脸时,需要同步向IPC设备注册人脸需要传递用户人脸照片、以及唯一标识用户的ID。
通过接口IPCameraManager.getInstance(context).addFaceRecord向IPC注册人脸。代码如下:
/*
*传递人脸照片路径: picPath,
* 用户唯一ID: uid
*/
IPCameraManager.getInstance(context).addFaceRecord(picPath, uid, new RPCCallback‹RPCResponse›() {
//IPC设备调用成功后会调用onComplete
@Override
public void onComplete(RPCResponse result) {
Log.i(TAG, "addFaceRecord, code:" + result.code());
if (result.code() != 0){
showToast(context, "添加人脸失败 code: " + result.code());
} else {
showToast(context, "添加人脸成功");
}
}
//IPC设备离线时,调用会调用onError
@Override
public void onError(Throwable t) {
super.onError(t);
Log.i(TAG, "addFaceRecord, Exception:" + t.getMessage());
showToaste(context, "添加人脸失败 ");
}
});
4、删除人脸
Android设备端删除用户时,需要同步删除IPC设备的人脸信息。Andorid端设备需要传递用户唯一ID。
调用接口 IPCameraManager.getInstance(context).addFaceRecord 向IPC删除人脸。代码如下:
//传递用户唯一ID:userId
IPCameraManager.getInstance(context).deleteFaceRecord(userId,new RPCCallback‹RPCResponse‹RPCResponse.FaceDeleteSubResult››() {
@Override
public void onComplete(RPCResponse‹RPCResponse.FaceDeleteSubResult› result) {
Log.i(TAG, "deleteFaceRecord, code:" + result.code());
if (result.code() != 0){
showToast(context, "删除人脸失败 code: " + result.code());
} else {
showToast(context, "删除人脸成功");
}
}
@Override
public void onError(Throwable t) {
super.onError(t);
Log.i(TAG, "addFaceRecord, Exception:" + t.getMessage());
}
});
5、 监听人脸识别消息
Android设备注册好人脸信息后,即可以监听IPC设备人脸识别信息。通过IPC返回的ID信息,查询数据库,显示用户详细信息。
调用IPCameraManager.getInstance(context).setFaceDetectListener即可监听人脸信息,代码如下:
IPCameraManager.getInstance(context).setFaceDetectListener(new FaceDetectListener() {
@Override
public void onFaceDetect(String userId) {
String userName = null;
if (userName == null) {
showToast(getApplicationContext(), "未注册的用户进店");
} else {
showToast(getApplicationContext(), "用户[ " + userName + " ]进店");
}
}
});
设备端OpenAPI Postman测试示例
1.准备工作
本文介绍如何使用Postman在局域网内完成商米IPC设备的首配操作,并进行调试。首先请安装说明书接上电源并通过网线连上网络,确保IPC设备亮蓝灯。
进行以下操作前,还需要获取以下信息,其中除了IPC设备IP和设备SN外,其它信息请联系商米售前技术团队获取:
项目 | 本文示例 |
app_id | 08BABCDABBB661234567 |
secret_key | ABCDEFG3F2BB03917123 |
激活码 | abcd2986jnts8987hntl1234 |
OpenAPI证书及其密码 | openapi.p12 |
Postman测试集 | Sunmi_IPC_OpenAPI_Postman_Collection_202007001.json |
设备IP | 192.168.103.198 |
设备SN | C201000P00123 |
2.Postman安装
- 通过Postman官网下载最新版本软件:链接,本文使用的版本为v7.27.1。
- 按照安装向导进行安装
3.导入OpenAPI测试集合及配置
- 启动Postman
- 点击File -> Import -> Upload Files, 打开商米提供的Postman测试集文件,即”Sunmi_IPC_OpenAPI_Postman_Collection_202007001.json”,确认后点击”Import”。
- 点击File -> Settings -> Certificate,打开证书管理页面,点击”Add Certificate”,在Host项目填入设备IP如192.168.103.198,在下方打开商米提供的证书并填写对应的密码,确认无误后点击”Add”。
- 4. 打开General页面,将”SSL certificate verificatio”选项置为`OFF`,并关闭页面。
- 5. 返回Postman主页面,在侧边栏的Sunmi IPC OpenAPI测试集点击右键,选择”Edit”, 打开”Variables”页面,在”CURRENT VALUE”栏更新对应信息。
4.激活示例
- 激活步骤请参考商米开发者平台智能摄像机IPC的设备管理部分
- 激活操作采用激活码代替secret_key,请参照第3步修改本测试集的”Variables”页面的secret_key参数为商米提供的激活码
- 从侧边栏打开Sunmi IPC OpenAPI -> 基本配置 -> 0. 激活设备,在窗口右侧”Body”中,修改参数”sn”的值为测试设备SN,点击”Send”,成功激活返回”code”为”0″,如下图所示
5.OpenAPI示例测试
OpenAPI开发指南
1.准备工作
1.1 申请API对接所需的材料
步骤一:联系商米售前技术团队,申请对接API所需要的账号信息,需要向售前技术支持团队提供如下软件开发商信息
客户提供的信息 | 说明 |
客户名称 | 软件商公司名称 |
对接摄像头型号 | 对接API的型号,如SS(Store Sense)、FS(Face Sense) |
客户设备的SN | 客户对接联调的所有设备的SN,需要商米云把这些SN加入到UAT环境 |
客户需求场景 | 描述大体的需求和使用场景 |
步骤二:售前技术团队根据客户的需求信息,返回软件商对接API所需的账号信息,包括
返回给客户的信息 | 说明 |
SaaS代号 | 商米内部管理SaaS软件商信息的代号 |
app_id | 激活设备端API所需的账号 |
secret_key | 调用API所需的签名秘钥 |
设备激活码 | 激活设备端API所需的激活码,用于签名校对软件商是否有权调用 |
可激活的设备数量上限 | UAT环境下的可激活设备的数量 |
openapi.bks | 双向验证中客户端自己的证书以及私钥。 |
sunmica.bks | 双向验证中用于校验服务端发送过来的的证书是否合法。 |
SDK开发包 | 用于对接的SDK,目前只支持安卓版本 |
UAT环境商米助手APP | 用于在UAT环境下给设备做首配 |
1.2 设备刷机UAT环境固件
一般,软件商客户在开发对接API阶段,建议使用调试环境,即商米的UAT环境,避免对正式生产环境造成困扰或者数据污染。
因此,需要将摄像头的固件升级到开发测试环境(UAT环境),具体操作流程参考 升级开发环境固件。
另外,刷机到UAT环境后,可以使用UAT环境商米助手进行首次配置,使摄像机工作起来,后续集成了Android SDK后可以在开发者自己的应用上对摄像头进行配置,而无需商米助手。
2.OpenAPI简介
摄像头API提供强大的功能,几乎包含了摄像头本身具有的所有功能,用户可以通过这些API开发自己的功能并集成到自己的应用上。
API提供两种使用方式,用户可以选择其中一种方式来完成自己的开发,简单介绍如下:
方式一:直接根据API接口实现原理和接口细节(详见API调用方式),自己开发客户端代码调用API。
方式二:推荐的方式,根据商米提供的平台SDK(当前只支持安卓SDK,详见设备端Android SDK),直接调用JAVA API来完成对摄像头API的调用,不需要过多关注API的接口细节,方便快捷,节省开发时间。
后续的集成指南介绍都以安卓SDK来展开。
3. Android SDK简介
3.1 相关文档
序号 | 内容项 | 获取方式 | 说明 |
---|---|---|---|
1 | SDK技术文档 | Android SDK技术文档。 | 包含了SDK的API说明与示例代码。 |
2 | SDK开发包 | 由技术支持提供给SaaS客户。 | 见章节3.2。 |
3 | SDK License | SaaS客户提供自身的客户信息进行申请,技术支持邮件发送给SaaS客户。 | License目前包含:app_id、secret_key、激活码 |
3.2 SDK开发包介绍
- SDK开发包组成
序号 | 文件/文件名 | 说明 |
---|---|---|
1 | IpcDemo | Demo工程源码,可直接编译出Demo APP |
2 | ipcsdk.aar | OpenAPI库 |
3 | demo.apk | 编译好的Demo APP |
- Demo工程源码结构
4. Android SDK集成指南
详见设备端Android SDK集成指南。
5. 开发联调
激活设备API后,可以正式调用摄像头所有开发的API完成自己的业务开发和调试,测试通过后即可准备上线。
6. 正式上线
由于开发联调是在UAT环境完成的,因此,客户确认业务测试没有问题后,给客户正式使用的时候需要上线到正式环境,即商米的 Release环境。
可以给设备升级到正式环境,见升级线上环境固件的操作指南。在正式环境下回归测试OK后,即可正式发布。
商米AI识客SDK开发指南
1.准备工作
1.1 申请API对接所需的材料
步骤一:联系商米售前技术团队,申请对接API所需要的账号信息,需要向售前技术支持团队提供如下软件开发商信息
客户提供的信息 | 说明 |
客户名称 | 软件商公司名称 |
对接摄像头型号 | 对接API的型号,如SS(Store Sense)、FS(Face Sense) |
收银设备硬件指纹 | 获取到SDK后,通过SDK获取硬件指纹信息,用于申请Licence |
步骤二:售前技术团队根据客户的需求信息,返回软件商对接API所需的账号信息,包括:
返回给客户的信息 | 说明 |
SaaS代号 | 商米内部管理SaaS软件商信息的代号 |
设备激活码 | 激活设备端所需的激活码,用于签名校对软件商是否有权调用 |
SDK开发包 | 用于人脸识别以及AI识客设备开发的SDK,目前只支持安卓版本 |
SDK开发包:
序号 | 文件/文件名 | 说明 |
---|---|---|
1 | FaceDemo | Demo工程源码 |
2 | face-release.aar | 商米人脸识别SDK |
3 | libipcsdk-release.aar | IPC联动版sdk |
4 | asset | 商米 人脸识别资源文件 |
2、申请Licence
登陆开发者网站,提供 以下信息申请Licence
提供的信息 | 说明 |
收银设备硬件指纹 | 通过SDK API获取设备硬件信息 |
人脸库 | 人脸库的限制大小 |
商米开发者网站会生成一个Licence文件。SaaS开发者获取到Licence后即使用SDK。
3、添加SDK到工程
3.1 导入SDK
右键点击app,选择New->Module->Import .JAR/.AAR Package
点击Finish即导入aar模块
3.2 权限申明
在AndroidManifest.xml文件里添加如下权限:
‹uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/›
‹uses-permission android:name="android.permission.INTERNET" /›
‹uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /›
‹uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /›
‹uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" /›
‹uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /›
‹uses-permission android:name="android.permission.CAMERA" /›
3.3 导入asset资源
导入提供的assets资源
说明:资源文件需要APP启动的时候复制到APP的运行目录,以方便SDK后续初始化。
3.4 导入licence文件
获取到licence_valid.txt文件,可以放到assets资源目录,也可以放到sdcard目录。
1)放到assets资源目录,则需要把licence_valid.txt文件从资源文件复制到app的工作目录。
2)如果放到sdcard目录,则需要app申请sdcard读取权限。
无论哪一种情况,最后使用的时候,需要传递licence_valid.txt文件的路径,以激活人脸识别SDK。详细见SDK初始化代码
4、DEMO说明
4.1 工程目录结构
4.1.1 app Module
app Module是DEMO的入口。其中包含了各个模块的入口函数调用。
4.1.2 commonlib
commonlib Module包含了整个DEMO通用的接口。其中包含摄像头管理,工具类。如下图:
4.1.2.1 FaceCameraManager
FaceCameraManager统一管理设备自带摄像头和USB摄像头。设备自带摄像头的流程是使用Android系统提供的摄像头通用方法。USB摄像头则使用UVC库提供的操作摄像头的方法。FaceCameraManager对上层应用提供统一的方法: startPreview和 stopPreview。
4.1.2.2 NormalCameraManager
NormalCameraManager使用Android系统提供的摄像头方法,实现startPreview和stopPreview。并在内部实现回调接口用于返回每一帧摄像头获得的数据。
4.1.2.3 UVCCameraManager
UVCCameraManager使用第三方库UVClib提供的方法用于操作USB摄像头,实现startPreview和stopPreview。 并在内部实现回调接口用于返回每一帧摄像头获得的数据 。
4.1.2.4 ByteUtils
ByteUtils提供Byte操作的一些通用方法。
4.1.2.5 DBHelper
DBHelper提供创建数据的方法。
4.1.2.6 ImageUtils
ImageUtils提供图片操作的一些方法。
4.1.3 libuvccamera
libuvccamera Module采用第三方库开源库libuvccamera
4.1.4 facedemo
facedemo Module是DEMO实现功能的主要模块。其包含:会员注册、会员管理、会员查询、IPC配置等功能。
5、SDK初始化
详细代码见 商米AI识客SDK示例代码-> SDK初始化
6、获取摄像头数据
详细代码见 商米AI识客SDK示例代码->获取摄像头信息
7、获取摄像头预览数据
详细代码见 商米AI识客SDK示例代码->获取摄像头预览信息
8、会员注册
详细代码见 商米AI识客SDK示例代码-> 会员注册
9、会员实时查询
会员实时查询,需要使用到,实时获取摄像头预览数据以及人脸特征提取两个功能
详细代码见 商米AI识客SDK示例代码->获取摄像头预览信息
详细代码见 商米AI识客SDK示例代码->人脸特征提取
10、配置摄像机
见IPC配置页面
示例参考
本文描述的示例代码都可在Demo工程源码中找到,故详细代码可阅读SDK开发包介绍中提到的Demo工程源码。
1. SDK初始化
APP_ID:激活与API调用校验使用的账号。
SECRET_KEY:API调用所需的签名密钥。
LICENSE:激活API所需的激活码。
IPCameraManager mIPCameraManager = IPCameraManager.getInstance(context);
mIPCameraManager.init(APP_ID, SECRET_KEY, LICENSE);
2.设备发现
当集成安卓SDK的机器和摄像头设备在同一个局域网内的时候,可以通过如下示例方法扫描到所有的摄像头设备。
设备发现listener返回的IPCameraInfo包含了IPC的SN、IP、MAC等基本信息。
mIPCameraManager.registerListener(new IPCameraListener() {
@Override
public void onDeviceOnline(IPCameraInfo device) {
showToast(getApplicationContext(), "[ " + device.getDeviceid() + " ]上线");
}
@Override
public void onDeviceOffline(IPCameraInfo device) {
showToast(getApplicationContext(), "[ " + device.getDeviceid() + " ]离线");
}
});
3.首次配网并激活
调用IPC前需要先获取到IPC的IP地址,故第一步是把IPC接入网络,IPC连接网络的方式有有线连接和无线连接两种方式。
由于有线网络相对不容易受到环境干扰,稳定性和可靠性较高,故首选是有线方式接入网络。
有线接入:
有线接入只需要通过网线把IPC设备和集成SDK的Android设备接入到同一局域网即可,不需要其它设置。
无线接入:
无线配网方式相对要复杂点,步骤如下:
- 使用手机/PC的无线网卡扫描IPC的AP热点,一般AP热点的名称为SUNMI_XXXX,其中XXXX为MAC地址最后2个字节的16进制数字,MAC地址可以通过设备机身后背的标贴或者包装盒的标贴查到,AP热点本身是无加密的。
- 使用手机/PC的无线网卡连接IPC的AP热点,此时手机/PC就会获取到IPC分配的IP地址(按照设备发现描述的方法即可获取到),一般会是192.168.200.XXX,手机/PC的网关地址就是IPC的地址,一般会是192.168.200.1。
- 调用无线配置 API(见获取无线扫描AP列表 (无需签名校验)的描述)获取IPC扫描到的AP热点。
- 调用无线配置 API(见设置无线参数(无需签名校验)的描述)设置IPC要连接的无线网络(例如无线路由器的SSID和密码),使得IPC能够从网关处获取到IP地址。
- 如果网络是可以正常上网的话,IPC取到IP地址后很快就会亮蓝灯,此时表明IPC可以正常连接Internet了。
// 获取IPC扫描的AP热点
private void getWifiList() {
BasicConfig.getInstance(context).getApListWithoutAuth(sunmiDevice.getDeviceid(),
new RPCCallback‹RPCResponse‹IpcApBean››() {
@Override
public void onComplete(RPCResponse‹IpcApBean› result) {
if (result.code() == RPCErrorCode.SUCCESS) {
wifiListGetSuccess(result.data());
} else {
Log.i(TAG, "getApListWithoutAuth failed, errcode: " + result.code());
}
}
});
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (wifiList.size() == 0) {
setNoWifiVisible(View.VISIBLE);
}
}
}, TIMEOUT_GET_WIFI);
}
// 设置IPC连接的AP热点
private void setIpcWifi(String ssid, String psw) {
showLoadingDialog();
BasicConfig.getInstance(context).setWifiConfWithoutAuth(sunmiDevice.getDeviceid(),
ssid, psw, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
if (result.code() == RPCErrorCode.SUCCESS) {
hideLoadingDialog();
shortTip("配置成功,请等待设备联网,联网后指示灯会变成蓝色");
createWaitDialog();
} else {
hideLoadingDialog();
shortTip("配置失败");
}
}
@Override
public void onError(Throwable t) {
hideLoadingDialog();
shortTip("配置失败");
}
});
}
激活IPC:
配网成功后,使用IPC的其它API时需要签名校验,所以在使用API之前需要激活IPC。激活只需要在第一次使用IPC时进行,后续都不再需要激活。
DeviceManage.getInstance(context).activate(ipcList.get(postion).getDeviceid(),
new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
if (result.code() == RPCErrorCode.SUCCESS || result.code() == RPCErrorCode.DEVICE_ACTIVATED) {
Log.i(TAG, "activate ipc success");
} else {
Log.i(TAG, "activate ipc failed");
}
}
@Override
public void onError(Throwable t) {
Log.i(TAG, "activate ipc failed");
}
});
4. 画面调整
由于IPC的人脸识别对于人脸图像质量有一定要求,因此在使用IPC前需要调整IPC的画面,以达到最好的体验效果。画面调整包括镜头的调焦、对焦。
private BasicConfig mBasicConfig;
public void init() {
...
mBasicConfig = BasicConfig.getInstance(context);
...
}
// 调焦
private void func1(){
...
mBasicConfig.setZoom(mDevice.getDeviceid(), zfBean.zoom, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
Log.i(TAG, "setZoom, code:" + result.code());
}
});
...
}
// 自动对焦
private void func2() {
...
mBasicConfig.autoFocus(mDevice.getDeviceid(), xRelative, yRelative, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
Log.d(TAG, "autoFocus, code:" + result.code());
if (result.code() == RPCErrorCode.SUCCESS) {
mBasicConfig.getZoomFocusConf(mDevice.getDeviceid(), new RPCCallback‹RPCResponse‹RPCResponse.ZoomFocusBean››() {
@Override
public void onComplete(RPCResponse‹RPCResponse.ZoomFocusBean› result) {
if (result.code() == RPCErrorCode.SUCCESS) {
zfBean = result.data();
mSbZoom.setProgress(zfBean.zoom);
}
}
});
}
}
});
...
}
// 自动对焦如果不够清晰,可以手动对焦进行微调
private void func3() {
...
mBasicConfig.manualFocus(mDevice.getDeviceid(), focus, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
if (result.code() == RPCErrorCode.SUCCESS) {
zfBean.focus = focus;
}
}
});
....
}
5. 设置门线
设置门线主要是更精确地判断人流的方向(进门、出门、路过),提高人流统计的准确度。
private void func() {
...
PeopleFlowStats.getInstance(context).setDoorLine(mDevice.getDeviceid(), 0, lineStart[0],
lineStart[1], lineEnd[0], lineEnd[1], new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
if (result.code() == RPCErrorCode.SUCCESS) {
stopPlay();
finish();
startActivity(new Intent(context, MainActivity.class));
}
}
});
...
}
6. 监听人脸识别消息
mIPCameraManager.setFaceDetectListener(new FaceDetectListener() {
@Override
public void onFaceDetect(String userId) {
String userName = null;
if (userName == null) {
showToast(getApplicationContext(), "未注册的用户进店");
} else {
showToast(getApplicationContext(), "用户[ " + userName + " ]进店");
}
}
});
商米AI识客SDK代码示例
1. SDK初始化
public boolean initFaceSdkInstance(Context context, String licencePath) {
//初始化配置文件
int ret = SunmiFaceSDK.init(confPath);
if (licencePath == null) {
Log.d(TAG, "Please input licence");
return false;
}
File f = new File(licencePath);
if (!f.exists()) {
Log.d(TAG, "licence is not exist");
return false;
}
String licence = readToString(licencePath);
//验证Licence
ret = SunmiFaceSDK.verifyLicence(context, licence);
if (ret != 0) {
Log.d(TAG, "Licence is not OK ErrorCode " + ret);
return false;
}
//设置配置参数
SunmiFaceConfigParam param = new SunmiFaceConfigParam();
SunmiFaceSDK.getConfig(param);
param.setDistance_threshold_(1.3f);
param.setYaw_threshold_(50.0f);
param.setPitch_threshold_(50.0f);
param.setRoll_threshold_(50.0f);
param.setMin_face_size(60);
param.setImage_quality_threshold_(10);
param.setMin_luminance_(10);
param.setMax_luminance_(180);
param.setLiveness_on_(false);
param.setThread_num_(1);
//设置配置参数
ret = SunmiFaceSDK.setConfig(param);
if (ret != 0) {
Log.d(TAG, "param is not init");
return false;
}
return true;
}
public void init(Context context) {
//设置licence_valid.txt为sd卡路径,也可以换成app运行工作目录。
String licencePath = Environment.getExternalStorageDirectory() + File.separator + "licence_valid.txt";
initFaceSdkInstance(context, licence_path);
// 调用IPCameraManager静态类getInstance初始化IPCManger对象
mIPCManager = IPCameraManager.getInstance(context);
}
2. 获取摄像头预览信息
//摄像头数据回调接口
CameraDataCallback cameraDataCallback = new CameraDataCallback() {
@Override
public void onImageDataArrival(Bitmap bitmap, int width, int height) {
if (mBackgroundHandler != null) {
if (mClickAction == 1 && !faceDetectState.get()) {
mBackgroundHandler.post(new MotionDetector(bitmap, width, height));
} else {
bitmap.recycle();
}
}
}
};
//开启摄像头预览
FaceCameraManager.getInstance().startPreview(getApplicationContext(), this, FaceCameraManager.getInstance().getCurCamera(), cameraView, PREVIEW_WIDTH, PREVIEW_HEIGHT, cameraDataCallback);
3. 获取摄像头拍照数据
{
//根据摄像头是USB还是系统摄像头,调用不同的方法,获取bitmap数据
if (FaceCameraManager.getInstance().getCurCamera() == FaceCameraManager.CAMERA_USB)
bitmap = mUVCCameraView.captureStillImage();
else
bitmap = mTextureView.getBitmap();
}
4. 获取特征值
说明:通过bitmap获取特征值
public ArrayList‹SunmiFaceFeature› getFeatures(Bitmap bitmap, int maxFace) {
int ret = 0;
//bitmap的RGB数据转化成BGR数据
byte[] srcData = ImageUtils.getPixelsBGR(bitmap);
//通过BGR数据构造SunmifaceImage对象
SunmiFaceImage image = new SunmiFaceImage(srcData, bitmap.getHeight(), bitmap.getWidth(), maxFace);
SunmiFaceImageFeatures features = new SunmiFaceImageFeatures();
//从SunmifaceImage数据中提取人脸特征
ret = SunmiFaceSDK.getImageFeatures(image, features);
//返回人脸特征数组
SunmiFaceFeature feature_ary = features.getFeatures_();
ArrayList‹SunmiFaceFeature› arrayList = new ArrayList‹›();
if (features.getFeatures_count_() == 0) {
SunmiFaceSDK.releaseImageFeatures(features);
return arrayList;
}
for (int i = 0; i ‹ features.getFeatures_count_(); i++) {
SunmiFaceFeature sunmiFaceFeature = SunmiFaceLib.SunmiFaceFeatureArray_getitem(feature_ary, i);
arrayList.add(sunmiFaceFeature);
}
SunmiFaceSDK.releaseImageFeatures(features);
return arrayList;
// return getFeature(srcData, bitmap.getWidth(),bitmap.getHeight(), 1);
}
5. 人脸特征值比较
public SunmiFaceCompareResult compareFeature1V1(float similar, float[] feature1, float[] feature2) {
//构造SunmiFaceFeature对象
SunmiFaceFeature feat1 = new SunmiFaceFeature();
SunmiFaceFeature feat2 = new SunmiFaceFeature();
feat1.setFeature_(feature1);
feat2.setFeature_(feature2);
SunmiFaceCompareResult result = new SunmiFaceCompareResult();
//调用compare1v1进行比较
int ret = SunmiFaceSDK.compare1v1(feat1, feat2, result);
return result;
}
6. 人脸会员库添加
说明: 添加人脸时,同时要向IPC注册人脸信息。向IPC传递信息包括:人脸会员库(GVIP),人脸照片、人脸会员唯一识别ID。
{
//使用faceFeature构建record
SunmiFaceDBRecord record = SunmiFaceSDK.faceFeature2FaceDBRecord(faceFeature);
record.setId_(user.getUserId());
record.setName_(user.getUserName());
//把record添加到人脸库
int ret = SunmiFaceSDK.addDBRecord(record);
//把人脸照片传递给IPC,向IPC添加人脸记录
if (mIPCManager != null) {
mIPCManager.addFaceRecord("GVIP", picPath, uid, new RPCCallback‹RPCResponse›() {
//IPC人脸添加记录完成后回调接口
@Override
public void onComplete(RPCResponse result) {
Log.i(TAG, "addFaceRecord, code:" + result.code());
}
//IPC人脸添加记录失败时回调接口
@Override
public void onAbort(int httpStatus) {
Log.i(TAG, "addFaceRecord, httpStatus:" + httpStatus);
}
//IPC人脸添加记录异常时回调接口
@Override
public void onException(Throwable t) {
Log.i(TAG, "addFaceRecord, Exception:" + t.getMessage());
}
});
}
}
7. 删除特征记录
说明: 删除会员信息时,需要把删除的人脸会员信息同步到IPC。向IPC传递用户的唯一识别ID
{
//删除人脸库记录
int result = SunmiFaceSDK.deleteDBRecord(user.getImageName());
//同步删除IPC人脸记录
if (mIPCManager != null) {
mIPCManager.deleteFaceRecord("GVIP", userId, new RPCCallback‹RPCResponse‹RPCResponse.FaceDeleteSubResult››() {
//IPC人脸删除记录完成后回调接口
@Override
public void onComplete(RPCResponse‹RPCResponse.FaceDeleteSubResult› result) {
Log.i(TAG, "deleteFaceRecord, code:" + result.code());
}
});
}
}
8 会员注册
/*
*向系统注册会员信息
*@groupname, 会员组
*@userNanem, 会员名
*@picPath, 会员照片存放路劲
*@userInfo, 会员详情
*@faceFeature, 会员照片提取的特征值
*/
public boolean registerUserIntoDBmanager(String groupName, String userName, String picPath, String userInfo, SunmiFaceFeature faceFeature) {
boolean isSuccess = false;
Group group = new Group();
group.setGroupId(groupName);
//创建会员
User user = new User();
user.setGroupId(groupName);
//final String uid = UUID.randomUUID().toString();
String uid = String.valueOf(System.currentTimeMillis());
user.setUserId(uid);
user.setUserName(userName);
user.setFeature(faceFeature.getFeature_());
//创建人脸库记录信息
SunmiFaceDBRecord record = SunmiFaceSDK.faceFeature2FaceDBRecord(faceFeature);
record.setId_(user.getUserId());
record.setName_(user.getUserName());
//像人脸库添加记录
int ret = SunmiFaceSDK.addDBRecord(record);
if (ret != 0) {
Log.d(TAG, "addDBRecord failed " + SunmiFaceSDK.getErrorString(ret));
return false;
}
//在会员数据库中记录,特征值对应的图片在人脸数据中的ID
user.setImageName(record.getImg_id_());
if (userInfo != null) {
user.setUserInfo(userInfo);
}
// 添加用户信息到数据库
boolean importUserSuccess = FaceManager.getInstance().userAdd(user);
if (importUserSuccess) {
// 如果添加到数据库成功,则添加用户组信息到数据库
// 如果当前图片组名和上一张图片组名相同,则不添加数据库到组表
if (FaceManager.getInstance().groupAdd(group)) {
isSuccess = true;
if (mIPCManager != null) {
Log.i(TAG, "picPath: " + picPath);
Log.i(TAG, "uid: " + uid);
//把会员照片同步发送到IPC,同步会员信息到IPC
mIPCManager.addFaceRecord("GVIP", picPath, uid, new RPCCallback‹RPCResponse›() {
@Override
public void onComplete(RPCResponse result) {
Log.i(TAG, "addFaceRecord, code:" + result.code());
}
@Override
public void onAbort(int httpStatus) {
Log.i(TAG, "addFaceRecord, httpStatus:" + httpStatus);
}
@Override
public void onException(Throwable t) {
Log.i(TAG, "addFaceRecord, Exception:" + t.getMessage());
}
});
}
} else {
isSuccess = false;
}
} else {
isSuccess = false;
}
return isSuccess;
}
9. IPC 回调接口
说明:用户获取IPC的状态、返回信息等。
{
//获取IPC信息
IPCameraManager manager = IPCameraManager.getInstance(getApplicationContext());
//IPC初始化
manager.init("test", "123456", "123456");
//注册IPC回调
manager.registerListener(new IPCameraListener() {
//IPC设备在线时回调接口
@Override
public void onDeviceOnline(IPCameraInfo device) {
showToast("[ " + device.getDeviceid() + " ]上线");
}
//IPC设备离线时回调接口
@Override
public void onDeviceOffline(IPCameraInfo device) {
showToast("[ " + device.getDeviceid() + " ]离线");
}
//IPC设备获取到进店信息后回调接口
@Override
public void onFaceDetect(String userId) {
String userName = null;
List‹User› users = DBManager.getInstance().queryUserByUserId(userId);
if (users != null && users.size() › 0) {
userName = users.get(0).getUserName();
}
if (userName == null) {
showToast("未注册的用户进店");
} else {
User user = users.get(0);
UserEvent userEvent = new UserEvent();
userEvent.setUserId(user.getUserId());
userEvent.setEnterTime(System.currentTimeMillis());
userEvent.setDesc("Enter Store");
DBManager.getInstance().addUserEvent(userEvent);
showToast("用户[ " + userName + " ]进店");
}
}
}
}
商米AI识客SDK接口说明
1. 商米人脸识别接口
见《商米人脸识别会员接口文档》,该文档在申请开发包的时候一并提供。
2. IPC联动接口
见IPCManager页面
商米AI识客SDK简介
1 简介
商米AI识客SDK (Android版)是一种面向SUNMI OS设备的人脸识别开发包,配套商米AI识客摄像头使用,SDK主要包含人脸采集、人脸识别、人脸特征数据管理、摄像头配置激活、会员进店提醒等功能,以aar包形式发布。如下是SDK整体框架以及Demo APP架构。
SDK框架图如下:
- 主要包含四层结构
- 1、C/C++ library实现人脸特征提取,人脸特征对比等算法功能,并提供人脸库管理(人脸库的添加、查询、删除等操作)
- 2、JNI层主要是封装C/C++实现的人脸识别的功能,为Java上层提供native接口
- 3、Java API层使用native层提供的接口为上层 APP提供SunmiFace接口,并提供IPC配置管理、通讯交互的功能
- 4、Demo APP,使用SDK提供的功能实现人脸识别、人脸库的管理、IPC配置管理、IPC通讯等功能。
2 业务场景
通过此SDK您可以完成类似如下的应用场景开发。