Failed to allocate 1240308 bytes in function 'OutOfMemoryError'

Hello, I am developing a project with the Ionic Angular platform. In my project, I am using the Cordova Camera Preview plugin to continuously process images from the phone camera with OpenCV. That is, images from the phone camera are processed every 100ms. The application works fine for the first 30-40 seconds. However, after that, it gives the error “Failed to allocate 1240308 bytes in function ‘OutOfMemoryError’”, and stops image processing. I have written the code I use below. I am deleting all of the Mat variables I use, but I still get the same error. How can deal with OutOfMemoryError of Opencv.js. Thank you for your help.

TestFunction() {
    try {
      this.cameraPreviewService.getCameraSize().then(cameraSize => {
        this.cameraSize = JSON.parse(cameraSize);
        this.captureImage().then(res => {
          console.log(res)
          requestAnimationFrame(this.TestFunction.bind(this));
        })    
      }).catch(error => { console.log(error) });

    } catch (error) {
      console.log( error)
    }

  }

captureImage(): Promise<string> {
    return new Promise((resolve, reject) => {
      try {
        let data = JSON.parse(JSON.stringify(this.cameraPreviewOptions))

        this.cameraPreviewService.testFunction(data).subscribe(response => {
          this.imgUrl = 'data:image/png;base64,' + response.imageBytes;
          let 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);
          if (imageMAT) {
            imageMAT.delete();
            this.output.delete();
            this.rotationMatrix.delete();
          }
          resolve("Image process is Finished")

        })

      } catch (error) {
        resolve(error);
      }
    })
  }
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) {
          //Rectangle found  
      
          cv.drawContours(imageMAT, contours, i, [0, 255, 0, 255], 2, cv.LINE_AA, hierarchy, 100);
         
        }
      }
      cv.imshow(this.canvas, imageMAT);
      hierarchy.delete();
      gray.delete();

    } catch (error) {
      console.log("ERROR IN FINDCOUNTOUR====>>>>>", error); 
      throw cv.exceptionFromPtr(error).msg;
    }
  }

rotateImage(image) {
    try {
      this.output = new cv.Mat();
      let size = new cv.Size();

      size.width = image.cols;
      size.height = image.rows;

      // To add transparent borders
      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;
        // Pad out the left and right before rotating to make the width the same as the height
        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;
        // Pad out the top and bottom before rotating to make the height the same as the width
        cv.copyMakeBorder(image, this.output, padding, padding, 0, 0, cv.BORDER_CONSTANT, scalar);
        size.height = width;
      }

      // Do the rotation
      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 {
        /* These arguments might not be in the right order as my solution only needed height 
         * > width so I've just assumed this is the order they'll need to be for width >= 
         * height.
         */
        rectangle = new cv.Rect(padding, 0, height, width);
      }

      // Crop the image back to its original dimensions
      this.output = this.output.roi(rectangle);
    } catch (error) {
      console.log("ERROR IN ROTATEMAGE====>>>>>", error);
      throw cv.exceptionFromPtr(error).msg;
    }
  }

I have no idea how that plugin or Ionic Angular work, of how you fetch the frames every 100 ms, but if the architecture and your code manages to fetch frames and instatiate processing them faster than they are processed, they will pile up in memory.

So I’d check that, and use a flag to throw away new frames until the pipeline is clear.

How can I check the memory clean or not? Which step can I get new Frame and process it? Could you please give any idea about that? Thank you.

maybe, isolate the findRectforImage() from your process, and run it on fixed input (a single image) a few 1000 times ?

what about the contours ? also, this looks suspect:

I tried the give function with a fixed input as you suggested. This time it gave an error message saying Cannot perform Construct on a detached ArrayBuffer . It was giving this error message before as well. Sometimes it gives this error, and other times it gives an out-of-memory error. I think both of these errors are due to a lack of memory.

no idea, what you really did. please show !

I run to process only single image with this line ‘requestAnimationFrame(this.TestFunction.bind(this))’ . I use a fixed image like this.imgUrl = “…/…/assets/11.png” ; instead of this.imgUrl = ‘data:image/png;base64,’ + response.imageBytes;. So I process 11.png file repeatedly again and again instead of camerapreivew image. At the end, I still get same error.

sorry, still entirely useless ;(
we need a minimal, reproducable example
also:

which one, now ?

All example code was initially provided in the first message. When you asked me to use a fixed input, I modified my code accordingly. In the original code, I was capturing image data within the captureImage method using the cameraPreviewService method. I changed this section of the code to use a fixed image file instead. If you review the first message carefully, you will see that the imgUrl variable was being set using data from the cameraPreviewService method. In the latest message, however, imgUrl is being set using an image file from the assets folder, and this data is being processed continuously, just like before. In other words, instead of using data from the cameraPreviewService method, I tried to process a fixed image file from the assets folder continuously. As a result, I received an error message that said ‘Cannot perform Construct on a detached ArrayBuffer.’ I had encountered this error before, and it is usually caused by either an ArrayBuffer error or an out-of-memory error. In my latest attempt, I received an ArrayBuffer-related error. I hope I have explained this clearly enough.