You need to enable JavaScript to run this app.
导航
Android(v4.1.0.0及以上)
最近更新时间:2025.01.24 16:38:39首次发布时间:2022.02.25 17:16:51
项目中加入SDK
  1. 打开压缩包 byted_effect_andr.zip,找到 effect-SDKXXX.aar 文件

  2. 拷贝其到项目中的主模块(一般是 app)的 libs 目录下,如拷贝到 app/libs/ 目录(没有 libs 文件夹,可手动创建)

  3. 打开主模块(一般是 app)下的 build.gradle,在 android 下加入 SDK 查询路径:

dependencies {
    //  api fileTree(include: ['*.jar', '*.aar'], dir: 'libs')  // sample中的写法
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])   // 接入时写法
}
  1. 添加素材,将提供的素材包(一般是 resource 文件夹)拷贝到项目的 assets 中
代码中集成SDK

以下指南针对使用 sample 中封装的 Java 代码进行集成,如果直接在项目中使用 CV SDK 提供的 C 接口集成,参见 接口说明-特效接口说明-算法

准备阶段

  1. 拷贝 androidsample 项目中的 com.bytedance.labcv.core 模块到自己的工程中,core模块是 SDK 的调用封装。特效相关的调用封装在EffectManager类中;基础算法相关的封装在algorithm目录下,xxxTask代表不同算法的封装;画质算法相关的调用封装在ImageQualityManager类中。

  2. 将素材拷贝代码 com.bytedance.labcv.demo.task.UnzipTask 及相关逻辑应用到自己项目中(没有此步骤,素材无法使用),更多内容参见 素材拷贝说明。

  3. (可选)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 的使用可以分为三个阶段:

  1. 初始化 特效SDK
  2. 使用 特效SDK 进行图像处理
  3. 特效SDK 参数设置,如设置美颜、贴纸、滤镜等

注意: SDK 的所有操作都应该在 openGL 线程中执行。如果是在推流 SDK 中集成一般直接在推流 SDK 提供的自定义美颜接口中进行即可,如果是本地环境,一般与 GLSurfaceView 一起使用,并在它提供的 openGL 环境中使用 CV SDK。

1.初始化特效SDK

初始化的调用时机一般在 openGL 环境初始化完成后,如在 onSurfaceCreated 中(如使用推流 SDK,一般在推流 SDK 提供的初始化自定义美颜的接口)执行,需要调用的函数为:

mEffectManager = new EffectManager(this, new EffectResourceHelper(this), EffectLicenseHelper.getInstance(this));
mEffectManager.setOnEffectListener(this);
int ret =  mEffectManager.init();
  • EffectManager接口参数说明:
public EffectManager(Context context, EffectResourceProvider mResourceProvider, EffectLicenseProvider mLicenseProvider)
参数名含义
context上下文对象,用于访问应用程序环境信息
mResourceProvider素材资源管理接口的实现对象,用于获取所需素材资源信息
mLicenseProvider鉴权管理接口的实现对象,用于获取鉴权相关信息

2.使用 SDK 进行特效处理

支持的输入数据类型

支持的数据类型支持的数据格式
texture2D

图像处理的调用,在 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 转成纹理使用,但这会造成耗时增加。

3.SDK 参数设置,如设置美颜、贴纸、滤镜等

注意,SDK 参数设置需要在初始化之后调用,请尽量与 SDK图像处理 处于同一线程使用,以避免可能出现的问题。

(1)设置美颜、美型、美妆

美颜、美型、美妆的设置使用的是同一个接口,一般来说使一个美颜生效需要两步:

第一步:设置素材对应的路径
第二步:设置素材中,特效的强度(一般强度默认为 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及以上)

(2)设置贴纸

设置贴纸接口

/**
 * 开启或者关闭贴纸 如果path为空 关闭贴纸
 * @param path 贴纸素材的文件路径
 */
boolean setSticker(String path);

// 示例
mEffectManager.setSticker("baibianfaxing");

此处的贴纸路径为素材包中 StickerResource.bundle/stickers 中的相对路径。

(3)设置滤镜

/**
 * 开启或者关闭滤镜 如果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 中的相对路径。

4.SDK 基础算法功能使用

以人脸算法为例:
人脸算法的调用封装在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 完成,如果需要拷贝的素材过多,可能会导致启动时间太长,对此有以下参考方法:

  1. 只将一些很简单的素材,如美颜、美型等内置
  2. 贴纸这种比较大的,可以做成在线下发,在 app 需要用到的时候通过网络下载到本地,既可以降低包大小,也可以提升启动速度
附录:timeStamp 获取

如果是使用相机采集数据,并使用了 SurfaceTexture 作为输出数据容器,可以直接调用它的 getTimestamp 方法:

long timeStamp = surfaceTexture.getTimestamp();

否则使用系统方法:

long timeStamp = System.nanoTime();
附录:ImageSource 类型

三种不同的类型主要在与对于输入纹理的处理和绘制上的区别,具体看下面。

类型区别输出纹理
TYPE_CAMERA会根据传进来的 srcTextureFormat、 cameraRotation 和 sensorRotation 对纹理做旋转,更容易保证效果正确,但由于需要两次旋转,性能较差输出纹理与输入纹理方向相同
TYPE_VIDEO会根据传进来的 srcTextureFormat 和 videoRotation 对纹理做旋转,需要做一次旋转输出纹理保持为正向,不一定与输入纹理同方向
TYPE_IMAGE不对纹理做预处理,需要保证传进来的纹理是 2D 且人脸是正的方向,相对的性能也更好输出与输入同向,未做任何旋转
附录:算法 key 与返回结果类型一览
算法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_VERIFYcom.bytedance.labcv.core.algorithm.FaceVerifyAlgorithmTask.FaceVerifyCaptureResult
AnimojiAnimojiAlgorithmTask.ANIMOJIcom.bytedance.labcv.effectsdk.BefAnimojiInfo
C1 场景分类C1AlgorithmTask.C1com.bytedance.labcv.effectsdk.BefC1Info
C2 场景分类C2AlgorithmTask.C2com.bytedance.labcv.effectsdk.BefC2Info
车辆检测CarAlgorithmTask.CAR_ALGO CarAlgorithmTask.CAR_RECOG(车辆检测) CarAlgorithmTask.BRAND_RECOG(车牌检测)com.bytedance.labcv.effectsdk.BefCarDetectInfo
专注度检测ConcentrateAlgorithmTask.CONCENTRATIONcom.bytedance.labcv.core.algorithm.ConcentrateAlgorithmTask.BefConcentrationInfo
视线估计GazeEstimationAlgorithmTask.GAZE_ESTIMATIONcom.bytedance.labcv.effectsdk.BefGazeEstimationInfo
头发分割HairParserAlgorithmTask.HAIR_PARSERcom.bytedance.labcv.effectsdk.HairParser.HairMask
手势检测HandAlgorithmTask.HANDcom.bytedance.labcv.effectsdk.BefHandInfo
人头分割HeadSegAlgorithmTask.HEAD_SEGMENTcom.bytedance.labcv.effectsdk.BefHeadSegInfo
距离估计HumanDistanceAlgorithmTask.HUMAN_DISTANCEcom.bytedance.labcv.effectsdk.BefDistanceInfo
光线分类LightClsAlgorithmTask.LIGHT_CLScom.bytedance.labcv.effectsdk.BefLightclsInfo
宠物脸检测PetFaceAlgorithmTask.PET_FACEcom.bytedance.labcv.effectsdk.BefPetFaceInfo
人体分割PortraitMattingAlgorithmTask.PORTRAIT_MATTINGcom.bytedance.labcv.effectsdk.PortraitMatting.MattingMask
骨骼检测SkeletonAlgorithmTask.SKELETONcom.bytedance.labcv.effectsdk.BefSkeletonInfo
天空分割SkySegAlgorithmTask.SKY_SEGMENTcom.bytedance.labcv.effectsdk.BefSkyInfo
视频分类VideoClsAlgorithmTask.VIDEO_CLScom.bytedance.labcv.effectsdk.BefVideoClsInfo
运动分类ActionRecognitionAlgorithmTask.ACTION_RECOGNITIONcom.bytedance.labcv.effectsdk.BefActionRecognitionInfo

备注: BefFaceInfo 里人脸106关键点的能见度参数 visibility_array ,现已废弃,不要使用该参数。

附录:安卓混淆说明

首先需要确定SDK的使用版本

V4.4.3版本之前

-keep class com.bytedance.labcv.effectsdk.** {*;}
-keep class com.bef.effectsdk.** {*;} 
-keep class com.bytedance.labcv.licenselibrary.** {*;}

V4.4.3版本及之后

-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();
 }