iOS AV Foundation 二维码扫描 03 为扫描到的二维码添加可视化效果

上一节,我们已经实现了二维码的扫描,这一节,我们利用元数据的bounds和corners实现一个可视化的扫描效果。

bounds定义了包含二维码图像的矩形,corners定义了二维码图像的实际坐标:技术分享技术分享

当摄像头和二维码图片完全对齐时,bounds和corners就是相同的。但是通常来说,几乎不可能让摄像头和二维码完全对齐。

打开ViewController.m,添加以下实例变量,用于存放所有检测到得二维码,以二维码的内容为索引。

NSMutableDictionary *_barcodes;
在viewDidLoad方法中初始化这个字典:

_barcodes = [NSMutableDictionary new];
定义一个Barcode类,用于存放已识别的二维码的元数据。

@interface Barcode : NSObject

@property (nonatomic, strong) AVMetadataMachineReadableCodeObject *metadataObject;
@property (nonatomic, strong) UIBezierPath *cornersPath;

@property (nonatomic, strong) UIBezierPath *boundingBoxPath;
@end

@implementation Barcode

@end
添加processMetadataObject :

- (Barcode *)processMetadataObject:(AVMetadataMachineReadableCodeObject *)code
{
    // 1
    Barcode *barcode = _barcodes[code.stringValue];
    
    // 2
    if (!barcode)
    {
        barcode = [Barcode new];
        _barcodes[code.stringValue] = barcode;
    }
    
    // 3
    barcode.metadataObject = code;
    
    // Create the path joining code's corners
    
    // 4
    CGMutablePathRef cornersPath = CGPathCreateMutable();
    
    // 5
    CGPoint point;
    CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)code.corners[0], &point);
    
    // 6
    CGPathMoveToPoint(cornersPath, nil, point.x, point.y);
    
    // 7
    for (int i = 1; i < code.corners.count; i++) {
        CGPointMakeWithDictionaryRepresentation((CFDictionaryRef)code.corners[i], &point);
        CGPathAddLineToPoint(cornersPath, nil, point.x, point.y);
    }
    
    // 8
    CGPathCloseSubpath(cornersPath);
    
    // 9
    barcode.cornersPath =[UIBezierPath bezierPathWithCGPath:cornersPath];
    CGPathRelease(cornersPath);
    
    // Create the path for the code's bounding box
    
    // 10
    barcode.boundingBoxPath = [UIBezierPath bezierPathWithRect:code.bounds];
    
    // 11
    return barcode;
}

  1.  查询Barcode对象字典,看是否有相同内容的Barcode已经存在。
  2.  如果没有,创建一个Barcode对象并将其加入到字典中。
  3.  存储二维码的元数据到新创建的Barcode对象中。
  4.  创建用于存储绘制二维码四个角路径的cornersPath。
  5.  使用CoreGraphics转换第一个角的坐标为CGPoint实例。
  6.  从第五步构造的角开始绘制路径。
  7.  循环遍历其它三个角,创建相应的路径。
  8.  绘制第四个点到第一个点路径后,关闭路径。
  9.  通过cornersPath创建UIBezierPath对象并将其存储到Barcode对象中。
  10.  通过bezierPathWithRect:方法创建边框块。
  11.  返回Barcode对象。

修改captureOutput:didOutputMetadataObjects:fromConnection方法:

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    // 1
    NSMutableSet *foundBarcodes = [NSMutableSet new];
    [metadataObjects enumerateObjectsUsingBlock: ^(AVMetadataObject *obj, NSUInteger idx, BOOL *stop) {
        NSLog(@"Metadata: %@", obj);
        // 2
        if ([obj isKindOfClass:[AVMetadataMachineReadableCodeObject class]])
        {
            // 3
            AVMetadataMachineReadableCodeObject *code = (AVMetadataMachineReadableCodeObject*)
            [_previewLayer transformedMetadataObjectForMetadataObject:obj];
            // 4
            Barcode *barcode = [self processMetadataObject:code];
            [foundBarcodes addObject:barcode];
        }
    }];
    dispatch_sync(dispatch_get_main_queue(), ^{
        // Remove all old layers
        // 5
        NSArray *allSublayers = [_previewView.layer.sublayers copy];
        [allSublayers enumerateObjectsUsingBlock: ^(CALayer *layer, NSUInteger idx, BOOL *stop) { if (layer != _previewLayer) {
            [layer removeFromSuperlayer];
        }
            
        }];
        // Add new layers
        // 6
        [foundBarcodes enumerateObjectsUsingBlock: ^(Barcode *barcode, BOOL *stop) {
            CAShapeLayer *boundingBoxLayer = [CAShapeLayer new]; boundingBoxLayer.path = barcode.boundingBoxPath.CGPath; boundingBoxLayer.lineWidth = 2.0f; boundingBoxLayer.strokeColor =
            [UIColor greenColor].CGColor; boundingBoxLayer.fillColor =
            [UIColor colorWithRed:0.0f green:1.0f blue:0.0f
                            alpha:0.5f].CGColor; [_previewView.layer addSublayer:boundingBoxLayer];
            CAShapeLayer *cornersPathLayer = [CAShapeLayer new]; cornersPathLayer.path = barcode.cornersPath.CGPath; cornersPathLayer.lineWidth = 2.0f; cornersPathLayer.strokeColor =
            [UIColor blueColor].CGColor; cornersPathLayer.fillColor =
            [UIColor colorWithRed:0.0f green:0.0f blue:1.0f
                            alpha:0.5f].CGColor; [_previewView.layer addSublayer:cornersPathLayer];
        }];
    });
}

  1. 创建用于遍历检测到的二维码的NSMutableSet。
  2. 处理类型为AVMetadataMachineReadableCodeObject的对象。
  3. 转换图像的bounds和corner坐标。将相对坐标转换为容器view的坐标。
  4. 处理二维码数据,将其加入到字典中。
  5. 移除预览view中的所有子层。
  6. 遍历所有检测到的二维码,为它们添加边界路径和角路径。这些layer有着不同的颜色,alpha值也被设置为0.5,这样我们可以透过叠加层看到原始二维码图片。

编译运行,效果如下:

技术分享

下一节,我们将为程序添加语音合成功能,自动朗读二维码的内容。

转载请注明出处:http://blog.csdn.net/yamingwu/article/details/44518051


郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。