Skip to content

Calling viewer.setImage() multiple times can cause memory leak #711

@Tinanuaa

Description

@Tinanuaa

Hi,
I'm using the itk-vtk-viewer in my react app as a component on one page, and it receives live frames from websocket and then display it by setImage(image,"live-frame"). But the browser can crash if the live viewer stayed for around 5 minutes. I notice the memory and CPU usage kept going on frame received. I've tried to use setInterval to set new random images on interval to confirm the memory leak was not caused by websocket, and I tried to pass viewer.getImage("live-frame") to viewer.setImage, and this doesn't cause memory leak either. My guess is each time I pass in the new image as ndarray, which will trigger the toMultiscaleSpatialImage to call itkImageToInMemoryMultiscaleSpatialImage, and this class might somehow cache the previous frames? Or the reference to the previous image is somehow still captured somewhere?

The related code snippet is as below

import "itk-vtk-viewer";
const Monitoring = () => {
const decodeArrayBuffer = (buffer) => {
    // Get a dataview of the arraybuffer
    const view = new DataView(buffer);
  
    // Extract the size of the json encoded header
    // as a 32bit int in pos 0 of the blob (little endian)
    const header_size = view.getUint32(0, true);
  
    // Create two new ArrayBuffers for the header and image
    // by slicing the original at the appropriate positions
    const header_bytes = buffer.slice(4, header_size + 4);
    const image_bytes = buffer.slice(header_size + 4);
  
    // Decode the header bytestream into a string and then
    // parse into a JSON object
    const decoder = new TextDecoder("utf-8");
  
    // TODO: Figure out why this requires so many parsings!
    const header_json = JSON.parse(
      JSON.parse(JSON.stringify(decoder.decode(header_bytes)))
    );
  
    // Return a dictionary with the header and image
    return { header: header_json, image: image_bytes };
  };

const [viewer, setViewer] = React.useState(null);
const liveFrameViewer = useRef(null);
  var intervalId = setInterval(function() {
     console.log("NOTE Interval reached every 5s");
             viewer?.setImage(
        encodeArray(ndarray(new Uint16Array(2560 * 2160).map(
         () => Math.random() * 1000
        ), [ 2560,2160])),"live-frame" 
       );

// the below code doesn't cause memory leak
//viewer?.setImage(
//         viewer.getImage("live-frame"),"live-frame" 
//       );
  }, 5000);

useEffect(() => {
    if (liveFrameViewer.current) {
      console.log("NOTE create viewer")
      itkVtkViewer
        .createViewer(liveFrameViewer.current, {
          image: encodeArray(ndarray(defaultImageData, [1024, 1024])),
          use2D: true,
          imageName:"live-frame",
        })
        .then((viewer) => {
          viewer.setImageColorMap('X Ray',0);
          setViewer(viewer);
        });
    }
  }, [liveFrameViewer]);

  return (
    <Box sx={{ width: "100%", height: "100%", minHeight: "1000px" }}>
        <Grid
              id="live-viewer"
              ref={liveFrameViewer}
              sx={{
                position: "relative",
                width: "100%",
                height: "70%",
                flex: "1 1 auto",
                p:3,
                m:3
              }}
            ></Grid>

    </Box>
  );
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions