Утечка памяти CoreML в iOS 14.5
В своем приложении я использовал VNImageRequestHandler с настраиваемой моделью MLModel для обнаружения объектов.
Приложение отлично работает с версиями iOS до 14.5.
Когда вышла iOS 14.5, все сломалось.
- Каждый раз при возникновении ошибки (Error Domain = com.apple.vis Code = 11 «обнаружено неизвестное исключение» UserInfo = {NSLocalizedDescription = обнаружено неизвестное исключение})
pixelBuffer
память удерживается и никогда не освобождается, буферы AVCaptureOutput были заполнены, а новый кадр не пришел. - Мне нужно изменить код, как показано ниже, скопировав pixelBuffer в другую переменную, я решил проблему, заключающуюся в том, что новый кадр не поступает, но проблема с утечкой памяти все еще возникает.
Из-за утечки памяти приложение через некоторое время вылетало.
Обратите внимание, что до 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
}
}
Кто-нибудь испытывал такую же проблему, как вы, ребята, ее исправляете?
3 ответа
Бета-версия iOS 14.7, доступная на портале для разработчиков, похоже, устранила эту проблему.
У меня есть частичное исправление с помощью библиотеки @Matthijs Hollemans CoreMLHelpers.
В моей модели 300 классов и 2363 якоря. Я использовал много кода Matthijs предоставленного здесь , чтобы преобразовать модель в MLModel.
На последнем этапе конвейер строится с использованием 3 подмоделей: raw_ssd_output, декодера и nms. Для этого обходного пути вам необходимо удалить
nms
модель из конвейера, а вывод
raw_confidence
а также
raw_coordinates
.
В свое приложение вам нужно добавить код из CoreMLHelpers.
Затем добавьте эту функцию для декодирования вывода из вашей модели ML:
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, чтобы найти максимальную оценку и лучший класс для каждого блока привязки.
В моем случае это помогло отключить нейронный движок, заставив CoreML работать только на CPU и GPU. Часто это происходит медленнее, но не вызывает исключения (по крайней мере, в нашем случае). В конце мы внедрили политику, запрещающую запускать некоторые из наших моделей на нейронном движке для определенных устройств iOS.
См. MLModelConfiguration.computeUntis, чтобы ограничить аппаратную модель, которую может использовать coreml.