如何使用Apple Vision将条码扫描范围限制在指定CGRect区域内
解决方法
要限定Vision的条码扫描范围到指定的预览区域,有两种可选实现方案:
方案1:设置检测感兴趣区域(性能更优)
直接给VNDetectBarcodesRequest指定regionOfInterest属性,Vision只会在该区域内执行条码检测,不会处理区域外的内容,性能开销更低。
- 首先利用
AVCaptureVideoPreviewLayer提供的坐标转换方法,把你自定义的预览区域转换为Vision要求的归一化坐标系(左下角为原点,取值范围0~1):
在setupPreview()方法的末尾添加以下代码:
// 将预览层的bounds转换为相机输出的归一化坐标 let scanRegion = previewLayer.metadataOutputRectConverted(fromLayerRect: previewLayer.bounds) // 给条码检测请求设置感兴趣区域 detectBarcodeRequest.regionOfInterest = scanRegion
- 如果你的预览区域会随界面布局变化(比如横竖屏旋转),需要在布局更新时重新计算区域:
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 刷新预览层frame previewLayer.frame = CGRect(x: 0, y: 0, width: 335, height: 150) // 重新计算感兴趣区域 let scanRegion = previewLayer.metadataOutputRectConverted(fromLayerRect: previewLayer.bounds) detectBarcodeRequest.regionOfInterest = scanRegion }
方案2:检测后过滤结果(灵活性更高)
如果需要自定义判断规则(比如允许条码部分落在扫描区域内也可识别),可以在拿到检测结果后,把条码的坐标转换为预览层坐标系,再判断是否符合要求:
修改processBarCode方法的逻辑:
func processBarCode(_ request: VNRequest) { DispatchQueue.main.async { [weak self] in guard let self = self, let results = request.results as? [VNBarcodeObservation] else { return } for barcode in results { // 将条码的归一化 boundingBox 转换为预览层的坐标 let barcodeFrame = self.previewLayer.layerRectConverted(fromMetadataOutputRect: barcode.boundingBox) // 判断条码是否完全在预览区域内,可根据需求调整为相交判断:barcodeFrame.intersects(self.previewLayer.bounds) if self.previewLayer.bounds.contains(barcodeFrame) { guard let payload = barcode.payloadStringValue, let symbology = barcode.symbology else { continue } print("有效扫描结果:\(payload) \(symbology) ") // 此处添加你的业务处理逻辑 break // 只取第一个符合要求的结果,不需要可删除 } } } }
注意事项
- 如果你的应用支持横竖屏切换,需要同步更新
VNImageRequestHandler的orientation参数,保证坐标转换的准确性。 - 方案1的性能表现更好,无特殊需求优先选择该方案。
内容的提问来源于stack exchange,提问作者Paul S.
相关产品推荐
相关产品推荐

