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:
gatherDataLoop
function inside the handleGatherDataForClass
event handler and passing the gatherDataState
variable as an argumentgatherDataState
instead of a react state to save the current gatherDataState
requestAnimationFrame
loop with the cancelAnimationFrame
function (saving the id globally and as state)I am not exactly sure why, but
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);
}