如何仅在 CGRect 中使用 Apple Vision 检测条形码?

Posted

技术标签:

【中文标题】如何仅在 CGRect 中使用 Apple Vision 检测条形码?【英文标题】:How to detect barcode using Apple Vision in CGRect only? 【发布时间】:2021-11-21 16:53:33 【问题描述】:

我有一个应用程序,它使用 CGRect(x: 0, y: 0, width: 335, height: 150) 来显示用于条形码扫描的摄像头。但是,当在相机外(不在 CGRect 中)出现条形码时,将被扫描。如何在预览层中将扫描区域限制为 CGRect?这是使用 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

编辑:

// MARK: - AVCaptureVideoDataOutputSampleBufferDelegate
extension CameraViewController: AVCaptureVideoDataOutputSampleBufferDelegate 
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) 
        
        guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else  return 
        
        let imageRequestHandler = VNImageRequestHandler(
            cvPixelBuffer: pixelBuffer,
            orientation: .up)
        
        do 
            try imageRequestHandler.perform([detectBarcodeRequest])
         catch 
            print(error)
        
    //captureOutput
    
//extension

【问题讨论】:

【参考方案1】:
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)
    

用法:

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

【讨论】:

为了让这个答案尽可能有帮助,请添加解释你到底在做什么以及为什么。 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案2】:

func captureOutput(_:didOutput:from:) 中,您很可能会将整个图像传递给VNImageRequestHandler。 相反,您需要将图像裁剪为可见的矩形。 另一种方法是通过regionOfInterest 将视觉锁定到图像的仅可见部分,如下面的@HurricaneOnTheMoon 建议。

【讨论】:

我已经添加了captureOutput的代码,我好像忘记了。我将如何裁剪图像?

以上是关于如何仅在 CGRect 中使用 Apple Vision 检测条形码?的主要内容,如果未能解决你的问题,请参考以下文章

将图像覆盖到 CGRect swift

通过在 imageview 中绘制圆圈裁剪图像

Ipad CGRect 中心

如何仅在中国启动我的 iOS iPhone 应用程序?

Flutter ios 应用程序仅在 iPad 上因“使用 Apple 登录”错误而被拒绝

IAP - 无法连接到iTunes Store - 仅在Apple审核期间