Capturing the image read by QRCode

In this app I get access to QRCode.
Reading works perfectly from the camera.

Now I am struggling to get the image that was processed by the built in QRCode reader.

I have found many hints on SO, but cannot make it work.

Here is the code I have now.
It is a bit long, I have to slit in 2 parts

I looked at:
// https://stackoverflow.com/questions/56088575/how-to-get-image-of-qr-code-after-scanning-in-swift-ios
// https://stackoverflow.com/questions/37869963/how-to-use-avcapturephotooutput

Code Block
import UIKit
import AVFoundation
class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
fileprivate var captureSession: AVCaptureSession! // use for QRCode reading
fileprivate var previewLayer: AVCaptureVideoPreviewLayer!
// To get the image of the QRCode
private var photoOutputQR: AVCapturePhotoOutput!
private var isCapturing = false
override func viewDidLoad() {
super.viewDidLoad()
var accessGranted = false
// switch AVCaptureDevice.authorizationStatus(for: .video) {
// HERE TEST FOR ACCESS RIGHT. WORKS OK ;
// But is .video enough ?
}
if !accessGranted { return }
captureSession = AVCaptureSession()
photoOutputQR = AVCapturePhotoOutput() // IS IT THE RIGHT PLACE AND THE RIGHT THING TO DO ?
captureSession.addOutput(photoOutputQR) // Goal is to capture an image of QRCode once acquisition is done
guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
let videoInput: AVCaptureDeviceInput
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
} catch { return }
if (captureSession.canAddInput(videoInput)) {
captureSession.addInput(videoInput)
} else {
failed()
return
}
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput) // SO I have 2 output in captureSession. IS IT RIGHT ?
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [.qr] // For QRCode video acquisition
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
previewLayer.frame = view.layer.bounds
previewLayer.frame.origin.y += 40
previewLayer.frame.size.height -= 40
previewLayer.videoGravity = .resizeAspectFill
view.layer.addSublayer(previewLayer)
captureSession.startRunning()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if (captureSession?.isRunning == false) {
captureSession.startRunning()
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.isRunning == true) {
captureSession.stopRunning()
}
}
// MARK: - scan Results
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
guard let stringValue = readableObject.stringValue else { return }
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
found(code: stringValue)
}
// ### Get image - IS IT THE RIGHT PLACE TO DO IT ?
// https://stackoverflow.com/questions/37869963/how-to-use-avcapturephotooutput
print("Do I get here ?", isCapturing)
let photoSettings = AVCapturePhotoSettings()
let previewPixelType = photoSettings.availablePreviewPhotoPixelFormatTypes.first!
print("previewPixelType", previewPixelType)
let previewFormat = [kCVPixelBufferPixelFormatTypeKey as String: previewPixelType,
kCVPixelBufferWidthKey as String: 160,
kCVPixelBufferHeightKey as String: 160]
photoSettings.previewPhotoFormat = previewFormat
if !isCapturing {
isCapturing = true
photoOutputQR.capturePhoto(with: photoSettings, delegate: self)
}
dismiss(animated: true)
}
}
extension ScannerViewController: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
isCapturing = false
print("photo", photo, photo.fileDataRepresentation())
guard let imageData = photo.fileDataRepresentation() else {
print("Error while generating image from photo capture data.");
return
}
}
}


I get the following print on console
Clearly photo is not loaded properly

Do I get here ? false
previewPixelType 875704422
photo <AVCapturePhoto: 0x281973a20 pts:nan 1/1 settings:uid:3 photo:{0x0} time:nan-nan> nil
Error while generating image from photo capture data.

Accepted Reply

There is a simple fix: regenerate the QRCiode from the decoded QRCode.
It works well, but I would like to get the image itself as read by the camera…

Replies

There is a simple fix: regenerate the QRCiode from the decoded QRCode.
It works well, but I would like to get the image itself as read by the camera…

Maybe you can try to get a snapshot of "self.view" when the QRCode parses successfully?

  • Thanks, I'll try it. Not sure it is possible in the view for auto capture of QR Code.

Add a Comment

Hello @Claude31,

I am facing same issue. Please share your working code if you fix that. Thanks in advance

  • I did not find any better way than recreate the QRCode from its text.

Add a Comment