二维码技术

二维码技术

QRCode:quick Response Code
作用:将数据转化为图形,本质内容就是字符串.

ZXing和ZBar第三方框架.

官方框架:
二维码生成:CoreImage
二维码扫描:AVFoundation

目标:生成一张二维码图片(字符串->二维码)
目标:给二维码修改黑白颜色(主要色系差别要对应,不然无法扫描)

加个滤镜,CIFalseColor

  1. 导入CoreImage框架:提供了针对静态或视频的处理分析的相关功能.

  2. 重点:过滤器,滤镜(核心类:CIFiter)
    作用:将输入的图片进行处理,生成新的图片.二维码滤镜不用输入图片,通过字符串输入就可以了.

  3. 获取到可用到的滤镜名
    [CIFilter filterNames...]
    拿到这个kCICategoryBuiltIn键,指的是CoreIamge

  4. 创建并返回指定名字的滤镜
    所内键的滤镜(已经提供好的)
    看文档找滤镜名的意思(比较多)
    找到QRCode滤镜(二维码)

  5. 为滤镜配置参数

    1. 注意参数的配置是使用KVC来设置的,不是通过属性设置的.
      1. inputMessage(NSData)
      2. input修正程度(修改识别程度可以更高)
  6. 获取滤镜生成的图片
    outputImage属性.(类型CIImage)变为UIImage变得模糊了

图片大小
CIImage的属性content(查一下)才有size属性.
怎么对CIImage进行放大?(矢量方法,无损的)

二维码识别:只是识别三个方框内容.其他部分可以自动识别,只要不被挡住太多.

- (void)setupQRCode {
    // 1.获取到指定类中所有可能用到的滤镜名
    //kCICategoryBuiltIn 指的是 CoreImage 所内建的滤镜 (已经提供好的)
    NSArray *filters = [CIFilter filterNamesInCategory:kCICategoryBuiltIn];
    NSLog(@"%@",filters);
    
    // 2. 创建滤镜
    self.filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    // 3. 为滤镜配置参数
    //inputMessage,inputCorrectionLevel
    //字符串转数据
    NSData *data = [@"集美貌与才华于一身的女子---李欣" dataUsingEncoding:NSUTF8StringEncoding];
    // 设置二维码内容
    [self.filter setValue:data forKey:@"inputMessage"];
    // 二维码修正度
    [self.filter setValue:@"H" forKey:@"inputCorrectionLevel"];
    
    // 获取滤镜处理图片
    CIImage *outputImage = self.filter.outputImage;
    // 处理滤镜图片的无损放大
    outputImage = [outputImage imageByApplyingTransform:CGAffineTransformMakeScale(10, 10)];
    
    
    // 增加特殊效果
    //    NSDictionary *para = @{
    //                           @"inputImage" : outputImage,
    //                           @"inputColor0" : [CIColor colorWithRed:1.0 green:1.0 blue:1.0],
    //                           @"inputColor1" :[CIColor colorWithRed:0.3 green:0.8 blue:0.8 alpha:0.8] ,
    //                           };
    //
    //    CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor" withInputParameters:para];
    //
    //    outputImage = colorFilter.outputImage;
    //转化为UIimage
    UIImage *resultImage = [UIImage imageWithCIImage:outputImage];
    
    // 绘图
    resultImage = [resultImage imageWithUseIcon:[UIImage imageNamed:@"C4EA925D-9ED2-47CB-A49C-C1BA9A33B8F3"]];
    self.imageView.image = resultImage;
}


目标:在二维码图片中间添加图片,绘制成新的图片

图形上下文技术

  1. 开启图形上下文
  2. 画原图
  3. 画中间头像
  4. 从上下文中获取最终图片
  5. 关闭图形上下文.
UIImage分类中的方法:
- (UIImage *)imageWithUseIcon:(UIImage *)iconImage {
    // 绘图
    //1. 开启图形上下文
    UIGraphicsBeginImageContext(self.size);
    //2. 换原图
    [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
    //3. 画中间的图
    CGFloat wh = MIN(self.size.width, self.size.height) * 0.25;
    CGFloat x = (self.size.width/2 - wh/2);
    CGFloat y = (self.size.height - wh)*0.5;
    [iconImage drawInRect:CGRectMake(x, y, wh, wh)];
    
    //4.得到新图
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    
    //5. 关闭图形上下文
    UIGraphicsEndPDFContext();
    
    return image;
}
目标:二维码的图片识别

相关框架CoreImage.
相关类:CIDetector.(识别特定的功能)

  1. 创建一个特征检测器CIDetector(参数配置)
  2. 注意直接通过UIImage加载到的图片,直接获取.CIImage属性,是nil.原因:UIKit内容都是CoreGraphics,所以能转换CGImage,而CIImage表示的是图片的数据与UIImage没有直接的关系,查看文档发现可以转化从CGImage到CIImage.
  3. 通过URL加载得到
    ...整理一下
目标:二维码扫描器

原理:

  1. 摄像头和设备输入端会把摄像头的数据流出去(数据流)
  2. session会话:可以协调输入/输出
  3. 数据处理器(判断含不含二维码)
  4. 展示摄像头数据?为什么是摄像头直接展示session会话),只是个展示作用.

不理解点:哪里开始作为二维码的数据起点和终端(怎么判断的),内部会做出处理.


Snip20161030_2.png

代码部分:

  1. 输入端
    1. 用到AVFoundation框架,导入框架
    2. AVCaptureDevice(capture捕获)
    3. AVCaptureInput(绑定输入端)
  2. 会话端(关联中心点)
    1. AVCaptureSession(Session关键字会话)
    2. 配置输入端和输出端
      1. 添加输入端到会话端
      2. 添加输出端(后面做的)
    3. 启动会话(后面做的).
  3. 创建输出端
    1. AVCaptureOutput:子类AVCaptureMetdataOutput:表示解析特定数据类型的输出端.
    2. 配置(查看文档,属性)
      2.1. 要解析的类型metdataObjectType,这个字符串类型是限定的.打印一下输出类型.获取二维码类型
      2.2. 设置解析结果的数据回调(使用代理)
      2.3. 实现代理方法完成回调,返回数据数组.(多个),AVMetadataMachineReadableCodeObject基类类型(一维码,二维码),怎么知道使这个类型,文档,或者直接打印,就知道类型)
      2.4. 停止会员(因为已经扫到了一个二维码,需要关闭流)
      但是界面还是白的
    3. 添加输出端到会话端.
  4. 展示预览图层.(系统已经提供好了)
    1. AVCaptureVideoPreViewLayer(查文档有相关代码写法)
  5. 添加灰暗的遮盖,通过路径绘制形状.

补充:输出百分比值(只有一块可以扫),能够限定输出端解析数据的范围
rectOfInterest:

  1. 是百分比值
  2. 顺序不是x,y,height,width.和传统的不同.

错误提示,输入端和输出端失败.要真机.还是报错?你的访问摄像头用户隐私配置了么??Xcode8当中使用真机调试,配置了OS_ACTIVITY_MODE为disable时,NSLog无法输出.

封装二维码:
  1. 点击按钮弹出二维码弹框
  2. 封装到控制器中.

完善: 用layer来做(继续封装类,只留中心位置的rect)

  1. CAShapeLayer:用来绘制某些形状的图层
  2. 形状有路径来决定.(UIBezierPath贝塞尔曲线)
  3. .CGPath得到形状的路径.

代码部分:


- (void)viewDidLoad {
    [super viewDidLoad];
    // 输入端(设备绑定输入端)
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    NSError *error;
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
    if (error) {
        NSLog(@"创建输出端错误");
        return;
    }
    
    //会话端
    _session = [[AVCaptureSession alloc]init];
    
    [_session addInput:input];
    
    //输出端
    AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc]init];
    // 2. 首先判断输入端能够添加到会话端波
    if ([_session canAddOutput:output] == NO) {
        NSLog(@"输出端添加失败!");
        return;
    }
    [_session addOutput:output];
    
    // 3.1 设置输出端当前要解析的数据类型
    output.metadataObjectTypes = @[@"org.iso.QRCode"];
//  Sending 'ZLQRCodeScannerViewController *const __strong' to parameter of incompatible type 'id'
    // 3.2 设置解析结果的数据回调, 让回调方法在指定的队列当中执行
    [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
    
    // ========== 3.3 修改输出端的有效数据解析范围 ==========
    // 值是CGRect, 默认值是 {0, 0, 1, 1}, 都是百分比值
    // 假设在屏幕中间, 固定250 * 250
    CGFloat wh = 250;
    CGFloat width = wh / self.view.bounds.size.width;
    CGFloat height = wh / self.view.bounds.size.height;
    CGFloat x = (self.view.bounds.size.width - wh) * 0.5 / self.view.bounds.size.width;
    CGFloat y = (self.view.bounds.size.height - wh) * 0.5 / self.view.bounds.size.height;
    
    // 参数是 {y, x, height, width}
    output.rectOfInterest = CGRectMake(y, x, height, width);
    
    // ========== 4. 预览图层 ==========
    // 预览图层, 能将Capture捕获的数据展示到界面上
    AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_session];
    // 注意: 要配置frame值
    previewLayer.frame = self.view.bounds;
    // 添加预览图层
    [self.view.layer addSublayer:previewLayer];
    
    // ========== 添加遮盖 ==========
    //    [self addCover];
    CGFloat coverX = (self.view.bounds.size.width - wh) * 0.5;
    CGFloat coverY = (self.view.bounds.size.height - wh) * 0.5;
    [self.view.layer coverButRect:CGRectMake(coverX, coverY, wh, wh)];
    
    // ========== 5. 启动会话 ==========
    // 开始运行!
    [_session startRunning];
}
/// 使用图层的CAShapeLayer子类,进行绘制,通过路径决定
- (void)addCover
{
    // 用来绘制某些形状的图层, 由路径来决定
    CAShapeLayer *topLayer = [[CAShapeLayer alloc] init];
    // 配置矩形路径
    UIBezierPath *rect = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, self.view.bounds.size.width, 200)];
    topLayer.path = rect.CGPath;
    
    // 配置填充的颜色和透明度
    topLayer.fillColor = [UIColor lightGrayColor].CGColor;
    topLayer.opacity = 0.6;
    
    [self.view.layer addSublayer:topLayer];
}


#pragma mark - AVCaptureMetadataOutputObjectsDelegate

/**
 输出端捕获到指定的数据类型时触发
 */
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    // metadataObjects: 保存了解析结果的数组
    // AVMetadataObject: 表示结果的类型, 是基类
    
    // 是一个具体的结果类型, 用来表示机器可识别编码(一维码, 二维码等等)
    //    AVMetadataMachineReadableCodeObject
    
    //    NSLog(@"%@", metadataObjects);
    //    printf("%s", [metadataObjects.description cStringUsingEncoding:NSUTF8StringEncoding]);
    
    for (AVMetadataMachineReadableCodeObject *object in metadataObjects) {
        
        printf("%s", [object.stringValue cStringUsingEncoding:NSUTF8StringEncoding]);
    }
    
    // 让会话停止
    [_session stopRunning];
}


CALayer分类中布局的方法,得到中间方形
- (void)coverButRect:(CGRect)rect
{
    // Rect为保留的矩形frame值
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    CGFloat leftWidth = (screenWidth - rect.size.width) / 2;
    
    CAShapeLayer* layerTop   = [[CAShapeLayer alloc] init];
    layerTop.fillColor       = [UIColor blackColor].CGColor;
    layerTop.opacity         = 0.5;
    layerTop.path            = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, screenWidth, rect.origin.y)].CGPath;
    [self addSublayer:layerTop];
    
    CAShapeLayer* layerLeft   = [[CAShapeLayer alloc] init];
    layerLeft.fillColor       = [UIColor blackColor].CGColor;
    layerLeft.opacity         = 0.5;
    layerLeft.path            = [UIBezierPath bezierPathWithRect:CGRectMake(0, rect.origin.y, leftWidth, rect.size.height)].CGPath;
    [self addSublayer:layerLeft];
    
    CAShapeLayer* layerRight   = [[CAShapeLayer alloc] init];
    layerRight.fillColor       = [UIColor blackColor].CGColor;
    layerRight.opacity         = 0.5;
    layerRight.path            = [UIBezierPath bezierPathWithRect:CGRectMake(screenWidth - leftWidth, rect.origin.y, rect.size.width, rect.size.height)].CGPath;
    [self addSublayer:layerRight];
    
    CAShapeLayer* layerBottom   = [[CAShapeLayer alloc] init];
    layerBottom.fillColor       = [UIColor blackColor].CGColor;
    layerBottom.opacity         = 0.5;
    layerBottom.path            = [UIBezierPath bezierPathWithRect:CGRectMake(0, CGRectGetMaxY(rect), screenWidth, screenHeight - CGRectGetMaxY(rect))].CGPath;
    [self addSublayer:layerBottom];
    
}

你可能感兴趣的:(二维码技术)