当前位置: > > > Swift - 使用AVFoundation实现条形码扫描(附:拉近镜头改善读取)

Swift - 使用AVFoundation实现条形码扫描(附:拉近镜头改善读取)

(本文代码已升级至Swift3)

1,条形码(一维码)的扫描读取
原来写过一篇文章,介绍如何使用摄像头扫描读取二维码:Swift - 二维码QRCode的读取(从图片读取 ,或通过摄像头扫描)要通过摄像头读取条形码,只需要将原来二维码读取代码中 metadataObjectTypes 做如下修改即可:
self.output.metadataObjectTypes = [AVMetadataObjectTypeEAN13Code,
        AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code,
        AVMetadataObjectTypeCode39Code,AVMetadataObjectTypeCode93Code]

2,拉近镜头,改善条形码读取效果
有网友反应,如果条码太小的时候(比如iwatch上支付宝的生成的小条码)就会识别不出来。
解决办法是:像支付宝、QQ一样,通过代码拉近镜头焦距,放大内容区域让机器更好的识别。

下面左图是原始大小,右图将画面放大了1.5倍:
     
import UIKit
import AVFoundation

class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate,
UIAlertViewDelegate{
    
    var scanRectView:UIView!
    var device:AVCaptureDevice!
    var input:AVCaptureDeviceInput!
    var output:AVCaptureMetadataOutput!
    var session:AVCaptureSession!
    var preview:AVCaptureVideoPreviewLayer!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        fromCamera()
    }
    
    //通过摄像头扫描
    func fromCamera() {
        do{
            self.device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
            
            self.input = try AVCaptureDeviceInput(device: device)
            
            self.output = AVCaptureMetadataOutput()
            output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
            
            self.session = AVCaptureSession()
            if UIScreen.main.bounds.size.height<500 {
                self.session.sessionPreset = AVCaptureSessionPreset640x480
            }else{
                self.session.sessionPreset = AVCaptureSessionPresetHigh
            }
            
            self.session.addInput(self.input)
            self.session.addOutput(self.output)
            
            self.output.metadataObjectTypes = [AVMetadataObjectTypeEAN13Code,
                           AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code,
                           AVMetadataObjectTypeCode39Code,AVMetadataObjectTypeCode93Code]
            
            //计算中间可探测区域
            let windowSize = UIScreen.main.bounds.size;
            let scanSize = CGSize(width:windowSize.width*3/4,
                                  height:windowSize.width*3/4)
            var scanRect = CGRect(x:(windowSize.width-scanSize.width)/2,
                                  y:(windowSize.height-scanSize.height)/2,
                                  width:scanSize.width, height:scanSize.height)
            //计算rectOfInterest 注意x,y交换位置
            scanRect = CGRect(x:scanRect.origin.y/windowSize.height,
                              y:scanRect.origin.x/windowSize.width,
                              width:scanRect.size.height/windowSize.height,
                              height:scanRect.size.width/windowSize.width)
            //设置可探测区域
            self.output.rectOfInterest = scanRect
            
            self.preview = AVCaptureVideoPreviewLayer(session:self.session)
            self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill
            self.preview.frame = UIScreen.main.bounds
            self.view.layer.insertSublayer(self.preview, at:0)
            
            //添加中间的探测区域绿框
            self.scanRectView = UIView();
            self.view.addSubview(self.scanRectView)
            self.scanRectView.frame = CGRect(x:0, y:0, width:scanSize.width,
                                             height:scanSize.height)
            self.scanRectView.center = CGPoint(x:UIScreen.main.bounds.midX,
                                               y:UIScreen.main.bounds.midY)
            self.scanRectView.layer.borderColor = UIColor.green.cgColor
            self.scanRectView.layer.borderWidth = 1
            
            //开始捕获
            self.session.startRunning()
            
            //放大
            do {
                try self.device!.lockForConfiguration()
            } catch _ {
                NSLog("Error: lockForConfiguration.");
            }
            self.device!.videoZoomFactor = 1.5
            self.device!.unlockForConfiguration()
            
        }catch _ as NSError{
            //打印错误消息
            let alertController = UIAlertController(title: "提醒",
                    message: "请在iPhone的\"设置-隐私-相机\"选项中,允许本程序访问您的相机",
                    preferredStyle: .alert)
            let cancelAction = UIAlertAction(title: "确定", style: .cancel, handler: nil)
            alertController.addAction(cancelAction)
            self.present(alertController, animated: true, completion: nil)
        }
    }
    
    //摄像头捕获
    func captureOutput(_ captureOutput: AVCaptureOutput!,
                       didOutputMetadataObjects metadataObjects: [Any]!,
                       from connection: AVCaptureConnection!) {
        var stringValue:String?
        if metadataObjects.count > 0 {
            let metadataObject = metadataObjects[0]
                as! AVMetadataMachineReadableCodeObject
            stringValue = metadataObject.stringValue
            
            if stringValue != nil{
                self.session.stopRunning()
            }
        }
        self.session.stopRunning()
        //输出结果
        let alertController = UIAlertController(title: "二维码",
                                    message: stringValue,preferredStyle: .alert)
        let okAction = UIAlertAction(title: "确定", style: .default, handler: {
            action in
            //继续扫描
            self.session.startRunning()
        })
        alertController.addAction(okAction)
        self.present(alertController, animated: true, completion: nil)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
评论2
  • 2楼
    2016-12-16 15:36
    人丑多读书丶

    航哥,为什么我的条形码只能扫码带数字的, 带字母的就扫描不出来。

    站长回复

    我测试了下可以扫描带字母的条形码啊。还有我把代码更新成Swift3的了,你可以再试试看。

  • 1楼
    2015-11-23 14:17
    mingricha

    爱死你了站长T.T 调了两天放大功能都不起作用,看了你的代码才知道是self.device!.videoZoomFactor = 1.5位置放错了导致的,我放到了session.startRunning前面,太感谢了

    站长回复

    我一开始也犯了这个错,后面调试了下才发现的。^_^