如何使用metadataOutputRectOfInterestForRect方法和rectOfInterest属性扫描特定区域? (二维码) [英] How do I use the metadataOutputRectOfInterestForRect method and rectOfInterest property to scan a specific area? (QR Code)

查看:193
本文介绍了如何使用metadataOutputRectOfInterestForRect方法和rectOfInterest属性扫描特定区域? (二维码)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Swift构建QR码扫描器,并且在这方面一切正常.我遇到的问题是,我试图使整个可见的AVCaptureVideoPreviewLayer中只有一小部分能够扫描QR码.我发现,为了指定屏幕的哪个区域将能够读取/捕获QR码,我必须使用称为rectOfInterestAVCaptureMetadataOutput属性.问题是,当我将其分配给CGRect时,无法进行任何扫描.在网上进行了更多研究之后,我发现有人建议我需要使用一种称为metadataOutputRectOfInterestForRect的方法将CGRect转换为属性rectOfInterest可以实际使用的正确格式.但是,我现在遇到的一个大问题是,当我使用此方法metadataoutputRectOfInterestForRect时,出现一条错误提示CGAffineTransformInvert: singular matrix.谁能告诉我为什么我会收到此错误?我相信我根据Apple开发人员文档正确使用了此方法,并且我相信需要根据我在网上找到的所有信息来使用此方法来实现我的目标.我将提供到目前为止找到的文档的链接以及用于扫描QR码的功能的代码示例

I am building a QR code scanner with Swift and everything works in that regard. The issue I have is that I am trying to make only a small area of the entire visible AVCaptureVideoPreviewLayer be able to scan QR codes. I have found out that in order to specify what area of the screen will be able to read/capture QR codes I would have to use a property of AVCaptureMetadataOutput called rectOfInterest. The trouble is when I assigned that to a CGRect, I couldn't scan anything. After doing more research online I have found some suggesting that I would need to use a method called metadataOutputRectOfInterestForRect to convert a CGRect into a correct format that the property rectOfInterest can actually use. HOWEVER, the big issue I have run into now is that when I use this method metadataoutputRectOfInterestForRect I am getting an error that states CGAffineTransformInvert: singular matrix. Can anyone tell me why I am getting this error? I believe I am using this method properly according to the Apple developer documentation and I believe I need to use this according to all the information I have found online to accomplish my goal. I will include links to the documentation I have found so far as well as a code sample of the function I am using to scan QR codes

代码示例

func startScan() {
        // Get an instance of the AVCaptureDevice class to initialize a device object and provide the video
        // as the media type parameter.
        let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)

        // Get an instance of the AVCaptureDeviceInput class using the previous device object.
        var error:NSError?
        let input: AnyObject! = AVCaptureDeviceInput.deviceInputWithDevice(captureDevice, error: &error)

        if (error != nil) {
            // If any error occurs, simply log the description of it and don't continue any more.
            println("\(error?.localizedDescription)")
            return
        }

        // Initialize the captureSession object.
        captureSession = AVCaptureSession()
        // Set the input device on the capture session.
        captureSession?.addInput(input as! AVCaptureInput)

        // Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
        let captureMetadataOutput = AVCaptureMetadataOutput()
        captureSession?.addOutput(captureMetadataOutput)

        // calculate a centered square rectangle with red border
        let size = 300
        let screenWidth = self.view.frame.size.width
        let xPos = (CGFloat(screenWidth) / CGFloat(2)) - (CGFloat(size) / CGFloat(2))
        let scanRect = CGRect(x: Int(xPos), y: 150, width: size, height: size)

        // create UIView that will server as a red square to indicate where to place QRCode for scanning
        scanAreaView = UIView()
        scanAreaView?.layer.borderColor = UIColor.redColor().CGColor
        scanAreaView?.layer.borderWidth = 4
        scanAreaView?.frame = scanRect
        view.addSubview(scanAreaView!)

        // Set delegate and use the default dispatch queue to execute the call back
        captureMetadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
        captureMetadataOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]



        // Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
        videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
        videoPreviewLayer?.frame = view.layer.bounds
        captureMetadataOutput.rectOfInterest = videoPreviewLayer!.metadataOutputRectOfInterestForRect(scanRect)
        view.layer.addSublayer(videoPreviewLayer)

        // Start video capture.
        captureSession?.startRunning()

        // Initialize QR Code Frame to highlight the QR code
        qrCodeFrameView = UIView()
        qrCodeFrameView?.layer.borderColor = UIColor.greenColor().CGColor
        qrCodeFrameView?.layer.borderWidth = 2
        view.addSubview(qrCodeFrameView!)
        view.bringSubviewToFront(qrCodeFrameView!)

        // Add a button that will be used to close out of the scan view
        videoBtn.setTitle("Close", forState: .Normal)
        videoBtn.setTitleColor(UIColor.blackColor(), forState: .Normal)
        videoBtn.backgroundColor = UIColor.grayColor()
        videoBtn.layer.cornerRadius = 5.0;
        videoBtn.frame = CGRectMake(10, 30, 70, 45)
        videoBtn.addTarget(self, action: "pressClose:", forControlEvents: .TouchUpInside)
        view.addSubview(videoBtn)


        view.bringSubviewToFront(scanAreaView!)

    }

请注意,引起错误的关注行是这样的: captureMetadataOutput.rectOfInterest = videoPreviewLayer!.metadataOutputRectOfInterestForRect(scanRect)

Please note that the line of interest causing the error is this: captureMetadataOutput.rectOfInterest = videoPreviewLayer!.metadataOutputRectOfInterestForRect(scanRect)

我尝试过的其他方法是直接将CGRect作为参数传递,并且导致了相同的错误.我还传入了scanAreaView!.bounds作为参数,因为它确实是我要查找的确切大小/面积,并且也导致了同样的确切错误.我在网上的其他代码示例中已经看到了这一点,并且它们似乎没有我遇到的错误.以下是一些示例:

Other things I have tried are passing in a CGRect directly as a parameter and that has caused the same error. I have also passed in scanAreaView!.bounds as a parameter as that is really the exact size/area I am looking for and that also causes the same exact error. I have seen this done in other's code examples online and they do not seem to have the errors I am having. Here are some examples:

AVCaptureSession条码扫描

Xcode AVCapturesession扫描特定框架中的条形码(rectOfInterest无法正常工作)

Apple文档

metadataOutputRectOfInterestForRect

rectOfInterest

我正在使用scanAreaView的图像作为指定区域,我正在尝试使其成为视频预览层的唯一可扫描区域:

Image of scanAreaView I am using as the designated area I am trying to make the only scannable area of the video preview layer:

推荐答案

我真的无法使用metadataOutputRectOfInterestForRect来澄清问题,但是,您也可以直接设置该属性.您需要预先指定视频的宽度和高度分辨率.我很快使用了640 * 480设置.如文档中所述,这些值必须为

I wasn't really able to clarify the issue with metadataOutputRectOfInterestForRect, however, you can directly set the property as well. You need to the have the resolution in width and height of your video, which you can specify in advance. I quickly used the 640*480 setting. As stated in the documentation, these values have to be

相对于设备的自然方向从左上角的(0,0)延伸到右下角的(1,1)".

"extending from (0,0) in the top left to (1,1) in the bottom right, relative to the device’s natural orientation".

请参见 https://developer.apple.com/documentation/avfoundation/avcaptureoutput/1616304-metadataoutputrectofinterestforr

下面是我尝试的代码

var x = scanRect.origin.x/480
var y = scanRect.origin.y/640
var width = scanRect.width/480
var height = scanRect.height/640
var scanRectTransformed = CGRectMake(x, y, width, height)
captureMetadataOutput.rectOfInterest = scanRectTransformed

我刚刚在iOS设备上对其进行了测试,它似乎可以正常工作.

I just tested it on an iOS device and it seems to work.

修改

至少我已经解决了metadataOutputRectOfInterestForRect问题.我相信您必须在正确设置好相机并使其运行后再执行此操作,因为相机的分辨率尚不可用.

At least I've solved the metadataOutputRectOfInterestForRect problem. I believe you have to do this after the camera has been properly set up and is running, as the camera's resolution is not yet available.

首先,在viewDidLoad()中添加一个通知观察器方法

First, add a notification observer method within viewDidLoad()

NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("avCaptureInputPortFormatDescriptionDidChangeNotification:"), name:AVCaptureInputPortFormatDescriptionDidChangeNotification, object: nil)

然后添加以下方法

func avCaptureInputPortFormatDescriptionDidChangeNotification(notification: NSNotification) {

    captureMetadataOutput.rectOfInterest = videoPreviewLayer.metadataOutputRectOfInterestForRect(scanRect)

}

然后您可以在此处重置rectOfInterest属性.然后,在您的代码中,可以在didOutputMetadataObjects函数中显示AVMetadataObject

Here you can then reset the rectOfInterest property. Then, in your code, you can display the AVMetadataObject within the didOutputMetadataObjects function

var rect = videoPreviewLayer.rectForMetadataOutputRectOfInterest(YourAVMetadataObject.bounds)

dispatch_async(dispatch_get_main_queue(),{
     self.qrCodeFrameView.frame = rect
})

我尝试过,并且矩形始终在指定区域内.

I've tried, and the rectangle was always within the specified area.

这篇关于如何使用metadataOutputRectOfInterestForRect方法和rectOfInterest属性扫描特定区域? (二维码)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆