精华内容
下载资源
问答
  • 二维码手画
    千次阅读
    2017-08-17 08:47:02

    #一、iOS实现原生扫码的意义

    二维码扫码功能对于现在的iOS App开发来说是非常重要的。
    通常为了节省开发时间,很多开发者会采用ZXing和ZBar等第三方SDK进行开发。
    这样的好处是快速便捷,但是缺点也是在于如果要自定义一部分功能,可能对源码进行第二次开发来说比较辛苦。
    如果采用的其他开发者提供的进一步封装的扫码功能,又有可能因为编码风格或者缺少注释变得晦涩难懂。
    所以掌握原生二维码扫码是非常重要的,优点也是非常明显:
    1、扫码识别效率高
    2、使用灵活,自定义UI方便
    3、便于维护


    #二、核心功能介绍

    ##1、扫码功能:

    原生扫码的功能主要采用的是AVFoundation类库下的AVCaptureMetadataOutput,这个输出类可以实现对二维码,条形码等等二维码的的直接识别。
    需要注意的是,如果想用开发Mac版本的二维码扫码功能, AVCaptureMetadataOutput是不能实现的,这个类只支持iOS的扫码功能。
    如果想要实现Mac上的二维码扫码功能可以参考我的另一篇文章:《基于MacOSX平台下的二维码扫码功能》
    链接地址:http://blog.csdn.net/super_dl_csdn/article/details/76460745
    具体的其他注意事项和细节,将会在后续的代码部分进行解释。
    ##2、蒙版功能:
    对于二维码扫码来说,实现一个黑色的半透明蒙版,并且限定扫描区域是十分有必要的。
    这对于引导用户正确的扫码,提高用户体验度和扫码效率是非常有用的。
    但是这个功能并非核心功能,而且需要配合后面的限定扫描区域进一步使用,如果并非必要,可以不作为主要的功能点。
    ##3、限定扫描区域和动画功能:
    如果不限行扫码的区域,对于启动扫码后的视频采集将会以全部屏幕的形势展现出来,这是不合理的。
    限定一个扫描区域也有利于用户有目的将二维码放置于合适的位置,也有利于提高识别的效率。
    添加扫码动画和适当的进行UI修饰,可以提高用户体验度,同时可是实时告知用户扫码功能是否启用。


    #三、项目核心代码实现

    整个代码分为两个大类:视图控制器部分代码和扫码类功能代码。具体的源码将会在后续给出链接,项目的结构如下:

    • ViewController:利用StoryBoard生成的根视图控制器,用于跳转扫码界面。
    • QRScanForIOS:二维码扫码类包,用于二维码扫码功能的实现,蒙版图层和扫描动画的封装。
    • QRManager:二维码扫码功能类,利用AVFoundation实现原生扫码功能
    • QMaskView:创建半透明的黑色蒙版视图
    • QRScanAnimationView:二维码扫码限制区域的界面设置和动画封装
    • QRScanForIOSHeader:类包头文件,包含该头文件即可使用相关功能
    • QRViewController:二维码扫码视图控制器,用于展现扫码功能界面

    ##1、扫码功能实现

    1)扫码头文件功能的实现:

    //返回类型的block
    typedef void(^finishBlock)(BOOL finish,NSError * error);
    
    /**
     初始化扫码Manange
    
     @param delegate 代理
     @param block 返回Block
     */
    - (void)initQrManagerWithDelegateL:(id)delegate
                       finishInitBlock:(finishBlock)block;
    
    
    /**
     设置扫码区域的相关参数
    
     @param supView 父视图
     @param viewFrame 扫码区域的大小
     */
    - (void)setPreviewLayerWithSupview:(UIView *)supView
                         withViewFrame:(CGRect)viewFrame;
    
    //开始扫描
    - (void)startScan;
    
    //停止扫描
    - (void)stopScan;
    
    
    1. 扫码功能实例方法的实现:
    
    @interface QRManager ()
    
    @property (nonatomic,strong) AVCaptureSession *session;
    @property (nonatomic,strong) AVCaptureMetadataOutput *metadataOutput;
    @end
    
    /**
     初始化扫码Manange
     
     @param delegate 代理
     @param block 返回Block
     */
    - (void)initQrManagerWithDelegateL:(id)delegate
                       finishInitBlock:(finishBlock)block
    {
    	//创建上下文启动器
        _session = [[AVCaptureSession alloc] init];
        //设置Device
        AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        NSError *error;
        AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
        if (deviceInput) {
    	    //理论上这里应该添加一个判断看看能不能添加input
            [_session addInput:deviceInput];
            //创建一个输出类型
            self.metadataOutput = [[AVCaptureMetadataOutput alloc] init];
            //设置代理,扫描结果将会利用代理返回
            [self.metadataOutput setMetadataObjectsDelegate:delegate queue:dispatch_get_main_queue()];
            // 这行代码要在设置 metadataObjectTypes 前
            [_session addOutput:self.metadataOutput]; 
            // 设置了识别类型为QRCode,枚举还有其他乐行可以进一步添加
            self.metadataOutput.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];
            //设置成功了返回YES
            block(YES,nil);
            
        }
        else
        {
            NSLog(@"%@", error);
            //失败,打印Error
            block(NO,error);
        }
    
    }
    /**
     设置扫码区域的相关参数
     
     @param supView 父视图
     @param viewFrame 扫码区域的大小
     */
    - (void)setPreviewLayerWithSupview:(UIView *)supView
                         withViewFrame:(CGRect)viewFrame
    {
    	//设置扫码区域
        AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_session];
        previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
        //默认的是全部的界面,这样整个背景都是摄像头信息
        previewLayer.frame = CGRectMake(0, 84, supView.frame.size.width, supView.frame.size.height-84);
        [supView.layer insertSublayer:previewLayer atIndex:0];
        
        //重点1:采集到的摄像头很多情况是顺时针旋转90度的,造成这个的原因可能是AVFoundation本身的原因,需要进行下面步骤的修正:
        //1、获取到AVCaptureVideoPreviewLayer的方向并进行修正
        AVCaptureConnection *previewLayerConnection=previewLayer.connection;
        //2、判断并修正
        if ([previewLayerConnection isVideoOrientationSupported])
        {
            [previewLayerConnection setVideoOrientation:[[UIApplication sharedApplication] statusBarOrientation]];
        }
        
        //重点2:全屏幕扫码不是我们需要想要的功能,我们需要限制扫码的范围就要使用rectOfInterest。
        //然而这个属性的frame是一个比例系数,范围是[0,1];很多人采用试的方式是不合理的,利用metadataOutputRectOfInterestForRect方法可以成功的将AVCaptureVideoPreviewLayer所在的父视图中的Frame转换成对应的rectOfInterest的frame。
    
    	//1、将传进来的需要限制区域的viewFrame转换成AVCaptureVideoPreviewLayer需要的Frame
        CGRect intertRect = [previewLayer metadataOutputRectOfInterestForRect:viewFrame];
        //2、设置限定区域
        self.metadataOutput.rectOfInterest = intertRect;
        
    }
    
    - (void)startScan
    {
        [self.session startRunning];
    }
    
    - (void)stopScan
    {
        [self.session stopRunning];
    }
    
    

    ##2、蒙版的制作

    蒙版的制作本项目采用的是创建一个继承自UIView的子类,然后利用贝塞尔曲线实现,头文件的内容如下:

    **
     根据蒙版的大小,视图扫码区域的大小创建蒙版
    
     @param maskFrame 蒙版在父视图中的大小
     @param scanFrame 扫码区域的大小
     @return 返回蒙版View
     */
    - (instancetype)initMaskViewWithFrame:(CGRect)maskFrame
                            withScanFrame:(CGRect)scanFrame;
    

    view的.m文件中实现如下:

    /**
     根据蒙版的大小,视图扫码区域的大小创建蒙版
     
     @param maskFrame 蒙版在父视图中的大小
     @param scanFrame 扫码区域的大小
     @return 返回蒙版View
     */
    - (instancetype)initMaskViewWithFrame:(CGRect)maskFrame
                            withScanFrame:(CGRect)scanFrame
    {
        self = [super initWithFrame:maskFrame];
        if (self) {
            self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
            
            UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
            
            [maskPath appendPath:[[UIBezierPath bezierPathWithRoundedRect:scanFrame cornerRadius:1] bezierPathByReversingPath]];
            
            CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
            
            maskLayer.path = maskPath.CGPath;
            
            self.layer.mask = maskLayer;
        }
        return self;
    }
    

    ##3、限制框的设置和动画制作

    这里仅仅是举了一个例子,采用一个定时器去执行动画,当然有更好的方法。
    头文件的部分代码如下:

    /**
     开始动画
     */
    - (void)startAnimation;
    
    
    /**
     结束动画
     */
    - (void)stopAnimation;
    

    动画类的.m文件实现如下:

    @interface QRScanAnimationView ()
    {
        UIImageView * line_imageView_ ;
        NSTimer * animation_timer_;
        int addOrCut_;
    }
    
    @end
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
    		//使用CGContextRef重写drawRect方法会产生一个默认的黑色的北京,需要在初始化方法中提前设置为clearcolor
            [self setBackgroundColor:[UIColor clearColor]];
            
            //线移动的imageView
            line_imageView_=[[UIImageView alloc]initWithImage:[UIImage imageNamed:@"qrcode_Scan_weixin_Line@2x.png"]];
            [self addSubview:line_imageView_];
            
            //初始位置为当前视图距离顶部的四分之一处
            [line_imageView_ setFrame:CGRectMake(0, self.bounds.size.height/4, self.bounds.size.width, 20)];
        }
        return self;
    }
    
    // 覆盖drawRect方法,你可以在此自定义绘画和动画
    - (void)drawRect:(CGRect)rect
    {
        //An opaque type that represents a Quartz 2D drawing environment.
        //一个不透明类型的Quartz 2D绘画环境,相当于一个画布,你可以在上面任意绘画
        CGFloat weight_ = self.frame.size.width;        //视图宽度
        CGFloat height_ = self.frame.size.height;       //视图高度
        
        CGFloat view_height_ = 10;
        CGFloat view_weight_ = 10;                      //纵向线段宽度
        
        CGFloat view_long_ = weight_/10;                //线段长度
        
    
    
        CGContextRef context = UIGraphicsGetCurrentContext();
        
       
        CGContextSetRGBFillColor (context,  0, 1, 0, 1.0);//设置填充颜色
        
        
        CGContextSetRGBStrokeColor(context,0, 1, 0, 1.0);//画笔线的颜色
        
        CGContextSetLineWidth(context, view_height_);
    
    
        
        
        //上,左,顶
    
        CGPoint aPoints[2];//坐标点
        aPoints[0] =CGPointMake(0, view_height_/2);//坐标1
        aPoints[1] =CGPointMake(view_long_, view_height_/2);//坐标2
        
        CGContextAddLines(context, aPoints, 2);//添加线
        CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
        
        //上,左,左
    
        aPoints[0] =CGPointMake(view_weight_/2, 0);//坐标1
        aPoints[1] =CGPointMake(view_weight_/2 , view_long_);//坐标2
        
        CGContextAddLines(context, aPoints, 2);//添加线
        CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
    
        //上,右,顶
        
        aPoints[0] =CGPointMake(weight_-view_long_,view_height_/2);//坐标1
        aPoints[1] =CGPointMake(weight_, view_height_/2);//坐标2
        
        CGContextAddLines(context, aPoints, 2);//添加线
        CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
    
        //上,右,右
     
        aPoints[0] =CGPointMake(weight_-view_weight_/2, 0);//坐标1
        aPoints[1] =CGPointMake(weight_-view_weight_/2 , view_long_);//坐标2
        
        CGContextAddLines(context, aPoints, 2);//添加线
        CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
    
        //下,左,左
        
        aPoints[0] =CGPointMake(view_weight_/2, height_-view_long_);//坐标1
        aPoints[1] =CGPointMake(view_weight_/2 , height_);//坐标2
        
        CGContextAddLines(context, aPoints, 2);//添加线
        CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
    
        //下,左,底
    
        aPoints[0] =CGPointMake(0, height_-view_height_/2);//坐标1
        aPoints[1] =CGPointMake(view_long_ , height_-view_height_/2);//坐标2
        
        CGContextAddLines(context, aPoints, 2);//添加线
        CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
        
        //下,右,右
        
        aPoints[0] =CGPointMake(weight_-view_weight_/2, height_-view_long_);//坐标1
        aPoints[1] =CGPointMake(weight_-view_weight_/2 , height_);//坐标2
        
        CGContextAddLines(context, aPoints, 2);//添加线
        CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
    
        //下,右,底
    
        aPoints[0] =CGPointMake(weight_-view_long_, height_-view_height_/2);//坐标1
        aPoints[1] =CGPointMake(weight_ , height_-view_height_/2);//坐标2
        
        CGContextAddLines(context, aPoints, 2);//添加线
        CGContextDrawPath(context, kCGPathStroke); //根据坐标绘制路径
    }
    
    /**
     开始动画
     */
    - (void)startAnimation
    {
        if (animation_timer_) {
            [animation_timer_ invalidate];
        }
     
    	//创建一个定时器,这种创建方式需要手动将timer放到runloop中
        animation_timer_=[NSTimer timerWithTimeInterval:0.01 repeats:YES block:^(NSTimer * _Nonnull timer) {
            
            if (line_imageView_.frame.origin.y>=self.frame.size.height*3/4) {
                addOrCut_=-1;
            }
            else if (line_imageView_.frame.origin.y<=self.frame.size.height/4)
            {
                addOrCut_=1;
            }
    
            [line_imageView_ setFrame:CGRectMake(line_imageView_.frame.origin.x, line_imageView_.frame.origin.y+addOrCut_, line_imageView_.frame.size.width, line_imageView_.frame.size.height)];
        }];
        
        [[NSRunLoop mainRunLoop]addTimer:animation_timer_ forMode:NSDefaultRunLoopMode];
    
        
    }
    - (void)stopAnimation
    {
        [animation_timer_ invalidate];
    }
    

    ##4、QRViewController中的设置和调用

    在需要调用扫码功能的界面中,可以进行一下设置就可以实现扫码功能。

    //这里采用了Masonry自动布局
    #import "Masonry.h"
    
    @interface QRViewController ()
    {
        CGRect scan_frame_;
        QRManager * qr_scan_manager_; //扫码控制中心                      
        QRMaskView * qr_mask_view_;  //顶部蒙版视图                       
        QRScanAnimationView * qr_scan_animation_view_; //扫码动画视图     
        
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.view.backgroundColor=[UIColor whiteColor];
    	//首先创建限制区域的大小和frame,如果需要修改限制区域的位置,只需要修改此处的frame即可
        qr_scan_animation_view_=[[QRScanAnimationView alloc]initWithFrame:                     CGRectMake(self.view.center.x-self.view.frame.size.height/4,                                    self.view.center.y-self.view.frame.size.height/4,                                    self.view.frame.size.height/2,                                      self.view.frame.size.height/2)];
        [self.view addSubview:qr_scan_animation_view_];
        
        //这个是用来设置扫描区域的frame的,这个frame需要注意的是,必须是AVCaptureVideoPreviewLayer所在的layer上的frame,在当前软件中,因为顶部有一个topview,所以应该要在原有的self.view.center.y的基础上上移20+64个像素
        
        scan_frame_ = CGRectMake(qr_scan_animation_view_.frame.origin.x,
    qr_scan_animation_view_.frame.origin.y-84,
    qr_scan_animation_view_.bounds.size.height,
    qr_scan_animation_view_.bounds.size.height) ;
        
        //创建蒙版的View
        qr_mask_view_=[[QRMaskView alloc]initMaskViewWithFrame:CGRectMake(0, 84, self.view.frame.size.width, self.view.frame.size.height - 84) withScanFrame:scan_frame_];
        
        [self.view addSubview:qr_mask_view_];
        
       
    	//初始化二维码扫码功能模块单利
        qr_scan_manager_ = [QRManager sharedManager];
        //设置相关参数
        [qr_scan_manager_ initQrManagerWithDelegateL:self finishInitBlock:^(BOOL finish, NSError *error) {
            if (finish) {
                //开启成功,开始设置扫码参数并开始进行扫码
                [qr_scan_manager_ setPreviewLayerWithSupview:self.view withViewFrame:scan_frame_];
            }
            else
            {
                NSString * qr_error_string_  =[NSString stringWithFormat:@"错误信息:%@",error];
                
                UIAlertController * alertController=[UIAlertController alertControllerWithTitle:@"扫描启动失败!" message:qr_error_string_ preferredStyle:UIAlertControllerStyleAlert];
                
                UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
                
                [alertController addAction:cancelAction];
                
                [self presentViewController:alertController animated:YES completion:nil];
            }
        }];
    }
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        
       
        //界面进入后需要开始进行扫码
        [qr_scan_manager_ startScan];
        //开启动画
        [qr_scan_animation_view_ startAnimation];
        
    
    }
    - (void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear: animated];
        //界面退出前停止扫码
        [qr_scan_manager_ stopScan];
        //停止动画
        [qr_scan_animation_view_ stopAnimation];
    }
    pragma mark - AVCaptureMetadataOutputObjectsDelegate
    - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
    {
        // id 类型不能点语法,所以要先去取出数组中对象
        AVMetadataMachineReadableCodeObject *object = [metadataObjects lastObject];
        
        if (object == nil) return;
        
        if ([object.type isEqualToString:AVMetadataObjectTypeQRCode] )
        {
            NSLog(@"得到的qr字符串为:%@",object.stringValue);
            //进一步的操作
            
            // .....
        }
    }
    

    #四、总结

    以上是基本的程序设计思路,下面附上代码的链接。
    代码可以直接下载下来使用。
    https://e.coding.net/SupDongLei/QRScanForIOS.git

    PS:感觉自己和二维码比较有缘2333333(手动滑稽)

    更多相关内容
  • 手绘二维码教程

    千次阅读 2021-05-19 15:46:18
    手绘二维码教程 先用软件生成二维码。注意是带网格的。然后绘制网格,最小宽度推荐3mm,太小了,就不太好操作。也可以大一点,看你贺卡的大小以及二维码的大小 第一步: 绘制网格,网格最小推荐3mm​​​​​​ ...

    手绘二维码教程

    先用软件生成二维码。注意是带网格的。然后绘制网格,最小宽度推荐3mm,太小了,就不太好操作。也可以大一点,看你贺卡的大小以及二维码的大小

    第一步:

    绘制网格,网格最小推荐3mm​​​​​​

    第二步:

    在网格中,粗略的标记需要抹黑的点

    第三步:

    逐个抹黑被标记的点。

    第四部根据之前的网格,再描一次网格线,这一步是为了让之前抹黑的点,更方。

     

     

    二维码内容:| 🌸 | (gitee.io)

    获取源码,请联系我

     

    小程序软件:弄个锤子

    小程序:C0109小程序初0109小程序初0109

     

    展开全文
  • iOS二维码限制区域识别、扫描动画

    千次阅读 2019-03-28 17:56:05
    AVCaptureDevice //初始化捕捉设备 AVCaptureDeviceInput //创建输入流 input AVCaptureSession //创建...AVCaptureMetadataOutput //输出 二维码 AVCaptureVideoDataOutput //输出 视频流(随着屏幕刷新返回一...

    AVCaptureDevice //初始化捕捉设备

    AVCaptureDeviceInput //创建输入流 input

    AVCaptureSession //创建会话

    AVCaptureVideoPreviewLayer //预览图层

    AVCaptureMetadataOutput //输出 二维码

    AVCaptureVideoDataOutput //输出 视频流(随着屏幕刷新返回一个图片很快相当于视频)

    session 关联设备、输入、输出、预览

    效果图

    直接上项目地址吧:现在csdn积分无法控制,直接把代码复制,可以直接复制走或者github下载即可

    csdn:https://download.csdn.net/download/wangxiaoertedaye/11069772

    gitHub:https://github.com/zxgiOS/erweima
    代码:图片只能上下载代码库里有,或让美工制作

    //
    //  ScanCodeViewController.m
    //  Productproject
    //
    //  Created by apple on 17/10/26.
    //  Copyright © 2017年. All rights reserved.
    //
    
    #import "ScanCodeViewController.h"
    
    #define kDeviceVersion [[UIDevice currentDevice].systemVersion floatValue]
    
    #define kScreenWidth  [UIScreen mainScreen].bounds.size.width
    #define kScreenHeight [UIScreen mainScreen].bounds.size.height
    #define kNavbarHeight ((kDeviceVersion>=7.0)? 64 :44 )
    
    #define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
    #define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
    
    #define kSCREEN_MAX_LENGTH (MAX(kScreenWidth, kScreenHeight))
    #define kSCREEN_MIN_LENGTH (MIN(kScreenWidth, kScreenHeight))
    
    #define IS_IPHONE4 (IS_IPHONE && kSCREEN_MAX_LENGTH < 568.0)
    #define IS_IPHONE5 (IS_IPHONE && kSCREEN_MAX_LENGTH == 568.0)
    #define IS_IPHONE6 (IS_IPHONE && kSCREEN_MAX_LENGTH == 667.0)
    #define IS_IPHONE6P (IS_IPHONE && kSCREEN_MAX_LENGTH == 736.0)
    
    @import AVFoundation;
    
    @interface ScanCodeViewController ()<AVCaptureMetadataOutputObjectsDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>{
        UILabel * introLab;
        BOOL isLightOn;
        UIButton *mineQRCode;
        UIButton *theLightBtn;
        BOOL hasTheVC;
        BOOL isFirst;
        BOOL upOrdown;
        int num;
        AVCaptureVideoPreviewLayer *preView;
        AVCaptureDevice *captureDevice;
        NSTimer * timer;
        
    }
    
    @property (nonatomic,strong) AVCaptureSession *session;
    @property (nonatomic,weak) AVCaptureMetadataOutput *output;
    @property (nonatomic,retain) UIImageView *lineIV;
    
    @end
    
    @implementation ScanCodeViewController
    
    -(void)initUI{
        isFirst=YES;
        upOrdown = NO;
        num =0;
    }
    - (void)startSessionRightNow:(NSNotification*)notification {
        [self creatTimer];
        [_session startRunning];
    }
    - (void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        if(isFirst)
        {
            [self creatTimer];
            [_session startRunning];
        }
        isFirst=NO;
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
        [self deleteTimer];
        [[NSNotificationCenter defaultCenter]removeObserver:self name:@"startSession" object:nil];
    }
    
    - (void)viewDidDisappear:(BOOL)animated {
        [super viewDidDisappear:animated];
    }
    #pragma mark - 删除timer
    - (void)deleteTimer
    {
        if (timer) {
            [timer invalidate];
            timer=nil;
        }
    }
    #pragma mark - 创建timer
    - (void)creatTimer
    {
        if (!timer) {
            timer=[NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(animation) userInfo:nil repeats:YES];
        }
    }
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        [[NSNotificationCenter defaultCenter]  addObserver:self selector:@selector(startSessionRightNow:) name:@"startSession" object:nil];
        if (!isFirst) {
            [self creatTimer];
            [_session startRunning];
        }
    }
    - (void)viewDidLoad {
        self.navigationItem.title = @"扫一扫";
        
        self.view.backgroundColor = [UIColor colorWithWhite:0.2 alpha:1];
        [super viewDidLoad];
        
        [self initUI];
        [self setupDevice];
    }
    -(void)setupDevice{
        //1.初始化捕捉设备(AVCaptureDevice),类型为AVMediaTypeVideo
        captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        NSError *error;
        //2.用captureDevice创建输入流input
        AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
        if (!input) {
            NSLog(@"%@", [error localizedDescription]);
            return ;
        }
        
        //创建会话
        _session = [[AVCaptureSession alloc] init];
        [_session setSessionPreset:AVCaptureSessionPresetHigh];
        
        if ([_session canAddInput:input]) {
            [_session addInput:input];
        }
        
        //预览视图
        preView = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_session];
        //设置预览图层填充方式
        [preView setVideoGravity:AVLayerVideoGravityResizeAspectFill];
        [preView setFrame:self.view.layer.bounds];
        
        [self.view.layer addSublayer:preView];
        
        
        //输出
        AVCaptureMetadataOutput *output = [[AVCaptureMetadataOutput alloc] init];
        [output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
        if ([_session canAddOutput:output]) {
            [_session addOutput:output];
        }
        self.output = output;
        //设置扫描范围
        output.rectOfInterest = CGRectMake(0.25,(kScreenWidth - self.view.layer.bounds.size.width * 0.7)/2/self.view.layer.bounds.size.width,  self.view.layer.bounds.size.width * 0.7/self.view.layer.bounds.size.height,(self.view.layer.bounds.size.width * 0.7)/self.view.layer.bounds.size.width);
        
        NSArray *arrTypes = output.availableMetadataObjectTypes;
        NSLog(@"%@",arrTypes);
        
        if ([_output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeQRCode] || [_output.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeCode128Code]) {
            _output.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];
            // [_session startRunning];
        } else {
            [_session stopRunning];
            //        rightButton.enabled = NO;
            UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"抱歉!" message:@"相机权限被拒绝,请前往设置-隐私-相机启用此应用的相机权限。" delegate:self cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
            [alert show];
            return;
        }
        UIView *drawView = [[UIView alloc]initWithFrame:self.view.bounds];
        drawView.backgroundColor = [UIColor blackColor];
        drawView.alpha = 0.5;
        [self.view addSubview:drawView];
        //选定一块区域,设置不同的透明度
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0,  self.view.bounds.size.width,  self.view.bounds.size.height)];
        [path appendPath:[[UIBezierPath bezierPathWithRoundedRect:CGRectMake((kScreenWidth - self.view.layer.bounds.size.width * 0.7)/2, self.view.layer.bounds.size.height * 0.25, self.view.layer.bounds.size.width * 0.7,self.view.layer.bounds.size.width * 0.7) cornerRadius:0] bezierPathByReversingPath]];
        CAShapeLayer *shapeLayer = [CAShapeLayer layer];
        shapeLayer.path = path.CGPath;
        [drawView.layer setMask:shapeLayer];
        UIImageView *codeFrame = [[UIImageView alloc] initWithFrame:CGRectMake((kScreenWidth - self.view.layer.bounds.size.width * 0.7)/2, self.view.layer.bounds.size.height * 0.25,  self.view.layer.bounds.size.width * 0.7, self.view.layer.bounds.size.width * 0.7)];
        codeFrame.contentMode = UIViewContentModeScaleAspectFit;
        //NSString *name = [@"Resource.bundle" stringByAppendingPathComponent:@"codeframe"];
        
        [codeFrame setImage:[UIImage imageNamed:@"codeframe"]];
        [self.view addSubview:codeFrame];
        
        introLab = [[UILabel alloc] initWithFrame:CGRectMake(preView.frame.origin.x, preView.frame.origin.y + preView.frame.size.height, preView.frame.size.width, 40)];
        introLab.numberOfLines = 1;
        introLab.textAlignment = NSTextAlignmentCenter;
        introLab.textColor = [UIColor whiteColor];
        introLab.adjustsFontSizeToFitWidth = YES;
        introLab.text = @"将二维码/条码放入框内,即可自动扫描";
        [self.view addSubview:introLab];
        
        //我的二维码按钮
        mineQRCode = [UIButton buttonWithType:UIButtonTypeCustom];
        mineQRCode.frame = CGRectMake(self.view.frame.size.width / 2 - 100 / 2, introLab.frame.origin.y+introLab.frame.size.height - 5, 100, introLab.frame.size.height);
        [mineQRCode setTitle:@"我的二维码" forState:UIControlStateNormal];
        [mineQRCode setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [mineQRCode addTarget:self action:@selector(showTheQRCodeOfMine:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:mineQRCode];
        mineQRCode.hidden = YES;
        
        //theLightBtn
        theLightBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        
        theLightBtn.frame = CGRectMake(self.view.frame.size.width / 2 - 100 / 2, mineQRCode.frame.origin.y + mineQRCode.frame.size.height + 20, 100, introLab.frame.size.height);
        
        [theLightBtn setImage:[UIImage imageNamed:@"light"] forState:UIControlStateNormal];
        [theLightBtn setImage:[UIImage imageNamed:@"lighton"] forState:UIControlStateSelected];
        [theLightBtn addTarget:self action:@selector(lightOnOrOff:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:theLightBtn];
        
        if (![captureDevice isTorchAvailable]) {
            theLightBtn.hidden = YES;
        }
        // Start
        _lineIV = [[UIImageView alloc] initWithFrame:CGRectMake((kScreenWidth - self.view.layer.bounds.size.width * 0.7)/2,self.view.layer.bounds.size.height * 0.25 , self.view.layer.bounds.size.width * 0.7, 5)];
        //NSString *lineName = [@"Resource.bundle" stringByAppendingPathComponent:@"line"];
        
        _lineIV.image = [UIImage imageNamed:@"line"];
        [self.view addSubview:_lineIV];
        
        
        //开始扫描
        [_session startRunning];
    }
    //手电筒?的开和关
    - (void)lightOnOrOff:(UIButton *)sender {
        sender.selected = !sender.selected;
        isLightOn = 1 - isLightOn;
        if (isLightOn) {
            [self turnOnLed:YES];
        }
        else {
            [self turnOffLed:YES];
        }
    }
    
    //打开手电筒
    - (void) turnOnLed:(bool)update {
        [captureDevice lockForConfiguration:nil];
        [captureDevice setTorchMode:AVCaptureTorchModeOn];
        [captureDevice unlockForConfiguration];
    }
    //关闭手电筒
    - (void) turnOffLed:(bool)update {
        [captureDevice lockForConfiguration:nil];
        [captureDevice setTorchMode: AVCaptureTorchModeOff];
        [captureDevice unlockForConfiguration];
    }
    - (void)showTheQRCodeOfMine:(UIButton *)sender {
        NSLog(@"showTheQRCodeOfMine");
    }
    - (void)animation {
        
        if (upOrdown == NO) {
            num ++;
            _lineIV.frame = CGRectMake((kScreenWidth - self.view.layer.bounds.size.width * 0.7)/2,self.view.layer.bounds.size.height * 0.25+ 2 * num, self.view.layer.bounds.size.width * 0.7, 5);
            if (IS_IPHONE5||IS_IPHONE4) {
                NSLog(@"%f",(int)self.view.frame.size.width*.7);
                if (2 * num == (int)(self.view.layer.bounds.size.width *.7)) {
                    upOrdown = YES;
                }else if (2 * num == (int)(self.view.layer.bounds.size.width *.7)-1){
                    
                    upOrdown = YES;
                    
                }
            }
            else {
                
                NSLog(@"%f",(int)self.view.frame.size.width*.7-3);
                NSLog(@"%d",2 * num);
                if (2 * num == (int)(self.view.frame.size.width*.7)) {
                    upOrdown = YES;
                }if (2 * num == (int)(self.view.layer.bounds.size.width *.7)-1){
                    
                    upOrdown = YES;
                    
                }
                
            }
        }
        else {
            num --;
            _lineIV.frame = CGRectMake((kScreenWidth - self.view.layer.bounds.size.width * 0.7)/2, self.view.layer.bounds.size.height * 0.25 + 2 * num, self.view.layer.bounds.size.width * 0.7, 5);
            
            if (num == 0) {
                upOrdown = NO;
            }
        }
    }
    
    #pragma mark - AVCaptureMetadataOutputObjectsDelegate
    - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
    {
        //判断是否有数据
        if (metadataObjects != nil && [metadataObjects count] > 0) {
            
            AVMetadataMachineReadableCodeObject *metadataObj = [metadataObjects objectAtIndex:0];
            //判断回传的数据类型
            if ([[metadataObj type] isEqualToString:AVMetadataObjectTypeQRCode]) {
                
                NSLog(@"stringValue = %@",metadataObj.stringValue);
                [self checkQRcode:metadataObj.stringValue];
            }
        }
        [_session stopRunning];
        [self performSelector:@selector(startReading) withObject:nil afterDelay:0.5];
    }
    
    -(void)startReading{
        [_session startRunning];
    }
    -(void)stopReading{
        [_session stopRunning];
    }
    /**
     * 判断二维码
     */
    - (void)checkQRcode:(NSString *)str{
        
        if (str.length == 0) {
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"找不到二维码" message:@"导入的图片里并没有找到二维码" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil];
            [alert show];
            return;
        }
        
        if ([str hasPrefix:@"http"]) {
            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
        }else{
            
            [_session stopRunning];
            
            [self KeepoutView:str];
            //弹出一个view显示二维码内容
            NSLog(@"%@",str);
        }
        
    }
    /**
     * 将二维码图片转化为字符
     */
    - (NSString *)stringFromFileImage:(UIImage *)img{
        int exifOrientation;
        switch (img.imageOrientation) {
            case UIImageOrientationUp:
                exifOrientation = 1;
                break;
            case UIImageOrientationDown:
                exifOrientation = 3;
                break;
            case UIImageOrientationLeft:
                exifOrientation = 8;
                break;
            case UIImageOrientationRight:
                exifOrientation = 6;
                break;
            case UIImageOrientationUpMirrored:
                exifOrientation = 2;
                break;
            case UIImageOrientationDownMirrored:
                exifOrientation = 4;
                break;
            case UIImageOrientationLeftMirrored:
                exifOrientation = 5;
                break;
            case UIImageOrientationRightMirrored:
                exifOrientation = 7;
                break;
            default:
                break;
        }
        
        NSDictionary *detectorOptions = @{ CIDetectorAccuracy : CIDetectorAccuracyHigh }; // TODO: read doc for more tuneups
        CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:detectorOptions];
        
        NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:img.CGImage]];
        
        CIQRCodeFeature * qrStr  = (CIQRCodeFeature *)features.firstObject;
        //只返回第一个扫描到的二维码
        return qrStr.messageString;
    }
    
    - (void)KeepoutView:(NSString*)orcodeStr{
        //做扫描成功之后的逻辑处理
        UIView *outView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
        outView.backgroundColor = [UIColor whiteColor];
        UIWindow *wind = [UIApplication sharedApplication].keyWindow;
        [self.view addSubview:outView];
        
    }
    
    -(void)dealloc{
        NSLog(@"%@ dealloc",NSStringFromClass(self.class));
    }
    
    
    @end
    

     

    展开全文
  • iOS原生二维码扫描,识别图片中的二维码信息。(赠送扫描动画,电筒?功能让你夜里轻松扫描二维码
  • 二维码由于不喜欢去查看zxing里面的api。所以就自己研究了下,不过由于时间问题,就只支持版本1.。(其实其他版本类似) 制作前需要了解: 一、版本公式是:(V-1)*4 + 21(V是版本号)最高版本为40 二、数字...

    二维码由于不喜欢去查看zxing里面的api。所以就自己研究了下,不过由于时间问题,就只支持版本1.。(其实其他版本类似)

    制作前需要了解:

    一、版本公式是:(V-1)*4 + 21(V是版本号)最高版本为40

    二、数字采用Numeric mode 数字编码

    三、0-9,大写的A到Z(没有小写),以及符号$ % * + – . / : 包括空格可以采用Alphanumeric mode 字符编码

    四、大写、小写、数字以及一些符号采用Byte mode, 字节编码

    五、中文采用Kanji mode 这是日文编码,也是双字节编码

    我这里只做了 数字编码( 其实其他编码也一样简单,就不BB了)

    从0到9。如果需要编码的数字的个数不是3的倍数,那么,最后剩下的1或2位数会被转成4或7bits(bits数=1+3*数字位数,针对版本1),则其它的每3位数字会被编成 10(v1-v9),12(v10-v26),14(27-40),(版本1为10)。

    示例:数字编码(官方例子)

    在Version 1的尺寸下,纠错级别为H的情况下,编码: 01234567

    1. 把上述数字分成三组: 012 345 67

    2. 把他们转成二进制:  012 转成 0000001100;  345 转成 0101011001;  67 转成 1000011。

    3. 把这三个二进制串起来: 0000001100 0101011001 1000011

    4. 把数字的个数转成二进制 (version 1-H是10 bits ): 8个数字的二进制是 0000001000

    5. 把数字编码的标志0001和第4步的编码加到前面:  0001 0000001000 0000001100 0101011001 1000011

    2.补位

    按8bits重排

    如果所有的编码加起来不是8个倍数我们还要在后面加上足够的0,比如上面一共有78个bits,所以,我们还要加上2个0,然后按8个bits分好组:

    00100000   01011011   00001011   01111000   11010001   01110010   11011100   01001101   01000011   01000000

    也就是按8的倍数重排如果不是8的倍数就在后面补0直至8的倍数

    然后

    "11101100","00010001" 重复添加也就是236 17

    剩余位的计算方法等于下图黄色区域的块数除以8 的余数

    数据码与纠错码排序方法总结:数据码排完后 在排纠错码。

    思路分析(自己理解方便记忆):

            假如数据码区有4块 就好像上学的时候分为四个班级 1班成绩(a)>2班(b)>3班(c)>4班(d)(>表示较好于)但是为了平衡分化因此采用成绩打乱的方式,a1>b1>c1>d1>a2>b2>c2>d2>''''''''(当然这只是理想状态),纠错码也一样。这样就好记它们到底怎么排序的了。

    可能这里由很多朋友想问很多版本即使纠错码与数据码混乱排序后还要加上Remainder Bits

    这个是我总结的(剩余位 见qrcode.doc表1 v2-6 :7 v1,7-13,35-40:0 v14-20,28-34:3 v21-27:4 )

    剩余位=需要填充的快数%8 

    黑白的区域在QR码规范中被指定为固定的位置,称为寻像图形(finder pattern) 和 定位图形

    黄色的区域用来保存被编码的数据内容以及纠错信息码。

    蓝色的区域,用来标识纠错的级别(也就是Level L到Level H)和所谓的”Mask pattern”,这个区域被称为“格式化信息”(format information)。

    数据整体填充方式

    数据局部填充方式

    信息区填充方式

    纠错码等级图

    mask等级图

    mask算法

    1.逐一检查每一行,在一行中连续5颜色相同的模块,加3,连续相同颜色模块超过5个以上,每增加一个,就加1。然后将每行中计算的数值加在一起,即为评判标准1的值。

    2.在二维码图像上寻找模块颜色相同的区域(最小为2×2的区域),然后代入公式3×(m-1)×(n-1)(这是一个m×n区域),将所有满足的区域代入公式所得的值全部相加起来。,即为评判标准2的值。

    3.寻找图案中dark-light-dark-dark-dark-light-dark-light-light-light-light图案(行/列都要计数),或者反过来,加40。即为评判标准3的值。

    4.  计算 黑色/总和 *100  然后取其前一个五的倍数以及后一个五的倍数 然后取其中较小值 a 值=|a-50|*2即为评判标准4的值。

    特别注意:mask进行标准计算时 时针对整个二维码,然而掩码只针对数据区

    代码:

    package com;

    import javax.imageio.ImageIO;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Stack;

    /**
     * Created by Administrator on 2020/5/25.
     */
    public class qrcodeEwUtil {
        public String data;
        public String dj="Q";
        public Map<String,String> mapCodeDj;//二维码纠错等级数组
        public String[] mapXorDj;//二维码mask等级数组
        private int WIDTH;
        private int HEIGHT;
        private int pd=30;
        public int[][] v1Mode;
        public String ewmDada;//二维码最终编码
        private int bs=6;//画二维码的时候对应的倍数 如 21*21 width=bs*21
        public String mask;
        public Map<String,int[][]> maskEnd =new HashMap<String, int[][]>();

      详情了解我的博客(二维码纠错码生成)
        makeJcm mkjcm;//二维码纠错码生成类

         详情了解我的博客(二维码数据生成)
        makeDataCode mkdata;//二维码数据码生成类

         详情了解我的博客(二维码信息码的生成)
        makeBchCode mkinfo;//二维码信息区生成类


        public qrcodeEwUtil(){}
        public qrcodeEwUtil(String data){
            this.data=data;
            init();
        }
        public qrcodeEwUtil(String data,String dj){
            this.data=data;
            if(dj!=null&&(dj.equals("L")||dj.equals("M")||dj.equals("H"))){
                this.dj=dj;
            }
            init();
        }
        public void init(){
            //jcm: mkjcm.jcmArr  data:mkdata.sjmArr
            getCodeBaseMap();
            v1ModeArr();
            for(int j=0;j<mapXorDj.length;j++){
            mask=mapXorDj[j];
            mkdata=new  makeDataCode(data,dj);//生成数据码

         详情了解我的博客(二维码数据生成)
            mkjcm=new makeJcm();
            mkjcm.makeQrJcm(mkdata.sjmArr,mkjcm.makeDxsDeep(mkdata.jcCodeNumTotal[0][mkdata.jcmDj]));//生成纠错码

         详情了解我的博客(二维码纠错码生成)
            int[] arr=new int[]{15,5};//版本一默认
            mkinfo=new makeBchCode(arr,mapCodeDj.get(dj)+mask);//生成信息码

         详情了解我的博客(二维码信息码的生成)
            mkinfo.infoCode=new StringBuffer(mkinfo.infoCode).reverse().toString();//为了方便后面运算 因此将信息码反转
            StringBuffer sbf=new StringBuffer("");

          //由于信息码以及纠错码是采用十进制 因此需要转换为二进制并连接起来 数据码在前,纠错码在后
            for(int i=0;i<mkdata.sjmArr.length;i++){
                sbf.append(sjzZh8byte(mkdata.sjmArr[i]));
            }
            for(int i=0;i<mkjcm.jcmArr.length;i++){
                sbf.append(sjzZh8byte(mkjcm.jcmArr[i]));
            }
            ewmDada=sbf.toString();
               if(maskEnd.isEmpty()){
                   maskEnd.put(mask,getImageMode());
               }else{

                 //二维码数组
                   int[][] arr2=getImageMode();

                //mask值
                   int a=pt1(arr2)+pt2(arr2)+pt3(arr2)+pt4(arr2);
                   boolean flag=false;

               //mask值最小如果有两个表示计算错误
                   if(maskEnd.size()!=1){
                       try {
                           throw new Exception("bad maskPartern!");
                       } catch (Exception e) {
                           e.printStackTrace();
                       }
                   }

                //将mask较小值的mask等级以及二维码数组存入集合中
                   for(String key:maskEnd.keySet()){
                        int b=pt1(maskEnd.get(key))+pt2(maskEnd.get(key))+pt3(maskEnd.get(key))+pt4(maskEnd.get(key));
                        flag=a<b?true:false;
                   }
                   if(flag){
                       maskEnd.clear();
                       maskEnd.put(mask,arr2);
                   }
               }
        }
        //drawImage("ewmModeDemoXorRgbch-"+dj+mask+"_" + data);

        //画二维码
            drawEwmImage("ewmModeDemo" + data);
        }
        public void getCodeBaseMap(){
            mapCodeDj=new HashMap<String, String>();
            mapXorDj=new String[]{"000","001","010","011","100","101","110","111"};
            mapCodeDj.put("L","01");
            mapCodeDj.put("M","00");
            mapCodeDj.put("Q","11");
            mapCodeDj.put("H","10");
            //div 表示整除运算符  mod求模运算符
            /*
            * 掩模图形参考    条件
    000    (i + j) mod 2 = 0
    001    i mod 2 = 0
    010    j mod 3 = 0
    011    (i + j) mod 3 = 0
    100    ((i div 2) + (j div 3)) mod 2 = 0
    101    (i j) mod 2 + (i j) mod 3 = 0
    110    ((i j) mod 2 + (i j) mod 3) mod 2 = 0
    111    ((i j) mod 3 + (i+j) mod 2) mod 2 = 0
            * */
        }

       //画二维码
        public BufferedImage createEwmImage(){
            WIDTH=bs*21+2*pd;
            HEIGHT=bs*21+2*pd;
            BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
            Graphics graphic = image.getGraphics();
            Graphics2D g2=(Graphics2D)graphic;
            g2.setColor(Color.WHITE);
            g2.fillRect(0, 0, WIDTH, HEIGHT);
            for(String key:maskEnd.keySet()){
                int[][]  arr=maskEnd.get(key);
                for(int i=0;i<arr.length;i++){
                 for(int j=0;j<arr[i].length;j++){
                     if(arr[i][j]==1){
                         g2.setColor(Color.BLACK);
                     }else{
                         g2.setColor(Color.WHITE);
                     }
                     for (int l = 0; l < bs; l++) {
                         g2.drawLine( i * bs + pd, j * bs + pd + l,  i * bs + pd + bs - 1, j * bs + pd + l);
                     }
                 }
                }
            }
            return image;
        }

      //得到二维码数组模型
        public int[][] getImageMode(){
            int a=20; int b=0;
            int[][] arr=new int[21][21];
            while(true){
                int lx=2;
                if(a==6){
                    a=a-1;
                    lx=1;
                }else{
                    a=a-2;
                }
                int fxNum=a>=6?a-1:a;
                if((fxNum+4)%4==3){
                    for(int j=0;j<21;j++) {
                        for (int i = lx; i >= 1; i--) {
                            if (v1Mode[a + i][j] == 3) {
                                b++;
                                String ewmdata = ewmDada.substring(b - 1, b);
                                arr[a + i][j]=maskEwm(a + i, j, ewmdata)==Color.BLACK?1:0;
                                //g2.setColor(maskEwm(a + i, j, ewmdata));
                            } else {
                                arr[a + i][j]=getColor(a + i, j)==Color.BLACK?1:0;
                                //g2.setColor(getColor(a + i, j));
                            }
                        }
                    }
                }else{
                    for(int j=20;j>=0;j--){
                        for(int i=lx;i>=1;i--){
                            if(v1Mode[a+i][j]==3){
                                b++;
                                String ewmdata=ewmDada.substring(b-1,b);
                                arr[a + i][j]=maskEwm(a + i, j, ewmdata)==Color.BLACK?1:0;
                                //g2.setColor(maskEwm(a + i, j, ewmdata));
                            }else{
                                arr[a + i][j]=getColor(a + i, j)==Color.BLACK?1:0;
                                //g2.setColor(getColor(a+i,j));
                            }
                        }
                    }
                }
                if(a<0){
                    break;
                }
            }
            return arr;
        }
        /*非数据区
        * 返回颜色 1:黑色 0:白色
        * */
        public Color getColor(int i,int j) {
            int a = 0;
            switch (v1Mode[i][j]) {
                case 0:
                    a = 0;
                    break;
                case 1:
                    a = 1;
                    break;
                case 2:
                    String strInfo = "";
                    if (i == 8) {
                        if (j <= 5) {
                            strInfo = mkinfo.infoCode.substring(j, j + 1);
                        } else if (j > 5 && j <= 13) {
                            strInfo = mkinfo.infoCode.substring(j - 1, j);
                        } else {
                            strInfo = mkinfo.infoCode.substring(j - 6, j - 5);
                        }
                    } else {
                        if (j == 8) {
                            if (i <= 5) {
                                strInfo = mkinfo.infoCode.substring(mkinfo.infoCode.length() - i - 1, mkinfo.infoCode.length() - i);
                            } else if (i > 5 && i <= 13) {
                                strInfo = mkinfo.infoCode.substring(mkinfo.infoCode.length() - i, mkinfo.infoCode.length() - i + 1);
                            } else {
                                strInfo = mkinfo.infoCode.substring(mkinfo.infoCode.length() - i + 5, mkinfo.infoCode.length() - i + 6);
                            }
                        }
                    }
                    if (strInfo.equals("1")) {
                        a = 1;
                    } else {
                        a = 0;
                    }
                    break;
            }
            if(a==1){
                return Color.BLACK;
            }
            return Color.WHITE;
        }

        /*数据区
        * 返回颜色 1:黑色 0:白色
        * */
        public Color maskEwm(int i,int j,String str){
            int modYs=getBackMask(j,i);
            int a=0;
            if(Integer.valueOf(str)==1){
            a=modYs;
            }else{
            a=(modYs+1)%2;
            }
            if(a==1){
                return Color.BLACK;
            }
            return Color.WHITE;
        }

       /*
        * mask运算规则,row表示行,col表示列
        * */
        public int getBackMask(int row,int col){
            int a=0;
            if(mask.equals("000")){
             a=(row+col)%2;
            }
            else if(mask.equals("001")){
                a=row%2;
            }
            else if(mask.equals("010")){
                a=col%3;
            }
            else if(mask.equals("011")){
                a=(row+col)%3;
            }
            else if(mask.equals("100")){
                a=((int)Math.floor(row/2)+(int)Math.floor(col/3))%2;
            }
            else if(mask.equals("101")){
                a=(row*col)%2+(row*col)%3;
            }
            else if(mask.equals("110")){
                a=((row*col)%2+(row*col)%3)%2;
            }
            else if(mask.equals("111")){
                a=((row+col)%2+(row*col)%3)%2;
            }
            return a==0?0:1;
        }
        public void drawEwmImage(String code){
            try {
                ImageIO.write(createEwmImage(), "jpg", new File("D://code/" + code + ".jpg"));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        /*
        * 将十进制转换成8bite的二进制数据
        * */
        public String sjzZh8byte(int a){
          if(a==0){
              return "00000000";
          }
            String str=Integer.toBinaryString(a);
            if(str.length()<8){
                int len=8-str.length();
                for(int i=0;i<len;i++){
                    str="0"+str;
                }
            }
            return str;
        }
        /*mask pattern1 算法
        * 1:mask pattern1 算法连续相同>=5块开始计算 等于相同数-2;
        * 2:横、纵都要计算
        * */
        public int pt1(int[][] arr){
            int a=0;
            for(int i=0;i<arr.length;i++){
                int b=-1;int count=0;
                for(int j=0;j<arr.length;j++){
                    if(b!=arr[i][j]){
                        b=arr[i][j];
                        if(count>=5){
                            a+=count-2;
                        }
                        count=1;
                    }else{
                        count++;
                    }
                }
            }
            for(int j=0;j<arr.length;j++){
                int b=-1;int count=0;
                for(int i=0;i<arr.length;i++){
                    if(b!=arr[i][j]){
                        b=arr[i][j];
                        if(count>=5){
                            a+=count-2;
                        }
                        count=1;
                    }else{
                        count++;
                    }
                }
            }
            return a;
        }
        /*mask pattern2 算法
        * 计算小方块 要求长、宽必须均>=2 算法:3(m-1)(n-1)
        * */
        public int pt2(int[][] arr){
            int a=0;
            int[][] arr2=new int[arr.length][arr.length];
            ArrayList<Integer> list;
            for(int i=0;i<arr.length-1;i++){
                for(int j=0;j<arr.length-1;j++){
                    if(arr2[i][j]==1){
                        continue;
                    }
                    int b=arr[i][j];
                    list =new ArrayList<Integer>();
                    for(int m=i;m<arr.length;m++){
                        if(arr[m][j]!=b){
                            break;
                        }
                       for(int n=j;n<arr.length;n++){
                           if(arr[m][n]==b){
                               //arr2[m][n]=1;
                               list.add(m-i,n-j);
                           }else{
                               break;
                           }
                       }
                    }
                    if(!list.isEmpty()&&list.size()>1){
                        int x=0;int y=0;int d=0;
                            for(int l=0;l<list.size();l++){
                                if(list.get(l)==0){
                                    if((l-x)>2&&y>0){
                                        a+=3*(l-x-2)*y;
                                        for(int m=x+1;m<=l-1;m++){
                                            for(int n=j;n<=j+y;n++){
                                                arr2[m][n]=1;
                                            }
                                        }
                                    }
                                    x=l;
                                    continue;
                                }else{
                                    y=Math.min(y,list.get(l));
                                }
                                if((l==list.size()-1)&&(l-x)>1&&y>0){
                                    a+=3*(l-x-1)*y;
                                    for(int m=x+1;m<=l;m++){
                                        for(int n=j;n<=j+y;n++){
                                            arr2[m][n]=1;
                                        }
                                    }
                                }
                            }
                        }
                }
            }
            return a;
        }
    /*mask pattern3 算法
     * 出现10111010000 或者00001011101 加40
     * */
        public int pt3(int[][] arr) {
            String str1="10111010000";
            String str2="00001011101";
            String str="";
            int a=0;
            for(int i=0;i<arr.length;i++){
                for(int j=0;j<=arr.length-11;j++){
                    boolean flag=true;
                    if(arr[i][j]==0){
                        str=str2;
                    }else{
                        str=str1;
                    }
                    for(int m=j+1;m<=arr.length-11;m++){
                        if(arr[i][m]!=Integer.valueOf(str.substring(m-j,m-j+1))){
                            flag=false;
                        }
                    }
                    if(flag){
                        a+=40;
                    }
                }
            }
            for(int j=0;j<arr.length;j++){
                for(int i=0;i<=arr.length-11;i++){
                    boolean flag=true;
                    if(arr[i][j]==0){
                        str=str2;
                    }else{
                        str=str1;
                    }
                    for(int m=i+1;m<=arr.length-11;m++){
                        if(arr[m][j]!=Integer.valueOf(str.substring(m-i,m-i+1))){
                            flag=false;
                        }
                    }
                    if(flag){
                        a+=40;
                    }
                }
            }
            return a;
        }
        /*  mask pattern4 算法
        * 计算 黑色/总和 *100  然后取其前一个五的倍数以及后一个五的倍数 然后取其中较小值 a
        * 值=|a-50|*2
        * */
        public int pt4(int[][] arr) {
            int a=0;
            int block_num=0;
            int total_num=arr.length*arr.length;
            for(int i=0;i<arr.length;i++){
                for(int j=0;j<arr.length;j++){
                    if(arr[i][j]==1){
                        block_num++;
                    }
                }
            }
            double block_bl=20*block_num/total_num;
            int back_num=(int)Math.floor(block_bl);
            int last_num=back_num+1;
            return Math.min(Math.abs(back_num-10),Math.abs(last_num-10))*10;
        }
        /*
        *
        *
        * 1表示黑色 0 表示白色 并且不能填充
        * 2 表示信息区  3 表示数据区  v1版本模型数组
        * */
        public void v1ModeArr(){
            int[][] v1ModeFfx=new int[21][21];
             v1Mode=new int[21][21];
            v1ModeFfx[0]=new int[]{1,1,1,1,1,1,1,0,2,3,3,3,3,0,1,1,1,1,1,1,1};
            v1ModeFfx[1]=new int[]{1,0,0,0,0,0,1,0,2,3,3,3,3,0,1,0,0,0,0,0,1};
            v1ModeFfx[2]=new int[]{1,0,1,1,1,0,1,0,2,3,3,3,3,0,1,0,1,1,1,0,1};
            v1ModeFfx[3]=v1ModeFfx[2];
            v1ModeFfx[4]=v1ModeFfx[2];
            v1ModeFfx[5]=v1ModeFfx[1];
            v1ModeFfx[6]=new int[]{1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1};
            v1ModeFfx[7]=new int[]{0,0,0,0,0,0,0,0,2,3,3,3,3,0,0,0,0,0,0,0,0};
            v1ModeFfx[8]=new int[]{2,2,2,2,2,2,1,2,2,3,3,3,3,2,2,2,2,2,2,2,2};
            v1ModeFfx[9]=new int[]{3,3,3,3,3,3,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
            v1ModeFfx[10]=new int[]{3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3};
            v1ModeFfx[11]=v1ModeFfx[9];
            v1ModeFfx[12]=v1ModeFfx[10];
            v1ModeFfx[13]=new int[]{0,0,0,0,0,0,0,0,1,3,3,3,3,3,3,3,3,3,3,3,3};
            v1ModeFfx[14]=new int[]{1,1,1,1,1,1,1,0,2,3,3,3,3,3,3,3,3,3,3,3,3};
            v1ModeFfx[15]=new int[]{1,0,0,0,0,0,1,0,2,3,3,3,3,3,3,3,3,3,3,3,3};
            v1ModeFfx[16]=new int[]{1,0,1,1,1,0,1,0,2,3,3,3,3,3,3,3,3,3,3,3,3};
            v1ModeFfx[17]=v1ModeFfx[16];
            v1ModeFfx[18]=v1ModeFfx[16];
            v1ModeFfx[19]=v1ModeFfx[15];
            v1ModeFfx[20]=v1ModeFfx[14];
            for(int i=0;i<21;i++){
                for(int j=0;j<21;j++){
                    v1Mode[j][i]=v1ModeFfx[i][j];
                }
            }
        }
        public static void main(String[] args) {

    //可以自定义等级 默认为Q 由于只有版本1,因此数字不要太长,否则有可能测试失败
            qrcodeEwUtil qr=new qrcodeEwUtil("01234567","M");
        }
    }

     

    展开全文
  • 手绘二维码攻略

    2022-02-27 14:56:49
    提到二维码想必大家都不陌生,扫码支付、添加好友以及关注公众号等,随处可见二维码身影。通常我们见到的二维码会有三个用于定位的黑白嵌套的方块,这基本上就是 QR 二维码(Quick Response Code)。 今天我们就...
  • // 在已生成的二维码logo gs.drawImage(logo, imgSize / 5 * 2, imgSize / 5 * 2, width / 5, height / 5, null); } catch (Exception e) { e.printStackTrace(); } gs.dispose(); // 释放空间 ...
  • 美化二维码

    千次阅读 2020-05-02 16:50:50
    二维码内容长度不作控制 大多数互联网技术使用到二维码生成,都是使用开源的包生成,输入字符信息,即可生成一张包含此字符信息的黑白二维码图片: 上面有两个二维码图片,扫码结果都是访问到同个页面,...
  • 二维码原理

    2019-11-07 08:53:39
    常见的二维码QR Code在1994年由日本的腾弘原发明,由条形码发展而来,相比条形码,存储能力大大提升了。 二维码组成和各部分含义 生成过程 1.Position Detection Pattern 首先,先把Position Detection图案...
  • 100个Python实战项目(一)使用 Python 生成二维码

    万次阅读 多人点赞 2021-08-19 14:43:16
    所以,在这篇文章中,我使用 Python 做了一个生成二维码的简易小程序 本文章为系列文章,共三个 python 项目。初学者程序员可以尝试实现这些项目,并在 Python 语言中动手操作。 让我们从第一个开始。 二维码代表...
  • 目标检测:二维码检测方案

    千次阅读 2022-03-26 21:59:52
    移动互联网时代,我们的身边,无处不见的二维码,在商店买东西可以用微信或支付宝的付款码、在电影院可以使用二维码在自助取票机上取票,朋友聚会时使用微信二维码互相加好友。移动终端设备的普及,出门只需要携带...
  • 所以,还是自己一个~ 首先先上效果图 当当当当~~~最后生成的海报中包括头像和姓名与二维码,当然图上的二维码是百度的二维码~最后生成base64用以分享到各大平台。 废话不多说,上代码(这个代码没有整理,因为懒...
  • 随着互联网短视频平台竞争加剧,如何针对性的给短视频平台用户推荐合适的内容,从而增加平台对于用户的粘性,巩固和扩大用户市场,是...近日,二维彩虹与抖音就定制一次性二维码展开深度合作 。帮助抖音解决用户数据筛
  • 今天突然想到二维码是如何存储信息的,手画二维码是否可行呢。于是就开始各种搜索,最终自己也利用Google的ZXing工具完成了一个生成二维码和解析二维码的简单程序。 一、 二维码生成原理(即工作原理) 二维码官方...
  • JAVA 生成数据表图标LOGO二维码,数据表是JAVA画布手动的,二维码可控制是否有logo图标,整合生成图表标签。
  • 初学安卓之二维码的简单实现前言生成二维码准备工作生成简单二维码修改二维码颜色在二维码中心添加logo图片GenerateQRcode完整代码扫描二维码准备工作打开真机调试集成工作实现功能实现扫码返回二维码内容打开闪光灯...
  • 二维码制作教程

    2019-03-20 09:42:38
  • php指定url生成二维码

    2021-05-13 17:38:13
    将包存放在vendor目录 (不同框架放置的位置可能会有... //生成二维码 public function QRcode($url=null) { $url = 'https://www.baidu.com/';//要转成二维码的url地址 require_once '../vendor/phpqrcode/php.
  • 刚好项目中涉及到了二维码扫描的功能,就记录下Zxing的使用过程,个人擅自添加了电筒到界面,可以给大家一些参考。首先看下效果图如下这个红色的电筒就是自定义加上去的,点击可以切换扫描时候的闪光灯的开关...
  • 阿里的人才画像 其实最近两年自己一直在做面试官,也面试过很多优秀的人,心里大概有一个标准,知道什么样的人才是我们想要的人。 但是这个标准我一直都没有仔细的去思考过,刚好最近有时间,我好好的思考了一下,...
  • CTF-misc(二维码)

    千次阅读 2020-04-11 09:49:54
    今天做了一道misc题,题目给出的附件是一个GIF图片,首先放到PS里把图片一帧帧分出来,中间有个二维码,扫了一下发现扫不出来,后来看了WP才知道这个二维码缺少了定位符,更没想到的是这个定位符居然要手动上去,...
  • 本教程基于Zxing,依赖Zxing.unity.dll,适用于PC端和Android端,离线可用。 Unity版本:2018.4.23f Zxing:GitHub下载 Unity Demo下载:点我下载 成品如下: ...///扫描二维码()=》识别二维码信息 publ
  • 自行创建Code文件夹cdCode2、下载MyQR库sudo pip3 install MyQR3、下载所需资源文件并解压Code/ $ wget http://labfile.oss.aliyuncs.com/courses/1126/Sources.zip #这里提供制作二维码所需要的图片资源 ...
  • 这时候我跟他们说,前端不了跳转小程序页面的二维码,要后端巴拉巴拉的。。。结果打脸时刻来了,(这个故事告诉我们,文档要看干净点) 扫普通链接二维码打开小程序 | 微信开放文档 发现可以在后台按照二维码...
  • 本文内容与技术关系不大,纯粹是一个简单的分享,内容主要是二维码的设计原理以及一些有趣的扩展,感兴趣的同学可以继续往下看~二维码原理优点二维码出现之前,在需要使用类似编码的场景时,采用的都是...
  • ">请扫描二维码登录p> div> transition> div> <script> var vm =new Vue({ el:'#app', data(){ return{ current:0 } }, methods:{ handleClick(index){ this.current=index } } }) script> body> html> 最后的呈现...
  • 近十来年,二维码技术已不再是“高科技”,各种教你制作二维码的教程和代码一篇又一篇。今天!小二把原理,代码一次性写出来,也希望各位能够理解。 我们微信一般使用的就是QRcode,这是一个矩阵式的二维码。是由小...
  • python 二维码+图像处理

    千次阅读 2019-03-19 15:55:31
    0x01 二维码 关于二维码的生成,python的生成方式及其简单粗暴,只需要调用qrcode库即可。先放下代码。 import qrcode import os def create_qr(self, url, qrcodename): qr = qrcode.QRCode( version=1, ...
  • 今天给大家带来一个简单的Python应用实验:生成个性二维码。 实验内容: 通过调用MyQR接口来实现生成个人所需二维码,并可以设置二维码的大小、是否在现有图片的基础上生成、是否生成动态二维码。主要面向Python3...
  • Java 生成二维码

    2021-08-31 15:30:34
    QRCode生成二维码网址:http://swetake.com/qrcode/index-e.html 2. maven项目手动引入jar包 mvninstall:install-file -DgroupId=包名 -DartifactId=项目名 -Dversion=版本号 -Dpackaging=jar-Dfile=jar文件所在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,218
精华内容 4,487
关键字:

二维码手画