// // CameraViewController.m // lpr // // Created by baotim on 2018/10/26. // Copyright © 2018 lprSample. All rights reserved. // #import "CameraViewController.h" #import "Utility.h" #import "Pipeline.h" //屏幕的宽、高 #define kScreenWidth [UIScreen mainScreen].bounds.size.width #define kScreenHeight [UIScreen mainScreen].bounds.size.height @interface CameraViewController () { AVCaptureSession *_session; AVCaptureDeviceInput *_captureInput; AVCaptureStillImageOutput *_captureOutput; AVCaptureVideoPreviewLayer *_preview; AVCaptureDevice *_device; NSTimer *_timer; //定时器 BOOL _on; //闪光灯状态 BOOL _capture;//导航栏动画是否完成 BOOL _isFoucePixel;//是否相位对焦 CGRect _imgRect;//拍照裁剪 int _count;//每几帧识别 CGFloat _isLensChanged;//镜头位置 /*相位聚焦下镜头位置 镜头晃动 值不停的改变 */ CGFloat _isIOS8AndFoucePixelLensPosition; /* 控制识别速度,最小值为1!数值越大识别越慢。 相机初始化时,设置默认值为1(不要改动),判断设备若为相位对焦时,设置此值为2(可以修改,最小为1,越大越慢) 此值的功能是为了减小相位对焦下,因识别速度过快 此值在相机初始化中设置,在相机代理中使用,用户若无特殊需求不用修改。 */ int _MaxFR; cv::Mat source_image; } @property (assign, nonatomic) BOOL adjustingFocus; @property (nonatomic, retain) CALayer *customLayer; @property (nonatomic,assign) BOOL isProcessingImage; @property (nonatomic, strong) UIImage* image; @end @implementation CameraViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.view.backgroundColor = [UIColor clearColor]; //初始化相机 [self initialize]; //创建相机界面控件 [self createCameraView]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; _capture = NO; [self performSelector:@selector(changeCapture) withObject:nil afterDelay:0.4]; //不支持相位对焦情况下(iPhone6以后的手机支持相位对焦) 设置定时器 开启连续对焦 if (!_isFoucePixel) { _timer = [NSTimer scheduledTimerWithTimeInterval:1.3 target:self selector:@selector(fouceMode) userInfo:nil repeats:YES]; } AVCaptureDevice*camDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; int flags = NSKeyValueObservingOptionNew; //注册通知 [camDevice addObserver:self forKeyPath:@"adjustingFocus" options:flags context:nil]; if (_isFoucePixel) { [camDevice addObserver:self forKeyPath:@"lensPosition" options:flags context:nil]; } [_session startRunning]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; self.navigationController.navigationBarHidden = NO; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; if (!_isFoucePixel) { [_timer invalidate]; _timer = nil; } AVCaptureDevice*camDevice =[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; [camDevice removeObserver:self forKeyPath:@"adjustingFocus"]; if (_isFoucePixel) { [camDevice removeObserver:self forKeyPath:@"lensPosition"]; } [_session stopRunning]; _capture = NO; } - (void)changeCapture { _capture = YES; } #pragma mark - Private Methods //初始化相机 - (void)initialize { //判断摄像头授权 AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if(authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied){ self.view.backgroundColor = [UIColor blackColor]; UIAlertView * alt = [[UIAlertView alloc] initWithTitle:@"未获得授权使用摄像头" message:@"请在'设置-隐私-相机'打开" delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alt show]; return; } _MaxFR = 1; //1.创建会话层 _session = [[AVCaptureSession alloc] init]; [_session setSessionPreset:AVCaptureSessionPreset1920x1080]; //2.创建、配置输入设备 NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; for (AVCaptureDevice *device in devices){ if (device.position == AVCaptureDevicePositionBack){ _device = device; _captureInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:nil]; } } [_session addInput:_captureInput]; //2.创建视频流输出 AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init]; captureOutput.alwaysDiscardsLateVideoFrames = YES; dispatch_queue_t queue; queue = dispatch_queue_create("cameraQueue", NULL); [captureOutput setSampleBufferDelegate:self queue:queue]; NSString* key = (NSString*)kCVPixelBufferPixelFormatTypeKey; NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]; NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; [captureOutput setVideoSettings:videoSettings]; [_session addOutput:captureOutput]; //3.创建、配置静态拍照输出 _captureOutput = [[AVCaptureStillImageOutput alloc] init]; NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey,nil]; [_captureOutput setOutputSettings:outputSettings]; [_session addOutput:_captureOutput]; //4.预览图层 _preview = [AVCaptureVideoPreviewLayer layerWithSession: _session]; _preview.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height); _preview.videoGravity = AVLayerVideoGravityResizeAspectFill; [self.view.layer addSublayer:_preview]; //判断是否相位对焦 if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) { AVCaptureDeviceFormat *deviceFormat = _device.activeFormat; if (deviceFormat.autoFocusSystem == AVCaptureAutoFocusSystemPhaseDetection){ _isFoucePixel = YES; _MaxFR = 2; } } } - (void)createCameraView { //设置覆盖层 CAShapeLayer *maskWithHole = [CAShapeLayer layer]; // Both frames are defined in the same coordinate system CGRect biggerRect = self.view.bounds; CGFloat offset = 1.0f; if ([[UIScreen mainScreen] scale] >= 2) { offset = 0.5; } CGRect smallFrame = CGRectMake(45, 100, 300, 500); CGRect smallerRect = CGRectInset(smallFrame, -offset, -offset) ; UIBezierPath *maskPath = [UIBezierPath bezierPath]; [maskPath moveToPoint:CGPointMake(CGRectGetMinX(biggerRect), CGRectGetMinY(biggerRect))]; [maskPath addLineToPoint:CGPointMake(CGRectGetMinX(biggerRect), CGRectGetMaxY(biggerRect))]; [maskPath addLineToPoint:CGPointMake(CGRectGetMaxX(biggerRect), CGRectGetMaxY(biggerRect))]; [maskPath addLineToPoint:CGPointMake(CGRectGetMaxX(biggerRect), CGRectGetMinY(biggerRect))]; [maskPath addLineToPoint:CGPointMake(CGRectGetMinX(biggerRect), CGRectGetMinY(biggerRect))]; [maskPath moveToPoint:CGPointMake(CGRectGetMinX(smallerRect), CGRectGetMinY(smallerRect))]; [maskPath addLineToPoint:CGPointMake(CGRectGetMinX(smallerRect), CGRectGetMaxY(smallerRect))]; [maskPath addLineToPoint:CGPointMake(CGRectGetMaxX(smallerRect), CGRectGetMaxY(smallerRect))]; [maskPath addLineToPoint:CGPointMake(CGRectGetMaxX(smallerRect), CGRectGetMinY(smallerRect))]; [maskPath addLineToPoint:CGPointMake(CGRectGetMinX(smallerRect), CGRectGetMinY(smallerRect))]; [maskWithHole setPath:[maskPath CGPath]]; [maskWithHole setFillRule:kCAFillRuleEvenOdd]; [maskWithHole setFillColor:[[UIColor colorWithWhite:0 alpha:0.35] CGColor]]; [self.view.layer addSublayer:maskWithHole]; [self.view.layer setMasksToBounds:YES]; /* 相机按钮 适配了iPhone和ipad 不同需求自行修改界面*/ //返回、闪光灯按钮 CGFloat backWidth = 35; if (kScreenHeight>=1024) { backWidth = 50; } CGFloat s = 80; CGFloat s1 = 0; if (kScreenHeight==480) { s = 60; s1 = 10; } UIButton *backBtn = [[UIButton alloc]initWithFrame:CGRectMake(kScreenWidth/16,kScreenWidth/16-s1, backWidth, backWidth)]; [backBtn addTarget:self action:@selector(backAction) forControlEvents:UIControlEventTouchUpInside]; [backBtn setImage:[UIImage imageNamed:@"back_camera_btn"] forState:UIControlStateNormal]; backBtn.titleLabel.textAlignment = NSTextAlignmentLeft; [self.view addSubview:backBtn]; UIButton *flashBtn = [[UIButton alloc]initWithFrame:CGRectMake(kScreenWidth-kScreenWidth/16-backWidth,kScreenWidth/16-s1, backWidth, backWidth)]; [flashBtn setImage:[UIImage imageNamed:@"flash_camera_btn"] forState:UIControlStateNormal]; [flashBtn addTarget:self action:@selector(modeBtn) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:flashBtn]; //拍照视图 上拉按钮 拍照按钮 UIButton *upBtn = [[UIButton alloc]initWithFrame:CGRectMake(kScreenWidth/2-60, kScreenHeight-20, 120, 20)]; upBtn.tag = 1001; [upBtn addTarget:self action:@selector(upBtn:) forControlEvents:UIControlEventTouchUpInside]; [upBtn setImage:[UIImage imageNamed:@"locker_btn_def"] forState:UIControlStateNormal]; [self.view addSubview:upBtn]; UIButton *photoBtn = [[UIButton alloc]initWithFrame:CGRectMake(kScreenWidth/2-30,kScreenHeight-s,60, 60)]; photoBtn.tag = 1000; photoBtn.hidden = YES; [photoBtn setImage:[UIImage imageNamed:@"take_pic_btn"] forState:UIControlStateNormal]; [photoBtn addTarget:self action:@selector(photoBtn) forControlEvents:UIControlEventTouchUpInside]; [photoBtn setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted]; [self.view addSubview:photoBtn]; [self.view bringSubviewToFront:photoBtn]; } #pragma mark - UIAlertViewDelegate - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{ [self.navigationController popViewControllerAnimated:YES]; } //隐藏状态栏 - (UIStatusBarStyle)preferredStatusBarStyle{ return UIStatusBarStyleDefault; } - (BOOL)prefersStatusBarHidden{ return YES; } //对焦 - (void)fouceMode { NSError *error; AVCaptureDevice *device = [self cameraWithPosition:AVCaptureDevicePositionBack]; if ([device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) { if ([device lockForConfiguration:&error]) { CGPoint cameraPoint = [_preview captureDevicePointOfInterestForPoint:self.view.center]; [device setFocusPointOfInterest:cameraPoint]; [device setFocusMode:AVCaptureFocusModeAutoFocus]; [device unlockForConfiguration]; } else { //NSLog(@"Error: %@", error); } } } - (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position { NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; for (AVCaptureDevice *device in devices){ if (device.position == position){ return device; } } return nil; } - (NSString *)getPath:(NSString*)fileName { NSString *bundlePath = [NSBundle mainBundle].bundlePath; NSString *path = [bundlePath stringByAppendingPathComponent:fileName]; return path; } - (NSString *)simpleRecognition:(cv::Mat&)src { NSString *path_1 = [self getPath:@"cascade.xml"]; NSString *path_2 = [self getPath:@"HorizonalFinemapping.prototxt"]; NSString *path_3 = [self getPath:@"HorizonalFinemapping.caffemodel"]; NSString *path_4 = [self getPath:@"Segmentation.prototxt"]; NSString *path_5 = [self getPath:@"Segmentation.caffemodel"]; NSString *path_6 = [self getPath:@"CharacterRecognization.prototxt"]; NSString *path_7 = [self getPath:@"CharacterRecognization.caffemodel"]; std::string *cpath_1 = new std::string([path_1 UTF8String]); std::string *cpath_2 = new std::string([path_2 UTF8String]); std::string *cpath_3 = new std::string([path_3 UTF8String]); std::string *cpath_4 = new std::string([path_4 UTF8String]); std::string *cpath_5 = new std::string([path_5 UTF8String]); std::string *cpath_6 = new std::string([path_6 UTF8String]); std::string *cpath_7 = new std::string([path_7 UTF8String]); pr::PipelinePR pr2 = pr::PipelinePR(*cpath_1, *cpath_2, *cpath_3, *cpath_4, *cpath_5, *cpath_6, *cpath_7); std::vector list_res = pr2.RunPiplineAsImage(src); std::string concat_results = ""; for(auto one:list_res) { if(one.confidence>0.7) { concat_results += one.getPlateName()+","; } } NSString *str = [NSString stringWithCString:concat_results.c_str() encoding:NSUTF8StringEncoding]; if (str.length > 0) { str = [str substringToIndex:str.length-1]; str = [NSString stringWithFormat:@"%@",str]; } else { str = [NSString stringWithFormat:@"未识别成功"]; } NSLog(@"===> 识别结果 = %@", str); return str; } #pragma mark - Actions //返回按钮按钮点击事件 - (void)backAction { [self dismissViewControllerAnimated:YES completion:nil]; } //闪光灯按钮点击事件 - (void)modeBtn { if (![_device hasTorch]) { //NSLog(@"no torch"); } else { [_device lockForConfiguration:nil]; if (!_on) { [_device setTorchMode: AVCaptureTorchModeOn]; _on = YES; }else{ [_device setTorchMode: AVCaptureTorchModeOff]; _on = NO; } [_device unlockForConfiguration]; } } //上拉按钮点击事件 - (void)upBtn:(UIButton *)upBtn { UIButton *photoBtn = (UIButton *)[self.view viewWithTag:1000]; photoBtn.hidden = NO; upBtn.hidden = YES; } //拍照按钮点击事件 - (void)photoBtn { self.isProcessingImage = YES; //get connection AVCaptureConnection *videoConnection = nil; for (AVCaptureConnection *connection in _captureOutput.connections) { for (AVCaptureInputPort *port in [connection inputPorts]) { if ([[port mediaType] isEqual:AVMediaTypeVideo] ) { videoConnection = connection; break; } } if (videoConnection) { break; } } //get UIImage [_captureOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) { if (imageSampleBuffer != NULL) { //停止取景 [_session stopRunning]; NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer]; UIImage *tempImage = [[UIImage alloc] initWithData:imageData]; UIImage *temp_image = [Utility scaleAndRotateImageBackCamera:tempImage]; source_image = [Utility cvMatFromUIImage:temp_image]; NSString* text = [self simpleRecognition:source_image]; NSMutableDictionary* resultDict = [NSMutableDictionary new]; resultDict[@"image"] = temp_image; resultDict[@"text"] = text; [self performSelectorOnMainThread:@selector(readyToGetImage:) withObject:resultDict waitUntilDone:NO]; self.isProcessingImage = NO; } }]; } //从摄像头缓冲区获取图像 #pragma mark - AVCaptureSession delegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(imageBuffer,0); uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer); size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); size_t width = CVPixelBufferGetWidth(imageBuffer); size_t height = CVPixelBufferGetHeight(imageBuffer); /*We unlock the image buffer*/ CVPixelBufferUnlockBaseAddress(imageBuffer,0); /*Create a CGImageRef from the CVImageBufferRef*/ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); CGImageRef newImage = CGBitmapContextCreateImage(newContext); /*We release some components*/ CGContextRelease(newContext); CGColorSpaceRelease(colorSpace); /*We display the result on the image view (We need to change the orientation of the image so that the video is displayed correctly)*/ self.image = [UIImage imageWithCGImage:newImage scale:1.0 orientation:UIImageOrientationUp]; /*We relase the CGImageRef*/ CGImageRelease(newImage); //检边识别 if (_capture == YES) { //导航栏动画完成 if (self.isProcessingImage==NO) { //点击拍照后 不去识别 if (!self.adjustingFocus) { //反差对焦下 非正在对焦状态(相位对焦下self.adjustingFocus此值不会改变) if (_isLensChanged == _isIOS8AndFoucePixelLensPosition) { _count++; if (_count >= _MaxFR) { //识别 UIImage *temp_image = [Utility scaleAndRotateImageBackCamera:self.image]; source_image = [Utility cvMatFromUIImage:temp_image]; NSString* text = [self simpleRecognition:source_image]; if (text.length == 7) { //识别成功 _count = 0; // 停止取景 [_session stopRunning]; //设置震动 AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); NSMutableDictionary* resultDict = [NSMutableDictionary new]; resultDict[@"image"] = temp_image; resultDict[@"text"] = text; [self performSelectorOnMainThread:@selector(readyToGetImage:) withObject:resultDict waitUntilDone:NO]; } } } else { _isLensChanged = _isIOS8AndFoucePixelLensPosition; _count = 0; } } } } CVPixelBufferUnlockBaseAddress(imageBuffer,0); } //找边成功开始拍照 - (void)readyToGetImage:(NSDictionary *)resultDict { [self dismissViewControllerAnimated:NO completion:^{ }]; if (self.resultCB) { self.resultCB(resultDict[@"text"], resultDict[@"image"]); } } - (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context { /*反差对焦 监听反差对焦此*/ if([keyPath isEqualToString:@"adjustingFocus"]){ self.adjustingFocus =[[change objectForKey:NSKeyValueChangeNewKey] isEqualToNumber:[NSNumber numberWithInt:1]]; } /*监听相位对焦此*/ if([keyPath isEqualToString:@"lensPosition"]){ _isIOS8AndFoucePixelLensPosition =[[change objectForKey:NSKeyValueChangeNewKey] floatValue]; //NSLog(@"监听_isIOS8AndFoucePixelLensPosition == %f",_isIOS8AndFoucePixelLensPosition); } } @end