swift - конвертирует UIImage в чистый черно-белый и обнаруживает DataMatrix - PullRequest
2 голосов
/ 11 июля 2019

Мне действительно нужна помощь.Я создаю считыватель DataMatrix, и часть кодов имеет только белый фон и вызывает любые проблемы с AVFoundation, но другая часть имеет серый с мерцающим фоном (см. Изображение ниже), и это сводит меня с ума.

То, что я пробовал:

1) AVFoundation с его metaDataOutput прекрасно работает только с белым фоном, и не было успеха с мерцающим серым

2) zxing - на самом деле не могу найти ни одного работающегопример для swift, и их пример из GitHub также не находит Datamatrix на сером (с datamatrix в качестве типа кода qr), будет благодарен за учебник или что-то вроде этого (zxingObjC для Swift)

3) около 20 библиотек изcocoapods / github - снова ничего с серой спиной

4) потом я обнаружил, что Vision отлично распознает Datamatrix на белом фоне по фотографии, поэтому я решил поработать с этой библиотекой и изменил способ: больше не ловить видеовыход,только UIImages, затем обрабатывать их и обнаруживать DataMatrix с помощью Vision Framework.

И для преобразования цветов япробовал:

CIF-фильтры (ColorsControls, NoirEffect), фильтры GPU (монохромный, яркость, средний светимость, adaptiveTreshold), играющие с параметрами

В конце концов, у меня нет решения, которое будет работать 10 из 10 с моимНаклейки DataMatrix.иногда это работает с GPUImageAverageLuminanceThresholdFilter и GPUImageAdaptiveThresholdFilter, но с 20% удачей.

И эта 20% удача только при дневном свете, с электрическим светом, я думаю, мерцает.

Любой совет будет полезен для меня!Возможно, есть хорошее решение с Zxing для Swift, которое я не могу найти.Или нет необходимости использовать Vision и получать кадры из AVFoundation, но как?

I-nigma и т. Д. Отлично ловят мои наклейки из живого видео, так что должен быть способ.Версия моего сканера для Android использует Zxing, и я полагаю, что Zxing выполняет свою работу ..

Моя схема сканирования: enter image description here

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")
            }
        }
    }
}
...