美文网首页程序员
AVFoundation开发秘籍笔记-07高级捕捉功能之机器码识

AVFoundation开发秘籍笔记-07高级捕捉功能之机器码识

作者: 竹与豆 | 来源:发表于2018-06-07 11:49 被阅读6次

    一、概述

    机器码识别,也就是条码扫描。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;
    
    }
    
    

    相关文章

      网友评论

        本文标题:AVFoundation开发秘籍笔记-07高级捕捉功能之机器码识

        本文链接:https://www.haomeiwen.com/subject/zwtgsftx.html