I'm trying to run the coco-ssd model using tensorflowJs
in a react
app (NOT react native);
I trialled it with images and got the desired outcome but now I want to run a video with it so I can track certain objects throughout the video.
The issue I am facing right now is that the model only returns a single prediction for the entire video. So it seems it is registering the first frame of the video and then nothing more.
In the component I am using to run this I have two video elements. Video1 is the video preview from the file upload. Right next to it I have video2 which is overlayed with a canvas element so that I can draw the bounding boxes, etc.
Initially I tried to simple pass in the video1 element to the model, but this did nothing. Then did some research into one of tensorflowjs
demos and saw they were capturing the stream from the video element and assigning it to the srcObj
of video2.
This has worked but only by capturing the first and single prediction so far.
It takes a dataFile
(the raw result from an input
element) and the already preloaded tensorflowjs
models as props.
Here is my component:
import React, { useEffect, useRef, useState } from "react";
import { connect } from "react-redux";
import Loader from "../loader";
function ResultsComponent(props) {
const { dataFile, models } = props;
const videoRef = useRef(null);
const videoStreamRef = useRef(null);
const canvasRef = useRef(null);
const [videoObj, setVideoObj] = useState(null);
const [changingVideo, setChangingVideo] = useState(false);
const [videoPlaying, setVideoPlaying] = useState(false);
const [streamReady, setStreamReady] = useState(false);
useEffect(() => {
async function runPredictions() {
if (videoPlaying && videoObj && streamReady && videoStreamRef.current) {
const { coco_ssd } = models;
const _videoStream = videoStreamRef.current;
const predictions = await coco_ssd.detect(_videoStream);
console.log(predictions);
}
}
runPredictions();
}, [videoPlaying, videoObj, models, streamReady]);
useEffect(() => {
async function detect() {
if (videoObj) {
const _video = videoRef.current;
const _videoStream = videoStreamRef.current;
_video.onloadeddata = () => {
_videoStream.srcObject = _video.captureStream(0);
setStreamReady(true);
};
_video.onplay = () => {
console.log("Started");
setVideoPlaying(true);
};
_video.onended = () => {
console.log("ended");
setVideoPlaying(false);
setStreamReady(false);
};
_video.onpause = () => {
console.log("paused");
setVideoPlaying(false);
};
const videoWidth = _videoStream.width;
const videoHeight = _videoStream.height;
// Set canvas width
canvasRef.current.width = videoWidth;
canvasRef.current.height = videoHeight;
_video.src = URL.createObjectURL(videoObj);
_video.load();
}
}
detect();
}, [videoObj]);
useEffect(() => {
if (dataFile) {
setVideoPlaying(false);
setChangingVideo(true);
setVideoObj(dataFile);
setChangingVideo(false);
}
}, [dataFile, setChangingVideo, setVideoObj]);
return (
<div>
{changingVideo ? (
<Loader />
) : (
<>
{videoObj ? (
<div>
<video
id="video-input-video-src"
ref={videoRef}
controls
width="440"
height="480"
muted
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
top: "1em",
left: "10px",
textAlign: "left",
zindex: 9,
}}
/>
<video
id="video-input-video-stream"
ref={videoStreamRef}
width="440"
height="480"
playsInline
autoPlay
muted
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
top: "1em",
right: "10px",
textAlign: "right",
zindex: 9,
}}
/>
<canvas
id="webcam-input-canvas"
ref={canvasRef}
style={{
position: "absolute",
marginLeft: "auto",
marginRight: "auto",
top: "1em",
right: "10px",
textAlign: "right",
zindex: 9,
width: 440,
height: 420,
}}
/>
</div>
) : null}
</>
)}
</div>
);
}
const mapStateToProps = (state) => {
const { models } = state;
return { models };
};
export default connect(mapStateToProps, null)(ResultsComponent);
Any ideas regarding this would be much appreciated!