简体   繁体   中英

infinite requestAnimationFrame Loop in next.js

I am trying to recreate this app in next.js https://codelabs.developers.google.com/tensorflowjs-transfer-learning-teachable-machine#0

To start the data collection, I added a onMouseDown MouseEvent on the button which, triggers this code:

const handleGatherDataForClass: MouseEventHandler = (e) => {
  let classNumber = parseInt(e.target.getAttribute('data-1hot'));
  console.log('inside EventHandler:', { classNumber });
  let state = gatherDataState === STOP_DATA_GATHER ? classNumber : STOP_DATA_GATHER;
  console.log({ state });
  setGatherDataState(state);
};

Where

  • classNumber = positive integer

  • STOP_DATA_GATHER = -1

  • gatherDataState = -1 (default)

Here is the original code snippet from the working app: https://codelabs.developers.google.com/tensorflowjs-transfer-learning-teachable-machine#11

After the state of the gatherDataState variable changes, the useEffect hook should run the dataGatherLoop function, which takes frames from the video stream and converts it into tensors:

useEffect(() => {
  dataGatherLoop();
}, [gatherDataState]);


function dataGatherLoop() {
  console.log('inside Loop: ', {
    gatherDataState
  });

  if (videoPlaying && gatherDataState !== STOP_DATA_GATHER) {
    let imageFeatures = tf.tidy(function() {
      let videoFrameAsTensor = tf.browser.fromPixels(VIDEO);
      let resizedTensorFrame = tf.image.resizeBilinear(
        videoFrameAsTensor, [MOBILE_NET_INPUT_HEIGHT, MOBILE_NET_INPUT_WIDTH],
        true
      );
      let normalizedTensorFrame = resizedTensorFrame.div(255);
      return mobilenet.predict(normalizedTensorFrame.expandDims()).squeeze();
    });
    imageFeatures.print();

    setTrainData((prev) => ({
      trainX: [...prev.trainX, imageFeatures],
      trainY: [...prev.trainY, gatherDataState],
    }));

    // Intialize array index element if currently undefined.
    let newCount = [...examplesCount];
    if (examplesCount[gatherDataState] === undefined) {
      newCount[gatherDataState] = 1;
      setExamplesCount(newCount);
    } else {
      newCount[gatherDataState]++;
      setExamplesCount(newCount);
    }


    window.requestAnimationFrame(dataGatherLoop);
  }
}

This loop runs, as long as the gatherDataState variable is a positive integer (not equal to -1 ) After the mouse button is released, an onMouseUp event is triggered which runs the same handleGatherDataForClass function as the onMouseDown event. This should change the state back to -1 and therefore stop the Loop.

Problem:
Even though the state is changing to -1 after the onMouseUp event is triggered, the gatherDataState ends up being a positive integer every time.. Therefore the loop is not stopping. (there is NO setGatherDataState function anywhere else in the code)

I tried:

  • writing the gatherDataLoop function inside the handleGatherDataForClass event handler and passing the gatherDataState variable as an argument
  • using a global variable for gatherDataState instead of a react state to save the current gatherDataState
  • canceling the requestAnimationFrame loop with the cancelAnimationFrame function (saving the id globally and as state)

I am not exactly sure why, but

  • using useRef to store the values being used inside the requestAnimationFrame loop, as well as the id for canceling the loop and
  • taking the loop out of the useEffect Hook and writing it inside the ClickEventhandler

worked for me. Here is the code:

 const handleGatherDataForClass: MouseEventHandler = (e) => {
    let classNumber = parseInt(e.target.getAttribute('data-1hot'));
    gatherDataStateRef.current = classNumber;
    isCollectingRef.current = !isCollectingRef.current;
    if (isCollectingRef.current) {
      collectRequestRef.current = requestAnimationFrame(dataGatherLoop);
    } else {
      cancelAnimationFrame(collectRequestRef.current);
    }
  };


function dataGatherLoop() {
    console.log('inside Loop: ', gatherDataStateRef.current);

    let imageFeatures = tf.tidy(function () {
      let videoFrameAsTensor = tf.browser.fromPixels(VIDEO);
      let resizedTensorFrame = tf.image.resizeBilinear(
        videoFrameAsTensor,
        [MOBILE_NET_INPUT_HEIGHT, MOBILE_NET_INPUT_WIDTH],
        true
      );
      let normalizedTensorFrame = resizedTensorFrame.div(255);
      return mobilenet.predict(normalizedTensorFrame.expandDims()).squeeze();
    });
    imageFeatures.print();

    setTrainData((prev) => ({
      trainX: [...prev.trainX, imageFeatures],
      trainY: [...prev.trainY, gatherDataStateRef.current],
    }));

    if (examplesCountRef.current[gatherDataStateRef.current] === undefined) {
      examplesCountRef.current[gatherDataStateRef.current] = 1;
    } else {
      examplesCountRef.current[gatherDataStateRef.current] += 1;
    }

    collectRequestRef.current = requestAnimationFrame(dataGatherLoop);
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM