问题描述
在我的应用程序中,我使用 VNImageRequestHandler 和自定义 MLModel 进行对象检测。
该应用适用于 14.5 之前的 iOS 版本。
当 iOS 14.5 到来时,它打破了一切。
- 每当
try handler.perform([visionRequest])
抛出错误时(Error Domain=com.apple.vis Code=11 "encountered unkNown exception" UserInfo={NSLocalizedDescription=encountered unkNown exception}),pixelBuffer
内存被保留并从未释放,它使 AVCaptureOutput 的缓冲区已满,然后新帧未出现。 - 我不得不改变如下代码,通过将pixelBuffer复制到另一个var,我解决了新帧不来的问题,但仍然出现内存泄漏问题。
由于内存泄漏,应用程序在一段时间后崩溃。
请注意,在 iOS 14.5 版本之前,检测工作完美,try handler.perform([visionRequest])
永远不会抛出任何错误。
这是我的代码:
private func predictWithPixelBuffer(sampleBuffer: CMSampleBuffer) {
guard let pixelBuffer = CMSampleBufferGetimageBuffer(sampleBuffer) else {
return
}
// Get additional info from the camera.
var options: [VNImageOption : Any] = [:]
if let cameraIntrinsicMatrix = CMGetAttachment(sampleBuffer,kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix,nil) {
options[.cameraIntrinsics] = cameraIntrinsicMatrix
}
autoreleasepool {
// Because of iOS 14.5,there is a bug that when perform vision request Failed,pixel buffer memory leaked so the AVCaptureOutput buffers is full,it will not output new frame any more,this is a temporary work around to copy pixel buffer to a new buffer,this currently make the memory increased a lot also. Need to find a better way
var clonePixelBuffer: CVPixelBuffer? = pixelBuffer.copy()
let handler = VNImageRequestHandler(cvPixelBuffer: clonePixelBuffer!,orientation: orientation,options: options)
print("[DEBUG] detecting...")
do {
try handler.perform([visionRequest])
} catch {
delegate?.detector(didOutputBoundingBox: [])
FailedCount += 1
print("[DEBUG] detect Failed \(FailedCount)")
print("Failed to perform Vision request: \(error)")
}
clonePixelBuffer = nil
}
}
有人遇到过同样的问题吗?如果是这样,您是如何解决的?
解决方法
我使用@Matthijs Hollemans CoreMLHelpers 库对此进行了部分修复。
我使用的模型有 300 个类和 2363 个锚点。我使用了大量 Matthijs 提供的代码 here 将模型转换为 MLModel。
在最后一步中,使用 3 个子模型构建管道:raw_ssd_output、解码器和 nms。对于此解决方法,您需要从管道中移除 nms
模型,并输出 raw_confidence
和 raw_coordinates
。
在您的应用中,您需要添加来自 CoreMLHelpers 的代码。
然后添加此函数以解码 MLModel 的输出:
func decodeResults(results:[VNCoreMLFeatureValueObservation]) -> [BoundingBox] {
let raw_confidence: MLMultiArray = results[0].featureValue.multiArrayValue!
let raw_coordinates: MLMultiArray = results[1].featureValue.multiArrayValue!
print(raw_confidence.shape,raw_coordinates.shape)
var boxes = [BoundingBox]()
let startDecoding = Date()
for anchor in 0..<raw_confidence.shape[0].int32Value {
var maxInd:Int = 0
var maxConf:Float = 0
for score in 0..<raw_confidence.shape[1].int32Value {
let key = [anchor,score] as [NSNumber]
let prob = raw_confidence[key].floatValue
if prob > maxConf {
maxInd = Int(score)
maxConf = prob
}
}
let y0 = raw_coordinates[[anchor,0] as [NSNumber]].doubleValue
let x0 = raw_coordinates[[anchor,1] as [NSNumber]].doubleValue
let y1 = raw_coordinates[[anchor,2] as [NSNumber]].doubleValue
let x1 = raw_coordinates[[anchor,3] as [NSNumber]].doubleValue
let width = x1-x0
let height = y1-y0
let x = x0 + width/2
let y = y0 + height/2
let rect = CGRect(x: x,y: y,width: width,height: height)
let box = BoundingBox(classIndex: maxInd,score: maxConf,rect: rect)
boxes.append(box)
}
let finishDecoding = Date()
let keepIndices = nonMaxSuppressionMultiClass(numClasses: raw_confidence.shape[1].intValue,boundingBoxes: boxes,scoreThreshold: 0.5,iouThreshold: 0.6,maxPerClass: 5,maxTotal: 10)
let finishNMS = Date()
var keepBoxes = [BoundingBox]()
for index in keepIndices {
keepBoxes.append(boxes[index])
}
print("Time Decoding",finishDecoding.timeIntervalSince(startDecoding))
print("Time Performing NMS",finishNMS.timeIntervalSince(finishDecoding))
return keepBoxes
}
然后当你收到 Vision 的结果时,你会像这样调用函数:
if let rawResults = vnRequest.results as? [VNCoreMLFeatureValueObservation] {
let boxes = self.decodeResults(results: rawResults)
print(boxes)
}
这个解决方案很慢,因为我移动数据和制定我的 BoundingBox
类型列表的方式。使用底层指针处理 MLMultiArray 数据会更有效,并且可能使用 Accelerate 找到每个锚框的最大分数和最佳类。
开发者门户上提供的 iOS 14.7 Beta 似乎已经解决了这个问题。
,就我而言,它通过强制 CoreML 仅在 CPU 和 GPU 上运行来帮助禁用神经引擎。这通常较慢,但不会引发异常(至少在我们的情况下)。最后,我们实施了一项政策,强制我们的某些模型不在某些 iOS 设备的神经引擎上运行。
请参阅 MLModelConfiguration.computeUntis 以限制 coreml 模型可以使用的硬件。