我真的需要帮助。我正在创建一个 DataMatrix 阅读器,部分代码只有白色背景,会导致 AVFoundation 出现任何问题,但另一部分是灰色的,带有微光背景(见下图),这让我抓狂。
我试过的:
1) AVFoundation 及其 metaDataOutput 仅适用于白色背景,而微光灰色则没有成功
2)zxing - 实际上找不到任何适用于 swift 的示例,并且他们从 GitHub 的示例也找不到灰色的 Datamatrix(使用 datamatrix 作为 qr 代码类型),将感谢这样的教程或 smth(zxingObjC for Swift)
3) 来自 cocoapods/github 的大约 20 个库 - 没有灰色的后背
4)然后我发现Vision完美地从照片中检测到白色的Datamatrix,所以我决定使用这个库并改变了方式:不再捕获视频输出,只捕获UIImages,然后处理它们并使用Vision框架检测DataMatrix。
并转换我尝试过的颜色:
CIFilters(ColorsControls、NoirEffect)、GPU 过滤器(单色、亮度、averageLuminance、adaptiveTreshold)与参数一起玩
最后,我没有可以使用我的 DataMatrix 贴纸从 10 到 10 的解决方案。有时它与GPUImageAverageLuminanceThresholdFilter
and一起使用GPUImageAdaptiveThresholdFilter
,但大约 20% 的运气。
我想,这 20% 的运气只在白天,在电灯下会发出微光。
任何建议都会对我有所帮助!也许 Zxing for Swift 有很好的解决方案,但我找不到。或者不需要使用 Vision 并从 AVFoundation 获取帧,但是如何?
I-nigma 等从实时视频中完美地捕捉到我的贴纸,所以应该有办法。我的扫描仪的 Android 版本使用 Zxing,我猜 Zxing 可以完成这项工作。
fileprivate func createSession(input:AVCaptureDeviceInput) -> Bool {
let session = AVCaptureSession()
if session.canAddInput(input) {
session.addInput(input)
} else {
return false
}
let output = AVCaptureVideoDataOutput()
if session.canAddOutput(output) {
output.setSampleBufferDelegate(self, queue: DispatchQueue(label: "com.output"))
output.videoSettings = [String(kCVPixelBufferPixelFormatTypeKey):kCVPixelFormatType_32BGRA]
output.alwaysDiscardsLateVideoFrames = true
session.addOutput(output)
}
self.videoSession = session
return true
}
extension ViewController: AVCaptureVideoDataOutputSampleBufferDelegate {
func convert(cmage:CIImage) -> UIImage
{
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
let threshold:Double = 1.0 / 3
let timeStamp = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
currentTime = Double(timeStamp.value) / Double(timeStamp.timescale)
if (currentTime - lastTime > threshold) {
if let image = imageFromSampleBuffer(sampleBuffer: sampleBuffer),
let cgImage = image.cgImage {
let ciImage = CIImage(cgImage: cgImage)
// with CIFilter
// let blackAndWhiteImage = ciImage.applyingFilter("CIColorControls", parameters: [kCIInputContrastKey: 2.5,
// kCIInputSaturationKey: 0,
// kCIInputBrightnessKey: 0.5])
// let imageToScan = convert(cmage: blackAndWhiteImage) //UIImage(ciImage: blackAndWhiteImage)
// resImage = imageToScan
// scanBarcode(cgImage: imageToScan.cgImage!)
let filter = GPUImageAverageLuminanceThresholdFilter()
filter.thresholdMultiplier = 0.7
let imageToScan = filter.image(byFilteringImage: image)
resImage = imageToScan!
scanBarcode(cgImage: imageToScan!.cgImage!)
}
}
}
fileprivate func imageFromSampleBuffer(sampleBuffer : CMSampleBuffer) -> UIImage? {
guard let imgBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
return nil
}
// Lock the base address of the pixel buffer
CVPixelBufferLockBaseAddress(imgBuffer, CVPixelBufferLockFlags.readOnly)
// Get the number of bytes per row for the pixel buffer
let baseAddress = CVPixelBufferGetBaseAddress(imgBuffer)
// Get the number of bytes per row for the pixel buffer
let bytesPerRow = CVPixelBufferGetBytesPerRow(imgBuffer)
// Get the pixel buffer width and height
let width = CVPixelBufferGetWidth(imgBuffer)
let height = CVPixelBufferGetHeight(imgBuffer)
// Create a device-dependent RGB color space
let colorSpace = CGColorSpaceCreateDeviceRGB()
// Create a bitmap graphics context with the sample buffer data
var bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Little.rawValue
bitmapInfo |= CGImageAlphaInfo.premultipliedFirst.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
//let bitmapInfo: UInt32 = CGBitmapInfo.alphaInfoMask.rawValue
let context = CGContext.init(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo)
// Create a Quartz image from the pixel data in the bitmap graphics context
let quartzImage = context?.makeImage()
// Unlock the pixel buffer
CVPixelBufferUnlockBaseAddress(imgBuffer, CVPixelBufferLockFlags.readOnly)
CVPixelBufferLockBaseAddress(imgBuffer, .readOnly)
if var image = quartzImage {
if shouldInvert, let inverted = invertImage(image) {
image = inverted
}
let output = UIImage(cgImage: image)
return output
}
return nil
}
fileprivate func scanBarcode(cgImage: CGImage) {
let barcodeRequest = VNDetectBarcodesRequest(completionHandler: { request, _ in
self.parseResults(results: request.results)
})
let handler = VNImageRequestHandler(cgImage: cgImage, options: [.properties : ""])
guard let _ = try? handler.perform([barcodeRequest]) else {
return print("Could not scan")
}
}
fileprivate func parseResults(results: [Any]?) {
guard let results = results else {
return print("No results")
}
print("GOT results - ", results.count)
for result in results {
if let barcode = result as? VNBarcodeObservation {
if let code = barcode.payloadStringValue {
DispatchQueue.main.async {
self.videoSession?.stopRunning()
self.resultLabel.text = code
self.blackWhiteImageView.image = self.resImage //just to check from what image code scanned
}
} else {
print("No results 2")
}
} else {
print("No results 1")
}
}
}
}