如何通过AVCaptureMetadataOutput或Vision检测条形码水平翻转?
问题
我有两个条形码,内容都是数字01069327,唯一区别是第二个条形码被水平翻转。出于业务需求,我需要识别出这种翻转状态。尝试用AVFoundation和Vision框架实现,但两种方案始终返回相同的状态值1,无法区分翻转状态。但在线条码识别工具能正确检测旋转状态,请问有没有方法获取翻转信息?也可以使用免费第三方库。
使用AVFoundation的尝试代码
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { if let metadataObject = metadataObjects.first { guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return } guard let stringValue = readableObject.stringValue else { return } let topLeft = readableObject.corners[0] let topRight = readableObject.corners[1] let bottomRight = readableObject.corners[2] let bottomLeft = readableObject.corners[3] let diffXRight = topRight.x - bottomRight.x let diffXLeft = topLeft.x - bottomLeft.x let flipThresholdX: CGFloat = 0.01 let isFlippedHorizontally = abs(diffXRight) > flipThresholdX || abs(diffXLeft) > flipThresholdX print("BARCODE: \(stringValue), flipped: \(isFlippedHorizontally ? 0 : 1)\n\n") self.parent.didFindCode(stringValue, isFlippedHorizontally ? 0 : 1) } }
使用Vision的尝试代码
private func createRequest() -> VNDetectBarcodesRequest { let request = VNDetectBarcodesRequest { request, error in [...] if let stringValue = observation.payloadStringValue { let topLeft = observation.topLeft let topRight = observation.topRight let bottomLeft = observation.bottomLeft let angle = atan2(topRight.y - topLeft.y, topRight.x - topLeft.x) * 180.0 / .pi let rotationThreshold: Float = 180.0 let isFlippedHorizontally = angle > rotationThreshold || angle < -rotationThreshold print("BARCODE: \(stringValue), flipped: \(isFlippedHorizontally ? 0 : 1)") self.parent.didFindCode(stringValue, isFlippedHorizontally ? 0 : 1) } } return request }
分析与解决方案
原代码问题分析
- AVFoundation方案:框架会自动规整条码的角点顺序,无论条码是否翻转,
corners数组的顺序都保持一致,通过左右边X差值无法判断真实翻转状态。 - Vision方案:计算的顶部边角度逻辑错误,水平翻转后该角度仍在±0°左右,不会触发±180°的阈值判断。
可行解决方案
方案1:修正Vision框架的判断逻辑
Vision返回的条码顶点坐标是归一化的图像坐标(原点在左上角,X向右递增),水平翻转的条码会出现topLeft.x > topRight.x的特征,直接对比即可:
private func createRequest() -> VNDetectBarcodesRequest { let request = VNDetectBarcodesRequest { request, error in guard let observations = request.results as? [VNBarcodeObservation], let observation = observations.first, let stringValue = observation.payloadStringValue else { return } // 水平翻转时,左上角X坐标大于右上角X坐标 let isFlippedHorizontally = observation.topLeft.x > observation.topRight.x print("BARCODE: \(stringValue), flipped: \(isFlippedHorizontally ? 0 : 1)") self.parent.didFindCode(stringValue, isFlippedHorizontally ? 0 : 1) } return request }
方案2:使用免费第三方库ZBarSDK
ZBar会返回条码的方向信息,可直接区分水平翻转状态:
- 集成ZBarSDK到项目中
- 通过
ZBarSymbol的orientation属性判断状态,ZBarOrientationLeft或ZBarOrientationRight对应水平翻转
示例代码片段:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { UIImage *image = info[UIImagePickerControllerOriginalImage]; ZBarImage *zbarImage = [ZBarImage imageWithUIImage:image]; ZBarScanner *scanner = [ZBarScanner new]; ZBarSymbolSet *symbols = [scanner scanImage:zbarImage]; for (ZBarSymbol *symbol in symbols) { NSString *code = symbol.data; BOOL isFlipped = (symbol.orientation == ZBarOrientationLeft || symbol.orientation == ZBarOrientationRight); NSLog(@"BARCODE: %@, flipped: %d", code, isFlipped ? 0 : 1); } [picker dismissViewControllerAnimated:YES completion:nil]; }
方案3:基于条码编码规则判断(特定场景)
如果是固定类型的条码(如EAN-13),可通过分析起始符/终止符的位置判断:
- EAN-13的起始符为
101,终止符也为101,但左右数据符编码规则不同 - 水平翻转后,起始符会出现在条码右侧,可通过图像中黑白条的分布特征识别位置差异
此方法需深入了解条码编码规则,适合特定业务场景。
内容的提问来源于stack exchange,提问作者elp
相关产品推荐
相关产品推荐

