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 A
In iOS 9.3.2 I was able to make metadataoutputRectOfInterestForRect
work calling it right after startRunning
method of AVCaptureSession
:
captureSession.startRunning()
let visibleRect = previewLayer.metadataOutputRectOfInterestForRect(previewLayer.bounds)
captureMetadataOutput.rectOfInterest = visibleRect
/// After
captureSession.startRunning()
/// Add this
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
}
I managed to create an effect of having a region of interest. I tried all the proposed solutions but the region was either a CGPoint.zero or had inappropriate size (after converting frames to a 0-1 coordinate). It's actually a hack for those who can't get the regionOfInterest
to work and doesn't optimize the detection.
In:
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection)
I have the following code:
let visualCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)
if self.viewfinderView.frame.contains(visualCodeObject.bounds) {
//visual code is inside the viewfinder, you can now handle detection
}
I know there are already solutions present and it's pretty late but i achieved mine by capturing the complete view image and then cropping it with specific rect.
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let imageData = photo.fileDataRepresentation() {
print(imageData)
capturedImage = UIImage(data: imageData)
var crop = cropToPreviewLayer(originalImage: capturedImage!)
let sb = UIStoryboard(name: "Main", bundle: nil)
let s = sb.instantiateViewController(withIdentifier: "KeyFobScanned") as! KeyFobScanned
s.image = crop
self.navigationController?.pushViewController(s, animated: true)
}
}
private func cropToPreviewLayer(originalImage: UIImage) -> UIImage? {
guard let cgImage = originalImage.cgImage else { return nil }
let scanRect = CGRect(x: stackView.frame.origin.x, y: stackView.frame.origin.y, width: innerView.frame.size.width, height: innerView.frame.size.height)
let outputRect = videoPreviewLayer.metadataOutputRectConverted(fromLayerRect: scanRect)
let width = CGFloat(cgImage.width)
let height = CGFloat(cgImage.height)
let cropRect = CGRect(x: outputRect.origin.x * width, y: outputRect.origin.y * height, width: outputRect.size.width * width, height: outputRect.size.height * height)
if let croppedCGImage = cgImage.cropping(to: cropRect) {
return UIImage(cgImage: croppedCGImage, scale: 1.0, orientation: originalImage.imageOrientation)
}
return nil
}
To read a QRCode/BarCode from a small rect(specific region) from a full camera view.
<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
--> AVCaptureMetadataOutput _videoPreviewLayer
--> AVCaptureVideoPreviewLayer scanRect
--> Rect where you want the QRCode to be read.I wrote the following:
videoPreviewLayer?.frame = view.layer.bounds
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
And this worked for me, but I still don't know why.