以下指南针对使用 sample 中封装的 Objective-C 代码进行集成,如果直接在项目中使用 CV SDK 提供的 C 接口集成,参见 接口说明-特效及接口说明-算法。
以上为主要接口,项目中的其他代码功能可以参考 Sample 文件结构,如果其他代码也有需要,可将这些也拷贝到自己项目中。
SDK 的封装接口主要都是在 Core 模块下,分为特效、算法、画质等,每一种功能都有单独的封装接口,可分别使用。
SDK 特效的统一封装接口为 BEEffectManager,SDK 的使用可以分为三个步骤:
注意,特效 SDK 全程依赖 OpenGL 环境,请保证所有 SDK 的函数调用都处于同一个 GlContext 下。
初始化 SDK 对应的方法为:
- (instancetype)initWithResourceProvider:(id<BEEffectResourceProvider>)provider;
CV SDK 的使用依赖于 openGL 环境,在调用 SDK 函数之前,先调用函数:
[EAGLContext setCurrentContext:context];
将上下文设置好。
如果项目中没有 GlContext,可以使用如下代码创建:
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
SDK 的处理方法主要为 BEEffectManager#processTexture:outputTexture:width:height:rotate:timeStamp,
/// @brief SDK 处理 /// @param texture 输入纹理 /// @param outputTexture 输出纹理 /// @param width 宽 /// @param height 高 /// @param rotate 算法检测角度 /// @param timeStamp 时间戳 - (bef_effect_result_t)processTexture:(GLuint)texture outputTexture:(GLuint)outputTexture width:(int)width height:(int)height rotate:(bef_ai_rotate_type)rotate timeStamp:(double)timeStamp;
它只能处理 2D 纹理并将结果输出到传入的 2D 纹理 outputTexture 中,如果当前的项目中无法直接拿到 2D 纹理,可以先试用 BEImageUtils 进行格式转换,这个类可以进行 CVPixelBuffer/Texture/Buffer 几种数据的转换,可根据实际情况使用,详情参见 BEImageUtils 使用。例如:
id<BEGLTexture> texture = [self.imageUtils transforCVPixelBufferToTexture:pixelBuffer]; // 获取对应的 OpenGL 纹理 int inputTexture = texture.texture; // 获取对应的纹理的宽高 int inputWidth = texture.width; int inputHeight = texture.height;
该函数还需要一个参数 timeStamp,即当前帧的时间,获取方法参见 timeStamp 获取。
函数的输出也是 2D 纹理,如果没有现成的纹理,可以使用 BEImageUtils 创建,例如:
BEPixelBufferGLTexture *outputTexture = [self.imageUtils getOutputPixelBufferGLTextureWithWidth:texture.width height:texture.height format:BE_BGRA]; // 获取对应的 OpenGL 纹理 int outTexture = outputTexture.texture; // 获取对应的 CVPixelBuffer CVPixelBufferRef outputPixelBuffer = outputTexture.pixelBuffer;
在接入测试时建议将输入输出都利用 BEImageUtils 转成 BECVPixelBuffer,可以方便地观察 SDK 的输入输出,排查可能遇到的问题。
注意,SDK 参数设置需要在初始化之后调用,请尽量与 SDK图像处理 处于同一线程使用,以避免可能出现的问题。
美颜、美型、美妆的设置使用的是同一个接口,一般来说使一个美颜生效需要两步:
设置素材路径接口
/// @brief 设置特效素材 /// @details 设置 ComposeMakeup.bundle 下的所有功能,包含美颜、美形、美体、美妆等 /// @param nodes 特效素材相对 ComposeMakeup.bundle/ComposeMakeup 的路径 - (void)updateComposerNodes:(NSArray<NSString *> *)nodes; /// @brief 设置特效素材 /// @details 设置 ComposeMakeup.bundle 下的所有功能,包含美颜、美形、美体、美妆等 /// @param nodes 特效素材相对 ComposeMakeup.bundle/ComposeMakeup 的路径 /// @param tags 每一个特效素材对应一个 tag,tag 会传递给 SDK 素材的的一些配置 - (void)updateComposerNodes:(NSArray<NSString *> *)nodes withTags:(NSArray<NSString *> *)tags; // 示例 [self.manager updateComposerNodes:[NSArray arrayWithObject:@"beauty_IOS_live"]];
此处的素材路径,是相对于 ComposeMakeup.bundle/ComposeMakeup 的路径,素材包结构参见 素材包结构说明。
注意,SDK 内部不会保存已设置的素材,所以此方法每次调用都需要将所有需要生效的素材路径加上。
设置素材中,特效强度接口
/// @brief 更新组合特效中某个功能的强度 /// @param node 特效素材相对于 ComposeMakeup.bundle/ComposeMakeup 的路径 /// @param key 素材中的功能 key /// @param intensity 强度 0-1 - (void)updateComposerNodeIntensity:(NSString *)node key:(NSString *)key intensity:(float)intensity; // 示例 [self.manager updateComposerNodeIntensity:@"beauty_IOS_live" key:@"whiten" intensity:0.8];
以美颜素材为例,需要通过 key 来控制我们要修改的是美白、磨皮还是锐化的强度,素材中 key 与功能的对应关系参见 素材 key 对应说明。
设置贴纸接口
/// @brief 设置贴纸路径 /// @details 贴纸素材的文件路径,相对 StickerResource.bundle 路径,为 null 时为关闭贴纸 /// @param path 贴纸路径 relative path of sticker - (void)setStickerPath:(NSString*) path; // 示例 [self.manager setStickerPath:@"baibianfaxing"];
此处的贴纸路径为素材包中 StickerResource.bundle/stickers 中的相对路径。
设置滤镜的接口
/// @brief 设置滤镜路径 /// @details 相对 FilterResource.bundle/Filter 路径,为 null 时关闭滤镜 /// @param path 相对路径 - (void)setFilterPath:(NSString *) path; /// @brief 设置滤镜强度 /// @param intensity 滤镜强度,0-1 - (void)setFilterIntensity:(float)intensity; // 示例 [self.manager setFilterPath:@"/Filter_01_38"]; [self.manager setFilterIntensity:0.8];
此处的滤镜路径为素材包中 FilterResource.bundle/Filter 中的相对路径。
算法对应的代码封装在 Core/Core/Algorithm 目录下,每一个算法都是作为单独封装存在的,互相之间一般不会有依赖。比如人脸检测对应的封装为 BEFaceAlgorithmTask,手势检测对应的封装为 BEHandAlgorithmTask,他们有一个基类 BEAlgorithmTask 定义了所有算法的通用接口,下面以人脸检测举例说明算法使用流程。
算法的初始化函数定义在 BEAlgorithmTask 中,
- (instancetype)initWithProvider:(id<BEAlgorithmResourceProvider>)provider;
其中 BEAlgorithmResourceProvider 为算法提供了一些资源信息,例如模型文件,每一个算法所需的资源不尽相同,所以每一个算法都有自己的定义,人脸检测的定义如下:
@protocol BEFaceResourceProvider <BEAlgorithmResourceProvider> - (const char *)faceModel; - (const char *)faceExtraModel; - (const char *)faceAttrModel; @end
每一个算法定义的 ResourceProvider 都可以在各自的头文件中找到,外部使用算法的时候,需要有一个类来实现这个协议,一般情况下,可以直接使用工程中自带的 BEAlgorithmResourceHelper,这个类实现了所有算法的 ResourceProvider。
以上为创建算法实例,创建完成之后还需要调用初始化函数才能完成算法初始化:
/// @brief 初始化算法 - (int)initTask;
返回值表示初始化结果。
算法检测的函数也定义在 BEAlgorithmTask 中,
/// @brief 算法调用 /// @param buffer buffer /// @param width width /// @param height height /// @param stride stride,一般是 width * 4 /// @param format format /// @param rotation rotation - (id)process:(const unsigned char *)buffer width:(int)width height:(int)height stride:(int)stride format:(bef_ai_pixel_format)format rotation:(bef_ai_rotate_type)rotation;
所有算法的算法检测函数都是这个,需要传入一个 buffer 以及相关信息。如果当前的项目中只有 CVPixelBuffer 的话,可以使用 BEImageUtils 进行转换:
BEBuffer *buffer = [self.imageUtils transforCVPixelBufferToBuffer:pixelBuffer outputFormat:BE_BGRA]; unsigned char *inputBuffer = buffer.buffer; int width = buffer.width; int height = buffer.height; int stride = buffer.bytesPerRow;
算法检测的结果在基类的声明中是一个 id,即也是每一个算法都不一样的,比如人脸检测的结果为 BEFaceAlgorithmResult,每一个算法的检测结果,同样可以在各自的头文件中找到定义。
算法参数设置的函数定义为:
/// @brief 设置算法参数 /// @param key 参数 key /// @param p 参数值 - (void)setConfig:(BEAlgorithmKey *)key p:(NSObject *)p;
其中 key表示参数的类型,p 为参数值,每一个算法的参数类型可以在算法的定义中看到,如人脸检测算法的定义:
@interface BEFaceAlgorithmTask : BEAlgorithmTask + (BEAlgorithmKey *)FACE_106; + (BEAlgorithmKey *)FACE_280; + (BEAlgorithmKey *)FACE_ATTR; + (BEAlgorithmKey *)FACE_MASK; + (BEAlgorithmKey *)MOUTH_MASK; + (BEAlgorithmKey *)TEETH_MASK; @property (nonatomic, assign) unsigned long long initConfig; @property (nonatomic, assign) unsigned long long detectConfig; @property (nonatomic, assign) int maxFaceNum; @end
它有 FACE_280、FACE_ATTR 这几种 key,我们可以使用 setConfig:p 函数来设置是否开启 280 点检测等,如
[self.algorithmTask setConfig:BEFaceAlgorithmTask.FACE_280 p:[NSNumber numberWithBool:YES]];
表示开启人脸检测的 280 点检测。
Common 模块中提供了 BEVideoSourceProvider 接口,可用于实现相机数据的采集或图片、视频的图像流输出,每一种模式都有一个单独的类。BEVideoCapture 的回调代理为 BEVideoCaptureDelegate,不同模式下的回调方法不同,会在下面说明。
相机预览模式对应的为 BEVideoCapture,其内部通过系统的 AVFoundation 功能进行相机数据采集。
初始化示例:
_capture = [[BEVideoCapture alloc] init]; _capture.delegate = self;
使用相机预览模式,需要实现的代理方法为:
/// @brief 输出 CMSampleBufferRef 回调 - (void)videoCapture:(id<BEVideoSourceProtocol>)source didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer withRotation:(int)rotation;
图片处理模式对应的类为 BEImageCapture,其内部将图片的 buffer 通过 NSTimer 模拟成一个视频流进行输出。
初始化示例:
_capture = [[BEImageCapture alloc] initWithImage:self.image]; _capture.delegate = self;
使用图片模式,需要实现的代理方法为:
/// @brier 输出 buffer 回调 /// @param source source /// @param buffer buffer /// @param width 宽 /// @param height 高 /// @param bytesPerRow bytesPerRow /// @param format buffer 格式 /// @param timeStamp 时间戳 - (void)videoCapture:(id<BEVideoSourceProtocol>)source didOutputBuffer:(unsigned char *)buffer width:(int)width height:(int)height bytesPerRow:(int)bytesPerRow format:(OSType)format timeStamp:(double)timeStamp;
视频处理模式对应的类为 BELocalVideoCapture,其内部通过 AVAssetReader 将本地视频解码为 CMSampleBuffer 输出。
初始化示例:
_capture = [[BELocalVideoCapture alloc] initWithAsset:self.asset]; _capture.delegate = self;
使用视频模式,需要实现的代理方法为:
/// @brief 输出 CMSampleBufferRef 回调 - (void)videoCapture:(id<BEVideoSourceProtocol>)source didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer withRotation:(int)rotation;
Common 中使用 BEGLView 继承 GLKView,在内部通过 openGL 对纹理进行绘制,如果自己项目中暂时没有将 CV SDK 处理后的图像绘制到屏幕上的方法,可以使用这个类。然后通过下面的方法绘制纹理:
- (void)renderWithTexture:(unsigned int)name size:(CGSize)size flipped:(BOOL)flipped applyingOrientation:(int)orientation fitType:(int)fitType;
如果是使用 SDK 处理相机输出的 CMSampleBufferRef,可通过如下方式获取:
CMTime sampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); double timeStamp = (double)sampleTime.value/sampleTime.timescale;
否则通过如下方式获取:
double timeStamp = [[NSDate date] timeIntervalSince1970]];
代码:
BEPixelBufferGLTexture *outputTexture = [self.imageUtils getOutputPixelBufferGLTextureWithWidth:texture.width height:texture.height format:BE_BGRA]; // 获取对应的 OpenGL 纹理 int outTexture = outputTexture.texture; // 获取对应的 CVPixelBuffer CVPixelBufferRef outputPixelBuffer = outputTexture.pixelBuffer;
代码:
id<BEGLTexture> texture = [self.imageUtils transforCVPixelBufferToTexture:pixelBuffer]; // 获取对应的 OpenGL 纹理 int inputTexture = texture.texture; // 获取对应的纹理的宽高 int inputWidth = texture.width; int inputHeight = texture.height;
代码:
BEBuffer *buffer = [self.imageUtils transforCVPixelBufferToBuffer:pixelBuffer outputFormat:BE_BGRA]; unsigned char *inputBuffer = buffer.buffer; int width = buffer.width; int height = buffer.height; int stride = buffer.bytesPerRow;