为了方便开发者快速接入Effect SDK,我们以源码的形式提供了桌面端不同开发框架的接入demo,本文档旨在阐述demo工程的使用,如果你想要直接使用Effect SDK底层C接口,请参考通用API接口说明-特效及demo内源码。
Effect SDK的使用主要包括:
用伪代码来大致展示为:
// Windows需要额外加载初始化angle动态库 angle::LoadEGL(); angle::LoadGLES(); // 创建OpenGL context gl_context = new OpenGLContext(); // 创建Effect SDK句柄 gl_context->makeCurrent(); bef_effect_handle_t handle; bef_effect_ai_create(&handle); // 在线授权验证及初始化Effect SDK bef_effect_ai_check_license(handle, license_path); bef_effect_ai_init(handle, ...); gl_context->releaseCurrent(); // 设置期望生效的特效 bef_effect_ai_set_effect(handle, effect_path); bef_effect_ai_composer_set_nodes_with_tags(handle, ...); // 调用Effect SDK进行特效处理 while(continue_effect_process){ gl_context->makeCurrent(); bef_effect_ai_algorithm_texture(handle, ...); /* * in_texture: 输入图片的原始纹理 * out_texture: Effect SDK进行特效处理之后的纹理 * 成功调用之后out_texture即为你期望特效效果的纹理,可以进行渲染或其他操作 */ bef_effect_ai_process_texture(handle, in_texture, out_texture, frame_timestamp); gl_context->releaseCurrent(); } // 销毁Effect SDK句柄 gl_context->makeCurrent(); bef_effect_ai_destroy(handle); gl_context->releaseCurrent(); delete gl_context;
注意⚠️
- 除设置期望生效的特效外的代码块必须在同一个线程(最好是一个单独的线程)且必须在有效的OpenGL context上下文内执行,而设置期望生效的特效的代码块可以在其他线程执行;即Effect SDK的初始化/销毁,特效处理必须在GL线程内进行,而特效开关设置可以在其他线程调用。
- 道具包是与App key/secret绑定的,前期集成测试可以使用我们demo中提供的key/secret与道具包,后期可以直接替换成自己的key/secret与道具包;如果你有多个key/secret,请不要混用对应的道具包。
下面我们会以各个不同开发框架的demo进行详细的阐述。
Qt demo是一个采集摄像头进行实时特效渲染处理的app,涵盖了Effect SDK种类繁多的特效(滤镜,贴纸,美妆,美体,虚拟背景等等)以及算法(人脸识别,手势识别,人体分割等等)能力,同时demo内使用C++对底层的Effect SDK的C接口进行了封装,方便开发者更快速接入Effect SDK。
注意⚠️
Windows平台上Effect SDK依赖angle动态库的能力,所以需要加载并初始化对应的组件(libGLESv2.dll与libEGL.dll),详情参考下述EffectManager模块的说明
OpenGL context必须为使用angle库创建的,请不要使用其他的GL context,详情请参考BEFEffectGLContext模块
主要组件及关键API说明 | ||
---|---|---|
EffectManager | 管理EffectHandle以及加载并初始化angle动态库符号 | |
loadEGLDLL/unloadEGLDLL | 初始化angle动态库符号 | |
createHandle/releaseHandle | 管理EffectHandle对象 | |
EffectHandle | 封装了底层Effect SDK的C接口,提供特效处理操作,如果你需要创建多个EffectHandle对象(比如同时采集多个摄像头视频流进行特效处理),那么需要打开MULTI_HANDLE_USE宏 | |
setInput | 设置需要特效处理的原始图片buffer,格式必须为:RGB/RGBA,BGR/BGRA,I420,同步操作,调用完成后即可通过getOutput获得结果 | |
getOutput | 获取特效处理后的图片buffer,格式和输入格式一致,buffer内存由EffectHandle对象管理,调用者无需释放 | |
updateComposerNodes/setFilter/setSticker | 设置预期的特效 | |
BEFEffectGLContext | 提供OpenGL context管理 | |
initGLContext/releaseGLContext | 初始化/销毁GL context | |
makeCurrentContext/doneCurrentContext | 设置GL context | |
BEImageUtil | 提供BGR/BGRA,RGB/RGBA,I420格式的shader转换 | |
transferBufferToTexture | 将指定格式的图片转换为指定格式的GPU纹理 | |
transferTextureToTexture | 将指定格式的纹理转换为另一个格式的纹理 | |
readPixel | 将纹理中的内容读取到CPU buffer中 | |
BEAlgorithmManager | 封装了算法检测相关接口,提供快速进行算法检测的能力 | |
initProcessInfo/initResourceFinder | 初始化算法检测模块 | |
addAlgorithm/removeAlgorithm | 开启/关闭要检测的算法小项 | |
processAlgorithm | 执行算法检测 | |
getInputInfo | 设置输入buffer | |
getOutputInfo | 获取算法检测结果 | |
BELicenseVC.h | 展示如何进行在线鉴权及license的下载校验,详细的鉴权原理及过程参考在线授权说明 | |
BEDataManager | 封装了特效道具包资源的使用,包括道具包内tag字段的使用,如果有某一个道具不知道如何使用,请参考BEDataManager内的使用方法 |
以上组件中,除了BELicenseVC和BEDataManager外,其他组件推荐直接集成到自己的项目中使用,可以大幅降低集成开发的成本。
add_subdirectory(EffectManager) # 用C++封装底层SDK的C接口,一般接入直接用这里提供的C++接口即可 add_subdirectory(Algorithm) # 提供算法(人脸,手势等)的接入封装代码 add_subdirectory(app) # UI部分,展示如何使用Algorithm和EffectManager封装的接口,这里UI框架使用的是Qt 5.15.2
只编译EffectManager即可,然后调用里面的EffectHandle对象接口就可以了,大致的流程为:
// 创建Effect SDK句柄管理器 IEffectManager* manager = createEffectManager(); // 创建美颜句柄 IEffectHandle* handle = manager->createHandle(); // 传入license,model文件路径 // license文件通过使用demo源码的licenseManager目录里面的接口通过curl下载到本地 // model文件根据购买的道具不同会对应不同的模型文件,可以联系商务获取,也可以先试用我们demo的模型进行测试 handle->initResource(); // 传入道具包的路径,设置想要开启的特效 // 路径为道具包绝对路径,所有路径均需确保是utf-8编码 handle->updateComposerNodes(); // 传入图片buffer进行特效处理,buffer格式目前支持RGB/RGBA, BGR/BGRA, I420 // 其他格式需要先转换成这些格式才能传给特效SDK handle->setInput(); // 获取特效处理后的图片buffer,返回的buffer格式和输入的格式一致 handle->getOutput(); // 销毁Effect SDK句柄 handle->releaseHandle(); // 销毁美颜句柄管理器 destroyEffectManager(manager);
详细的流程可以参考demo内VideoFilter.cpp里面对EffectHandle的使用。
注意⚠️
- 传入道具包路径参数的时候必须是全路径,且字符串为UTF8编码
- 使用IEffectHandle的接口时可以无需考虑OpenGL context设置,IEffectHandle对象内部已经进行了封装处理
- IEffectHandle对象是线程安全的,可以在其他线程调用,但是如果你想同时对多路视频流进行特效处理,比如同时采集多个摄像头视频流,那么最好创建多个IEffectHandle对象,每一个对象单独处理一路视频,需要打开MULTI_HANDLE_USE宏
- 特效处理是耗时操作,所以尽量不要在UI线程调用特效接口,以免阻塞App UI
那么编译EffectManager以及Algorithm即可,在成功搭建了EffectManager之后,可以参考Demo内"BEAlgorithmVC"内对算法相关功能的处理,大致的流程为:
// 和前面的特效处理流程一样 // 需要先创建并成功初始化IEffectManager以及IEffectHandle对象 // 详见前述特效处理流程 // 创建IAlgorithmManager对象 IAlgorithmManager* algo_mgr = createAlgorithmManager(); // 初始化算法模块,提供license和模型文件路径 // 路径为绝对路径,所有路径均需确保是utf-8编码 algo_mgr->initResourceFinder(); algo_mgr->initProcessInfo(); // 注册渲染回调到IEffectHandle中 // 这里用一个lambda对象作示例 IEffectHandle->setRenderCallback([](){ // 在IEffectHandle的渲染回调中做算法检测 // 将IEffectHandle吐出的buffer输入到IAlgorithmManager中 algo_mgr->getInputInfo()->clear(); algo_mgr->getInputInfo()->m_buffer = input->frame.imageData; algo_mgr->getInputInfo()->m_width = input->frame.width; algo_mgr->getInputInfo()->m_height = input->frame.height; algo_mgr->getInputInfo()->m_bytePerRow = input->frame.width * input->frame.channel; algo_mgr->getInputInfo()->m_format = (bef_ai_pixel_format)input->pixelFormat; // 这里的orientation根据实际输入填写,详见头文件中bef_ai_rotate_type注释 algo_mgr->getInputInfo()->m_ori = BEF_AI_CLOCKWISE_ROTATE_0; // 执行算法检测 algo_mgr->processAlgorithm(); // 拿到检测结果 result = algo_mgr->getOutputInfo(); }); // 添加想要执行的算法检测小项 // 详见BE_FEATURE枚举类型定义 algo_mgr->addAlgorithm(); // 关闭指定的检测小项 algo_mgr->removeAlgorithm(); // 销毁IAlgorithmManager对象 destroyAlgorithmManager();
详细的算法检测流程可以参考demo内BEAlgorithmVC.cpp模块。
那么还需要编译app模块,需要搭建Qt 5.15.2的环境。
目前源码目录中已经包含了Effect SDK的开发文件与资源文件:
头文件:third/effect_sdk/inc
库文件:third/effect_sdk/libs
美颜素材:app/EffectResource/resource/ComposeMakeup
贴纸素材:app/EffectResource/resource/stickers_resource
滤镜素材:app/EffectResource/resource/Filter
模型文件:app/EffectResource/resource/model
自己集成时使用 sdk的编译压缩包,不要直接使用demo目录的effect_sdk库,方便后续的问题跟进。
set QtPath="C:\Qt\5.15.2\msvc2019" set QtBinPath="C:\Qt\5.15.2\msvc2019\bin" set MS_BUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\MSBuild.exe" cmake -G "Visual Studio 16 2019" ../ -A Win32 -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DPLATFORM=%PLATFORM% -DCMAKE_PREFIX_PATH=%QtPath% -DENABLE_PROFILE=%PROFILE_STATUS%
build成功之后会在对应目录生成VS工程文件 ByteEffects.sln,打开即可编译调试。
编译也可以直接用Qt Creator(建议版本6.0.0以上)直接打开当前目录的CMakeLists.txt,然后进行编译运行。
选择32位或64位的配置:
配置参数参考:
Win32 demo利用了Qt demo内的EffectManager模块,使用OpenCV渲染来展示基本的特效处理,如果你觉得Qt demo工程太庞大不好搭建调试,那么可以考虑尝试构建Win32 demo,执行demo内的recource_copy_win.bat执行资源拷贝后,打开EffectDemo内的VS工程编译即可。
注意⚠️
目前线上Windows端的标品只提供Qt Demo,如需Win32 Demo,请联系我们单独获取。
EffectManager // 和Qt demo内的一样,提供底层Effect SDK的封装 EffectBaseApp // 使用OpenCV,展示基本的美颜特效处理流程
// 创建IEffectManager IEffectManager* effectManager = createEffectManager(); // 加载并初始化angle动态库符号 effectManager->loadEGLDLL(); // 验证本地license,如果license校验失败则尝试拉取最新的在线license并保存到本地 std::string licensePath = appPath + "license/license.bag"; int licenseStatus = effectManager->checkLicenseStatus(licensePath.c_str()); if (licenseStatus != 0) { // request license file char* authMsg = nullptr; int authMsgLen = 0; char* license = nullptr; int len = 0; effectManager->getAuthMessage(&authMsg, &authMsgLen); // 这里替换为你自己申请的key/secret std::string key = "biz_license_tool_test_keyd64a6d073fbd42f8979a626572220595"; std::string secret = "58979358c3e69215762eb5ecc3df7c9e"; int code = request_license(key.c_str(), secret.c_str(), authMsg, &license, &len); if (code == 0) { FileUtil::createDirectory(appPath + "license"); FileUtil::writeToFile(licensePath.c_str(), (unsigned char*)license, len); delete[] license; } effectManager->releaseAuthMessage(authMsg); } // 设置资源路径 std::string modelDir = appPath + "../../resource/model/"; std::string composerDir = appPath + "../../resource/ComposeMakeup/"; std::string stickerDir = appPath + "../../resource/stickers_resource/"; std::string filterDir = appPath + "../../resource/Filter/"; // 构造并初始化IEffectHandle IEffectHandle* effectHandle = effectManager->createHandle(); effectHandle->setUsePBO(true); effectHandle->initResource(licensePath.c_str(), modelDir.c_str()); // 设置美颜道具 #ifdef TEST_BEAUTY std::vector<std::string> nodePaths; std::vector<std::string> tags; nodePaths.push_back(composerDir + "beauty_IOS_lite"); nodePaths.push_back(composerDir + "beauty_IOS_lite"); nodePaths.push_back(composerDir + "beauty_IOS_lite"); nodePaths.push_back(composerDir + "reshape_lite"); nodePaths.push_back(composerDir + "beauty_IOS_lite"); // 设置风格妆 #ifdef TEST_STYLE_MAKEUP nodePaths.push_back(composerDir + "style_makeup/baicha"); #endif // 设置美颜道具小项的tag tags.push_back("smooth"); tags.push_back("whiten"); tags.push_back("sharp"); tags.push_back("Internal_Deform_Overall"); tags.push_back("Internal_Deform_Eye"); // 设置风格妆小项的tag,没有tag可以填空字符串 #ifdef TEST_STYLE_MAKEUP tags.push_back(""); #endif // 设置nodePaths中添加的所有特效 effectHandle->updateComposerNodes(nodePaths, tags); // 设置不同小项的强度 // 使用“道具包路径,道具包tag”来定位具体要设置的美颜小项 // 每一个道具包内部的tag(如果有)都可能不一样,具体可以参考Qt demo内‘BEDataManager’对象内部的代码 // 或者查看通用API文档:https://cv-api.bytedance.com/doc/openapi/2036/200123 effectHandle->updateComposerNodeIntensity(nodePaths[0].c_str(), tags[0].c_str(), 0.5); effectHandle->updateComposerNodeIntensity(nodePaths[1].c_str(), tags[1].c_str(), 0.35); effectHandle->updateComposerNodeIntensity(nodePaths[2].c_str(), tags[2].c_str(), 0.7); effectHandle->updateComposerNodeIntensity(nodePaths[3].c_str(), tags[3].c_str(), 0.35); effectHandle->updateComposerNodeIntensity(nodePaths[4].c_str(), tags[4].c_str(), 0.35); #ifdef TEST_STYLE_MAKEUP effectHandle->updateComposerNodeIntensity(nodePaths[5].c_str(), "Makeup_ALL", 0.8); effectHandle->updateComposerNodeIntensity(nodePaths[5].c_str(), "Filter_ALL", 0.5); #endif #endif // 设置滤镜及滤镜强度 #ifdef TEST_FILTER std::string filterPath = filterDir + "Filter_02_14"; effectHandle->setFilter(filterPath.c_str()); effectHandle->setFilterIntensity(0.8); #endif // 设置贴纸 #ifdef TEST_STICKER std::string stickerPath = stickerDir + "stickers/heimaoyanjing"; effectHandle->setSticker(stickerPath.c_str()); #endif // 采集摄像头视频源进行特效处理 int count = 0; for (; cv::waitKey(1) != 27; cam >> frame) { if (frame.rows == 0 || frame.cols == 0) { continue; } // 设置要处理的图片buffer // 格式必须为:BGR/BGRA/RGB/RGBA/I420,其他格式需要转换成这些格式之后才能使用 effectHandle->setInput(imageWidth, imageHeight, frame.data, pixel_format_rgba); // 获得特效处理之后的buffer,格式和输入格式保持一致 unsigned char* data = effectHandle->getOutput(); cv::Mat framOut(imageHeight, imageWidth, CV_8UC4, data); cvtColor(framOut, framOut, cv::COLOR_RGBA2BGR); //cvtColor(framOut, framOut, cv::COLOR_RGBA2BGR); imshow("EffectBaseApp", framOut); } // 销毁IEffectHandle及IEffectManager effectManager->releaseHandle(effectHandle); destroyEffectManager(effectManager);
demo的逻辑比较简单易懂,主要是利用EffectManager模块的能力,EffectManager的具体使用可以参考Qt demo中的说明。
C# demo和Win32 demo一样,利用了EffectManager模块封装的能力,提供使用C#采集摄像头视频流进行特效处理的示例。
注意⚠️
目前线上Windows端的标品只提供Qt Demo,如需C# Demo,请联系我们单独获取。
ByteEffect.cs // 封装C++ EffectManager模块的胶水层,详见Qt demo中EffectManager模块的说明 Program.cs // 使用OpenCV,展示基本的美颜特效处理流程
// 加载并初始化angle动态库符号 ByteEffect.loadEGLDLL(); var exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; var appDir = Path.GetDirectoryName(exePath); var licensePath = $@"{appDir}\effect\license\license.bag"; var licensePathPtr = MallocIntPtr(licensePath); // 校验license,失败则拉取最新的license到本地 var licenseStatus = ByteEffect.checkLicenseFile(licensePathPtr); if (licenseStatus != 0) { Console.WriteLine($@"license error: {licenseStatus}"); // replace your key and secret string key = "biz_license_tool_test_keyb2cc169b1c7340dfaa2c70027acb99f5"; string secret = "dc5468fe3f3b555cda0d04b9f856f5a3"; var licenseDir = $@"{appDir}\effect\license\"; if (!Directory.Exists(licenseDir)) Directory.CreateDirectory(licenseDir); RequestLicenseFile(licensePath, key, secret); } // 创建EffectHandle对象 IntPtr effectHandle = ByteEffect.createEffectHandle(); var resourceDir = $@"{appDir}\..\..\..\resource"; var modelDirPtr = MallocIntPtr($@"{resourceDir}\model"); var composerPath = $@"{resourceDir}\ComposeMakeup"; var filterPath = $@"{resourceDir}\Filter"; var stickerPath = $@"{resourceDir}\stickers_resource\stickers"; // 初始化Effect SDK ByteEffect.initResource(effectHandle, licensePathPtr, modelDirPtr); // 设置滤镜及滤镜强度 #if TEST_FILTER var filterPathPtr = MallocIntPtr(filterPath + "\\Filter_02_14"); ByteEffect.setFilter(effectHandle, filterPathPtr); ByteEffect.setFilterIntensity(effectHandle, 0.8f); FreeIntPtr(filterPathPtr); filterPathPtr = IntPtr.Zero; #endif // 设置贴纸 #if TEST_STICKER // only one sticker can exist at the same time var stickerPathPtr = MallocIntPtr(stickerPath + "\\heimaoyanjing"); ByteEffect.setSticker(effectHandle, stickerPathPtr); FreeIntPtr(stickerPathPtr); stickerPathPtr = IntPtr.Zero; // if you want to reset sticker, you can do like this code // var emptyPathPtr = MallocIntPtr(stickerPath + ""); // ByteEffect.setSticker(effectHandle, stickerPath + ""); // FreeIntPtr(emptyPathPtr); #endif // 设置虚拟背景 #if TEST_VIRTUAL_BACKGROUND var mattingPathPtr = MallocIntPtr(stickerPath + "\\matting_bg"); var bgPathPtr = MallocIntPtr(resourceDir + "\\bg.png"); var keyPtr = MallocIntPtr(RENDER_CACHE_TETURE_KEY); ByteEffect.setSticker(effectHandle, mattingPathPtr); ByteEffect.setRenderCacheTexture(effectHandle, keyPtr, bgPathPtr); FreeIntPtr(mattingPathPtr); FreeIntPtr(bgPathPtr); FreeIntPtr(keyPtr); mattingPathPtr = IntPtr.Zero; bgPathPtr = IntPtr.Zero; keyPtr = IntPtr.Zero; #endif // 设置美颜 #if TEST_COMPOSER var beautyPathPtr = MallocIntPtr(composerPath + "\\beauty_IOS_lite"); var reshapePathPtr = MallocIntPtr(composerPath + "\\reshape_lite"); IntPtr[] nodes = new IntPtr[3]; nodes[0] = beautyPathPtr; nodes[1] = reshapePathPtr; // 设置风格妆 #if TEST_STYLE_MAKEUP // set style makeup resource var aidouPathPtr = MallocIntPtr(composerPath + "\\style_makeup\\aidou"); nodes[2] = aidouPathPtr; ByteEffect.updateComposerNodes(effectHandle, nodes, 3); #else ByteEffect.updateComposerNodes(effectHandle, nodes, 2); #endif // 设置不同小项的强度 // 使用“道具包路径,道具包tag”来定位具体要设置的美颜小项 // 每一个道具包内部的tag(如果有)都可能不一样,具体可以参考Qt demo内‘BEDataManager’对象内部的代码 // 或者查看通用API文档:https://cv-api.bytedance.com/doc/openapi/2036/200123 ByteEffect.updateComposerNodeIntensity(effectHandle, beautyPathPtr, "smooth", 0.7f); ByteEffect.updateComposerNodeIntensity(effectHandle, beautyPathPtr, "whiten", 0.35f); ByteEffect.updateComposerNodeIntensity(effectHandle, beautyPathPtr, "sharp", 0.7f); ByteEffect.updateComposerNodeIntensity(effectHandle, reshapePathPtr, "Internal_Deform_Overall", 0.8f); ByteEffect.updateComposerNodeIntensity(effectHandle, reshapePathPtr, "Internal_Deform_Eye", 0.35f); // if you want remove composer // reset intensity // ByteEffect.updateComposerNodeIntensity(effectHandle, beautyPathPtr, "smooth", 0.0f); // after reset intensity, if you want remove beauty_IOS_lite, code like this // and it also remove (smooth, whiten, sharp) // { // // nodes[0] = reshapePathPtr; // ByteEffect.updateComposerNodes(effectHandle, nodes, 1); // } FreeIntPtr(beautyPathPtr); FreeIntPtr(reshapePathPtr); beautyPathPtr = IntPtr.Zero; reshapePathPtr = IntPtr.Zero; #endif var window = new Window("EffectDemo"); var image = new Mat(); while (true) { capture.Read(image); if (image.Empty()) break; Cv2.Flip(image, image, FlipMode.Y); // Cv2.CvtColor(image, image, ColorConversionCodes.BGR2RGBA); Cv2.CvtColor(image, image, ColorConversionCodes.BGR2BGRA); // 设置要处理的图片buffer // 格式必须为:BGR/BGRA/RGB/RGBA/I420,其他格式需要转换成这些格式之后才能使用 ByteEffect.setInput(effectHandle, image.Width, image.Height, image.Data, (int)ByteEffect.BytePixelFormat.pixel_format_bgra); // 获得特效处理之后的buffer,格式和输入格式保持一致 IntPtr outImage = ByteEffect.getOutput(effectHandle); var frameOut = new Mat(image.Height, image.Width, MatType.CV_8UC4, outImage); Cv2.CvtColor(frameOut, frameOut, ColorConversionCodes.BGRA2BGR); // show image window.ShowImage(frameOut); int flag = Cv2.WaitKey(1); if (flag >= 0) { break; } } FreeIntPtr(licensePathPtr); FreeIntPtr(modelDirPtr); licensePathPtr = IntPtr.Zero; modelDirPtr = IntPtr.Zero; // 销毁Effect SDK资源 ByteEffect.releaseEffectHandle(effectHandle); ByteEffect.unloadEGLDLL();
demo的逻辑比较简单易懂,主要是利用C++ EffectManager模块的能力,EffectManager的具体使用可以参考Qt demo中的说明。
Mac平台上提供了使用Objective C对摄像头实时采集并进行特效处理的原生App demo,UI界面与特效功能都和Windows平台的Qt demo对齐保持一致。
注意⚠️
Mac平台的OpenGL context必须为3.2 Core Profile,在创建NSOpenGLContext的时候请指定NSOpenGLProfileVersion3_2Core,3.2+版本需要集成阶段做好完备的测试确保道具都能正常加载
主要组件及关键API说明 | ||
---|---|---|
EffectHandle | 封装了底层Effect SDK的C接口,提供特效处理操作 | |
initEffect/releaseEffect | 管理EffectHandle对象 | |
setOpenGLContext | 设置NSOpenGLContext,必须为3.2 Core Profile | |
updateComposerNodes/setFilterPath/setStickerPath | 设置预期的特效 | |
processWithBuffer | ||
BEImageUtils | 提供CVPixelBuffer与RGBA/BGRA/I420的转换 | |
transforCVPixelBufferToBuffer | CVPixelBuffer与RGBA/BGRA/I420的转换 | |
transforBufferToBuffer | 不同格式的buffer转换 | |
BERequester/BELicenseCheck | 展示如何进行在线鉴权及license的下载校验,详细的鉴权原理及过程参考在线授权说明 | |
BEEffectDataManager | 封装了特效道具包资源的使用,包括道具包内tag字段的使用,如果有某一个道具不知道如何使用,请参考BEEffectDataManager内的使用方法 | |
BEAlgorithmTask | 算法检测小项task的基类 | |
initWithProvider | 构造检测task,每一个算法所需要的资源文件不尽相同,可以直接用工程中的 BEAlgorithmResourceHelper 实现类,它可以拿到所有算法所需要的资源 | |
initTask | 初始化算法检测task | |
process | 执行算法检测 | |
setConfig | 设置算法参数,每一个算法都有不同的参数key,定义在各算法的头文件中 | |
destroyTask | 销毁算法检测task |
EffectManager # 封装底层SDK的C接口,一般接入直接用这里提供的接口即可 Algorithm # 提供算法(人脸,手势等)的接入封装代码 ByteEffects # UI部分,展示如何使用Algorithm和EffectManager封装的接口
那么将EffectManager编译成静态库即可,然后调用里面的EffectHandle对象接口就可以了,大致的流程为:
//创建EffectHandle美颜句柄 handle = [[EffectHandle alloc] initWithPath:licenseDirPath, modelDir:modelDirPath]; // 设置NSOpenGLContext(这里的OpenGL context必须为3.2 Core Profile,即NSOpenGLProfileVersion3_2Core) handle->setOpenGLContext(); // 初始化Effect SDK // 需要在GL context环境执行,即设置OpenGL context:调用EffectHandle->lockOpenGLContext/unlockOpenGLContext,详见demo源码 handle->initEffect(); // 传入道具包的路径,设置想要开启的特效 handle->updateComposerNodes(""); // 传入图片buffer进行特效处理, 处理完后会返回处理好的texture给调用方 // demo里面默认输入buffer格式为BGRA,如果有其他格式需要自行修改demo代码或使用BEImageUtils进行格式转换 // 需要在GL context环境执行,即设置OpenGL context:调用EffectHandle->lockOpenGLContext/unlockOpenGLContext,详见demo源码 handle->processWithBuffer(); // 释放美颜句柄 // 需要在GL context环境执行,即设置OpenGL context:调用EffectHandle->lockOpenGLContext/unlockOpenGLContext,详见demo源码 handle->releaseEffect();
那么将EffectManager以及Algorithm编译成静态库,然后使用BEAlgorithmTaskFactory创建想要执行检测的算法task,大致的流程为:
// 和前面的特效处理流程一样 // 需要先创建并成功初始化Effect SDK // 详见前述特效处理流程 // 创建算法检测小项task tsk = [BEAlgorithmTaskFactory create:[BEAlgorithmKey create:key isTask:YES] provider:[[BEAlgorithmResourceHelper alloc] initWithLicense:licenseDirPath]]; // 初始化task tsk.initTask(); // 设置算法检测参数 [tsk setConfig:[BEAlgorithmKey create:MOUTH_MASK_KEY isTask:NO] p:[NSNumber numberWithBool:selected]]; // 执行算法检测并拿到结果 result = [tsk process:buffer width:iWidth height:iHeight stride:iWidth*4 format:BEF_AI_PIX_FMT_BGRA8888 rotation:0];
详细的算法使用可以参考demo内BEAlgorithmVC模块的使用。
那么首先需要配置编译环境
目前源码目录中已经包含了effect sdk的开发文件与资源文件:
头文件:include/BytedEffectSDK
库文件:libs
模型文件:ByteEffects/Resource/ModelResource.bundle
美颜素材:ByteEffects/Resource/ComposeMakeup.bundle
滤镜素材:ByteEffects/Resource/FilterResource.bundle
贴纸素材:ByteEffects/Resource/StickerResource.bundle
集成时请使用SDK的编译压缩包,不要直接使用demo目录的effect_sdk库,方便后续的问题跟进。
在demo源码目录执行pod init,pod成功后打开ByteEffects.xcworkspace工程,同时配置自己的开发账号的AppID即可进行编译:
注意⚠️
默认是配置的x86_64架构的sdk, 若本地开发环境是m1版本的Mac,需要替换libs目录的库为arm64架构的sdk
Electron demo内提供了Mac和Windows两个平台预编译好的EffectManager模块,可以直接在代码内使用,EffectManager模块的使用方法和前述demo内一样,这里就不再赘述了。
const effectNode = require('../plugin/effect_node/effect_node.node'); // 检查授权文件是否有效,success: 0 , 其他错误码需要重新下载授权 var licenseStatus = effectNode.checkLicenseStatus(licenseDir + 'license.bag'); // 根据key和secret生成授权内容 var requestContent = effectNode.getRequestContent(key, secret); // content 内容为effectNode 接口getRequestContent返回的内容 function licenseRequest(content, secret) { const {net} = require('@electron/remote'); const request = net.request({ headers: { 'Content-Type': 'application/json', }, method: 'POST', url: 'https://cv-tob.bytedance.com/v1/api/sdk/tob_license/getlicense' }); request.write(content); request.on('response', (response) => { console.log(`**statusCode:${response.statusCode}`); if (response.statusCode != 200) { // error code } response.on("data", (chunk)=>{ const jsonObj = JSON.parse(chunk); console.log("status_code:", jsonObj.status_code); if (jsonObj.status_code != 0) { // error code } else { // write license to locale file var license = effectNode.writeLicenseResult(secret, jsonObj.data, jsonObj.digest, licenseDir + 'license.bag'); } }) }); //close request request.end(); return 0; }
// 初始化egl相关动态库 // 只有Windows需要 effectNode.loadEGLDLL(); // 创建EffectHandle对象 effectHandle = effectNode.createEffectObject(); // 初始化EffectHandle句柄 // 这里的license.bag是前面在线鉴权拉取的最新license文件 effectHandle.initResource(licenseDir + 'license.bag', modelDir); // 获取c++ native层接口,用于传递到c++ camera采集模块处理图像帧 var effectInterface = effectHandle.effectInterface(); // 如果有处理视频帧的node模块,可以设置effectInterface内部进行特效处理 // videoCap.setEffectInterface(effectInterface); // 传入道具包路径,设置预期特效 effectHandle.updateComposerNodes({ "composerDir":composerNodeDir, "tags":tags, }); // 更新素材强度值 effectHandle.updateComposerNodeIntensity(nodePath, key, value); // 设置滤镜素材 effectHandle.setFilter(filterpath); // 设置滤镜强度 (value: 0.0 - 1.0) effectHandle.setFilterIntensity(value); // 设置贴纸 effectHandle.setSticker(stickerPath); // 设置处理的图像buffer // @param: buffer 图像buffer (type ArrayBuffer) // @param: width 图像宽度 // @param: height 图像宽度 // @param: format 图像格式 0: RGBA 1: BGRA effectHandle.setInput(buffer, with, height, format); // 获取特效处理后的buffer,图像格式和宽高与输入的格式一致 effectHandle.getOutput(); // 设置 render cache texture,配合素材使用 // 目前用于虚拟背景 // @param: key 纹理名称 /// @param: path 文件绝对路径 effectHandle.setRenderCacheTexture(key, path); // 设置是否同步加载素材 // @param: needLoad (bool 类型) 设置true则会同步加载,默认异步加载素材 effectHandle.setNeedLoadResource(needLoad);
关于EffectHandle的详细用法,请参阅前述demo文档的详细说明。