坐姿检测接入文档
文档状态
| 文件标识: | TuringOS-Posture-V1.0 |
|---|---|
| 系统版本: | SDK |
| 完成日期: | 2022年06月19日 |
文档修订记录
| 文档版本号 | 修订日期 | 修订原因 | 备注 |
|---|---|---|---|
| V1.0 | 2022.06.19 | 创建文档 | |
| V1.1 | 2023.05.12 | 支持由外部传图片数据进行检测 | |
| V1.2 | 2023.07.04 | SDK内部相机支持不显示预览界面 | 调用startPostureDetect(),viewGroup=null,cameraConfig不为null |
| V1.3 | 2023.07.21 | SDK内部相机支持Camera1 | 设置方式:CameraConfig.Builder#setCameraApi(1) |
概述
实现注意力、接受度、精神状态、坐姿检测。
-
注意力检测,具体包括歪头、侧脸、低头、仰头、闭眼。
-
接受度检测,具体包括点头、摇头、高兴、疑惑、举手。
-
精神状态检测,具体包括打哈欠、瞌睡、趴。
-
坐姿检测,具体包括坐姿不正、距离估计。坐姿不正为:歪头、低头、仰头几种情况。
集成步骤
1. 添加依赖库
//必须依赖库
implementation 'com.google.code.gson:gson:2.6.2'
implementation 'com.squareup.okhttp3:okhttp:3.9.0'
implementation "org.java-websocket:Java-WebSocket:1.4.0"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.aliyun.dpa:oss-android-sdk:2.9.11'//SDKV2.4.1.25 以后需加入
//坐姿检测
implementation 'com.squareup.moshi:moshi:1.9.3'
implementation "androidx.security:security-crypto:1.1.0-alpha03"
2. AndroidManifest配置
添加权限
<!--网络-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!--文件读写-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--deviceID-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--绘本、指尖查词、坐姿-->
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<!-- 读取手机状态,获取IMEI -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<!-- android 10 在application节点添加以下适配高版本 -->
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
3. 代码混淆
代码混淆文件proguard-project.txt,添加如下内容
-keep class com.turing.**{ *;}
-dontwarn com.turing.**
-keep class org.java_websocket.**{*;}
-dontwarn org.java_websocket.**
-keep class org.slf4j.**{*;}
-dontwarn org.slf4j.**
-keep class org.eclipse.** { *; }
-dontwarn org.eclipse.**
-keep class com.Posture.detection**{
*;
}
-keep class com.tcl.detection.**{
*;
}
4. SDK 初始化
使用坐姿检测功能前调用,请先调用SDK初始化代码。SDK初始化详情见《Android SDK接入指南说明文档》
TuringInitializer.getInstance().initV3XXX()
5. 开启功能集成
使用说明见下面【功能集成】描述。
实例化
var turingPosture = TuringPosture.getInstance(this);
功能集成
坐姿检测的图片来源为:SDK 内部相机 或 客户端传入图片。
1. 内部相机开启检测
方法
void startPostureDetect(ViewGroup viewGroup, CameraConfig cameraConfig, PostureReqConfig config, PostureListener listener);
有两种方式:
① 内部相机显示预览图和检测数据: viewGroup不为null,cameraConfig不为null。
② 内部相机不显示预览图,只用检测数据: viewGroup为null,cameraConfig不为null。
输入说明
| 参数 | 类型 | 说明 |
|---|---|---|
| layout | ViewGroup | 预览界面的布局,建议使用FrameLayout |
| cameraConfig | CameraConfig | 相机配置参数 |
| config | PostureReqConfig | 坐姿配置参数 |
| listener | PostureListener | 回调接口 |
业务开发过程中要变更配置参数,可以使用已创建的 CameraConfig、PostureReqConfig 对象来修改其字段,不用每次都创建新对象。
- CameraConfig 说明
| 参数 | 类型 | 是否必须 | 取值范围 | 说明 |
|---|---|---|---|---|
| viewSize | Size | N | - | 预览布局的大小。 默认:MATCH_PARENT |
| previewSize | Size | N | - | 预览图大小。(传给算法检测的图) |
| cameraId | String | N | 设备支持的cameraid | 摄像头ID (默认”0“) 一般前置为"1",后置为"0",也可传入其他值。 |
| cameraApi | int | N | [1,2] | 相机版本(默认:2) 1-Camera1 2-Camera2 |
| preViewRotation | int | N | - | 旋转预览视图的角度。默认:0 |
| imageRotation | int | N | 0,90,180,270 | 图片旋转角度 (如 果 不 是 人 脸 正 向 图 片 , 则 需 要 旋 转 到 正 面)。 默认:90。 |
preViewRotation 字段只改变人眼看到的预览画面,不会影响检测结果;
imageRotation 字段真正作用于图片,会影响检测结果,一般情况前置摄像头:平板横屏设置180,手机竖屏设置270。
配置 CameraConfig 参数,更改以下字段值需要重新启动相机:
-
viewSize
-
previewSize
-
cameradId
-
preViewRotation
会启动相机的方法:startPostureDetect()、onResume()。
- PostureReqConfig 说明
| 参数 | 类型 | 是否必须 | 取值范围 | 说明 |
|---|---|---|---|---|
| type | PostureItem | Y | - | 检测项 |
| intervalDetect | int | Y | - | 帧检测间隔:建议设置为3,不要超过10 |
type 检测项:
-
PostureItem.ITEM_ATTENTION 注意力
-
PostureItem.ITEM_ACCEPTANCE 接受度
-
PostureItem.ITEM_MENTAL 疲劳度
-
PostureItem.ITEM_SITTING 坐姿检测
-
PostureItem.ITEM_ALL 以上全检测
-
PostureListener 说明
接口回调为主线程
public interface PostureListener {
/**
* 成功回调
* @param result 检测结果
*/
void onResult(PostureResult result);
/**
* 错误回调
* @param code
* @param msg
*/
void onError(int code, String msg);
/**
* 授权结果
* @param flag true-授权成功 false-授权失败
* /
void onAuthSuccess(boolean flag);
}
输出说明
PostureResult
| 参数 | 类型 | 是否必返 | 取值范围 | 说明 |
|---|---|---|---|---|
| currentType | int | Y | - | 当前检测项 |
| attention | Attention | N | - | 注意力检测结果 |
| acceptance | Acceptance | N | - | 接受度检测结果 |
| mental | Mental | N | - | 疲劳度检测结果 |
| sitting | Sitting | N | - | 坐姿检测结果 |
- attention 结果说明
| 参数 | 类型 | 是否必返 | 取值范围 | 说明 |
|---|---|---|---|---|
| little | boolean | Y | - | 歪头 |
| sideface | boolean | Y | - | 侧脸 |
| bow | boolean | Y | - | 低头 |
| please | boolean | Y | - | 仰头 |
| closeeye | boolean | Y | - | 闭眼 |
| score | float | Y | 0~100 | 得分 |
- acceptance 结果说明
| 参数 | 类型 | 是否必返 | 取值范围 | 说明 |
|---|---|---|---|---|
| nodhead | boolean | Y | - | 点头 |
| shakehead | boolean | Y | - | 摇头 |
| happy | boolean | Y | - | 高兴 |
| confusion | boolean | Y | - | 疑惑 |
| raisehand | boolean | Y | - | 举手 |
| score | float | Y | 0~100 | 得分 |
- mental 结果说明
| 参数 | 类型 | 是否必返 | 取值范围 | 说明 |
|---|---|---|---|---|
| yawning | boolean | Y | - | 打哈欠 |
| dozeoff | boolean | Y | - | 打瞌睡 |
| lying | boolean | Y | - | 趴着 |
| score | float | Y | 0~100 | 得分 |
- sitting 结果说明
| 参数 | 类型 | 是否必返 | 取值范围 | 说明 |
|---|---|---|---|---|
| notStraight | boolean | Y | - | true-坐姿不端正 false-坐姿端正 |
| distance | float | Y | - | 距离,单位:cm |
| score | float | Y | 0~100 | 得分 |
请求示例
-
情况一,显示预览界面。
```kotlin private fun postureDetect() { // 1. 设置相机参数 cameraConfig = CameraConfig.Builder() .setCameraId("1") // 前置摄像头 .setViewSize(Size(280,200)) // 预览布局的大小 .setPreviewSize(Size(1920,1080)) // 检测图的大小,影响算法检测 .setPreViewRotation(0) // 预览布局旋转角度,不影响算法检测 .setImageRotation(180) // 检测的图旋转角度。保证传给算法是正向图 .build()
// 2. 设置检测参数 postureConfig = PostureReqConfig() postureConfig.type = 5 // 检测所有项 postureConfig.intervalDetect = 3 // 检测帧检测
// 3. 启动相机 turingPosture.startPostureDetect(binding.preframe,cameraConfig, postureConfig,object : PostureListener { override fun onResult(result: PostureResult?) { // 检测结果 if (null != result) { formatStr(result) } }
override fun onError(code: Int, msg: String?) { showToast(msg) } override fun onAuthSuccess(flag: Boolean) { // 鉴权成功 or 失败 Log.d("PostureActivity", "onAuthSuccess() called with: flag = $flag") } })}
``` -
情况二,不显示预览界面,只用检测数据: 将viewGroup设置为null。
2. 传输图片开启检测
适用于客户端开发相机或静态图片检测,将图片传输给SDK进行检测。
注:需要先调用 startPostureDetect(null,null,postureReqConfig,listener)进行初始化,等初始化成功后才能调用sendPostureDetect()。
方法
void sendPostureDetect(InputImgData inputImgData);
输入说明
| 参数 | 类型 | 说明 |
|---|---|---|
| inputImgData | InputImgData | 图片参数 |
InputImgData 说明
| 参数 | 类型 | 是否必须 | 取值范围 | 说明 |
|---|---|---|---|---|
| inputBuffer1 | byte[] | Y | - | 输入图片帧或者数据的buffer地址 (RGB24,BGR24,RGBA 或者YUV第一个Plane分量的buff地址) |
| inputBuffer2 | byte[] | N | - | 输入图片帧或者数据的buffer地址 (YUV第二个Plane分量的buff地址,RGB24,BGR24,RGBA可不用此项置空便可) |
| inputBuffer3 | byte[] | N | - | 输入图片帧或者数据的buffer地址 (YUV第三个Plane分量的buff地址,RGB24,BGR24,RGBA可不用此项置空便可) |
| rowStride1 | int | N | - | yuv 第一个Plane分量或者 inputData1 对应的RowStride; 非YUV数据置0便可 |
| rowStride2 | int | N | - | yuv 第一个Plane分量或者 inputData2 对应的RowStride; 非YUV数据置0便可 |
| rowStride3 | int | N | - | yuv 第一个Plane分量或者 inputData3 对应的RowStride; 非YUV数据置0便可 |
| width | int | Y | - | 图片宽 |
| height | int | Y | - | 图片高 |
| rotation | byte | Y | - | 0:如不需旋转 1:顺时针旋转90度 2:顺时针旋转180度 3:顺时针旋转270度 |
| imgFormat | byte | Y | - | 0: RGB24 1: BGR24 2: RGBA 3: NV21 4: NV12 5: I420-YU12 6: I420-yv12 |
| focalParameters | float[] | N | - | [0] 输入为摄像头焦距(focal_length),若无法获取且对距离估计精度要求不高则赋值outDetectScoreDistance[0]= -1.0f [1] 输入为摄像头感光元件宽(sensor_width),若无法获取且对距离估计精度要求不高则赋值outDetectScoreDistance[1]= -1.0f 默认值: [-1.0f , -1.0f] |
输出说明
请求示例
情况1,客户端开发相机
// 这里展示客户端使用Camera 2自定义开发相机,拿到图片数据(预览图)后,如何调用SDK方法。
-
先调用初始化
```kotlin // 设置检测参数 postureConfig = PostureReqConfig() postureConfig.type = 5 // 检测所有项 postureConfig.intervalDetect = 3 // 检测帧检测
// 初始化,获取检测结果 turingPosture.startPostureDetect( null,null,postureConfig, object : PostureListener { override fun onResult(result: PostureResult?) { if (null != result) { formatStr(result) } } override fun onError(code: Int, msg: String?) { showToast(msg) } override fun onAuthSuccess(flag: Boolean) { Log.d("PostureActivity", "onAuthSuccess() called with: flag = $flag") } })```
-
再传入图片数据
```kotlin // 客户端开发相机 Camera 2 的预览图数据回调
private var _imgRotation = 0 // 根据实际设备取图进行设置
private final ImageReader.OnImageAvailableListener mPreviewImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader imageReader) { Image image = imageReader.acquireLatestImage(); if (null == image) { return; }
if (null != cameraCallback && !cameraIsClose()) { // 图片数据,进行坐姿检测 runPostureDetect(image) } image.close(); } }; // 获取摄像头焦距、摄像头感光元件宽 public void calculateFocalLengthInPixels(float[ ] value) { if (null == characteristics) { value[0] = -1f; value[1] = -1f; return; } try { //通过物理尺寸, 对焦距离, 算出FOV(Field of view), 相机水平弧度, 垂直弧度 float[ ] floats = characteristics.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS); if (floats != null && floats.length > 0) { value[0] = floats[0]; } SizeF sizeF = characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE); if (sizeF != null) { value[1] = sizeF.getWidth(); } } catch (Exception e) { } } private void runPostureDetect(Image yuvImage){ // 注意,一定要在 onAuthSuccess() 初始化成功后调用 byte[ ] yuvBuf1 = null, yuvBuf2 = null, yuvBuf3 = null; int yuvBufLen1 = 0, yuvBufLen2 = 0, yuvBufLen3 = 0; ByteBuffer bf1 = null, bf2 = null, bf3 = null; Image.Plane[ ] yuvBufPlane = yuvImage.getPlanes(); bf1 = yuvBufPlane[0].getBuffer(); bf2 = yuvBufPlane[1].getBuffer(); bf3 = yuvBufPlane[2].getBuffer(); if (yuvBufLen1 != bf1.capacity()) { yuvBufLen1 = bf1.capacity(); yuvBuf1 = new byte[yuvBufLen1]; } bf1.clear(); bf1.get(yuvBuf1, 0, yuvBuf1.length); if (yuvBufLen2 != bf2.capacity()) { yuvBufLen2 = bf2.capacity(); yuvBuf2 = new byte[yuvBufLen2]; } bf2.clear(); bf2.get(yuvBuf2, 0, yuvBuf2.length); if (yuvBufLen3 != bf3.capacity()) { yuvBufLen3 = bf3.capacity(); yuvBuf3 = new byte[yuvBufLen3]; } bf3.clear(); bf3.get(yuvBuf3, 0, yuvBuf3.length); int _width = yuvImage.getWidth(); int _height = yuvImage.getHeight(); int _rowStride1 = yuvBufPlane[0].getRowStride(); int _rowStride2 = yuvBufPlane[1].getRowStride(); int _rowStride3 = yuvBufPlane[2].getRowStride(); int _imgFormat =3; if (2 == yuvBufPlane[1].getPixelStride()) { //NV12 OR NV21 _imgFormat = 3; //inArgsData[2]-支持:{0:RGB24,1:BGR24,2:RGBA,3:NV21,4:NV12,5:I420-YU12;6:I420-yv12} } else { //1==yuvBufPlane[1].getPixelStride() I420 y-u-v _imgFormat = 6; //inArgsData[2]-支持:{0:RGB24,1:BGR24,2:RGBA,3:NV21,4:NV12,5:I420-YU12;6:I420-yv12} } float[ ] focalParameters = {-1f, -1f}; postureCamera2.calculateFocalLengthInPixels(focalParameters); // 获取摄像头焦距、摄像头感光元件宽 //------------ 上面是获取参数值,下面是调用产研SDK进行检测 ------------ var inputImgData = InputImgData() inputImgData.inputBuffer1 = yuvBuf1 inputImgData.inputBuffer2 = yuvBuf2 inputImgData.inputBuffer3 = yuvBuf3 inputImgData.rowStride1 = _rowStride1 inputImgData.rowStride2 = _rowStride2 inputImgData.rowStride3 = _rowStride3 inputImgData.width = _width inputImgData.height = _height inputImgData.rotation = _imgRotation // 图片角度, //选择旋转度数{0:如不需旋转,1:顺时针旋转90度;2:顺时针旋转180度;3:顺时针旋转270度}(如果不是人脸正向图片,则需要旋转到正面,算法才有效) inputImgData.imgFormat = _imgFormat // 数据格式:{{0:RGB24,1:BGR24,2:RGBA,3:NV21,4:NV12,5:I420_yu12,6:I420_yv12}} // 调用方法进行检测 turingPosture.sendPostureDetect(inputImgData) }```
情况2,传入图片
-
先调用初始化
```kotlin // 设置检测参数 postureConfig = PostureReqConfig() postureConfig.type = 5 // 检测所有项 postureConfig.intervalDetect = 3 // 检测帧检测
// 初始化,获取检测结果 turingPosture.startPostureDetect( null,null,postureConfig, object : PostureListener { override fun onResult(result: PostureResult?) { if (null != result) { formatStr(result) } } override fun onError(code: Int, msg: String?) { showToast(msg) } override fun onAuthSuccess(flag: Boolean) { Log.d("PostureActivity", "onAuthSuccess() called with: flag = $flag") } })```
-
再读取本地图片进行检测
```kotlin private fun readImgFile(file File){ val options = BitmapFactory.Options() options.inMutable = true options.inPreferredConfig = Bitmap.Config.ARGB_8888
var bitmap = BitmapFactory.decodeFile(file.absolutePath, options) val width = bitmap.width val height = bitmap.height val pixels = ByteArray(bitmap.getWidth() * bitmap.getHeight() * 4) bitmap.copyPixelsToBuffer(ByteBuffer.wrap(pixels)) sendImgToPostureDetect(pixels, width, height)}
private fun readImgFile(imgData: ByteArray, width: Int, height: Int) { var inputImgData = InputImgData() inputImgData.inputBuffer1 = imgData inputImgData.width = width inputImgData.height = height inputImgData.rotation = 0
//选择旋转度数{0:如不需旋转,1:顺时针旋转90度;2:顺时针旋转180度;3:顺时针旋转270度}(如果不是人脸正向图片,则需要旋转到正面,算法才有效) inputImgData.imgFormat = 2 // 数据格式:{{0:RGB24,1:BGR24,2:RGBA,3:NV21,4:NV12,5:I420_yu12,6:I420_yv12}} turingPosture.sendPostureDetect(inputImgData)} ```
3. 停止/启动识别
方法
/**
* 在 Activity 生命周期方法 onPause() 中调用,SDK 内部会关闭相机
*/
void onPause();
/**
* 在 Activity 生命周期方法 onResume() 中调用,SDK 内部会开启相机
*/
void onResume();
4. 资源释放
方法
/**
* 建议不使用时及时释放资源,再次使用相机功能,需要重新调用 startPostureDetect()
*/
void release()
常见问题
1. 如何确定SDK自带相机设置了图片旋转角度,传给算法的检测图是人脸正向图片?
可以调用 TuringPosture().getInstance(context).debugSavaPreView(),在相册中查看保存的预览图片。
2. 使用SDK自带相机,不想要显示预览界面,如何实现?
方式一:(推荐)
调用 startPostureDetect() 方法 viewGroup=null,cameraConfig不为null。
方式二:(不推荐)
① 预览界面viewgroup 宽高设置为1dp,必须要传入图片分辨率参数,否则sdk内部的默认值会影响结果。 分辨率取值范围:640×480 ~ 1280×720。
建议客户端自定义相机开发实现功能,实现方式见【使用示例--情况2,客户端开发相机】。