打开压缩包 byted_effect_andr.zip,找到 effect-SDKXXX.aar 文件
拷贝其到项目中的主模块(一般是 app)的 libs 目录下,如拷贝到 app/libs/ 目录(没有 libs 文件夹,可手动创建)
打开主模块(一般是 app)下的 build.gradle,在 android 下加入 SDK 查询路径:
dependencies { // api fileTree(include: ['*.jar', '*.aar'], dir: 'libs') // sample中的写法 implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) // 接入时写法 }
以下指南针对使用 sample 中封装的 Java 代码进行集成,如果直接在项目中使用 CV SDK 提供的 C 接口集成,参见 接口说明-特效及接口说明-算法。
拷贝 androidsample 项目中的 com.bytedance.labcv.core 模块到自己的工程中,core模块是 SDK 的调用封装。特效相关的调用封装在EffectManager类中;基础算法相关的封装在algorithm目录下,xxxTask代表不同算法的封装;画质算法相关的调用封装在ImageQualityManager类中。
将素材拷贝代码 com.bytedance.labcv.demo.task.UnzipTask 及相关逻辑应用到自己项目中(没有此步骤,素材无法使用),更多内容参见 素材拷贝说明。
(可选)SDK版本为v4.2.1及以上的情况,需要将EffectLicenseHelper.java中的_licenseMode设置为LICENSE_MODE_ENUM.OFFLINE_LICENSE,并依据绑定了自身应用包名的license的名字修改Config.LICENSE_NAME。
以上为主要接口,以上代码可能会对 sample 中的其他代码有依赖,可将这些也拷贝到自己项目中。
注意1
版本4.4.3及以上,由于安全合规原因, com.bytedance.labcv
更改为 com.effectsar.labcv
文档中所有的 com.bytedance.labcv
处,在sample中的实际位置均更改为 com.effectsar.labcv
包括文档中所有含以上名称的类名,以及准备阶段中的位置,搜索时,请替换为com.effectsar.labcv
注意2
接入的主项目工程如果是 androidx 工程,需要在 gradle.properties 中添加 android.enableJetifier=true
以特效SDK为例,特效SDK 的统一封装接口为 EffectManager,SDK 的使用可以分为三个阶段:
注意: SDK 的所有操作都应该在 openGL 线程中执行。如果是在推流 SDK 中集成,一般直接在推流 SDK 提供的自定义美颜接口中进行即可,如果是本地环境,一般与 GLSurfaceView 一起使用,并在它提供的 openGL 环境中使用 CV SDK。
初始化的调用时机一般在 openGL 环境初始化完成后,如在 onSurfaceCreated 中(如使用推流 SDK,一般在推流 SDK 提供的初始化自定义美颜的接口)执行,需要调用的函数为:
mEffectManager = new EffectManager(this, new EffectResourceHelper(this), EffectLicenseHelper.getInstance(this)); mEffectManager.setOnEffectListener(this); int ret = mEffectManager.init();
public EffectManager(Context context, EffectResourceProvider mResourceProvider, EffectLicenseProvider mLicenseProvider)
参数名 | 含义 |
---|---|
context | 上下文对象,用于访问应用程序环境信息 |
mResourceProvider | 素材资源管理接口的实现对象,用于获取所需素材资源信息 |
mLicenseProvider | 鉴权管理接口的实现对象,用于获取鉴权相关信息 |
支持的输入数据类型
支持的数据类型 | 支持的数据格式 |
---|---|
texture | 2D |
图像处理的调用,在 onDrawFrame 中执行(如果使用推流 SDK,可在推流 SDK 提供的接口中调用),对应的函数为:
// 将输入纹理转换出人脸为正的2D纹理 ProcessInput input = transToPortrait(); // 准备帧缓冲区纹理对象 int dstTexture = mImageUtil.prepareTexture(input.getWidth(), input.getHeight()); // 执行特效处理,输出的纹理是人脸为正的2D纹理 boolean ret = mEffectManager.process(input.getTexture(),dstTexture, input.getWidth(),input.getHeight(),input.getSensorRotation(), mImageSourceProvider.getTimestamp());
补充说明:
如果推流SDK默认接收的输入纹理是原始带有旋转角度的纹理,为了适配推流SDK,可以参考调用如下代码,将特效SDK输出的纹理转回原始的角度。
ImageUtil.Transition transition = new ImageUtil.Transition().rotate(mImageSourceProvider.getOrientation()).flip(false, mImageSourceProvider.isFront()).reverse(); int texture = mImageUtil.transferTextureToTexture(dstTexture,BytedEffectConstants.TextureFormat.Texure2D, input.getWidth(), input.getHeight(),transition);
说明
只能处理2D纹理并将结果输出到传入的2D纹理中,如果当前的项目中无法直接拿到2D纹理,可以先使用ImageUtil这个类中的方法将格式转为2D纹理,可根据实际情况使用,详情参见ImageUtil类的说明。
process接口参数说明:
参数名 | 含义 |
---|---|
srcTexture | 输入纹理ID,需要确保该纹理是一张人脸为正的图像,如果是前置摄像头,需要同时完成镜像处理 |
dstTexture | 输出纹理ID |
width | 输入纹理宽度 |
height | 输入纹理高度 |
sensorRotation | 手机角度,通过手机传感器取得 |
timeStamp | 当前时间,参见 timeStamp获取 |
处理结果:
process 方法的输出为渲染后的 2D 纹理。
注意,不推荐使用 SDK 直接处理 buffer,SDK 最终需要接收的是待处理纹理,如需要处理 buffer,建议先将 buffer 转成纹理使用,但这会造成耗时增加。
注意,SDK 参数设置需要在初始化之后调用,请尽量与 SDK图像处理 处于同一线程使用,以避免可能出现的问题。
美颜、美型、美妆的设置使用的是同一个接口,一般来说使一个美颜生效需要两步:
第一步:设置素材对应的路径
第二步:设置素材中,特效的强度(一般强度默认为 0,所以这一步不执行会没有效果)
设置素材路径接口
/** * 设置特效组合,目前支持美颜、美形、美体、 美妆特效的任意叠加 */ boolean setComposeNodes(String[] nodes); // 示例 mEffectManager.setComposeNodes(new String[]{"beauty_Android_lite"});
此处的素材路径,是相对于 ComposeMakeup.bundle/ComposeMakeup 的路径,素材包结构参见 素材包结构说明(v4.2.1及以上)
注意,SDK 内部不会保存已设置的素材,所以此方法每次调用都需要将所有需要生效的素材路径加上。
设置素材中,特效强度接口
/** * 更新组合特效(美颜、美形、美体、 美妆)中某个节点的强度 */ boolean updateComposerNodeIntensity(String node, String key, float intensity); // 示例 mEffectManager.updateComposerNodeIntensity("beauty_Android_lite", "whiten", 0.8f);
以美颜素材为例,需要通过 key 来控制我们要修改的是美白、磨皮还是锐化的强度,素材中 key 与功能的对应关系参见 素材key对应说明(421及以上)。
设置贴纸接口
/** * 开启或者关闭贴纸 如果path为空 关闭贴纸 * @param path 贴纸素材的文件路径 */ boolean setSticker(String path); // 示例 mEffectManager.setSticker("baibianfaxing");
此处的贴纸路径为素材包中 StickerResource.bundle/stickers 中的相对路径。
/** * 开启或者关闭滤镜 如果path为空 关闭滤镜 * * @param path path of filter file 滤镜资源文件路径 */ boolean setFilter(String path); /** * 设置滤镜强度 * * @param intensity intensity 参数值 */ boolean updateFilterIntensity(float intensity); // 示例 mEffectManager.setFilter("Filter_01_38"); mEffectManager.updateFilterIntensity(0.8f);
此处的滤镜路径为素材包中 FilterResource.bundle/Filter 中的相对路径。
以人脸算法为例:
人脸算法的调用封装在FaceAlgorithmTask中
1.算法初始化
public int initTask()
返回值表示初始化结果。
2.算法检测
public BefFaceInfo process(ByteBuffer buffer, int width, int height, int stride, BytedEffectConstants.PixlFormat pixlFormat, BytedEffectConstants.Rotation rotation)
注意
此处取人脸信息的调用有几个前提条件:
a. 在使用了需要有人脸的特效后调用,算法结果从特效中取。因此如果只调用了全局特效(如老版本的全局美白磨皮锐化、画质项、滤镜、仅前景或仅抠背景类贴纸)的情况下,该接口无输出。
b. 授权必须包含人脸106关键点及人脸数量,否则该接口无输出。
3.算法参数设置
算法参数设置的函数定义为:
public void setConfig(AlgorithmTaskKey key, Object p)
其中 key表示参数的类型,p 为参数值,每一个算法的参数类型可以在算法的定义中看到,如key可以使用 FACE_280、FACE_ATTR 这几种。
启用FaceAlgorithmTask则需在AlgorithmTaskFactory进行注册。
AlgorithmTaskFactory.register(FaceAlgorithmTask.FACE, new AlgorithmTaskFactory.AlgorithmTaskGenerator<FaceAlgorithmTask.FaceResourceProvider>() { @Override public AlgorithmTask<FaceAlgorithmTask.FaceResourceProvider, ?> create(Context context, FaceAlgorithmTask.FaceResourceProvider provider) { return new FaceAlgorithmTask(context, provider); } });
android 中内置素材时是把素材放到 asset 中,这里面的文件内容无法通过路径获取,所以 sample 中的做法是在初次启动 app 时将其拷贝到应用私有目录中,通过 com.bytedance.labcv.demo.task.UnzipTask 完成,如果需要拷贝的素材过多,可能会导致启动时间太长,对此有以下参考方法:
如果是使用相机采集数据,并使用了 SurfaceTexture 作为输出数据容器,可以直接调用它的 getTimestamp 方法:
long timeStamp = surfaceTexture.getTimestamp();
否则使用系统方法:
long timeStamp = System.nanoTime();
三种不同的类型主要在与对于输入纹理的处理和绘制上的区别,具体看下面。
类型 | 区别 | 输出纹理 |
---|---|---|
TYPE_CAMERA | 会根据传进来的 srcTextureFormat、 cameraRotation 和 sensorRotation 对纹理做旋转,更容易保证效果正确,但由于需要两次旋转,性能较差 | 输出纹理与输入纹理方向相同 |
TYPE_VIDEO | 会根据传进来的 srcTextureFormat 和 videoRotation 对纹理做旋转,需要做一次旋转 | 输出纹理保持为正向,不一定与输入纹理同方向 |
TYPE_IMAGE | 不对纹理做预处理,需要保证传进来的纹理是 2D 且人脸是正的方向,相对的性能也更好 | 输出与输入同向,未做任何旋转 |
算法 | key | 返回结果 |
---|---|---|
人脸检测 | FaceAlgorithmTask.FACE FaceAlgorithmTask.FACE_280(280 点) FaceAlgorithmTask.FACE_ATTR(人脸属性) FaceAlgorithmTask.FACE_MASK(人脸 mask) FaceAlgorithmTask.MOUTH_MASK(嘴唇 mask) FaceAlgorithmTask.TEETH_MASK(牙齿 mask) | com.bytedance.labcv.effectsdk.BefFaceInfo |
人脸比对 | FaceVerifyAlgorithmTask.FACE_VERIFY | com.bytedance.labcv.core.algorithm.FaceVerifyAlgorithmTask.FaceVerifyCaptureResult |
Animoji | AnimojiAlgorithmTask.ANIMOJI | com.bytedance.labcv.effectsdk.BefAnimojiInfo |
C1 场景分类 | C1AlgorithmTask.C1 | com.bytedance.labcv.effectsdk.BefC1Info |
C2 场景分类 | C2AlgorithmTask.C2 | com.bytedance.labcv.effectsdk.BefC2Info |
车辆检测 | CarAlgorithmTask.CAR_ALGO CarAlgorithmTask.CAR_RECOG(车辆检测) CarAlgorithmTask.BRAND_RECOG(车牌检测) | com.bytedance.labcv.effectsdk.BefCarDetectInfo |
专注度检测 | ConcentrateAlgorithmTask.CONCENTRATION | com.bytedance.labcv.core.algorithm.ConcentrateAlgorithmTask.BefConcentrationInfo |
视线估计 | GazeEstimationAlgorithmTask.GAZE_ESTIMATION | com.bytedance.labcv.effectsdk.BefGazeEstimationInfo |
头发分割 | HairParserAlgorithmTask.HAIR_PARSER | com.bytedance.labcv.effectsdk.HairParser.HairMask |
手势检测 | HandAlgorithmTask.HAND | com.bytedance.labcv.effectsdk.BefHandInfo |
人头分割 | HeadSegAlgorithmTask.HEAD_SEGMENT | com.bytedance.labcv.effectsdk.BefHeadSegInfo |
距离估计 | HumanDistanceAlgorithmTask.HUMAN_DISTANCE | com.bytedance.labcv.effectsdk.BefDistanceInfo |
光线分类 | LightClsAlgorithmTask.LIGHT_CLS | com.bytedance.labcv.effectsdk.BefLightclsInfo |
宠物脸检测 | PetFaceAlgorithmTask.PET_FACE | com.bytedance.labcv.effectsdk.BefPetFaceInfo |
人体分割 | PortraitMattingAlgorithmTask.PORTRAIT_MATTING | com.bytedance.labcv.effectsdk.PortraitMatting.MattingMask |
骨骼检测 | SkeletonAlgorithmTask.SKELETON | com.bytedance.labcv.effectsdk.BefSkeletonInfo |
天空分割 | SkySegAlgorithmTask.SKY_SEGMENT | com.bytedance.labcv.effectsdk.BefSkyInfo |
视频分类 | VideoClsAlgorithmTask.VIDEO_CLS | com.bytedance.labcv.effectsdk.BefVideoClsInfo |
运动分类 | ActionRecognitionAlgorithmTask.ACTION_RECOGNITION | com.bytedance.labcv.effectsdk.BefActionRecognitionInfo |
备注: BefFaceInfo 里人脸106关键点的能见度参数 visibility_array ,现已废弃,不要使用该参数。
首先需要确定SDK的使用版本
-keep class com.bytedance.labcv.effectsdk.** {*;} -keep class com.bef.effectsdk.** {*;} -keep class com.bytedance.labcv.licenselibrary.** {*;}
-keep class com.effectsar.labcv.effectsdk.** {*;} -keep class com.bef.effectsdk.** {*;} -keep class com.effectsar.labcv.licenselibrary.** {*;}
以下几个版本,SDK升级时需要注意几个情况:
442之前版本,升级442/443
正使用442之前版本的客户,如果升级442/443版本,集成的代码不变,不拷贝新版本的sample代码的情况下,需要在EffectManager中添加 mRenderManager.loadLib();
例如:
public EffectManager(Context context, EffectResourceProvider mResourceProvider, EffectLicenseProvider mLicenseProvider) { mContext = context; this.mResourceProvider = mResourceProvider; this.mLicenseProvider = mLicenseProvider; mRenderManager = new RenderManager(); mRenderManager.loadLib(); // 需要添加这一行 }
442/443版本,升级450
正使用442/443版本客户,如果升级450版本,集成的代码不变,不拷贝新版本sample代码的情况下,升级450版本时,可以把EffectManager中的 RenderManager.loadLib();
相关代码注释掉。 例如:
public EffectManager(Context context, EffectResourceProvider mResourceProvider, EffectLicenseProvider mLicenseProvider) { mContext = context; this.mResourceProvider = mResourceProvider; this.mLicenseProvider = mLicenseProvider; /*这几行可以注释掉 * try { * RenderManager.loadLib(); * } catch (UnsatisfiedLinkError e) { * ExternalLibraryLoader externalLibraryLoader = new ExternalLibraryLoader(new SdCardLibrarySource(context)); * externalLibraryLoader.loadLib(); * } */ mRenderManager = new RenderManager(); }