我需要一次对数百张图像进行文本识别,但每次,内存都会增加约 25mb+。 我已经在互联网上搜索过,但找不到导致记忆保留的原因或如何释放它。 通过插入中断,我可以看到每次调用 imageRequestHandler.perform() 时都会发生大小跳跃。 下面是相关代码。 有什么建议吗?
func textRecognition(image:CGImage) {
let textRecognitionRequest = VNRecognizeTextRequest(
completionHandler: self.handleDetectedText)
textRecognitionRequest.recognitionLevel = .accurate
textRecognitionRequest.recognitionLanguages = ["en_US"]
textRecognitionRequest.usesLanguageCorrection = false
// request handler
let textRequest = [textRecognitionRequest]
let imageRequestHandler = VNImageRequestHandler(cgImage: image, orientation: .up, options: [:])
DispatchQueue.global(qos: .userInitiated).async {
do {
// perform request
try imageRequestHandler.perform(textRequest)
} catch let error {
print("Error \(error)")
}
}
}
func handleDetectedText(request: VNRequest?, error:Error?){
if let error = error { print("ERROR: \(error)"); return }
guard let results = request?.results, results.count > 0 else {
DispatchQueue.main.async {
self.result_field.isEnabled=false
self.result_field.text = "Scan failed - Retry"
let desc = NSMutableAttributedString(string: "Retake Photo", attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 22, weight: .regular)])
self.take_button.setAttributedTitle(desc, for: UIControl.State.normal)
self.take_button.isHidden = false
self.take_button.isEnabled = true
}
return // code to process the text replaced by the 'return' statement
}}}
我建议进行分析,看看是什么占用了内存。但总的来说,我认为有两个领域可以探索潜在的改进:
图像可能会导致更高的内存使用量。我们(开发人员)的倾向是捕捉“最佳”形象。然而,文本识别可能不需要巨大的高质量图像(并且对于较小的图像给出相同的结果)。因此,请尝试在捕获和/或处理时缩小图像:
AVCapturePhotoSettings
进行实验,即photoQualityPrioritization
和照片format
,CGImage
尺寸/也许将其设为黑白。但这些操作也并非没有成本。或者尝试将 CVPixelBuffer
直接传递到 VNImageRequestHandler
(不转换为 CGImage - 如果您从相机拍摄图像)看看
autoreleasepool
是否有利于您的内存使用。最明显的位置是 imageRequestHandler.perform
:尝试在专用队列上执行它,并为该队列设置 autoreleaseFrequency: .workItem
:
private static let performQueue = DispatchQueue(
label: "your label",
qos: .userInitiated,
autoreleaseFrequency: .workItem, // <-- switching from default .never
target: .global(qos: .userInitiated) <-- same priority as the one you have
)
// ...
Self.performQueue.async {
try imageRequestHandler.perform(textRequest)
}
它有什么作用:
队列在块执行之前配置一个自动释放池,并在块执行完成后释放该池中的对象。
由于识别请求可能有很多临时对象,您可能会从此设置中受益,因为这些对象将在请求完成后立即释放(而不是“最终”)。不能保证它会有所帮助,但值得一试。
再次强调,这些只是建议,但如果减少内存对您来说如此重要,那么确实需要“性能评估”。虽然说实话我觉得25MB还是蛮合理的。