OpenCV.js内存溢出问题:Ionic Angular相机图像处理报错求助
解决OpenCV.js在Ionic Angular项目中的OutOfMemoryError问题
针对你遇到的内存泄漏问题,以下是具体的修复步骤和优化建议:
1. 释放未清理的轮廓资源
在findRectforImage函数中,你创建了MatVector类型的contours,但未释放它;同时循环中获取的每个cnt对象也需要手动释放,否则会快速堆积内存:
findRectforImage(imageMAT) { try { let rectCoordinates = []; let contours = new cv.MatVector(); let hierarchy = new cv.Mat(); let gray = new cv.Mat(); cv.cvtColor(imageMAT, gray, cv.COLOR_RGBA2GRAY, 0); cv.threshold(gray, gray, 100, 255, cv.THRESH_BINARY); cv.findContours(gray, contours, hierarchy, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE); for (let i = 0; i < contours.size(); ++i) { let cnt = contours.get(i); let rect = cv.boundingRect(cnt); let aspectRatio = rect.width / rect.height; if (aspectRatio >= 0.9 && aspectRatio <= 1.1 && rect.width >= 15) { cv.drawContours(imageMAT, contours, i, [0, 255, 0, 255], 2, cv.LINE_AA, hierarchy, 100); } cnt.delete(); // 释放单个轮廓的内存 } cv.imshow(this.canvas, imageMAT); // 释放所有创建的资源 hierarchy.delete(); gray.delete(); contours.delete(); // 释放整个轮廓集合 } catch (error) { console.log("ERROR IN FINDCOUNTOUR>>>>>>>", error); // 出错时也要确保资源释放 if (contours) contours.delete(); if (hierarchy) hierarchy.delete(); if (gray) gray.delete(); throw cv.exceptionFromPtr(error).msg; } }
2. 修复rotateImage中的内存泄漏
rotateImage中存在两处内存泄漏:重复分配this.output未释放旧对象、roi操作丢弃原Mat对象。修改如下:
rotateImage(image) { try { // 先释放之前的output(如果存在) if (this.output) { this.output.delete(); } this.output = new cv.Mat(); let size = new cv.Size(); size.width = image.cols; size.height = image.rows; let scalar = new cv.Scalar(0, 0, 0, 0); let center; let padding; let height = size.height; let width = size.width; if (height > width) { center = new cv.Point(height / 2, height / 2); padding = (height - width) / 2; cv.copyMakeBorder(image, this.output, 0, 0, padding, padding, cv.BORDER_CONSTANT, scalar); size.width = height; } else { center = new cv.Point(width / 2, width / 2); padding = (width - height) / 2; cv.copyMakeBorder(image, this.output, padding, padding, 0, 0, cv.BORDER_CONSTANT, scalar); size.height = width; } // 释放旧的旋转矩阵(如果存在) if (this.rotationMatrix) { this.rotationMatrix.delete(); } this.rotationMatrix = cv.getRotationMatrix2D(center, -90, 1); cv.warpAffine( this.output, this.output, this.rotationMatrix, size, cv.INTER_LINEAR, cv.BORDER_CONSTANT, new cv.Scalar() ); let rectangle; if (height > width) { rectangle = new cv.Rect(0, padding, height, width); } else { rectangle = new cv.Rect(padding, 0, height, width); } // 处理roi时,先保存原output,创建新对象后释放原内存 let originalOutput = this.output; this.output = this.output.roi(rectangle); originalOutput.delete(); } catch (error) { console.log("ERROR IN ROTATEMAGE>>>>>>>", error); // 出错时释放已创建的资源 if (this.output) { this.output.delete(); this.output = null; } if (this.rotationMatrix) { this.rotationMatrix.delete(); this.rotationMatrix = null; } throw cv.exceptionFromPtr(error).msg; } }
3. 避免并发图像处理堆积
当前的requestAnimationFrame调用可能导致上一次图像处理未完成,下一次任务又启动,造成多份Mat同时占用内存。添加状态标记确保串行执行:
// 组件中添加状态变量 private isProcessing = false; TestFunction() { if (this.isProcessing) return; // 避免并发执行 this.isProcessing = true; try { this.cameraPreviewService.getCameraSize().then(cameraSize => { this.cameraSize = JSON.parse(cameraSize); this.captureImage().then(res => { console.log(res) this.isProcessing = false; requestAnimationFrame(this.TestFunction.bind(this)); }).catch(error => { console.log(error); this.isProcessing = false; requestAnimationFrame(this.TestFunction.bind(this)); }) }).catch(error => { console.log(error); this.isProcessing = false; requestAnimationFrame(this.TestFunction.bind(this)); }); } catch (error) { console.log(error); this.isProcessing = false; requestAnimationFrame(this.TestFunction.bind(this)); } }
4. 完善错误处理中的资源释放
在captureImage中,确保无论成功还是失败,所有创建的Mat都被释放:
captureImage(): Promise<string> { return new Promise((resolve, reject) => { let imageMAT = null; try { let data = JSON.parse(JSON.stringify(this.cameraPreviewOptions)) this.cameraPreviewService.testFunction(data).subscribe(response => { try { this.imgUrl = 'data:image/png;base64,' + response.imageBytes; imageMAT = cv.imread(this.ImageElement); let desiredWidth = this.answerShadowDivWidth; let desiredHeight = this.answerShadowDivHeight; let dsize = new cv.Size(desiredWidth, desiredHeight); this.rotateImage(imageMAT); cv.resize(this.output, imageMAT, dsize, 0, 0, cv.INTER_LINEAR); this.findRectforImage(imageMAT); } catch (innerError) { console.log(innerError); resolve(innerError); } finally { // 确保资源释放 if (imageMAT) imageMAT.delete(); if (this.output) this.output.delete(); if (this.rotationMatrix) this.rotationMatrix.delete(); resolve("Image process is Finished"); } }, error => { // 订阅错误时释放资源 if (imageMAT) imageMAT.delete(); if (this.output) this.output.delete(); if (this.rotationMatrix) this.rotationMatrix.delete(); resolve(error); }); } catch (error) { if (imageMAT) imageMAT.delete(); if (this.output) this.output.delete(); if (this.rotationMatrix) this.rotationMatrix.delete(); resolve(error); } }) }
额外优化建议
- 降低处理频率:如果业务允许,将处理间隔从100ms调整为200ms或更长,减少内存占用压力。
- 缩小图像尺寸:在
cv.imread后先将图像缩放到更小的尺寸,再进行旋转、轮廓检测等操作,减少单张图像的内存占用。 - 复用Mat对象:提前初始化常用的Mat对象,每次使用前释放旧数据再重新赋值,避免频繁创建销毁对象。
内容的提问来源于stack exchange,提问作者Halil POLAT
相关产品推荐
相关产品推荐

