123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- #import "AVCaptureViewController.h"
- #import <AVFoundation/AVFoundation.h>
- #import <AssetsLibrary/AssetsLibrary.h>
- #import "LHSIDCardScaningView.h"
- #import "IDInfo.h"
- #import "excards.h"
- #import "UIImage+Extend.h"
- #import "RectManager.h"
- #import "UIAlertController+Extend.h"
- @interface AVCaptureViewController () <AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureMetadataOutputObjectsDelegate>
- @property (nonatomic,strong) AVCaptureDevice *device;
- @property (nonatomic,strong) AVCaptureSession *session;
- @property (nonatomic,strong) NSNumber *outPutSetting;
- @property (nonatomic,strong) AVCaptureVideoDataOutput *videoDataOutput;
- @property (nonatomic,strong) AVCaptureMetadataOutput *metadataOutput;
- @property (nonatomic,strong) AVCaptureVideoPreviewLayer *previewLayer;
- @property (nonatomic,assign) CGRect faceDetectionFrame;
- @property (nonatomic,strong) dispatch_queue_t queue;
- @property (nonatomic,assign,getter = isTorchOn) BOOL torchOn;
- @end
- @implementation AVCaptureViewController
- #pragma mark - 检测是模拟器还是真机
- #if TARGET_IPHONE_SIMULATOR
- -(void)viewDidLoad {
- [super viewDidLoad];
-
- self.navigationItem.title = @"扫描身份证";
-
- __weak __typeof__(self) weakSelf = self;
- UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"返回" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
-
- [weakSelf dismissViewControllerAnimated:YES completion:nil];
-
- }];
- UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"模拟器没有摄像设备" message:@"请使用真机测试!!!" okAction:okAction cancelAction:nil];
-
- [self presentViewController:alertC animated:YES completion:nil];
- }
- #else
- #pragma mark - 懒加载
- #pragma mark device
- -(AVCaptureDevice *)device {
- if (_device == nil) {
- _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
-
- NSError *error = nil;
- if ([_device lockForConfiguration:&error]) {
- if ([_device isSmoothAutoFocusSupported]) {
- _device.smoothAutoFocusEnabled = YES;
- }
-
- if ([_device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
- _device.focusMode = AVCaptureFocusModeContinuousAutoFocus;
- }
-
- if ([_device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure ]) {
- _device.exposureMode = AVCaptureExposureModeContinuousAutoExposure;
- }
-
- if ([_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance]) {
- _device.whiteBalanceMode = AVCaptureWhiteBalanceModeContinuousAutoWhiteBalance;
- }
-
-
- [_device unlockForConfiguration];
- }
- }
-
- return _device;
- }
- #pragma mark outPutSetting
- -(NSNumber *)outPutSetting {
- if (_outPutSetting == nil) {
- _outPutSetting = @(kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange);
- }
-
- return _outPutSetting;
- }
- #pragma mark metadataOutput
- -(AVCaptureMetadataOutput *)metadataOutput {
- if (_metadataOutput == nil) {
- _metadataOutput = [[AVCaptureMetadataOutput alloc]init];
-
- [_metadataOutput setMetadataObjectsDelegate:self queue:self.queue];
- }
-
- return _metadataOutput;
- }
- #pragma mark videoDataOutput
- -(AVCaptureVideoDataOutput *)videoDataOutput {
- if (_videoDataOutput == nil) {
-
- _videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
-
- _videoDataOutput.alwaysDiscardsLateVideoFrames = YES;
- _videoDataOutput.videoSettings = @{(id)kCVPixelBufferPixelFormatTypeKey:self.outPutSetting};
-
-
- [_videoDataOutput setSampleBufferDelegate:self queue:self.queue];
- }
-
- return _videoDataOutput;
- }
- #pragma mark session
- -(AVCaptureSession *)session {
- if (_session == nil) {
- _session = [[AVCaptureSession alloc] init];
-
- _session.sessionPreset = AVCaptureSessionPresetHigh;
-
-
- NSError *error = nil;
- AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:&error];
-
- if (error) {
- UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
- [self alertControllerWithTitle:@"没有摄像设备" message:error.localizedDescription okAction:okAction cancelAction:nil];
- }else {
- if ([_session canAddInput:input]) {
- [_session addInput:input];
- }
-
- if ([_session canAddOutput:self.videoDataOutput]) {
- [_session addOutput:self.videoDataOutput];
- }
- if(_cardType == 0)
- {
- if ([_session canAddOutput:self.metadataOutput]) {
- [_session addOutput:self.metadataOutput];
-
- self.metadataOutput.metadataObjectTypes = @[AVMetadataObjectTypeFace];
- }
- }
- }
- }
-
- return _session;
- }
- #pragma mark previewLayer
- -(AVCaptureVideoPreviewLayer *)previewLayer {
- if (_previewLayer == nil) {
- _previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
-
- _previewLayer.frame = self.view.frame;
- _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
- }
-
- return _previewLayer;
- }
- #pragma mark queue
- -(dispatch_queue_t)queue {
- if (_queue == nil) {
- _queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- }
-
- return _queue;
- }
- #pragma mark - 运行session
- - (void)runSession {
- if (![self.session isRunning]) {
- dispatch_async(self.queue, ^{
- [self.session startRunning];
- });
- }
- }
- #pragma mark - 停止session
- -(void)stopSession {
- if ([self.session isRunning]) {
- dispatch_async(self.queue, ^{
- [self.session stopRunning];
- });
- }
- }
- #pragma mark - 打开/关闭手电筒
- -(void)turnOnOrOffTorch {
- self.torchOn = !self.isTorchOn;
-
- if ([self.device hasTorch]){
- [self.device lockForConfiguration:nil];
-
- if (self.isTorchOn) {
- self.navigationItem.rightBarButtonItem.image = [[UIImage imageNamed:@"nav_torch_on"] originalImage];
- [self.device setTorchMode:AVCaptureTorchModeOn];
- } else {
- self.navigationItem.rightBarButtonItem.image = [[UIImage imageNamed:@"nav_torch_off"] originalImage];
- [self.device setTorchMode:AVCaptureTorchModeOff];
- }
- [self.device unlockForConfiguration];
- }else {
- UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
- [self alertControllerWithTitle:@"提示" message:@"您的设备没有闪光设备,不能提供手电筒功能,请检查" okAction:okAction cancelAction:nil];
- }
- }
- #pragma mark - view即将出现时
- -(void)viewWillAppear:(BOOL)animated {
- [super viewWillAppear:animated];
-
-
- [[[self.navigationController.navigationBar subviews] objectAtIndex:0] setAlpha:0];
-
-
- [self checkAuthorizationStatus];
-
-
- self.torchOn = NO;
- self.navigationItem.rightBarButtonItem.image = [[UIImage imageNamed:@"nav_torch_off"] originalImage];
- }
- #pragma mark - view即将消失时
- -(void)viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:animated];
-
-
- [[[self.navigationController.navigationBar subviews] objectAtIndex:0] setAlpha:1];
-
- [self stopSession];
- }
- - (void)viewDidLoad {
- [super viewDidLoad];
-
- self.navigationItem.title = @"扫描身份证";
-
-
- const char *thePath = [[[NSBundle mainBundle] resourcePath] UTF8String];
- int ret = EXCARDS_Init(thePath);
- if (ret != 0) {
- NSLog(@"初始化失败:ret=%d", ret);
- }
-
-
- [self.view.layer addSublayer:self.previewLayer];
-
-
- LHSIDCardScaningView *IDCardScaningView = [[LHSIDCardScaningView alloc] initWithFrame:self.view.frame];
- IDCardScaningView.cardType = _cardType;
- [IDCardScaningView loadImage];
- self.faceDetectionFrame = IDCardScaningView.facePathRect;
- [self.view addSubview:IDCardScaningView];
-
-
-
-
-
- [self addCloseButton];
-
-
- self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:nil style:UIBarButtonItemStylePlain target:self action:@selector(turnOnOrOffTorch)];
- }
- #pragma mark - 添加关闭按钮
- -(void)addCloseButton {
- UIButton *closeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
-
- [closeBtn setImage:[UIImage imageNamed:@"common_icon_close"] forState:UIControlStateNormal];
- CGFloat closeBtnWidth = 40;
- CGFloat closeBtnHeight = closeBtnWidth;
- CGRect viewFrame = self.view.frame;
- closeBtn.frame = (CGRect){CGRectGetMaxX(viewFrame) - closeBtnWidth - 15, CGRectGetMaxY(viewFrame) - closeBtnHeight - 20, closeBtnWidth, closeBtnHeight};
-
- [closeBtn addTarget:self action:@selector(close) forControlEvents:UIControlEventTouchUpInside];
-
- [self.view addSubview:closeBtn];
- }
- #pragma mark 绑定“关闭按钮”的方法
- -(void)close {
- [self dismissViewControllerAnimated:YES completion:nil];
-
- }
- #pragma mark - 检测摄像头权限
- -(void)checkAuthorizationStatus {
- AVAuthorizationStatus authorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
-
- switch (authorizationStatus) {
- case AVAuthorizationStatusNotDetermined:[self showAuthorizationNotDetermined]; break;
- case AVAuthorizationStatusAuthorized:[self showAuthorizationAuthorized]; break;
- case AVAuthorizationStatusDenied:[self showAuthorizationDenied]; break;
- case AVAuthorizationStatusRestricted:[self showAuthorizationRestricted]; break;
- }
- }
- #pragma mark - 相机使用权限处理
- #pragma mark 用户还未决定是否授权使用相机
- -(void)showAuthorizationNotDetermined {
- __weak __typeof__(self) weakSelf = self;
- [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
- granted? [weakSelf runSession]: [weakSelf showAuthorizationDenied];
- }];
- }
- #pragma mark 被授权使用相机
- -(void)showAuthorizationAuthorized {
- [self runSession];
- }
- #pragma mark 未被授权使用相机
- -(void)showAuthorizationDenied {
- NSString *title = @"相机未授权";
- NSString *message = @"请到系统的“设置-隐私-相机”中授权此应用使用您的相机";
-
- UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"去设置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
-
- [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
- }];
- UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:nil];
- [self alertControllerWithTitle:title message:message okAction:okAction cancelAction:cancelAction];
- }
- #pragma mark 使用相机设备受限
- -(void)showAuthorizationRestricted {
- NSString *title = @"相机设备受限";
- NSString *message = @"请检查您的手机硬件或设置";
-
- UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
- [self alertControllerWithTitle:title message:message okAction:okAction cancelAction:nil];
- }
- #pragma mark - 展示UIAlertController
- -(void)alertControllerWithTitle:(NSString *)title message:(NSString *)message okAction:(UIAlertAction *)okAction cancelAction:(UIAlertAction *)cancelAction {
- UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message okAction:okAction cancelAction:cancelAction];
- [self presentViewController:alertController animated:YES completion:nil];
- }
- #pragma mark - AVCaptureMetadataOutputObjectsDelegate
- #pragma mark 从输出的元数据中捕捉人脸
- -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
- if (metadataObjects.count) {
- AVMetadataMachineReadableCodeObject *metadataObject = metadataObjects.firstObject;
-
- AVMetadataObject *transformedMetadataObject = [self.previewLayer transformedMetadataObjectForMetadataObject:metadataObject];
- CGRect faceRegion = transformedMetadataObject.bounds;
-
- if (metadataObject.type == AVMetadataObjectTypeFace) {
- NSLog(@"是否包含头像:%d, facePathRect: %@, faceRegion: %@",CGRectContainsRect(self.faceDetectionFrame, faceRegion),NSStringFromCGRect(self.faceDetectionFrame),NSStringFromCGRect(faceRegion));
-
- if (CGRectContainsRect(self.faceDetectionFrame, faceRegion)) {
-
- if (!self.videoDataOutput.sampleBufferDelegate) {
- [self.videoDataOutput setSampleBufferDelegate:self queue:self.queue];
- }
- }
- }
- }
- }
- #pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate
- #pragma mark 从输出的数据流捕捉单一的图像帧
- -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
- if ([self.outPutSetting isEqualToNumber:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]] || [self.outPutSetting isEqualToNumber:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]]) {
- CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
-
- if ([captureOutput isEqual:self.videoDataOutput]) {
-
- [self IDCardRecognit:imageBuffer];
-
-
- if(_cardType == 0)
- {
- if (self.videoDataOutput.sampleBufferDelegate) {
- [self.videoDataOutput setSampleBufferDelegate:nil queue:self.queue];
- }
- }
-
- }
- } else {
- NSLog(@"输出格式不支持");
- }
- }
- #pragma mark - 身份证信息识别
- - (void)IDCardRecognit:(CVImageBufferRef)imageBuffer {
- CVBufferRetain(imageBuffer);
-
-
- if (CVPixelBufferLockBaseAddress(imageBuffer, 0) == kCVReturnSuccess) {
- size_t width= CVPixelBufferGetWidth(imageBuffer);
- size_t height = CVPixelBufferGetHeight(imageBuffer);
-
- CVPlanarPixelBufferInfo_YCbCrBiPlanar *planar = CVPixelBufferGetBaseAddress(imageBuffer);
- size_t offset = NSSwapBigIntToHost(planar->componentInfoY.offset);
- size_t rowBytes = NSSwapBigIntToHost(planar->componentInfoY.rowBytes);
- unsigned char* baseAddress = (unsigned char *)CVPixelBufferGetBaseAddress(imageBuffer);
- unsigned char* pixelAddress = baseAddress + offset;
-
- static unsigned char *buffer = NULL;
- if (buffer == NULL) {
- buffer = (unsigned char *)malloc(sizeof(unsigned char) * width * height);
- }
-
- memcpy(buffer, pixelAddress, sizeof(unsigned char) * width * height);
-
- unsigned char pResult[1024];
- int ret = EXCARDS_RecoIDCardData(buffer, (int)width, (int)height, (int)rowBytes, (int)8, (char*)pResult, sizeof(pResult));
- if (ret <= 0) {
- NSLog(@"ret=[%d]", ret);
- } else {
- NSLog(@"ret=[%d]", ret);
-
-
- AudioServicesPlaySystemSound(1108);
-
- if ([self.session isRunning]) {
- [self.session stopRunning];
- }
- char ctype;
- char content[256];
- int xlen;
- int i = 0;
-
- IDInfo *iDInfo = [[IDInfo alloc] init];
- iDInfo.type=self.cardType+1;
- ctype = pResult[i++];
-
- while(i < ret){
- ctype = pResult[i++];
- for(xlen = 0; i < ret; ++i){
- if(pResult[i] == ' ') { ++i; break; }
- content[xlen++] = pResult[i];
- }
-
- content[xlen] = 0;
-
- if(xlen) {
- NSStringEncoding gbkEncoding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
- if(ctype == 0x21) {
- iDInfo.num = [NSString stringWithCString:(char *)content encoding:gbkEncoding];
- } else if(ctype == 0x22) {
- iDInfo.name = [NSString stringWithCString:(char *)content encoding:gbkEncoding];
- } else if(ctype == 0x23) {
- iDInfo.gender = [NSString stringWithCString:(char *)content encoding:gbkEncoding];
- } else if(ctype == 0x24) {
- iDInfo.nation = [NSString stringWithCString:(char *)content encoding:gbkEncoding];
- } else if(ctype == 0x25) {
- iDInfo.address = [NSString stringWithCString:(char *)content encoding:gbkEncoding];
- } else if(ctype == 0x26) {
- iDInfo.issue = [NSString stringWithCString:(char *)content encoding:gbkEncoding];
- } else if(ctype == 0x27) {
- iDInfo.valid = [NSString stringWithCString:(char *)content encoding:gbkEncoding];
- }
- }
- }
- NSArray *saleArray = [iDInfo.valid componentsSeparatedByString:@"-"];
- iDInfo.valid=[NSString stringWithFormat:@"%@-%@-%@",[saleArray[1] substringToIndex:4],[saleArray[1] substringWithRange:NSMakeRange(4,2)],[saleArray[1] substringFromIndex:6]];
- if (iDInfo) {
- NSLog(@"\n正面\n姓名:%@\n性别:%@\n民族:%@\n住址:%@\n公民身份证号码:%@\n\n反面\n签发机关:%@\n有效期限:%@",iDInfo.name,iDInfo.gender,iDInfo.nation,iDInfo.address,iDInfo.num,iDInfo.issue,iDInfo.valid);
-
- CGRect effectRect = [RectManager getEffectImageRect:CGSizeMake(width, height)];
- CGRect rect = [RectManager getGuideFrame:effectRect];
-
- UIImage *image = [UIImage getImageStream:imageBuffer];
- UIImage *subImage = [UIImage getSubImage:rect inImage:image];
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [self.delegate AVCaptureViewReData:iDInfo img:subImage];
-
- [self dismissViewControllerAnimated:YES completion:nil];
- });
-
-
- }
- }
-
- CVPixelBufferUnlockBaseAddress(imageBuffer, 0);
- }
-
- CVBufferRelease(imageBuffer);
- }
- - (void)didReceiveMemoryWarning {
- [super didReceiveMemoryWarning];
-
- }
- #endif
- @end
|