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
dans la documentation d'Apple
metadataOutputRectOfInterestForRect
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:
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."
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.
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
Swift 4:
captureSession?.startRunning()
let scanRect = CGRect(x: 0, y: 0, width: 100, height: 100)
let rectOfInterest = layer.metadataOutputRectConverted(fromLayerRect: scanRect)
metaDataOutput.rectOfInterest = rectOfInterest
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
}
/// 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
}
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:
-
captureMetadataOutput
--> Avcapturemetataoutput 1519100920" -
_videoPreviewLayer
-- > AVCaptureVideoPreviewLayer -
scanRect
--> Rect où vous souhaitez que le QRCode pour être lu.
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.