You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何识别黑底黑点式二维码?已尝试Vision+AVFoundation未解决

黑底黑点二维码识别解决方案建议

需求描述

识别黑底黑点样式的二维码(二维码为黑色点状图案,背景为黑色)。

已尝试方案

使用Vision Framework结合AVFoundation进行扫描,同时调整ISO参数,但均未成功识别。

可行解决方案建议

1. 修复核心逻辑错误

现有代码存在关键问题:仅对预览画面应用了滤镜,但二维码识别仍使用原始低对比度的像素缓冲区,这直接导致识别失败。必须将预处理后的图像传给Vision进行识别。

2. 针对性图像预处理

黑底黑点二维码的核心问题是对比度极低,Vision默认依赖清晰的明暗边界识别二维码。需要通过以下步骤强化边界:

  • 图像反转:将黑底黑点转换为白底灰点,放大明暗差异
  • 对比度增强:进一步拉大二维码与背景的灰度差
  • 阈值化处理:将灰度差异转化为明确的黑白边界

3. 可选替代方案

如果Vision Framework经过预处理后仍识别困难,可以尝试使用ZXingObjC库,它对低对比度、非标准样式的二维码兼容性更好,支持自定义图像预处理逻辑。


修改后的代码示例

import Foundation
import UIKit
import AVFoundation
import Vision

class QRScannerVC: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate {
    
    var captureSession: AVCaptureSession!
    var previewLayer: AVCaptureVideoPreviewLayer!
    let invertFilter = CIFilter(name: "CIColorInvert")
    let contrastFilter = CIFilter(name: "CIColorControls")
    let thresholdFilter = CIFilter(name: "CIColorThreshold")

    override func viewDidLoad() {
        super.viewDidLoad()
        
        captureSession = AVCaptureSession()
        captureSession.sessionPreset = .high // 提高分辨率助力识别
        
        guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
        let videoInput: AVCaptureDeviceInput
        
        do {
            videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
            // 调整摄像头参数,优化进光量
            try videoCaptureDevice.lockForConfiguration()
            videoCaptureDevice.isAutoExposureEnabled = true
            videoCaptureDevice.isAutoWhiteBalanceEnabled = true
            videoCaptureDevice.unlockForConfiguration()
        } catch {
            return
        }
        
        if captureSession.canAddInput(videoInput) {
            captureSession.addInput(videoInput)
        } else {
            return
        }
        
        let videoOutput = AVCaptureVideoDataOutput()
        videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
        captureSession.addOutput(videoOutput)
        videoOutput.connection(with: .video)?.videoOrientation = .portrait
        
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.layer.bounds
        previewLayer.videoGravity = .resizeAspectFill
        view.layer.addSublayer(previewLayer)
        
        captureSession.startRunning() // 配置完成后再启动会话,避免异常
    }
    
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
        // 应用预处理滤镜
        guard let processedPixelBuffer = applyPreprocessing(to: pixelBuffer) else { return }
        
        // 更新预览画面
        let previewImage = CIImage(cvPixelBuffer: processedPixelBuffer)
        if let previewCGImage = CIContext().createCGImage(previewImage, from: previewImage.extent) {
            DispatchQueue.main.async {
                self.previewLayer.contents = previewCGImage
            }
        }
        
        // 使用预处理后的图像进行识别
        let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: processedPixelBuffer, orientation: .up, options: [:])
        do {
            try imageRequestHandler.perform([detectQRCodeRequest])
        } catch {
            print(error)
        }
    }

    lazy var detectQRCodeRequest: VNDetectBarcodesRequest = {
        let request = VNDetectBarcodesRequest(completionHandler: { [weak self] request, error in
            guard let results = request.results as? [VNBarcodeObservation], let result = results.first, let payload = result.payloadStringValue else {
                return
            }
            DispatchQueue.main.async {
                let alert = UIAlertController(title: "识别成功", message: payload, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "确定", style: .default))
                self?.present(alert, animated: true)
            }
        })
        request.symbologies = [.qr] // 仅保留QR码识别,提升效率
        return request
    }()
    
    func applyPreprocessing(to pixelBuffer: CVPixelBuffer) -> CVPixelBuffer? {
        let inputImage = CIImage(cvPixelBuffer: pixelBuffer)
        
        // 1. 反转图像:黑底变白底,黑点变灰点
        invertFilter?.setValue(inputImage, forKey: kCIInputImageKey)
        guard let invertedImage = invertFilter?.outputImage else { return nil }
        
        // 2. 增强对比度:拉大灰度差异
        contrastFilter?.setValue(invertedImage, forKey: kCIInputImageKey)
        contrastFilter?.setValue(3.0, forKey: kCIInputContrastKey) // 对比度可根据实际场景调整
        guard let contrastImage = contrastFilter?.outputImage else { return nil }
        
        // 3. 阈值化:将灰点转为白点,形成明确黑白边界
        thresholdFilter?.setValue(contrastImage, forKey: kCIInputImageKey)
        thresholdFilter?.setValue(0.6, forKey: kCIInputThresholdKey) // 阈值可根据实际场景调整
        guard let outputImage = thresholdFilter?.outputImage else { return nil }
        
        // 渲染处理后的图像到新的像素缓冲区
        var outputPixelBuffer: CVPixelBuffer?
        let attributes = [
            kCVPixelBufferCGImageCompatibilityKey: true,
            kCVPixelBufferCGBitmapContextCompatibilityKey: true
        ] as CFDictionary
        CVPixelBufferCreate(nil, Int(outputImage.extent.width), Int(outputImage.extent.height), kCVPixelFormatType_32BGRA, attributes, &outputPixelBuffer)
        
        guard let buffer = outputPixelBuffer else { return nil }
        CIContext().render(outputImage, to: buffer)
        return buffer
    }
}

示例二维码样式

黑底黑点二维码示例1
黑底黑点二维码示例2
黑底黑点二维码示例3


内容的提问来源于stack exchange,提问作者Bhautik R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 17:47:06