Home > Back-end >  How to detect barcode using Apple Vision in CGRect only?
How to detect barcode using Apple Vision in CGRect only?

Time:09-30

I have an app that uses a CGRect(x: 0, y: 0, width: 335, height: 150) to show the camera for barcode scanning. However when presented a barcode off camera (not in the CGRect) will get scanned. How can I limit the area for scanning to the CGRect in my preview layer? This is using Vision.

let captureSession = AVCaptureSession()
var previewLayer: AVCaptureVideoPreviewLayer!
var activeInput: AVCaptureDeviceInput!

lazy var detectBarcodeRequest = VNDetectBarcodesRequest { request, error in
    guard error == nil else {
        
        print("Barcode Error: \(String(describing: error?.localizedDescription))")
    
        return
    }
    
    self.processBarCode(request)
}

override func viewDidLoad() {
    super.viewDidLoad()
    
    setupSession()
    setupPreview()
    startSession()
}

func setupSession() {
    
    guard let camera = AVCaptureDevice.default(for: .video) else {
        return
        
    }
    do {
        let videoInput = try AVCaptureDeviceInput(device: camera)
        
        for input in [videoInput] {
            if captureSession.canAddInput(input) {
                captureSession.addInput(input)
            }
        }
        activeInput = videoInput
    } catch {
        print("Error setting device input: \(error)")
        return
    }
    
    let captureOutput = AVCaptureVideoDataOutput()
    
    captureOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)]
    captureOutput.setSampleBufferDelegate(self, queue: DispatchQueue.global(qos: DispatchQoS.QoSClass.default))
    
    captureSession.addOutput(captureOutput)

}

func setupPreview() {
    
    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    
    previewLayer.videoGravity = .resizeAspectFill
    previewLayer.connection?.videoOrientation = .portrait
    previewLayer.frame = CGRect(x: 0, y: 0, width: 335, height: 150)
    
    view.layer.insertSublayer(previewLayer, at: 0)

}//setupPreview

func startSession() {
    if !captureSession.isRunning {
        DispatchQueue.global(qos: .default).async { [weak self] in
            self?.captureSession.startRunning()
        }
    }
}

// MARK: - processBarCode
func processBarCode(_ request: VNRequest) {
    
    
    DispatchQueue.main.async {
        guard let results = request.results as? [VNBarcodeObservation] else {
            return
        }
        
        if let payload = results.first?.payloadStringValue, let symbology = results.first?.symbology {
            print("payload is \(payload) \(symbology) ")
            
        }
        
    }
    
}//processBarCode

CodePudding user response:

extension AVCaptureVideoPreviewLayer {
    func rectOfInterestConverted(parentRect: CGRect, fromLayerRect: CGRect) -> CGRect {
        let parentWidth = parentRect.width
        let parentHeight = parentRect.height
        let newX = (parentWidth - fromLayerRect.maxX)/parentWidth
        let newY = 1 - (parentHeight - fromLayerRect.minY)/parentHeight
        let width = 1 - (fromLayerRect.minX/parentWidth   newX)
        let height = (fromLayerRect.maxY/parentHeight) - newY

        return CGRect(x: newX, y: newY, width: width, height: height)
    }
}

Usage:

if let rect = videoPreviewLayer?.rectOfInterestConverted(parentRect: self.view.frame, fromLayerRect: scanAreaView.frame) {
    captureMetadataOutput.rectOfInterest = rect
}

CodePudding user response:

In func captureOutput(_:didOutput:from:) you most likely passing whole image into VNImageRequestHandler.
Instead you need to crop image to your visible rect.
Another approach would be lock Vision to only visible part of image via regionOfInterest as @HurricaneOnTheMoon proposed below.

  • Related