You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行OpenCV JS美颜代码数分钟后触发cv.multiply错误求助

OpenCV JS美颜功能运行1分钟后出现cv.multiply错误排查

我正尝试用OpenCV JS实现美颜功能,代码正常运行约1分钟后,控制台出现和cv.multiply相关的错误,想请教这是OpenCV本身的问题还是内存问题,以下是我的代码:

let video = document.getElementById("videoInput"); // video is the id of video tag
let canvas = document.getElementById('canvasOutput');

let ctx = canvas.getContext("2d");
video.width = 640;
video.height = 480;
navigator.mediaDevices
.getUserMedia({ video: true, audio: false })
.then(function(stream) {
video.srcObject = stream;
video.play();

const FPS = 30;
function processVideo() {
  let src = new cv.Mat(video.height, video.width, cv.CV_8UC4);
  let img = new cv.Mat();
  let cap = new cv.VideoCapture(video);
  
  let blur = new cv.Mat(video.height, video.width, cv.CV_8UC4);
  let hpf1 = new cv.Mat();
  let hpf = new cv.Mat(video.height, video.width, cv.CV_8UC4);
    
  let skinArea = new cv.Mat(video.height, video.width, cv.CV_8UC1);
  let begin = Date.now();
  cap.read(src);
  img=src
  cv.GaussianBlur(img, blur, new cv.Size(15, 15), 0,0,cv.BORDER_DEFAULT)
  cv.GaussianBlur(img, hpf1, new cv.Size(3, 3), 0,0,cv.BORDER_DEFAULT)

  cv.subtract(img,hpf1,hpf1)
  let minRange = new cv.Mat(img.rows, img.cols, img.type(), [120, 80, 60, 0]);
  let maxRange = new cv.Mat(img.rows, img.cols, img.type(), [255, 190, 120, 255]);
  cv.inRange(img, minRange, maxRange, skinArea);
  
  maxRange.delete()
  minRange.delete()

  let mat_1 = new cv.Mat();
  cv.imshow("canvasOutput1", skinArea);

  mat_1 = new cv.Mat(img.rows, img.cols, cv.CV_8UC1,new cv.Scalar(1,1,1))
  let blur_hpf= new cv.Mat();
  cv.add(blur,hpf1,blur_hpf)
  hpf1.delete()
  blur.delete()
  let blurred = new cv.Mat(),background = new cv.Mat();

  let ones = new cv.Mat(img.rows, img.cols, cv.CV_8UC1, new cv.Scalar(1));

  cv.min(skinArea, ones, skinArea)
  ones.delete()
  cv.subtract(mat_1, skinArea,blurred)
  cv.cvtColor(skinArea, skinArea, cv.COLOR_GRAY2BGRA);
  cv.cvtColor(blurred, blurred, cv.COLOR_GRAY2BGRA);

  cv.multiply(skinArea,blur_hpf,skinArea)

  let final_out = new cv.Mat()
  if(blurred == undefined){
    background = img
    final_out = img
  }
  else if(blurred == undefined || img == undefined){
    console.log("not image")
  }
  else{
  cv.multiply(blurred,img,background)
  cv.add(skinArea,background,final_out)
  }
  
  background.delete()
  blurred.delete()
  skinArea.delete()
  let kernel = cv.matFromArray(3,3,cv.CV_32FC1, [0.110, 0.160, 0.07,0.145, 0.143, 0.12,0.150, 0.150, 0.09]);
  cv.filter2D(final_out, final_out, -1, kernel);
  
  cv.imshow("canvasOutput", final_out);
  
  src.delete()
  final_out.delete()
          
  let delay = 100/FPS - (Date.now() - begin);
  setTimeout(processVideo, 0);
}
// schedule first one.
setTimeout(processVideo, 0);});

问题原因:内存泄漏

这不是OpenCV本身的问题,是代码里未正确释放Mat对象导致的内存泄漏。运行一段时间后内存耗尽,触发cv.multiply相关错误。

具体遗漏释放的对象:

  • hpf:初始化后全程未调用delete()
  • 第一个mat_1:创建后被新的Mat覆盖,原引用丢失无法释放
  • blur_hpf:使用后未调用delete()
  • kernel:创建后未调用delete()

修复方案:

给所有创建的Mat对象添加delete(),确保每个对象都被正确释放。修复后的代码如下:

let video = document.getElementById("videoInput"); // video is the id of video tag
let canvas = document.getElementById('canvasOutput');

let ctx = canvas.getContext("2d");
video.width = 640;
video.height = 480;
navigator.mediaDevices
.getUserMedia({ video: true, audio: false })
.then(function(stream) {
video.srcObject = stream;
video.play();

const FPS = 30;
function processVideo() {
  let src = new cv.Mat(video.height, video.width, cv.CV_8UC4);
  let img = new cv.Mat();
  let cap = new cv.VideoCapture(video);
  
  let blur = new cv.Mat(video.height, video.width, cv.CV_8UC4);
  let hpf1 = new cv.Mat();
  let hpf = new cv.Mat(video.height, video.width, cv.CV_8UC4);
    
  let skinArea = new cv.Mat(video.height, video.width, cv.CV_8UC1);
  let begin = Date.now();
  cap.read(src);
  img=src;
  cv.GaussianBlur(img, blur, new cv.Size(15, 15), 0,0,cv.BORDER_DEFAULT);
  cv.GaussianBlur(img, hpf1, new cv.Size(3, 3), 0,0,cv.BORDER_DEFAULT);

  cv.subtract(img,hpf1,hpf1);
  let minRange = new cv.Mat(img.rows, img.cols, img.type(), [120, 80, 60, 0]);
  let maxRange = new cv.Mat(img.rows, img.cols, img.type(), [255, 190, 120, 255]);
  cv.inRange(img, minRange, maxRange, skinArea);
  
  maxRange.delete();
  minRange.delete();

  // 先释放第一个mat_1再覆盖
  let mat_1 = new cv.Mat();
  mat_1.delete();
  mat_1 = new cv.Mat(img.rows, img.cols, cv.CV_8UC1,new cv.Scalar(1,1,1));
  
  cv.imshow("canvasOutput1", skinArea);

  let blur_hpf= new cv.Mat();
  cv.add(blur,hpf1,blur_hpf);
  hpf1.delete();
  blur.delete();
  let blurred = new cv.Mat(),background = new cv.Mat();

  let ones = new cv.Mat(img.rows, img.cols, cv.CV_8UC1, new cv.Scalar(1));

  cv.min(skinArea, ones, skinArea);
  ones.delete();
  cv.subtract(mat_1, skinArea,blurred);
  mat_1.delete(); // 释放mat_1
  cv.cvtColor(skinArea, skinArea, cv.COLOR_GRAY2BGRA);
  cv.cvtColor(blurred, blurred, cv.COLOR_GRAY2BGRA);

  cv.multiply(skinArea,blur_hpf,skinArea);
  blur_hpf.delete(); // 释放blur_hpf

  let final_out = new cv.Mat();
  if(blurred == undefined){
    background = img;
    final_out = img;
  }
  else if(blurred == undefined || img == undefined){
    console.log("not image");
  }
  else{
    cv.multiply(blurred,img,background);
    cv.add(skinArea,background,final_out);
  }
  
  background.delete();
  blurred.delete();
  skinArea.delete();
  let kernel = cv.matFromArray(3,3,cv.CV_32FC1, [0.110, 0.160, 0.07,0.145, 0.143, 0.12,0.150, 0.150, 0.09]);
  cv.filter2D(final_out, final_out, -1, kernel);
  kernel.delete(); // 释放kernel
  
  cv.imshow("canvasOutput", final_out);
  
  src.delete();
  final_out.delete();
  hpf.delete(); // 释放hpf
          
  let delay = 1000/FPS - (Date.now() - begin); // 修正延迟计算错误
  setTimeout(processVideo, delay > 0 ? delay : 0);
}
// schedule first one.
setTimeout(processVideo, 0);});

额外优化点:

  • 修正了延迟计算错误:原代码100/FPS改为1000/FPS,确保帧率稳定
  • 避免Mat对象被覆盖前未释放,比如第一个mat_1先delete再重新赋值

内容的提问来源于stack exchange,提问作者vaibhav jain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 16:20:48