Отображение обрезанного cvpixelbuffer как uiimage в представлении swfitui - PullRequest
1 голос
/ 26 мая 2020

Я занимаюсь хобби-разработчиком и делаю приложение для своей работы. Мне нужно обрезать обнаруженный объект (этикетку продукта) и отправить его в другое представление (чтобы позже применить ocr). Теперь я загрузил пример яблока, на котором отображается желтый прямоугольник с закругленными углами, я заменяю модель на созданную мной и успешно распознаю изображение (бумажная этикетка). Теперь я хочу отобразить его в быстром просмотре. Это мой код

Swift UI uiviewcontrollerpresentable struct

    struct CameraCont:UIViewControllerRepresentable {

    @Binding var croppedImage:UIImage?

    func makeCoordinator() -> Coordinator {
        return Coordinator(croppedImage: $croppedImage)
    }

    class Coordinator:NSObject, SendImageToSwiftUIDelegate {

        @Binding var croppedImage:UIImage!

        init(croppedImage:Binding<UIImage?>) {
            _croppedImage = croppedImage
        }

        func sendImage(image: UIImage) {
            croppedImage = image
            print("Delegate called")
        }
    }

    typealias UIViewControllerType = VisionObjectRecognitionViewController

    func makeUIViewController(context: Context) -> VisionObjectRecognitionViewController {
        let  vision = VisionObjectRecognitionViewController()
        vision.sendImageDelegate = context.coordinator
        return vision
    }

    func updateUIViewController(_ uiViewController: VisionObjectRecognitionViewController, context: Context) {

    }
  }

Теперь я создал протокол для связи с uiviewpresentable, и его делегат успешно вызывается. Это мой подкласс контроллера представления.

protocol SendImageToSwiftUIDelegate {
    func sendImage(image:UIImage)
}

class VisionObjectRecognitionViewController: ViewController {

    var sendImageDelegate:SendImageToSwiftUIDelegate!

    private var detectionOverlay: CALayer! = nil
    private var observationWidthBiggherThan180 = false
    private var rectToCrop = CGRect()

    // Vision parts
    private var requests = [VNRequest]()

    @discardableResult
    func setupVision() -> NSError? {
        // Setup Vision parts
        let error: NSError! = nil

        guard let modelURL = Bundle.main.url(forResource: "exampleModelFP16", withExtension: "mlmodelc") else {
            return NSError(domain: "VisionObjectRecognitionViewController", code: -1, userInfo: [NSLocalizedDescriptionKey: "Model file is missing"])
        }
        do {
            let visionModel = try VNCoreMLModel(for: MLModel(contentsOf: modelURL))

            let objectRecognition = VNCoreMLRequest(model: visionModel, completionHandler: { (request, error) in
                DispatchQueue.main.async(execute: {
                    // perform all the UI updates on the main queue
                    if let results = request.results {
                        self.drawVisionRequestResults(results)
                    }
                })
            })
            self.requests = [objectRecognition]
        } catch let error as NSError {
            print("Model loading went wrong: \(error)")
        }

        return error
    }

    func drawVisionRequestResults(_ results: [Any]) {
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        detectionOverlay.sublayers = nil // remove all the old recognized objects
        for observation in results where observation is VNRecognizedObjectObservation {
            guard let objectObservation = observation as? VNRecognizedObjectObservation else {
                continue
            }
            // Select only the label with the highest confidence.
            let topLabelObservation = objectObservation.labels[0]
            let objectBounds = VNImageRectForNormalizedRect(objectObservation.boundingBox, Int(bufferSize.width), Int(bufferSize.height))


            let shapeLayer = self.createRoundedRectLayerWithBounds(objectBounds)

            let textLayer = self.createTextSubLayerInBounds(objectBounds,
                                                            identifier: topLabelObservation.identifier,
                                                            confidence: topLabelObservation.confidence)
            shapeLayer.addSublayer(textLayer)
            detectionOverlay.addSublayer(shapeLayer)
            if shapeLayer.bounds.size.width > 180 {
                // perform crop and apply perspective filter
                self.observationWidthBiggherThan180 = true
                self.rectToCrop = shapeLayer.bounds

            } else {
                self.observationWidthBiggherThan180 = false
            }
        }
        self.updateLayerGeometry()
        CATransaction.commit()
    }

    // MARK: - Function to capure the output of the camera and perform the image recognition

    override func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
            return
        }

        if observationWidthBiggherThan180 {
            let imageToCrop = CIImage(cvPixelBuffer: pixelBuffer)
            let perspectiveTransform = CIFilter(name: "CIPerspectiveTransform")
            sendImageDelegate.sendImage(image: UIImage(ciImage: imageToCrop))
            print(imageToCrop)
        }

        let exifOrientation = exifOrientationFromDeviceOrientation()

        let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, orientation: exifOrientation, options: [:])
        do {
            try imageRequestHandler.perform(self.requests)

        } catch {
            print(error)
        }
    }

    override func setupAVCapture() {
        super.setupAVCapture()

        // setup Vision parts
        setupLayers()
        updateLayerGeometry()
        setupVision()

        // start the capture
        startCaptureSession()
    }

    func setupLayers() {
        detectionOverlay = CALayer() // container layer that has all the renderings of the observations
        detectionOverlay.name = "DetectionOverlay"
        detectionOverlay.bounds = CGRect(x: 0.0,
                                         y: 0.0,
                                         width: bufferSize.width,
                                         height: bufferSize.height)
        detectionOverlay.position = CGPoint(x: rootLayer.bounds.midX, y: rootLayer.bounds.midY)
        rootLayer.addSublayer(detectionOverlay)
    }

    func updateLayerGeometry() {
        let bounds = rootLayer.bounds
        var scale: CGFloat

        let xScale: CGFloat = bounds.size.width / bufferSize.height
        let yScale: CGFloat = bounds.size.height / bufferSize.width

        scale = fmax(xScale, yScale)
        if scale.isInfinite {
            scale = 1.0
        }
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)

        // rotate the layer into screen orientation and scale and mirror
        detectionOverlay.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: scale, y: -scale))
        // center the layer
        detectionOverlay.position = CGPoint(x: bounds.midX, y: bounds.midY)

        CATransaction.commit()

    }

    func createTextSubLayerInBounds(_ bounds: CGRect, identifier: String, confidence: VNConfidence) -> CATextLayer {
        let textLayer = CATextLayer()
        textLayer.name = "Object Label"
        let formattedString = NSMutableAttributedString(string: String(format: "\(identifier)\nConfidence:  %.2f", confidence))
        let largeFont = UIFont(name: "Helvetica", size: 24.0)!
        formattedString.addAttributes([NSAttributedString.Key.font: largeFont], range: NSRange(location: 0, length: identifier.count))
        textLayer.string = formattedString
        textLayer.bounds = CGRect(x: 0, y: 0, width: bounds.size.height - 10, height: bounds.size.width - 10)
        textLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
        textLayer.shadowOpacity = 0.7
        textLayer.shadowOffset = CGSize(width: 2, height: 2)
        textLayer.foregroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [0.0, 0.0, 0.0, 1.0])
        textLayer.contentsScale = 2.0 // retina rendering
        // rotate the layer into screen orientation and scale and mirror
        textLayer.setAffineTransform(CGAffineTransform(rotationAngle: CGFloat(.pi / 2.0)).scaledBy(x: 1.0, y: -1.0))
        return textLayer
    }

    func createRoundedRectLayerWithBounds(_ bounds: CGRect) -> CALayer {
        let shapeLayer = CALayer()
        shapeLayer.bounds = bounds
        shapeLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
        shapeLayer.name = "Found Object"
        shapeLayer.backgroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1.0, 1.0, 0.2, 0.4])
        shapeLayer.cornerRadius = 7
        return shapeLayer
    }

}

Если размер представления больше 180 пикселей (телефон рядом с этикеткой). Проблема в том, что я не могу отобразить все, что я обрезал из буфера пикселей. Когда я закрываю свое отображение, отображается пустое пространство и не отображается никаких изображений.

    struct ContentView: View {


    @State private var image:Image?
    @State private var showingCamera = false
    @State private var inputImage:UIImage?

    var body: some View {
        VStack {
            image?
                .resizable()
                .scaledToFit()
            Button("Show camera") {
                self.showingCamera = true
            }
        }
        .sheet(isPresented: $showingCamera, onDismiss: loadImage) {
            CameraCont(croppedImage: self.$inputImage)
                .edgesIgnoringSafeArea(.top)
        }
    }

    func loadImage() {
        guard let inputImage = inputImage else {return}
        image = Image(uiImage: inputImage)
    }
}

Если кто-то знает, в чем я ошибаюсь, дайте мне подсказку. Я думаю, проблема в том, когда я создаю изображение
let imageToCrop = CIImage (cvPixelBuffer: pixelBuffer) Но я не знаю решения.

Большое спасибо.

Также мне нужно Чтобы узнать об AVFoundation и преобразовании координат, может ли кто-нибудь порекомендовать мне хорошее место для его изучения?

1 Ответ

0 голосов
/ 26 мая 2020

Решено. Да, когда я создавал UIImage, была неправильная линия. Это правильный способ создания UIImage:

    if observationWidthBiggherThan180 {
        let ciimage : CIImage = CIImage(cvPixelBuffer: pixelBuffer)
        let context:CIContext = CIContext(options: nil)
        let cgImage:CGImage = context.createCGImage(ciimage, from: ciimage.extent)!
        let myImage:UIImage = UIImage(cgImage: cgImage)
        sendImageDelegate.sendImage(image:myImage)
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...