Comment utiliser la méthode metadataOutputRectOfInterestForRect et la propriété rectOfInterest pour scanner une zone spécifique? (Code QR)

je construis un scanner QR code avec Swift et tout fonctionne à cet égard. Le problème que j'ai est que j'essaie de faire seulement une petite partie de l'ensemble visible AVCaptureVideoPreviewLayer être en mesure de scanner les codes QR. J'ai découvert que pour spécifier quelle zone de l'écran sera capable de lire/capturer les codes QR, je devrais utiliser une propriété de AVCaptureMetadataOutput appelé rectOfInterest . Le problème, c'est que quand J'ai assigné ça à un CGRect, je n'ai rien pu scanner. Après avoir fait plusieurs recherche en ligne j'ai trouvé certains suggérant que je devrais utiliser une méthode appelée metadataOutputRectOfInterestForRect pour convertir un CGRect dans un format correct que la propriété rectOfInterest peut effectivement utiliser. Cependant, le grand problème que j'ai rencontré maintenant est que lorsque j'utilise cette méthode metadataoutputRectOfInterestForRect je reçois une erreur qui indique CGAffineTransformInvert: singular matrix . Quelqu'un peut me dire pourquoi j'obtiens cette erreur? Je crois que j'utilise cette méthode correctement selon la documentation du développeur Apple et je crois que je dois utiliser selon toutes les informations que j'ai trouvé en ligne pour accomplir mon objectif. Je vais inclure des liens vers la documentation que j'ai trouvée jusqu'à présent ainsi qu'un exemple de code de la fonction que j'utilise pour numériser les codes QR

CODE SAMPLE

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!)

    }

veuillez noter que la ligne d'intérêt à l'origine de l'erreur est la suivante:: captureMetadataOutput.rectOfInterest = videoPreviewLayer!.metadataOutputRectOfInterestForRect(scanRect)

D'autres choses que j'ai essayé de passer dans un CGRect directement comme un paramètre et qui a provoqué la même erreur. J'ai également passé dans scanAreaView!.bounds comme paramètre car c'est vraiment la taille exacte/zone que je cherche et qui provoque également la même erreur exacte. J'ai vu Cela fait dans les exemples de code d'autres en ligne et ils ne semblent pas avoir les erreurs que j'ai. Voici quelques exemples:

AVCaptureSession de codes à barres de numérisation

Xcode AVCapturesession scanner le code à Barres dans cadre spécifique (recto interest is not working)

dans la documentation d'Apple

metadataOutputRectOfInterestForRect

recto-interest

Image de scanAreaView que j'utilise comme zone désignée j'essaie de faire la seule zone scannable de la couche de prévisualisation vidéo:

enter image description here

24
demandé sur Community 2015-09-04 18:12:42

7 réponses

Je n'ai pas vraiment été en mesure de clarifier la question avec metadataOutputRectOfInterestForRect, cependant, vous pouvez définir directement la propriété ainsi. Vous avez besoin d'avoir la résolution en largeur et la hauteur de votre vidéo, que vous pouvez spécifier à l'avance. J'ai rapidement utilisé le réglage 640*480. Comme indiqué dans la documentation, ces valeurs doivent être

" s'étendant de (0,0) dans le haut à gauche à (1,1) dans le bas à droite, par rapport à la position orientation."

voir https://developer.apple.com/documentation/avfoundation/avcaptureoutput/1616304-metadataoutputrectofinterestforr

ci-dessous est le code que j'ai essayé

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

je viens de le tester sur un appareil iOS et il semble fonctionner.

Modifier

au moins, j'ai résolu la question des métadats. problème. Je pense que vous devez le faire après que la caméra a été correctement mise en place et fonctionne, car la résolution de la caméra n'est pas encore disponible.

d'abord, ajouter une méthode de notification observateur dans viewDidLoad ()

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

ajouter la méthode suivante

func avCaptureInputPortFormatDescriptionDidChangeNotification(notification: NSNotification) {

    captureMetadataOutput.rectOfInterest = videoPreviewLayer.metadataOutputRectOfInterestForRect(scanRect)

}

ici vous pouvez réinitialiser la propriété rectOfInterest. Ensuite, dans votre code, vous pouvez afficher L'objet Avmetataobject dans les objets didoutputmetataobjects fonction

var rect = videoPreviewLayer.rectForMetadataOutputRectOfInterest(YourAVMetadataObject.bounds)

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

j'ai essayé, et le rectangle était toujours dans la zone spécifiée.

23
répondu peacer212 2017-06-30 13:14:07

Dans iOS 9.3.2 j'ai été en mesure de faire metadataoutputRectOfInterestForRect le travail de l'appeler juste après startRunning méthode de AVCaptureSession :

captureSession.startRunning()
let visibleRect = previewLayer.metadataOutputRectOfInterestForRect(previewLayer.bounds)
captureMetadataOutput.rectOfInterest = visibleRect
9
répondu Avt 2016-06-02 23:31:29

Swift 4:

captureSession?.startRunning()
let scanRect = CGRect(x: 0, y: 0, width: 100, height: 100)
let rectOfInterest = layer.metadataOutputRectConverted(fromLayerRect: scanRect)
metaDataOutput.rectOfInterest = rectOfInterest
5
répondu Lal Krishna 2018-02-01 09:50:39

j'ai réussi à créer un effet de disposer d'une région d'intérêt. J'ai essayé toutes les solutions proposées mais la région était soit un CGPoint.zéro ou avait une taille inappropriée (après conversion des trames en coordonnées 0-1). C'est en fait un hack pour ceux qui ne peuvent pas faire fonctionner le regionOfInterest et qui n'optimisent pas la détection.

, Dans:

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) 

j'ai le code suivant:

let visualCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
if self.viewfinderView.frame.contains(visualCodeObject.bounds) { 
    //visual code is inside the viewfinder, you can now handle detection
}
3
répondu Kacper Cz 2017-12-19 09:53:49

/// Après

captureSession.startRunning()

/// Ajouter ce

if let videoPreviewLayer = self.videoPreviewLayer {
self.captureMetadataOutput.rectOfInterest =
videoPreviewLayer.metadataOutputRectOfInterest(for:
self.getRectOfInterest())


fileprivate func getRectOfInterest() -> CGRect {
        let centerX = (self.frame.width / 2) - 100
        let centerY = (self.frame.height / 2) - 100
        let quadr: CGFloat = 200

        let myRect = CGRect(x: centerX, y: centerY, width: quadr, height: quadr)

        return myRect
    }
1
répondu Mihail Salari 2017-03-17 14:59:06

pour lire un QRCode/code à barres à partir d'un petit rect(région spécifique) à partir d'une vue caméra complète.

<br> **Mandatory to keep the below line after (start running)** <br>
[captureMetadataOutput setRectOfInterest:[_videoPreviewLayer metadataOutputRectOfInterestForRect:scanRect] ];

[_captureSession startRunning];
[captureMetadataOutput setRectOfInterest:[_videoPreviewLayer metadataOutputRectOfInterestForRect:scanRect] ];

Note:

  1. captureMetadataOutput --> Avcapturemetataoutput 1519100920"
  2. _videoPreviewLayer -- > AVCaptureVideoPreviewLayer
  3. scanRect --> Rect où vous souhaitez que le QRCode pour être lu.
0
répondu Niresh S 2018-06-20 14:39:10

j'ai écrit ce qui suit:

videoPreviewLayer?.frame = view.layer.bounds
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill

et ça a marché pour moi, mais je ne sais toujours pas pourquoi.

-3
répondu Jin 2016-07-04 10:44:06