全部产品

iOS本地双录使用文档

更新时间:2020-07-14 13:12:32

基本介绍

您可以阅读本文,了解快速运行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。

  1. 通过继承AVCaptureVideoDataOutputSampleBufferDelegate和AVCaptureAudioDataOutputSampleBufferDelegate,获取摄像头的视频流和麦克风的音频流。
  2. 定义audioConnection和videoConnection,用以区分回调函数中拿到的数据类型是音频数据还是视频数据。
  3. initSession中定义了使用前置摄像头,同时设置了摄像头获取视频数据的格式为kCVPixelFormatType_32BGRA。
  4. 定义人脸检测框View和手势检测框View,可以标示出所检测到的人脸和手。
  5. 定义获取角度函数。由于所有AI检测都需要输入当前手机角度,此方法可以返回当前手机的旋转角度。
  6. VideoBaseViewController里还设置了相机预览的output方向和镜像,从而保证输出数据和当前屏幕显示一致。
  7. 请注意VideoBaseViewController.m里还提供了updateLayoutWithOrientationOrPresetChanged方法,用于人脸检测框View和手势检测框View的宽高适配。
  1. @interface VideoBaseViewController : UIViewController <AVCaptureVideoDataOutputSampleBufferDelegate,AVCaptureAudioDataOutputSampleBufferDelegate>
  2. @property (strong, nonatomic) VideoBaseDetectView *detectView; //人脸框
  3. @property (strong, nonatomic) VideoBaseDetectView *handDetectView; //手势框
  4. //Connection
  5. @property (nonatomic, strong) AVCaptureConnection *audioConnection;//音频录制连接
  6. @property (nonatomic, strong) AVCaptureConnection *videoConnection;//视频录制连接
  7. - (NSDictionary*)calculateInAndOutAngle; // 手机旋转角度
  8. @end
  1. - (void)initSession {
  2. _captureInput = [[AVCaptureDeviceInput alloc]initWithDevice:[self cameraWithPosition:_isFrontCamera] error:nil];
  3. AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init];
  4. dispatch_queue_t queue = dispatch_queue_create("cameraQueue", NULL);
  5. /**
  6. print available video output format:[self availableVideoFormatTypes:captureOutput];
  7. 一般只支持这几种格式:
  8. kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange(yuv420sp nv12)
  9. kCVPixelFormatType_420YpCbCr8BiPlanarFullRange(yuv420sp nv12)
  10. kCVPixelFormatType_32BGRA
  11. */
  12. captureOutput.videoSettings = @{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)
  13. };
  14. [captureOutput setSampleBufferDelegate:self queue:queue];
  15. self.captureSession = [[AVCaptureSession alloc] init];
  16. if ([self.captureSession canAddInput:_captureInput]) {
  17. [self.captureSession addInput:_captureInput];
  18. }
  19. if ([self.captureSession canAddOutput:captureOutput]) {
  20. [self.captureSession addOutput:captureOutput];
  21. }
  22. //添加麦克风的输入
  23. AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
  24. AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:nil];
  25. if ([self.captureSession canAddInput:audioDeviceInput]) {
  26. [self.captureSession addInput:audioDeviceInput];
  27. }
  28. AVCaptureAudioDataOutput *audioOutput = [[AVCaptureAudioDataOutput alloc] init];
  29. dispatch_queue_t audioQueue = dispatch_queue_create("Audio Capure Queue", DISPATCH_QUEUE_SERIAL);
  30. [audioOutput setSampleBufferDelegate:self queue:audioQueue];
  31. if ([self.captureSession canAddOutput:audioOutput]) {
  32. [self.captureSession addOutput:audioOutput];
  33. }
  34. self.videoConnection = [captureOutput connectionWithMediaType:AVMediaTypeVideo];
  35. //提交配置
  36. [self.captureSession commitConfiguration];
  37. self.captureSession.sessionPreset = _sessionPreset;// 分辨率
  38. }

定义人脸绘制视图

参考代码:FaceDetectView。

  • 接收NSArray *detectResult,绘制检测出的人脸框。
  • 定义drawRect方法,更新视图。

定义手势绘制视图

参考代码:HandDetectView。

  • 接收NSArray *detectResult,绘制手框。
  • 定义drawRect方法,更新视图。

本地双录的实现

参考代码:FaceSDKDemoViewController。本地双录中包含以下功能:

  1. 通过IDRSSDK,对音频流和视频流做AI检测,目前包含激活词识别、人脸追踪、人脸识别、活体检测、身份证识别和签名动作识别。
  2. 整体流程控制,包括播放音频、当检测失败时,跳过或者暂停当前流程。目前Demo中当流程是预置好的固定流程。
  3. 录像。
  4. 生成云端检测所需要的辅助检测文件。
  5. 上传录像和辅助检测文件到oss上。
  6. (可选)调用http接口,创建云端检测任务

AI检测

1. 初始化IDRSSDK。

  1. [IDRSSDK initWithUrl:@"http://console.idrs.aliyuncs.com"
  2. appId:@"B038BEEF-B288-4ED7-B9EF-04A85459C6B0"
  3. packageName:@"com.taobao.alinnkit.test"
  4. deviceId:@"ddddd"
  5. success:^(id _Nonnull responseObject) {
  6. dispatch_async(dispatch_get_main_queue(), ^{
  7. self->_idrsSDK = responseObject;
  8. });
  9. } failure:^(NSError * _Nonnull error) {
  10. //错误信息处理
  11. }];

2. 视频流的AI检测

  • 在- (void)captureOutput:(AVCaptureOutput )output didOutputSampleBuffer:(nonnull CMSampleBufferRef)sampleBuffer fromConnection:(nonnull AVCaptureConnection )connection中可以拿到当前视频的sampleBuffer,在这里,我们可以进行视频的AI检测。
1. 判断当前sampleBuffer是音频还是视频:
  1. // self.videoConnection在VideoBaseViewController中定义并初始化
  2. if (self.videoConnection == connection) {
  3. dataType = CAMERA_VIDEO;
  4. } else {
  5. dataType = CAMERA_AUDIO;
  6. }
2. 获取当前设备角度:
  1. NSDictionary *angleDic = [self calculateInAndOutAngle];
  2. float inAngle = 0;
  3. float outAngle = 0;
  4. if (angleDic != nil) {
  5. inAngle = [angleDic[@"inAngle"] floatValue];
  6. outAngle = [angleDic[@"outAngle"] floatValue];
  7. }
3. 采集人脸照片

通过对当前视频流做人脸检测,将指定区域内的人脸截图,并计算此人脸特征值,为后面的人照对比做准备。首先对视频流做人脸检测:

  1. CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
  2. IDRSFaceDetectParam *detectParam = [[IDRSFaceDetectParam alloc]init];
  3. detectParam.dataType = IDRSFaceDetectInputTypePixelBuffer;
  4. detectParam.buffer = pixelBuffer;
  5. detectParam.inputAngle = inAngle;
  6. detectParam.outputAngle = outAngle;
  7. // 采集人脸
  8. //获取数据流中的人脸位置
  9. NSArray<FaceDetectionOutput *> *detectedFace = [_idrsSDK detectFace:detectParam];

视频流中会有多个人脸,我们需要判断,指定区域内是否有人脸:

  1. -(BOOL)pickFaceAction:(NSArray*)detectResult{
  2. FaceDetectView *view;
  3. if ([self.detectView isKindOfClass:NSClassFromString(@"FaceDetectView")]) {
  4. view = (FaceDetectView*)self.detectView;
  5. }
  6. float w = view.presetSize.width;
  7. float h = view.presetSize.height;
  8. if (ScreenWidth>ScreenHeight) {
  9. w = view.presetSize.height;
  10. h = view.presetSize.width;
  11. }
  12. _kw = view.frame.size.width/w;
  13. _kh = view.frame.size.height/h;
  14. for(int i = 0; i < detectResult.count; i++) {
  15. FaceDetectionOutput *outputModel = detectResult[i];
  16. int x = outputModel.rect.origin.x *_kw;
  17. int y = outputModel.rect.origin.y *_kh;
  18. int w = outputModel.rect.size.width *_kw;
  19. int h = outputModel.rect.size.height *_kh;
  20. // 判断脸是否在框里
  21. if (x>CGRectGetMinX(_greenpick.frame) && y>CGRectGetMinY(_greenpick.frame) && w+x<CGRectGetMaxX(_greenpick.frame) && h+y<CGRectGetMaxY(_greenpick.frame)) {
  22. return YES;
  23. }
  24. }
  25. return NO;
  26. }

如果指定区域内有人脸,将此区域内图像截图(后续需要将截图的base64保存到辅助检测meta文件中,供云端检测做人照对比),并计算这个人脸的特征值:

  1. // 视频流截图
  2. -(UIImage*)screenshotFace:(CMSampleBufferRef)sampleBuffer{
  3. UIImage *image = [self getImageFromCameraVideo:sampleBuffer];
  4. //剪切图片
  5. CGRect rect = CGRectMake(CGRectGetMinX(_greenpick.frame)/_kw, CGRectGetMinY(_greenpick.frame)/_kh, CGRectGetWidth(_greenpick.bounds)/_kw, CGRectGetHeight(_greenpick.bounds)/_kh);//这里可以设置想要截图的区域
  6. CGImageRef imageRefRect =CGImageCreateWithImageInRect([image CGImage], rect);
  7. UIImage *sendImage =[[UIImage alloc] initWithCGImage:imageRefRect];
  8. UIImage *newimage = [UIImage imageWithCGImage:sendImage.CGImage scale:1.0 orientation:UIImageOrientationUpMirrored];
  9. return newimage;
  10. }

UIImage转化成base64

  1. //UIImage转化成base64
  2. - (NSString *)imageToString:(UIImage *)image {
  3. NSData *imagedata = UIImagePNGRepresentation(image);
  4. NSString *image64 = [imagedata base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
  5. NSString*image64NoR = [image64 stringByReplacingOccurrencesOfString:@"\r" withString:@""];
  6. return image64NoR;
  7. }

视频流截图,采集人脸

  1. //视频流截图,采集人脸
  2. UIImage*face = [self screenshotFace:sampleBuffer];
  3. //用图片检测引擎,检测图片中中的人脸特征
  4. IDRSFaceDetectParam *detect = [[IDRSFaceDetectParam alloc]init];
  5. detect.dataType = IDRSFaceDetectInputTypeImage; //指定检测数据类型是图片
  6. detect.image = face; //指定检测数据
  7. detect.inputAngle = inAngle; //指定检测的输入角度
  8. detect.outputAngle = outAngle; //指定检测的输出角度
  9. detect.supportFaceRecognition = true; //开启人脸特征值检测
  10. detect.supportFaceLiveness = true; //开启活体检测
  11. NSArray<FaceDetectionOutput *> *imageface = [weakSelf.idrsSDK detectFace:detect];
  12. //保存当前角色的人脸特征
  13. if (imageface.count > 0) {
  14. weakSelf.face1Feature = imageface[0].feature;
  15. //将人脸base64数据保存到meta文件
  16. [weakSelf.idrsSDK addFace:@"保险代理" image: [self imageToString:face]];
  17. }
4. 人照对比、同框检测、活体检测

首先检测视频流中的所有人脸:

  1. CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
  2. IDRSFaceDetectParam *detectParam = [[IDRSFaceDetectParam alloc]init];
  3. detectParam.dataType = IDRSFaceDetectInputTypePixelBuffer; //检测数据类型是视频流
  4. detectParam.buffer = pixelBuffer;
  5. detectParam.inputAngle = inAngle;
  6. detectParam.outputAngle = outAngle;
  7. detectParam.faceNetType = 1; //指定当前检测引擎是本地视频流
  8. detectParam.supportFaceRecognition = true;
  9. detectParam.supportFaceLiveness = true;
  10. [_idrsSDK faceTrackFromVideo:detectParam faceDetectionCallback:^(NSError *error, NSArray<FaceDetectionOutput*> *faces) {
  11. //faces为检测出的人脸
  12. }];

将检测出的人脸,与刚才采集到的照片人脸做人照对比。人照对比分数>0.5, 就可以认为是同一个人:

  1. // 将采集到的两个角色的照片,分别和当前检测到到人脸做对比,人照对比分数>0.5,就认为人照对比成功
  2. for (FaceDetectionOutput *face in faces) {
  3. float score2 =[weakSelf.idrsSDK faceRecognitionSimilarity:face.feature feature2:weakSelf.face2Feature];
  4. float score1 = [weakSelf.idrsSDK faceRecognitionSimilarity:face.feature feature2:weakSelf.face1Feature];
  5. }

可以根据人脸结果的livenessType值,来判断是否是活体:

  1. int livenessType; // 0是真人,1是打印/照片翻拍,2是视频翻拍
5. 身份证识别

首先,显示身份证引导框:

  1. CGRect rect = self.view.bounds;
  2. // 身份证框位置为0.2 0.2 0.6 0.6
  3. CGRect scanFrame = CGRectMake(rect.size.height*0.2,rect.size.width*0.2, rect.size.height*0.6, rect.size.width*0.6);
  4. NSString *bundlePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:@"Resources.bundle"];
  5. NSString * scanImagePath = [bundlePath stringByAppendingPathComponent:@"scan"];
  6. UIImage *scanImage = [UIImage imageNamed:scanImagePath];
  7. _IDcarImageview = [[UIImageView alloc] initWithFrame:scanFrame];
  8. _IDcarImageview.hidden = YES;
  9. [self.view addSubview:_IDcarImageview];
  10. _IDcarImageview.image = scanImage;
  11. _IDcarImageview.backgroundColor = [UIColor clearColor];

检测视频流中中身份证信息:

  1. CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
  2. IDRSIDCardDetectParam *detectParam = [[IDRSIDCardDetectParam alloc]init];
  3. detectParam.buffer = pixelBuffer;
  4. detectParam.dataType = IDRSIDCardInputTypePixelBuffer;
  5. // 身份证roi数据。跟上面身份证框位置对应,这四个值分别为标识框左上角x坐标、左上角y坐标、宽、高。
  6. NSArray<NSNumber*> *kXMediaOptionsROIKey = @[@(0.2),@(0.2),@(0.6),@(0.6)];
  7. NSArray<NSString*> *ocrResult = [_idrsSDK detectIDCard:detectParam roiKey:kXMediaOptionsROIKey rotate:angleDic[@"outAngle"] isFrontCamera:YES];
  8. if (ocrResult!=nil && ocrResult.count > 0) {
  9. // 采集到了身份证信息,身份证完全识别出来时,ocrResult.count == 6
  10. if (ocrResult.count == 6) {
  11. dispatch_async(dispatch_get_main_queue(), ^{
  12. //更新meta文件
  13. [self.idrsSDK addPolicy:ocrResult[5] title:@"人身保险投保提示书"];
  14. });
  15. }
  16. }
6. 签名动作识别

识别结果中,如果hand_phone_action 是1 或者2, 则认为签名成功

  1. CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
  2. IDRSHandDetectParam *handParam = [[IDRSHandDetectParam alloc]init];
  3. handParam.dataType = IDRSHandInputTypeBGRA;
  4. handParam.buffer = pixelBuffer;
  5. handParam.outAngle = outAngle;
  6. NSArray<HandDetectionOutput *> *handResults = [_idrsSDK detectHandGesture:handParam];
  7. if (handResults.count > 0) {
  8. // 检测出签名动作
  9. if (handResults[0].hand_phone_action !=0) {
  10. }
  11. }
  12. });

3. 音频流的激活词检测

开启激活词检测。目前能识别出的激活词有:是的/确认/同意/清楚/知道/明白 这六个词:

  1. _idrsSDK.onNuiCallback = ^(NSString *result) {
  2. // 识别结果
  3. NSLog(@"语音识别结果%@",result);
  4. if ([result isEqual: @"\"同意\""] || [result isEqual: @"\"清楚\""]) {
  5. // 识别出来了
  6. }
  7. };
  8. [_idrsSDK startDialog];

关闭激活词检测:

  1. [_idrsSDK stopDialog];

4. 录像

开启录像:

  1. // 定义录像保存成功的callback
  2. _idrsSDK.onRecordCallback = ^(NSString *result) {
  3. NSLog(@"保存本地视频--%@",result);
  4. };
  5. // 根据当前时间生成录像文件名
  6. NSString*filename = [self getVideoName];
  7. _videoFileName = filename;
  8. //定义云端辅助检测meta文件名:录像名+.meta
  9. _metaFileName = [NSString stringWithFormat:@"%@.meta",filename];
  10. //指定录像保存路径
  11. NSString*filepath = [self getVideoPathWithVideoName:filename];
  12. _videoFilePath = filepath;
  13. _metaFilePath = [NSString stringWithFormat:@"%@.meta",filepath];
  14. //开始录像
  15. [_idrsSDK startRecordWithFileName:_videoName andFilePath:filepath];

同样,通过captureOutput方法获取音视频流,并将数据送给IDRSSDK,进行录像:

  1. - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(nonnull CMSampleBufferRef)sampleBuffer fromConnection:(nonnull AVCaptureConnection *)connection{
  2. // 给录像数据
  3. [_idrsSDK getAudioVideoForRecord:sampleBuffer dataType:dataType];
  4. }

结束录像: [_idrsSDK stopRecord];

5. 生成云端检测所需要的辅助检测文件

根据流程所对应的检测规则,meta文件中需要定义:

  1. {
  2. "policy":{
  3. "title":"人身保险投保提示书",
  4. "holderIdCardNumber":"140321199210010021"
  5. },
  6. "segments":[
  7. {
  8. "sequence":1,
  9. "beginTime":0,
  10. "endTime":40,
  11. "description":"s1 desc",
  12. "actions":[
  13. {
  14. "action":"id_card_recognize",
  15. "beginTime":20,
  16. "endTime":30
  17. }
  18. ]
  19. },
  20. {
  21. "sequence":2,
  22. "beginTime":40,
  23. "endTime":100,
  24. "description":"s2 desc"
  25. }
  26. ],
  27. "faces":[
  28. {
  29. "label":"张三",
  30. "imageBase64":"data:image/gif;base64,..."
  31. },
  32. {
  33. "label":"销售",
  34. "imageBase64":"data:image/gif;base64,..."
  35. }
  36. ],
  37. "words":[
  38. {
  39. "beginTime":39,
  40. "endTime":44,
  41. "word":"同意"
  42. },
  43. {
  44. "beginTime":126,
  45. "endTime":131,
  46. "word":"清楚"
  47. }
  48. ],
  49. "videoMD5":"3911fa3359e2ee94a3ceb69d9d3cb661"
  50. }
字段 描述
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接口,获取文件待上传地址
  1. // 获取指定上传地址
  2. [idrs_NetWorkingManager ossUpdataWithfileName:fileName block:^(id _Nonnull response, NSError * _Nonnull err) {
  3. if (err != nil){
  4. return;
  5. }
  6. NSString* Url = response[@"Data"];
  7. // 将文件上传到指定地址
  8. [self putVideo:Url isMeta:isMeta];
  9. }];
2. 将文件上传到指定地址上:
  1. -(void)putVideo:(NSString*)url isMeta:(BOOL)isMeta{
  2. NSString*filePath = @"";
  3. if (isMeta) {
  4. //生成保存Meta文件
  5. filePath = [_idrsSDK saveMetaWithfileName:_metaFileName andfilePath:_metaFilePath];
  6. }else{
  7. filePath = [_idrsSDK getLocationVideoPath];
  8. }
  9. [idrs_NetWorkingManager updataFileWithUrl:url filePath:filePath complete:^(id _Nonnull responseObject, NSError * _Nonnull error) {
  10. if (error != nil) {
  11. //报错处理
  12. return;
  13. }
  14. if (!isMeta) {
  15. [self startTask:url];
  16. }else {
  17. [self getURLisMeta:false];
  18. }
  19. NSLog(@"上传成功:%@",responseObject);
  20. }];
  21. }

7. http请求相关说明

上传视频文件、开启云端检测任务、获取云端检测任务都需要调用双录质检服务接口。无论是公有云还是专有云,都需要app自己构造请求参数,请求参数中需要包括:调用方法、请求参数、调用者身份认证签名。其中,公有云的调用者身份认证签名用的是阿里云账号的AK/SK。专有云调用者身份认证签名用的是在双录质检控制台上注册用户的AK/SK。

  • idrs_NetWorking:http接口调用,包括Get方法和Post方法。
  1. @interface idrs_NetWorking : NSObject
  2. //http请求调用
  3. +(void)HTTPWithMethod:(NSString*)get_post body:(NSDictionary*)body success:(void (^)(id responseObject))success
  4. failure:(void (^)(NSError *error))failure;
  5. @end
  6. idrs_NetWorkingManager:上传视频文件相关方法
  7. @interface idrs_NetWorkingManager : NSObject
  8. // 获取上传文件路径
  9. +(void)ossUpdataWithfileName:(NSString*)fileName block:(void (^)(id response, NSError *err))block;
  10. // 上传文件到指定url
  11. +(void)updataFileWithUrl:(NSString*)url filePath:(NSString*)filePath complete:(idrs_request_call_back)complete;
  12. @end