【问题标题】:Memory Crash when detecting object with MLModel使用 MLModel 检测对象时发生内存崩溃
【发布时间】:2022-11-02 09:05:44
【问题描述】:

我在 CreateML 中制作了一个 MLModel,它将检测图像中的冰球。我使用手机上的摄像头拍摄视频,在录制视频时,我将每一帧转换为 CGImage 并尝试检测每一帧中的圆盘。 起初,当我收到内存崩溃时,我尝试删除同时运行的轨迹检测,但这并没有改变。在运行时监控内存使用情况时,我的应用程序使用少量且一致的内存;超出限制的是“其他进程”,这很令人困惑。我还删除了一个过滤掉置信度低(低于 0.5)的对象的 for 循环,但这也没有效果。

作为 MLModel 和机器学习的新手,有人能引导我走向正确的方向吗?如果需要更多细节,如果我遗漏了什么,请告诉我。我将附上所有代码,因为它只有 100 行左右,它可能对上下文很重要。但是,initializeCaptureSession 方法和 captureOutput 方法可能是要查看的方法。

import UIKit
import AVFoundation
import ImageIO
import Vision

class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate {
    
    var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
    var camera: AVCaptureDevice?
    var microphone: AVCaptureDevice?
    let session = AVCaptureSession()
    var videoDataOutput = AVCaptureVideoDataOutput()
    var audioDataOutput = AVCaptureAudioDataOutput()
    
    @IBOutlet var trajectoriesLabel: UILabel!
    @IBOutlet var pucksLabel: UILabel!
    
    
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initializeCaptureSession()
        // Do any additional setup after loading the view.
    }
    
    
    // Lazily create a single instance of VNDetectTrajectoriesRequest.
    private lazy var request: VNDetectTrajectoriesRequest = {
        request.objectMinimumNormalizedRadius = 0.0
        request.objectMaximumNormalizedRadius = 0.5
        return VNDetectTrajectoriesRequest(frameAnalysisSpacing: .zero, trajectoryLength: 10, completionHandler: completionHandler)
        
    }()

    // AVCaptureVideoDataOutputSampleBufferDelegate callback.
    func captureOutput(_ output: AVCaptureOutput,
                       didOutput sampleBuffer: CMSampleBuffer,
                       from connection: AVCaptureConnection) {
        // Process the results.

        do {
            let requestHandler = VNImageRequestHandler(cmSampleBuffer: sampleBuffer)
            guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else{
                print("cannot make pixelbuffer for image conversion")
                return
            }
            CVPixelBufferLockBaseAddress(pixelBuffer, .readOnly)
            let baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer)
            let width = CVPixelBufferGetWidth(pixelBuffer)
            let height = CVPixelBufferGetHeight(pixelBuffer)
            let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
            let colorSpace = CGColorSpaceCreateDeviceRGB()
            let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue)
            guard let context = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else{
                print("cannot make context for image conversion")
                return
            }
            guard let cgImage = context.makeImage() else{
                print("cannot make cgimage for image conversion")
                return
            }
            CVPixelBufferUnlockBaseAddress(pixelBuffer, .readOnly)
            let model = try VNCoreMLModel(for: PucksV7(configuration: MLModelConfiguration()).model)
            
            
            let request = VNCoreMLRequest(model: model)
            let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
            try? handler.perform([request])
            
            guard let pucks = request.results as? [VNDetectedObjectObservation] else{
                print("Could not convert detected pucks")
                return
            }
            
            DispatchQueue.main.async {
                self.pucksLabel.text = "Pucks: \(pucks.count)"
            }
            
            try requestHandler.perform([request])
        } catch {
            // Handle the error.
        }
    }

    func completionHandler(request: VNRequest, error: Error?) {
        //identify results
        guard let observations = request.results as? [VNTrajectoryObservation] else { return }
        // Process the results.
        self.trajectoriesLabel.text = "Trajectories: \(observations.count)"
    }
    
    func initializeCaptureSession(){
        session.sessionPreset = .hd1920x1080
        camera = AVCaptureDevice.default(for: .video)
        microphone = AVCaptureDevice.default(for: .audio)
        
        do{
            session.beginConfiguration()
            
            //adding camera
            let cameraCaptureInput = try AVCaptureDeviceInput(device: camera!)
            if session.canAddInput(cameraCaptureInput){
                session.addInput(cameraCaptureInput)
            }
            
            
            //output
            let queue = DispatchQueue(label: "output")
            if session.canAddOutput(videoDataOutput) {
                videoDataOutput.alwaysDiscardsLateVideoFrames = true
                videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
                videoDataOutput.setSampleBufferDelegate(self, queue: queue)
                session.addOutput(videoDataOutput)
                
            }
            
            let captureConnection = videoDataOutput.connection(with: .video)
            // Always process the frames
            captureConnection?.isEnabled = true
            do {
                try camera!.lockForConfiguration()
                camera!.unlockForConfiguration()
            } catch {
                print(error)
            }
            
            session.commitConfiguration()
            
            cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
            cameraPreviewLayer?.videoGravity = .resizeAspectFill
            cameraPreviewLayer?.frame = view.bounds
            cameraPreviewLayer?.connection?.videoOrientation = .landscapeRight
            
            view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
            DispatchQueue.global(qos: .background).async {
                self.session.startRunning()
            }
  
        } catch {
            print(error.localizedDescription)
        }
    }

}

【问题讨论】:

    标签: ios swift avcapturesession mlmodel


    【解决方案1】:

    执行速度。您调度线程的速度超过了它们的处理速度。

    根据我的经验,不是在这个平台上,使用 cnn 的对象检测速度不够快,无法以 30 fps 的速度实时处理来自相机的每一帧。

    借助硬件加速,例如“Apple 神经引擎”,这是可能的(我的办公桌上有一个 FPGA,它使用 15 瓦在“硬件”中实时执行此任务)。

    我建议每 50 帧处理一次并加快速度,直到失败。

    另一个问题是图像大小。为了提高性能,图像必须尽可能小并且仍能检测到特征。

    输入图像越大,需要的卷积层越多。大多数模型都在较小的范围内,例如 200x200 像素。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-09
      • 2018-05-24
      • 1970-01-01
      • 1970-01-01
      • 2019-08-11
      • 1970-01-01
      相关资源
      最近更新 更多