资讯 社区 文档
技术能力
语音技术
文字识别
人脸与人体
图像技术
语言与知识
视频技术

虚拟形象Android开发文档

虚拟形象Android开发文档

简介

百度虚拟形象sdk是基于图像识别、语音识别与合成、人像建模等人工智能技术,以电子屏、VR等设备为载体,实现人机交互的虚拟人,面向餐饮、金融、广电、教育、营销等行业,提供全新智能对客服务,降低人力成本,提升服务质量和效率。同时基于百度AR技术,塑造卡通形象,打造更有亲和力的虚拟形象。

开发指南

概述

DigitalHuman SDK 以DigitalHuman API的形式提供给开发者,包含JAVA,Objective-C两种语言形式。

功能

DigitalHuman SDK 实现功能包括:

  • 播报

    输入文本进行语音播报

  • 对话

    输入文本或录音进行对话聊天

  • 模型渲染

    初始化完成即可将虚拟形象渲染在页面上

  • 切换配饰

    切换虚拟形象的配饰,包括但不限于服装和发型

  • 动作和表情触发

    输入不同参数来触发不同的动作和表情

  • 视角变换

    切换虚拟形象全身和半身

开发配置

  • Android Studio 3.1及以上
  • android系统4.4及以上

SDK Java API 快速入门

概述

demo文件夹内是使用DigitalHuman SDK相关api实现虚拟形象功能的demo工程源码。

文件列表

  • digitalhuman.aar

    虚拟形象相关服务集合的SDK文件,与digitalhumansdk.jar、so可以二选一使用。点击下载

  • digitalhumansdk.jar、so

    虚拟形象相关服务的SDK⽂件,与digitalhuman.aar可以二选一使用。点击下载

  • dumix.license

    使用虚拟形象相关服务的鉴权文件,由开发者提供应用包名后生成;初始SDK中不包含license。

  • demo

    sdk相关接口接入示例,点击下载

  • DigitalHumandemo.apk

    虚拟形象样板体验demo,点击下载

工程设置

  1. 资源配置

    sdk有两种接入方式,推荐使用jar方式引入不可同时使用两种方式!!!

    (1)、aar方式接入。将digitalhuman.arr和dumix.license分别放入libs和assets工程目录下

    image

    (2)、jar和so接入。将digitalhumansdk.jar和相关so文件复制到libs目录

  2. 添加依赖

    在app目录下的build.gradle中添加依赖

    android{
    	... // 其它配置
    
    	defaultConfig{
    		...
    
    		minSdkVersion 19
    		targetSdkVersion 28
    
    	}
    
    	repositories {
        	flatDir {
            	dirs 'libs'
        	}
    	}
      
      // jar方式引入需添加如下代码
      sourceSets {
          main {
              jniLibs.srcDirs = ['libs']
          }
      }
    }
    dependencies {
    	implementation fileTree(dir: 'libs', include: ['*.jar'])
      
    	// aar形式引入SDK
    	// implementation(name: 'digitalhumansdk', ext: 'aar')
    }
  3. 添加权限

    配置清单文件 app/src/main/AndroidManifest.xml中添加以下权限

    <!--访问网络权限-->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!--文件读写权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!--录音权限-->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
  4. 混淆忽略配置

    ### 使用digitalhumansdk时添加如下忽略
    -keep class com.baidu.ar.** {*;}
    -keep interface com.baidu.ar.** { *; }

接入示例

  1. 权限申请

    private static final String[] ALL_PERMISSIONS = new String[]{
            Manifest.permission.INTERNET,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.RECORD_AUDIO};
    
    
    private void checkPermissions() {
        // 6.0以下版本直接同意使用权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!hasNecessaryPermission()) {
                requestPermissions(ALL_PERMISSIONS, REQUEST_CODE_ASK_ALL_PERMISSIONS);
            } else {
                initDigitalHuman(); // 权限申请成功后才能进行sdk初始化
            }
        } else {
            initDigitalHuman();
        }
    }
    /**
     * 检查权限
     *
     * @return
     */
    private boolean hasNecessaryPermission() {
        List<String> permissionsList = new ArrayList();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            for (String permission : ALL_PERMISSIONS) {
                if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
                    permissionsList.add(permission);
                }
            }
        }
        return permissionsList.size() == 0;
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                               @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (hasNecessaryPermission()) {
            initDigitalHuman();
        }
    }
  2. sdk初始化

    digitalHuman = DigitalHuman
                    .getInstance()
                    .setContext(ARActivity.this)	// 设置上下文
                    .setResolution(720, 1280)					// 设置渲染分辨率
    //                .setCasePath(casePath) 			// 可使用本地模型资源,默认下发
                    .setDumixListener(new DumixListener() {	// 设置sdk中系列状态监听
                        @Override
                        public void onStateChanged(final int type, final String msg) {
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                  // 状态监听结果处理
                                    stateChangeResult(type, msg);
                                }
                            });
                        }
                         // 交互回调
                        @Override
                        public void onDrawViewTouch(MotionEvent event) {
                            switch (event.getAction()) {
                                case MotionEvent.ACTION_DOWN:
    
                                    break;
                            }
                        }
                    })
                    .initDumix(mDrawView); // 初始化并启动虚拟形象sdk
  3. 文本播报

    digitalHuman.sendReadingMessage(sendMsg, new DigitalHuman.SubtitleCallback() {
        @Override
        public void subtitle(final String text) {
            // 虚拟形象播报的文本,在播报开始时返回
        }
    });
  4. 文本输入对话

    digitalHuman.sendChatMessage(sendMsg, new DigitalHuman.SubtitleCallback() {
        @Override
        public void subtitle(String text) {
            // 虚拟形象播报的文本,在播报开始时返回
        }
    });
  5. 语音输入对话

    // 开始录音
    digitalHuman.startRecordAudio(new DigitalHuman.AudioProcessListener() {
        @Override
        public void audioProcessResult(final TextResponseBodyWrap responseBody) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (responseBody.isCompleted()) {
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                // UI修改,停止录音,发送语音解析结果
                              	mDigitalHuman.finishRecordAudio();
                                sendMsg(responseBody.getContent(););
                            }
                        }, 500);
                    }
                }
            });
        }
    });
    
    // 结束录音。主动调用完成录音或者语句间隔超过2秒会自动结束录音
    digitaHuman.finishRecordAudio();
    
    /**
     * 发送文本开始对话
     * sendMsg 录音语音解析结果
     */
    private void sendMsg(String sendMsg){
    		digitalHuman.sendChatMessage(sendMsg, new DigitalHuman.SubtitleCallback() {
        		@Override
        		public void subtitle(String text) {
            		// 虚拟形象播报的文本,在播报开始时返回
        		}
    		});
    }
  6. 切换配饰

    以切换服装为例,实际调用changeRenderParameter,参数列表见Api说明。

    /**
     * 切换衣服
     *
     * @param clothName
     */
    private void changeClothes(String clothName) {
        HashMap<String, Object> message = new HashMap<>();
        message.put("event_name", "change_outfit_operation");
        HashMap<String, Object> dataWrapper = new HashMap<>();
        dataWrapper.put("node", "avatar");
        dataWrapper.put("outfit", clothName);
        message.put("event_data", dataWrapper);
        digitalHuman.changeRenderParameter(message);
    }
  7. 打断

    该方法打断当前的数字人播报,动作和口型回归初始态,被打断的语句信息丢失。

    // 打断当前的播报或者对话
    private void interruptReading() {
        if (digitalHuman != null) {
            digitalHuman.breakAudio();
        }
    }
  8. 暂停

    系统暂停后,相关的渲染和交互都会停止,推荐在activity的onPause中调用。

    if (digitalHuman != null) {
        digitalHuman.pause();
    }
  9. 继续

    继续之前暂停的渲染和交互,推荐在activity的onResume中调用

    if (digitalHuman != null) {
        digitalHuman.resume();
    }
  10. 退出

    退出并销毁虚拟形象,推荐在activity的onDestroy中调用

    if (digitalHuman != null) {
        digitalHuman.destroy();
    }
  11. 通过pcm音频数据获取表情数据

    // 发送开始标志
    mDigitalHuman.queryBlendShape(BlendShapeRequest.begin());
    while ((len = inputStream.read(buffer)) != -1) {
      // 发送 pcm 数据,需 Base64 转码
      mDigitalHuman.queryBlendShape(BlendShapeRequest.audio(Base64.encodeToString(buffer, 0, len, Base64.NO_WRAP)));
    }
    // 发送结束标志,需发送文本和音频信息:采样率、采样位宽、声道数
    mDigitalHuman.queryBlendShape(BlendShapeRequest.end(text, sampleRate, bitsPerSample, numChannel));
    
    // 注册 BlendShape 数据结果回调
    mDigitalHuman.setQueryBlendShapeListener(new IQueryBlendShapeCallback() {
                @Override
                public void onBlendShapeResponse(BlendShapeResponse blendShapeResponse) {
                  		// 获取 BlendShape 数据
                      List<HashMap<String, Double>> blendShapeMaps = blendShapeResponse.getBlendShapeMaps();
                  		// 设置表情数据,并开始播放
                  		 blendShapePlayer.setDataSource(blendShapeResponse.getBlendShapeMaps());
                       blendShapePlayer.start();
    									
                }
            });
    
    // 构造表情播放器
    blendShapePlayer = new BlendShapePlayer(mDigitalHuman);
  12. BlendShape表情播放

    // 构造表情播放器
    BlendShapePlayer  blendShapePlayer = new BlendShapePlayer(digitalHuman);
    
    // 设置表情数据
    blendShapePlayer.setDataSource(blendShapeMaps);
    
    // 开始播放
    blendShapePlayer.start();
    
    // 暂停播放
    blendShapePlayer.pause();
    
    // 恢复播放
    blendShapePlayer.resume();
    
    // 停止播放
    blendShapePlayer.stop();
    
    // 设置播放完成监听
    blendShapePlayer.setOnCompletionListener(new BlendShapePlayer.OnCompletionListener() {
                @Override
                public void onCompletion() {
                }
            });
  13. 发送对话消息 For Unit
// 构造Unit消息参数
ReqExtra reqExtra = new ReqExtra();    reqExtra.setAccessToken("24.0ed6db25a484b872536031ae17403e10.2592000.1614333577.282335-23178123");
reqExtra.setLogId("UNITTEST_10000");
reqExtra.setServiceId("S42464");
reqExtra.setUserId("88888");
reqExtra.setSkillIds("");  多个id用,分隔
reqExtra.setSessionId(mUnitSession); // session信息,通过14. Unit Session信息回调获取

digitalHuman.sendChatMessageForUnit(sendMsg,reqExtra, new DigitalHuman.SubtitleCallback() {
                @Override
                public void subtitle(String text) {
                    
                }
            });

Unit消息参数信息,请通过智能对话定制与服务平台 UNIT获取

  1. Unit Session信息回调

     digitalHuman.setUnitSessionListener(new IUnitSessionListener() {
                @Override
                public void onUnitSessionResult(String sessionStr) {
                    mUnitSession = sessionStr;
                }
            });

API参考

DigitalHuman.java

public static DigitalHuman getInstance();

获取DigitalHuman的实例化对象

public DigitalHuman setContext(Context context);

设置上下文

public DigitalHuman setRatio(int width, int height);

设置设置渲染分辨率

public DigitalHuman setDumixListener(DumixListener dumixListener);
public interface DumixListener {
    void stateChange(int type, String msg);

    void onDrawViewTouch(MotionEvent event);
}

设置状态监听。type取值参考DumixConstance.java

public DigitalHuman setCasePath(String casePath);

设置模型资源路径。需要使用本地的模型资源时调用该接口(资源模型默认下发)

public DigitalHuman setUrl(String url)

设置服务地址(如果是自己搭建的服务,可使用该设置)

public DigitalHuman initDumix(TextureView mSurfaceView);

初始化并启动SDK

public void sendReadingMessage(String textMsg, SubtitleCallback subtitleCallback);

发送文本进行播报

public void sendChatMessage(String textMsg, SubtitleCallback subtitleCallback);

发送文本进行聊天

/**
 * 开始录音
 * @param processListener 录音音频解析成文本的监听
 */
public void startRecordAudio(AudioProcessListener processListener);
/**
 * 主动完成录音
 */
public void finishRecordAudio();

语音对话,录音解析完成后需要调用文本对话服务

public void interruptReading()

播报打断

/**
 * 修改渲染参数
 *
 * @param params 参数
 */
public void changeRenderParameter(HashMap<String, Object> params);

切换配饰+动作+表情触发+视角变换

参数构造请参考changeRenderParameter参数说明

DumixConstance.java

DumixState public Eunm:

状态名 状态值 含义
LOAD_CASE_COMPLETED 0 资源加载成功
REQUEST_SUCCESS 2 播报请求成功
REQUEST_FAIL 3 播报请求失败
START_PLAY_RADIO 4 开始播报
RADIO_PLAY_COMPLETED 5 播报完毕
SOCKET_CONNECT 6 流式服务连接成功
SOCKET_FAIL 7 流式服务连接失败

changeRenderParameter参数说明

投建的HashMap需要包含event_name和event_data字段,字段里内容如下,event_data内部为多级HashMap:

含义 event_name event_data 备注
换装(裙装/西装) change_outfit_operation node avatar 节点:预留字段
outfit dress/suit 服装:裙装/西装
换发型(短发/马尾辫) change_hair_operation node avatar 节点:预留字段
Hair short/ponytail 发型:短发/马尾
动作触发 animation_operation node avatar 节点:预留字段
repeat_count n(整数) 动画循环次数:0代表无穷
chip idle(聆听态)/right_hand(右伸手)/front_hand(前伸手)/wave(招手)/emphasize(强调) 动画短名称:聆听态/右伸手/前伸手/招手/强调
operation play/stop 操作:播放/停止
相机位置切换 camera_position_operation position ArrayList 数组,数组长度为3 全身相机姿态参数:0,8,47半身相机姿态参数:0,10.5,25调用例子中见调用示例,三个Float代表相机位置的三维坐标,其中第二个(y坐标)控制相机高低,第三个(z坐标)控制远近,如果效果不佳,可以根据这个参数意义微调。
胸章Logo切换 change_logo_operation node avatar
logo none/baidu/baidu_paw/net/net_icon 对应胸牌:无胸牌、百度胸牌、百度熊掌、智能云胸牌、智能云图标调用例子中见调用示例