verificacion online leer escanear desde con como codigo celular camara barras ios swift qr-code avcapturesession

ios - escanear - leer codigo qr online con camara web



¿Cómo uso el método metadataOutputRectOfInterestForRect y la propiedad rectOfInterest para escanear un área específica?(Código QR) (7)

Swift 4:

captureSession?.startRunning() let scanRect = CGRect(x: 0, y: 0, width: 100, height: 100) let rectOfInterest = layer.metadataOutputRectConverted(fromLayerRect: scanRect) metaDataOutput.rectOfInterest = rectOfInterest

Estoy construyendo un escáner de código QR con Swift y todo funciona en ese sentido. El problema que tengo es que estoy tratando de hacer que solo un área pequeña de todo AVCaptureVideoPreviewLayer sea ​​capaz de escanear códigos QR. Descubrí que para especificar qué área de la pantalla podría leer / capturar códigos QR, tendría que usar una propiedad de AVCaptureMetadataOutput llamada rectOfInterest . El problema es que cuando asigné eso a un CGRect, no pude escanear nada. Después de investigar más en línea, he encontrado algunos que sugieren que necesitaría usar un método llamado metadataOutputRectOfInterestForRect para convertir un CGRect en un formato correcto que la propiedad rectOfInterest pueda usar. SIN EMBARGO, el gran problema que me he encontrado ahora es que cuando uso este método metadataoutputRectOfInterestForRect , CGAffineTransformInvert: singular matrix un error que indica CGAffineTransformInvert: singular matrix . ¿Alguien puede decirme por qué estoy recibiendo este error? Creo que estoy usando este método correctamente de acuerdo con la documentación del desarrollador de Apple y creo que necesito usar esto de acuerdo con toda la información que he encontrado en línea para lograr mi objetivo. Incluiré enlaces a la documentación que he encontrado hasta ahora, así como un ejemplo de código de la función que estoy utilizando para escanear códigos QR

Muestra de codigo

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

Tenga en cuenta que la línea de interés que causa el error es la siguiente: captureMetadataOutput.rectOfInterest = videoPreviewLayer!.metadataOutputRectOfInterestForRect(scanRect)

Otras cosas que he intentado son pasar un CGRect directamente como parámetro y eso ha provocado el mismo error. También he pasado en scanAreaView!.bounds como un parámetro, ya que realmente es el tamaño / área exacta que estoy buscando y eso también causa el mismo error exacto. He visto esto hecho en otros ejemplos de código en línea y parece que no tienen los errores que estoy teniendo. Aquí hay unos ejemplos:

Escaneo de código de barras AVCaptureSession

Xcode AVCapturesession scan Barcode en un cuadro específico (rectOfInterest no funciona)

Documentación de apple

metadataOutputRectOfInterestForRect

rectOfInterest

Imagen de scanAreaView que estoy usando como el área designada que estoy tratando de hacer que sea la única área escaneable de la capa de vista previa de video:


/// Después

captureSession.startRunning()

/// Agrega esto

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 }


En iOS 9.3.2 pude hacer que metadataoutputRectOfInterestForRect funcione llamándolo justo después del método AVCaptureSession de AVCaptureSession :

captureSession.startRunning() let visibleRect = previewLayer.metadataOutputRectOfInterestForRect(previewLayer.bounds) captureMetadataOutput.rectOfInterest = visibleRect


Escribí lo siguiente:

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

Y esto funcionó para mí, pero todavía no sé por qué.


Logré crear el efecto de tener una región de interés. Probé todas las soluciones propuestas, pero la región era un CGPoint.zero o tenía un tamaño inadecuado (después de convertir los marcos en una coordenada 0-1). En realidad, es un truco para aquellos que no pueden hacer que regionOfInterest el regionOfInterest y no optimiza la detección.

En:

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

Tengo el siguiente código:

let visualCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj) if self.viewfinderView.frame.contains(visualCodeObject.bounds) { //visual code is inside the viewfinder, you can now handle detection }


Para leer un QRCode / BarCode desde una pequeña rect (región específica) desde una vista completa de la cámara.

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

Nota:

  1. captureMetadataOutput -> AVCaptureMetadataOutput
  2. _videoPreviewLayer -> AVCaptureVideoPreviewLayer
  3. scanRect -> Rect donde desea que se lea el scanRect QR.

Realmente no pude aclarar el problema con metadataOutputRectOfInterestForRect, sin embargo, también puede establecer la propiedad directamente. Debe tener la resolución en ancho y alto de su video, que puede especificar de antemano. Rápidamente usé la configuración de 640 * 480. Como se indica en la documentación, estos valores deben ser

"extendiéndose desde (0,0) en la parte superior izquierda a (1,1) en la parte inferior derecha, en relación con la orientación natural del dispositivo".

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

A continuación se muestra el código que probé

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

Acabo de probarlo en un dispositivo iOS y parece funcionar.

Editar

Al menos he resuelto el problema metadataOutputRectOfInterestForRect. Creo que tienes que hacer esto después de que la cámara se haya configurado y esté funcionando correctamente, ya que la resolución de la cámara aún no está disponible.

Primero, agregue un método de notificación de observador dentro de viewDidLoad ()

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

Luego agrega el siguiente método

func avCaptureInputPortFormatDescriptionDidChangeNotification(notification: NSNotification) { captureMetadataOutput.rectOfInterest = videoPreviewLayer.metadataOutputRectOfInterestForRect(scanRect) }

Aquí puede restablecer la propiedad rectOfInterest. Luego, en su código, puede visualizar AVMetadataObject dentro de la función didOutputMetadataObjects

var rect = videoPreviewLayer.rectForMetadataOutputRectOfInterest(YourAVMetadataObject.bounds) dispatch_async(dispatch_get_main_queue(),{ self.qrCodeFrameView.frame = rect })

Lo he intentado, y el rectángulo siempre estuvo dentro del área especificada.