一、概述
机器码识别,也就是条码扫描。AVFoundation定义了多种欧冠条码符号进行实时识别的方法,前置或后置摄像头都可以。
真个流程同人脸识别大体相似,区别就是输入元数据格式不同,另外就是对于元数据的处理和视图处理不同。
只要掌握流程,再去做更多的定制就容易很多,首先要明白基本原理以及视频捕捉基本原理
二、创建项目
1、创建并配置会话
- 1、创建会话
self.captureSession = [[AVCaptureSession alloc] init];
self.captureSession.sessionPreset = AVCaptureSessionPreset640x480;
//设置捕捉会话预设类型,可以使用任意类型
//苹果建议使用最低合理解决方案以提高性能
- 2、设置会话输入
// 创建会话输入
AVCaptureDevice *videoDevice =
[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
AVCaptureDeviceInput *videoInput =
[AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:error];
if (videoInput) {
if ([self.captureSession canAddInput:videoInput]) {
[self.captureSession addInput:videoInput];
self.activeVideoInput = videoInput;
}
if (self.activeCamera.autoFocusRangeRestrictionSupported) { // 判断是否支持自动对焦
if ([self.activeCamera lockForConfiguration:error]) {
self.activeCamera.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;
//捕捉设备的自动对焦通常在任何距离都可以进行扫描
//不过大部分条码距离都不愿,所以可以缩小扫描区域来提升识别成功率
[self.activeCamera unlockForConfiguration];
}
}
}
- 3、设置会话输出
self.metadataOutput = [[AVCaptureMetadataOutput alloc] init];
if ([self.captureSession canAddOutput:self.metadataOutput]) {
[self.captureSession addOutput:self.metadataOutput];
dispatch_queue_t mainQueue = dispatch_get_main_queue();
[self.metadataOutput setMetadataObjectsDelegate:self queue:mainQueue];
NSArray *types = @[AVMetadataObjectTypeQRCode,
AVMetadataObjectTypeAztecCode,];
;
self.metadataOutput.metadataObjectTypes = types;
//设置元数据类型,这里这是感兴趣对象是QR码和Aztec码
}
- 4、实现代理
AVCaptureMetadataOutputObjectsDelegate
回调,当检测到条码数据时回调。
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection {
//处理元数据
[self didDetectCodes: metadataObjects];
}
2、处理元数据,并设置条码框
- 1、如果只是为了条码的值
- (void)didDetectCodes:(NSArray *)codes {
for (AVMetadataMachineReadableCodeObject *code in array) {
NSString *stringValue = code.stringValue;
//这个就是条形码的值
//不过一般一次只有一个值,或者直接取第一个元素即为条码值
}
//或者直接取第一个值
AVMetadataMachineReadableCodeObject * metadataObject = [codes objectAtIndex : 0 ];
NSString *codeString = metadataObject.stringValue;
}
- 2、标记出二维码所在位置
使用元数据的bounds
属性确定其位置,并将之标记出来。
- (void)didDetectCodes:(NSArray *)codes {
NSArray *array = [self transformedCodesFromCodes:codes];
NSMutableArray *lostCodes = [self.codeLayers.allKeys mutableCopy];
for (AVMetadataMachineReadableCodeObject *code in array) {
NSString *stringValue = code.stringValue;
//这个就是条形码的值
if (stringValue) {
[lostCodes removeObject:stringValue];
} else {
continue;//重新下一个循环
}
NSArray *layers = self.codeLayers[stringValue];
if (!layers) {
layers = @[[self makeBoundsLayer],[self makeCornersLayer]];
self.codeLayers[stringValue] = layers;
[self.previewLayer addSublayer:layers[0]];
[self.previewLayer addSublayer:layers[1]];
}
CAShapeLayer *boundsLayer = layers[0];
boundsLayer.path = [self bezierPathForBounds:code.bounds].CGPath;
//得到一个CGPathRef赋给图层的path属性
CAShapeLayer *cornersLayer = layers[1];
cornersLayer.path = [self bezierPathForCorners:code.corners].CGPath;
//对于cornersLayer,基于元数据对象创建一个CGPath
NSLog(@"String :%@",stringValue);
}
for (NSString *stringValue in lostCodes) {
for (CALayer *layer in self.codeLayers[stringValue]) {
[layer removeFromSuperlayer];
}
[self.codeLayers removeObjectForKey:stringValue];
}
}
- (NSArray *)transformedCodesFromCodes:(NSArray *)codes {
NSMutableArray *transformCodes = [NSMutableArray array];
for (AVMetadataObject *code in codes) {
AVMetadataObject *transformCode = [self.previewLayer transformedMetadataObjectForMetadataObject:code];
//将设备坐标元数据对象转换为视图坐标空间对象
[transformCodes addObject:transformCode];
}
return transformCodes;
}
- (UIBezierPath *)bezierPathForBounds:(CGRect)bounds {
// 图层边界,创建一个和对象的bounds关联的UIBezierPath
return [UIBezierPath bezierPathWithRect:bounds];
}
- (CAShapeLayer *)makeBoundsLayer {
//CAShapeLayer 是具体化的CALayer子类,用于绘制Bezier路径
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.strokeColor = [UIColor colorWithRed:0.96f green:0.75f blue:0.06f alpha:1.0f].CGColor;
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 4.0f;
return shapeLayer;
}
- (CAShapeLayer *)makeCornersLayer {
CAShapeLayer *cornersLayer = [CAShapeLayer layer];
cornersLayer.lineWidth = 2.0f;
cornersLayer.strokeColor = [UIColor colorWithRed:0.172 green:0.671 blue:0.428 alpha:1.0].CGColor;
cornersLayer.fillColor = [UIColor colorWithRed:0.190 green:0.753 blue:0.489 alpha:0.5].CGColor;
return cornersLayer;;
}
- (UIBezierPath *)bezierPathForCorners:(NSArray *)corners {
UIBezierPath *path = [UIBezierPath bezierPath];
for (int i = 0; i < corners.count; i ++) {
CGPoint point = [self pointForCorner:corners[i]];
//遍历每个条目,为每个条目创建一个CGPoint
if (i == 0) {
[path moveToPoint:point];
} else {
[path addLineToPoint:point];
}
}
[path closePath];
return path;
return nil;
}
- (CGPoint)pointForCorner:(NSDictionary *)corner {
//包含角点对象的字典具有x值的条目和y值的条目,可以手动取出这些值手动创建CGPoint
//Quartz框架提供方法实现此功能CGPointMakeWithDictionaryRepresentation
//兑换如字典和指针即可
CGPoint point;
CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)corner, &point);
return point;
}
网友评论