一斗塘、概述
機器碼識別欣舵,也就是條碼掃描。AVFoundation定義了多種歐冠條碼符號進行實時識別的方法揭鳞,前置或后置攝像頭都可以炕贵。
真?zhèn)€流程同人臉識別大體相似,區(qū)別就是輸入元數(shù)據(jù)格式不同野崇,另外就是對于元數(shù)據(jù)的處理和視圖處理不同称开。
只要掌握流程,再去做更多的定制就容易很多乓梨,首先要明白基本原理以及視頻捕捉基本原理
二鳖轰、創(chuàng)建項目
1、創(chuàng)建并配置會話
- 1扶镀、創(chuàng)建會話
self.captureSession = [[AVCaptureSession alloc] init];
self.captureSession.sessionPreset = AVCaptureSessionPreset640x480;
//設置捕捉會話預設類型蕴侣,可以使用任意類型
//蘋果建議使用最低合理解決方案以提高性能
- 2、設置會話輸入
// 創(chuàng)建會話輸入
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;
//捕捉設備的自動對焦通常在任何距離都可以進行掃描
//不過大部分條碼距離都不愿狈惫,所以可以縮小掃描區(qū)域來提升識別成功率
[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;
//設置元數(shù)據(jù)類型,這里這是感興趣對象是QR碼和Aztec碼
}
- 4胧谈、實現(xiàn)代理
AVCaptureMetadataOutputObjectsDelegate
回調(diào)忆肾,當檢測到條碼數(shù)據(jù)時回調(diào)。
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputMetadataObjects:(NSArray *)metadataObjects
fromConnection:(AVCaptureConnection *)connection {
//處理元數(shù)據(jù)
[self didDetectCodes: metadataObjects];
}
2菱肖、處理元數(shù)據(jù)客冈,并設置條碼框
- 1、如果只是為了條碼的值
- (void)didDetectCodes:(NSArray *)codes {
for (AVMetadataMachineReadableCodeObject *code in array) {
NSString *stringValue = code.stringValue;
//這個就是條形碼的值
//不過一般一次只有一個值稳强,或者直接取第一個元素即為條碼值
}
//或者直接取第一個值
AVMetadataMachineReadableCodeObject * metadataObject = [codes objectAtIndex : 0 ];
NSString *codeString = metadataObject.stringValue;
}
- 2场仲、標記出二維碼所在位置
使用元數(shù)據(jù)的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;//重新下一個循環(huán)
}
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渠缕,基于元數(shù)據(jù)對象創(chuàng)建一個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];
//將設備坐標元數(shù)據(jù)對象轉(zhuǎn)換為視圖坐標空間對象
[transformCodes addObject:transformCode];
}
return transformCodes;
}
- (UIBezierPath *)bezierPathForBounds:(CGRect)bounds {
// 圖層邊界鸽素,創(chuàng)建一個和對象的bounds關(guān)聯(lián)的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]];
//遍歷每個條目亦鳞,為每個條目創(chuàng)建一個CGPoint
if (i == 0) {
[path moveToPoint:point];
} else {
[path addLineToPoint:point];
}
}
[path closePath];
return path;
return nil;
}
- (CGPoint)pointForCorner:(NSDictionary *)corner {
//包含角點對象的字典具有x值的條目和y值的條目馍忽,可以手動取出這些值手動創(chuàng)建CGPoint
//Quartz框架提供方法實現(xiàn)此功能CGPointMakeWithDictionaryRepresentation
//兌換如字典和指針即可
CGPoint point;
CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)corner, &point);
return point;
}