基本介绍
您可以阅读本文,了解快速运行IDRSSDK的操作方法,以及如何开启本地双录,调用相关AI检测接口。
创建项目工程
使用Xcode创建一个新的项目。
配置步骤
1. 下载SDK
将IDRSSDK.framework文件目录中的Resources.bundle拷贝一份出来,将IDRSSDK.framework和Resources.bundle一起,复制一份到您的app文件夹下
2. 配置参数
a. 将IDRSSDK.framework动态库添加进来,同时添加其他必要的库文件,如图所示b. 将Resources.bundle资源添加进来,如图所示:
c. 设置Other Linker Flags为-ObjC,如图所示:
d. 增加支持http协议允许,如图所示:
e. 允许使用图片库、相机、麦克风、照片库和照片权限
调用说明
开启音视频数据获取
参考代码:VideoBaseViewController。
- 通过继承AVCaptureVideoDataOutputSampleBufferDelegate和AVCaptureAudioDataOutputSampleBufferDelegate,获取摄像头的视频流和麦克风的音频流。
- 定义audioConnection和videoConnection,用以区分回调函数中拿到的数据类型是音频数据还是视频数据。
- initSession中定义了使用前置摄像头,同时设置了摄像头获取视频数据的格式为kCVPixelFormatType_32BGRA。
- 定义人脸检测框View和手势检测框View,可以标示出所检测到的人脸和手。
- 定义获取角度函数。由于所有AI检测都需要输入当前手机角度,此方法可以返回当前手机的旋转角度。
- VideoBaseViewController里还设置了相机预览的output方向和镜像,从而保证输出数据和当前屏幕显示一致。
- 请注意VideoBaseViewController.m里还提供了updateLayoutWithOrientationOrPresetChanged方法,用于人脸检测框View和手势检测框View的宽高适配。
@interface VideoBaseViewController : UIViewController <AVCaptureVideoDataOutputSampleBufferDelegate,AVCaptureAudioDataOutputSampleBufferDelegate>
@property (strong, nonatomic) VideoBaseDetectView *detectView; //人脸框
@property (strong, nonatomic) VideoBaseDetectView *handDetectView; //手势框
//Connection
@property (nonatomic, strong) AVCaptureConnection *audioConnection;//音频录制连接
@property (nonatomic, strong) AVCaptureConnection *videoConnection;//视频录制连接
- (NSDictionary*)calculateInAndOutAngle; // 手机旋转角度
@end
- (void)initSession {
_captureInput = [[AVCaptureDeviceInput alloc]initWithDevice:[self cameraWithPosition:_isFrontCamera] error:nil];
AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init];
dispatch_queue_t queue = dispatch_queue_create("cameraQueue", NULL);
/**
print available video output format:[self availableVideoFormatTypes:captureOutput];
一般只支持这几种格式:
kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange(yuv420sp nv12)
kCVPixelFormatType_420YpCbCr8BiPlanarFullRange(yuv420sp nv12)
kCVPixelFormatType_32BGRA
*/
captureOutput.videoSettings = @{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)
};
[captureOutput setSampleBufferDelegate:self queue:queue];
self.captureSession = [[AVCaptureSession alloc] init];
if ([self.captureSession canAddInput:_captureInput]) {
[self.captureSession addInput:_captureInput];
}
if ([self.captureSession canAddOutput:captureOutput]) {
[self.captureSession addOutput:captureOutput];
}
//添加麦克风的输入
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];
if ([self.captureSession canAddInput:audioDeviceInput]) {
[self.captureSession addInput:audioDeviceInput];
}
AVCaptureAudioDataOutput *audioOutput = [[AVCaptureAudioDataOutput alloc] init];
dispatch_queue_t audioQueue = dispatch_queue_create("Audio Capure Queue", DISPATCH_QUEUE_SERIAL);
[audioOutput setSampleBufferDelegate:self queue:audioQueue];
if ([self.captureSession canAddOutput:audioOutput]) {
[self.captureSession addOutput:audioOutput];
}
self.videoConnection = [captureOutput connectionWithMediaType:AVMediaTypeVideo];
//提交配置
[self.captureSession commitConfiguration];
self.captureSession.sessionPreset = _sessionPreset;// 分辨率
}
定义人脸绘制视图
参考代码:FaceDetectView。
- 接收NSArray
*detectResult,绘制检测出的人脸框。
- 定义drawRect方法,更新视图。
定义手势绘制视图
参考代码:HandDetectView。
- 接收NSArray
*detectResult,绘制手框。
- 定义drawRect方法,更新视图。
本地双录的实现
参考代码:FaceSDKDemoViewController。本地双录中包含以下功能:
- 通过IDRSSDK,对音频流和视频流做AI检测,目前包含激活词识别、人脸追踪、人脸识别、活体检测、身份证识别和签名动作识别。
- 整体流程控制,包括播放音频、当检测失败时,跳过或者暂停当前流程。目前Demo中当流程是预置好的固定流程。
- 录像。
- 生成云端检测所需要的辅助检测文件。
- 上传录像和辅助检测文件到oss上。
- (可选)调用http接口,创建云端检测任务
AI检测
1. 初始化IDRSSDK。
[IDRSSDK initWithUrl:@"http://console.idrs.aliyuncs.com"
appId:@"B038BEEF-B288-4ED7-B9EF-04A85459C6B0"
packageName:@"com.taobao.alinnkit.test"
deviceId:@"ddddd"
success:^(id _Nonnull responseObject) {
dispatch_async(dispatch_get_main_queue(), ^{
self->_idrsSDK = responseObject;
});
} failure:^(NSError * _Nonnull error) {
//错误信息处理
}];
2. 视频流的AI检测
- 在- (void)captureOutput:(AVCaptureOutput )output didOutputSampleBuffer:(nonnull CMSampleBufferRef)sampleBuffer fromConnection:(nonnull AVCaptureConnection )connection中可以拿到当前视频的sampleBuffer,在这里,我们可以进行视频的AI检测。
1. 判断当前sampleBuffer是音频还是视频:
// self.videoConnection在VideoBaseViewController中定义并初始化
if (self.videoConnection == connection) {
dataType = CAMERA_VIDEO;
} else {
dataType = CAMERA_AUDIO;
}
2. 获取当前设备角度:
NSDictionary *angleDic = [self calculateInAndOutAngle];
float inAngle = 0;
float outAngle = 0;
if (angleDic != nil) {
inAngle = [angleDic[@"inAngle"] floatValue];
outAngle = [angleDic[@"outAngle"] floatValue];
}
3. 采集人脸照片
通过对当前视频流做人脸检测,将指定区域内的人脸截图,并计算此人脸特征值,为后面的人照对比做准备。首先对视频流做人脸检测:
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
IDRSFaceDetectParam *detectParam = [[IDRSFaceDetectParam alloc]init];
detectParam.dataType = IDRSFaceDetectInputTypePixelBuffer;
detectParam.buffer = pixelBuffer;
detectParam.inputAngle = inAngle;
detectParam.outputAngle = outAngle;
// 采集人脸
//获取数据流中的人脸位置
NSArray<FaceDetectionOutput *> *detectedFace = [_idrsSDK detectFace:detectParam];
视频流中会有多个人脸,我们需要判断,指定区域内是否有人脸:
-(BOOL)pickFaceAction:(NSArray*)detectResult{
FaceDetectView *view;
if ([self.detectView isKindOfClass:NSClassFromString(@"FaceDetectView")]) {
view = (FaceDetectView*)self.detectView;
}
float w = view.presetSize.width;
float h = view.presetSize.height;
if (ScreenWidth>ScreenHeight) {
w = view.presetSize.height;
h = view.presetSize.width;
}
_kw = view.frame.size.width/w;
_kh = view.frame.size.height/h;
for(int i = 0; i < detectResult.count; i++) {
FaceDetectionOutput *outputModel = detectResult[i];
int x = outputModel.rect.origin.x *_kw;
int y = outputModel.rect.origin.y *_kh;
int w = outputModel.rect.size.width *_kw;
int h = outputModel.rect.size.height *_kh;
// 判断脸是否在框里
if (x>CGRectGetMinX(_greenpick.frame) && y>CGRectGetMinY(_greenpick.frame) && w+x<CGRectGetMaxX(_greenpick.frame) && h+y<CGRectGetMaxY(_greenpick.frame)) {
return YES;
}
}
return NO;
}
如果指定区域内有人脸,将此区域内图像截图(后续需要将截图的base64保存到辅助检测meta文件中,供云端检测做人照对比),并计算这个人脸的特征值:
// 视频流截图
-(UIImage*)screenshotFace:(CMSampleBufferRef)sampleBuffer{
UIImage *image = [self getImageFromCameraVideo:sampleBuffer];
//剪切图片
CGRect rect = CGRectMake(CGRectGetMinX(_greenpick.frame)/_kw, CGRectGetMinY(_greenpick.frame)/_kh, CGRectGetWidth(_greenpick.bounds)/_kw, CGRectGetHeight(_greenpick.bounds)/_kh);//这里可以设置想要截图的区域
CGImageRef imageRefRect =CGImageCreateWithImageInRect([image CGImage], rect);
UIImage *sendImage =[[UIImage alloc] initWithCGImage:imageRefRect];
UIImage *newimage = [UIImage imageWithCGImage:sendImage.CGImage scale:1.0 orientation:UIImageOrientationUpMirrored];
return newimage;
}
UIImage转化成base64
//UIImage转化成base64
- (NSString *)imageToString:(UIImage *)image {
NSData *imagedata = UIImagePNGRepresentation(image);
NSString *image64 = [imagedata base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSString*image64NoR = [image64 stringByReplacingOccurrencesOfString:@"\r" withString:@""];
return image64NoR;
}
视频流截图,采集人脸
//视频流截图,采集人脸
UIImage*face = [self screenshotFace:sampleBuffer];
//用图片检测引擎,检测图片中中的人脸特征
IDRSFaceDetectParam *detect = [[IDRSFaceDetectParam alloc]init];
detect.dataType = IDRSFaceDetectInputTypeImage; //指定检测数据类型是图片
detect.image = face; //指定检测数据
detect.inputAngle = inAngle; //指定检测的输入角度
detect.outputAngle = outAngle; //指定检测的输出角度
detect.supportFaceRecognition = true; //开启人脸特征值检测
detect.supportFaceLiveness = true; //开启活体检测
NSArray<FaceDetectionOutput *> *imageface = [weakSelf.idrsSDK detectFace:detect];
//保存当前角色的人脸特征
if (imageface.count > 0) {
weakSelf.face1Feature = imageface[0].feature;
//将人脸base64数据保存到meta文件
[weakSelf.idrsSDK addFace:@"保险代理" image: [self imageToString:face]];
}
4. 人照对比、同框检测、活体检测
首先检测视频流中的所有人脸:
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
IDRSFaceDetectParam *detectParam = [[IDRSFaceDetectParam alloc]init];
detectParam.dataType = IDRSFaceDetectInputTypePixelBuffer; //检测数据类型是视频流
detectParam.buffer = pixelBuffer;
detectParam.inputAngle = inAngle;
detectParam.outputAngle = outAngle;
detectParam.faceNetType = 1; //指定当前检测引擎是本地视频流
detectParam.supportFaceRecognition = true;
detectParam.supportFaceLiveness = true;
[_idrsSDK faceTrackFromVideo:detectParam faceDetectionCallback:^(NSError *error, NSArray<FaceDetectionOutput*> *faces) {
//faces为检测出的人脸
}];
将检测出的人脸,与刚才采集到的照片人脸做人照对比。人照对比分数>0.5, 就可以认为是同一个人:
// 将采集到的两个角色的照片,分别和当前检测到到人脸做对比,人照对比分数>0.5,就认为人照对比成功
for (FaceDetectionOutput *face in faces) {
float score2 =[weakSelf.idrsSDK faceRecognitionSimilarity:face.feature feature2:weakSelf.face2Feature];
float score1 = [weakSelf.idrsSDK faceRecognitionSimilarity:face.feature feature2:weakSelf.face1Feature];
}
可以根据人脸结果的livenessType值,来判断是否是活体:
int livenessType; // 0是真人,1是打印/照片翻拍,2是视频翻拍
5. 身份证识别
首先,显示身份证引导框:
CGRect rect = self.view.bounds;
// 身份证框位置为0.2 0.2 0.6 0.6
CGRect scanFrame = CGRectMake(rect.size.height*0.2,rect.size.width*0.2, rect.size.height*0.6, rect.size.width*0.6);
NSString *bundlePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Resources.bundle"];
NSString * scanImagePath = [bundlePath stringByAppendingPathComponent:@"scan"];
UIImage *scanImage = [UIImage imageNamed:scanImagePath];
_IDcarImageview = [[UIImageView alloc] initWithFrame:scanFrame];
_IDcarImageview.hidden = YES;
[self.view addSubview:_IDcarImageview];
_IDcarImageview.image = scanImage;
_IDcarImageview.backgroundColor = [UIColor clearColor];
检测视频流中中身份证信息:
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
IDRSIDCardDetectParam *detectParam = [[IDRSIDCardDetectParam alloc]init];
detectParam.buffer = pixelBuffer;
detectParam.dataType = IDRSIDCardInputTypePixelBuffer;
// 身份证roi数据。跟上面身份证框位置对应,这四个值分别为标识框左上角x坐标、左上角y坐标、宽、高。
NSArray<NSNumber*> *kXMediaOptionsROIKey = @[@(0.2),@(0.2),@(0.6),@(0.6)];
NSArray<NSString*> *ocrResult = [_idrsSDK detectIDCard:detectParam roiKey:kXMediaOptionsROIKey rotate:angleDic[@"outAngle"] isFrontCamera:YES];
if (ocrResult!=nil && ocrResult.count > 0) {
// 采集到了身份证信息,身份证完全识别出来时,ocrResult.count == 6
if (ocrResult.count == 6) {
dispatch_async(dispatch_get_main_queue(), ^{
//更新meta文件
[self.idrsSDK addPolicy:ocrResult[5] title:@"人身保险投保提示书"];
});
}
}
6. 签名动作识别
识别结果中,如果hand_phone_action 是1 或者2, 则认为签名成功
CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
IDRSHandDetectParam *handParam = [[IDRSHandDetectParam alloc]init];
handParam.dataType = IDRSHandInputTypeBGRA;
handParam.buffer = pixelBuffer;
handParam.outAngle = outAngle;
NSArray<HandDetectionOutput *> *handResults = [_idrsSDK detectHandGesture:handParam];
if (handResults.count > 0) {
// 检测出签名动作
if (handResults[0].hand_phone_action !=0) {
}
}
});
3. 音频流的激活词检测
开启激活词检测。目前能识别出的激活词有:是的/确认/同意/清楚/知道/明白 这六个词:
_idrsSDK.onNuiCallback = ^(NSString *result) {
// 识别结果
NSLog(@"语音识别结果%@",result);
if ([result isEqual: @"\"同意\""] || [result isEqual: @"\"清楚\""]) {
// 识别出来了
}
};
[_idrsSDK startDialog];
关闭激活词检测:
[_idrsSDK stopDialog];
4. 录像
开启录像:
// 定义录像保存成功的callback
_idrsSDK.onRecordCallback = ^(NSString *result) {
NSLog(@"保存本地视频--%@",result);
};
// 根据当前时间生成录像文件名
NSString*filename = [self getVideoName];
_videoFileName = filename;
//定义云端辅助检测meta文件名:录像名+.meta
_metaFileName = [NSString stringWithFormat:@"%@.meta",filename];
//指定录像保存路径
NSString*filepath = [self getVideoPathWithVideoName:filename];
_videoFilePath = filepath;
_metaFilePath = [NSString stringWithFormat:@"%@.meta",filepath];
//开始录像
[_idrsSDK startRecordWithFileName:_videoName andFilePath:filepath];
同样,通过captureOutput方法获取音视频流,并将数据送给IDRSSDK,进行录像:
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(nonnull CMSampleBufferRef)sampleBuffer fromConnection:(nonnull AVCaptureConnection *)connection{
// 给录像数据
[_idrsSDK getAudioVideoForRecord:sampleBuffer dataType:dataType];
}
结束录像: [_idrsSDK stopRecord];
5. 生成云端检测所需要的辅助检测文件
根据流程所对应的检测规则,meta文件中需要定义:
{
"policy":{
"title":"人身保险投保提示书",
"holderIdCardNumber":"140321199210010021"
},
"segments":[
{
"sequence":1,
"beginTime":0,
"endTime":40,
"description":"s1 desc",
"actions":[
{
"action":"id_card_recognize",
"beginTime":20,
"endTime":30
}
]
},
{
"sequence":2,
"beginTime":40,
"endTime":100,
"description":"s2 desc"
}
],
"faces":[
{
"label":"张三",
"imageBase64":"data:image/gif;base64,..."
},
{
"label":"销售",
"imageBase64":"data:image/gif;base64,..."
}
],
"words":[
{
"beginTime":39,
"endTime":44,
"word":"同意"
},
{
"beginTime":126,
"endTime":131,
"word":"清楚"
}
],
"videoMD5":"3911fa3359e2ee94a3ceb69d9d3cb661"
}
字段 | 描述 |
---|---|
policy | holderIdCardNumber为保险代理的身份证号,这个值应该从客户业务系统中获取,用来验证视频中所展示的身份证是否与业务系统中的一致。title为出示的文档标题,这个值应该从客户业务系统中获取,用来验证视频中所展示的文档标题是否与业务系统中的一致。 |
segments | 检测视频段信息。每项内容包括:beginTime:必选,开始时间(秒) endTime:必选,结束时间(秒)sequence:必选,当前所在章节号description:可选,章节描述信息actions:可选,当前章节中,某一个检测项的开始/结束时间例如,章节1的开始时间是0秒,结束时间是40秒。章节1中的身份证识别是在20秒-30秒之间。 |
faces | 待检测的人脸照片。其中,imageBase64字段需要在图片的base64 value前增加data:image/gif;base64, 字符串。 |
words | 待检测的激活词信息。这个值从定义的章节中读取。例如,第一章节要检测出“同意”,因此定义39秒-44秒中需要检测出“同意”。 |
videoMD5 | 视频文件MD5。防止篡改视频。 |
6. 上传视频文件和meta文件到oss上
- 上传文件到oss上需要两步:
1. 调用http接口,获取文件待上传地址
// 获取指定上传地址
[idrs_NetWorkingManager ossUpdataWithfileName:fileName block:^(id _Nonnull response, NSError * _Nonnull err) {
if (err != nil){
return;
}
NSString* Url = response[@"Data"];
// 将文件上传到指定地址
[self putVideo:Url isMeta:isMeta];
}];
2. 将文件上传到指定地址上:
-(void)putVideo:(NSString*)url isMeta:(BOOL)isMeta{
NSString*filePath = @"";
if (isMeta) {
//生成保存Meta文件
filePath = [_idrsSDK saveMetaWithfileName:_metaFileName andfilePath:_metaFilePath];
}else{
filePath = [_idrsSDK getLocationVideoPath];
}
[idrs_NetWorkingManager updataFileWithUrl:url filePath:filePath complete:^(id _Nonnull responseObject, NSError * _Nonnull error) {
if (error != nil) {
//报错处理
return;
}
if (!isMeta) {
[self startTask:url];
}else {
[self getURLisMeta:false];
}
NSLog(@"上传成功:%@",responseObject);
}];
}
7. http请求相关说明
上传视频文件、开启云端检测任务、获取云端检测任务都需要调用双录质检服务接口。无论是公有云还是专有云,都需要app自己构造请求参数,请求参数中需要包括:调用方法、请求参数、调用者身份认证签名。其中,公有云的调用者身份认证签名用的是阿里云账号的AK/SK。专有云调用者身份认证签名用的是在双录质检控制台上注册用户的AK/SK。
- idrs_NetWorking:http接口调用,包括Get方法和Post方法。
@interface idrs_NetWorking : NSObject
//http请求调用
+(void)HTTPWithMethod:(NSString*)get_post body:(NSDictionary*)body success:(void (^)(id responseObject))success
failure:(void (^)(NSError *error))failure;
@end
idrs_NetWorkingManager:上传视频文件相关方法
@interface idrs_NetWorkingManager : NSObject
// 获取上传文件路径
+(void)ossUpdataWithfileName:(NSString*)fileName block:(void (^)(id response, NSError *err))block;
// 上传文件到指定url
+(void)updataFileWithUrl:(NSString*)url filePath:(NSString*)filePath complete:(idrs_request_call_back)complete;
@end