Utilizing WebWorkers with @mediapipe/tasks-vision (Pose Landmarker): A Step-by-Step Guide

I've been experimenting with using a web worker to detect poses frame by frame, and then displaying the results on the main thread. However, I'm encountering some delays and synchronization issues.

My setup involves Next.js 14.0.4 with @mediapipe/[email protected].

pose-landmarker-worker.ts

import { PoseLandmarker } from "@mediapipe/tasks-vision";
import { createPoseLandmarker } from "@/lib/create-pose-landmarker";

let poseLandmarker: PoseLandmarker;

self.onmessage = async function (event: MessageEvent) {
    const data = event.data;

    if (data.action === "init") {
        if (!data.runningMode) {
            throw new Error("Could not initialize. The runningMode prop is not specified");
        }

        if (!poseLandmarker) {
            const poseLandmarkerInstance = await createPoseLandmarker(data.runningMode);

            if (!poseLandmarkerInstance) {
                throw new Error("Failed to create pose landmarker instance");
            }

            poseLandmarker = poseLandmarkerInstance;
            console.log("Created new pose landmarker instance");
        } else {
            console.warn("pose landmarker already initialized");
        }
    } else if (data.action === "detectForVideo") {
        const imageBitmap = data.frame as ImageBitmap;
        const result = poseLandmarker.detectForVideo(imageBitmap, data.timestamp);
        self.postMessage(result);
    }
};

page.tsx

"use client";
import React, { useEffect, useRef } from "react";
import { PoseLandmarkerResult } from "@mediapipe/tasks-vision";
import { drawConnectors } from "@/lib/draw-utils";

export default function WebWorkers2() {
    const videoRef = useRef<HTMLVideoElement>(null);

    const offscreenCanvasRef = useRef<OffscreenCanvas | null>(null);
    const offscreenCanvasCtxRef = useRef<OffscreenCanvasRenderingContext2D | null>(null);

    const drawCanvasRef = useRef<HTMLCanvasElement>(null);
    const workerRef = useRef<Worker | null>(null);

    const drawVideoToCanvas = () => {
        const video = videoRef.current;
        const worker = workerRef.current;

        if (!video || !worker) {
            console.log("invalid data");
            return;
        }
        const width = video.width;
        const height = video.height;

        if (!offscreenCanvasRef.current) {
            const offscreenCanvasInstance = new OffscreenCanvas(width, height);
            offscreenCanvasRef.current = offscreenCanvasInstance;
            offscreenCanvasCtxRef.current = offscreenCanvasInstance.getContext("2d", { willReadFrequently: true });
        }

        const ctx = offscreenCanvasCtxRef.current;
        const offscreenCanvas = offscreenCanvasRef.current;

        if (!offscreenCanvas || !ctx) {
            return;
        }

        ctx.drawImage(video, 0, 0, width, height);
        const imageBitmap = offscreenCanvas.transferToImageBitmap();
        const timestamp = performance.now();
        worker.postMessage({ action: "detectForVideo", frame: imageBitmap, timestamp }, [imageBitmap]);
        requestAnimationFrame(drawVideoToCanvas);
    };

    useEffect(() => {
        workerRef.current = new Worker(new URL("@/lib/pose-landmarker-worker.ts", import.meta.url));

        const ctx = drawCanvasRef.current?.getContext("2d")!;

        workerRef.current.onmessage = function (event: MessageEvent) {
            const result = event.data as PoseLandmarkerResult;

            if (ctx) {
                ctx.save();
                ctx.clearRect(0, 0, 360, 640);
                drawConnectors(result, ctx);
                ctx.restore();
            }
        };

        return () => {
            const worker = workerRef.current;
            if (worker) {
                worker.terminate();
            }
        };
    }, []);

    return (
        <div className="flex h-screen flex-col">
            <div className="flex ">
                <div className="relative">
                    <video
                        ref={videoRef}
                        onLoadedData={(event) => {
                            workerRef.current?.postMessage({
                                action: "init",
                                runningMode: "VIDEO",
                            });
                            setTimeout(drawVideoToCanvas, 3000);
                        }}
                        src={"/static/videos/body_scan.mp4"}
                        height={640}
                        width={360}
                        playsInline
                        autoPlay
                        muted
                        loop
                    />
                    <canvas ref={drawCanvasRef} className="absolute left-0 top-0" height={640} width={360} />
                </div>
            </div>
        </div>
    );
}

The outcome: https://drive.google.com/file/d/1KBt_VRzuE9zc-sco9d3tHZHj059fTYnf/view?usp=drive_link

Answer №1

I have not had the opportunity to test this out yet, but one thing that stands out is the absence of an await in the call to detectForVideo within the web worker. You might want to consider adding the await keyword:

const result = poseLandmarker.detectForVideo(imageBitmap, data.timestamp);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

React-Native-Web generates default classes for inline styles and erroneously applies them in a jumbled sequence

Currently working on a project involving react native web and NextJS. I have encountered an issue with inline styles in React that seems to be caused by RN-Web. It appears that there is a mechanism within the framework that automatically generates classes ...

Performing optimized searches in Redis

In the process of creating a wallet app, I have incorporated redis for storing the current wallet balance of each user. Recently, I was tasked with finding a method to retrieve the total sum of all users' balances within the application. Since this in ...

What is the process for implementing a loading state during the next auth signin on my application?

After submitting the form, the signin function is triggered and everything happens in the background without allowing me to set a state for it. To enable the signin functionality, I have imported the following: import { signIn } from "next-auth/react ...

Navigating from a Card to a new View in Angular

I am currently developing a project using Angular (latest version). Within my application, I have the functionality to dynamically generate bootstrap cards from an Order Array and display them in my "Order-Item-Component through its respective template. ...

Tips on transitioning a Node.js application from JavaScript to TypeScript incrementally

I have a JavaScript node application that has grown quite large and I am considering migrating to TypeScript for faster development and easier code maintenance. I have installed TypeScript along with the node and mocha types using the following commands: ...

Does Next.js pre-render every page, or does it only pre-render the initial page?

As I dive into the world of nextjs, I'm coming across conflicting information. Some sources claim that nextjs only prerenders the first page, while others suggest that all pages are prerendered by default. This contradiction has left me confused about ...

Issue with MUI 5 Button component not receiving all necessary props

Currently, I am attempting to create a customized MUI5-based button in a separate component with the following code: import {Button, buttonClasses, ButtonProps, styled} from '@mui/material'; interface MxFlatButtonProps extends Omit<ButtonProp ...

Discovering the generic parameter in the return type using TypeScript

I am struggling with a specific issue export type AppThunk<ReturnType> = ThunkAction< ReturnType, RootState, unknown, Action<string> >; After implementing the above code snippet export const loadCourse = (id: string): AppThunk ...

When selecting a MenuItem, only a ReactOwner has the ability to add a ref using addComponentAsRefTo(...)

I'm currently working on a basic component that looks like this: class App extends React.Component<{}, {}> { constructor() { super(); } render() { return ( <MuiThemeProvider muiTheme={muiTheme}> <div> ...

Encountering a snag while setting up Google authentication on my website

Currently, I am in the process of integrating Google Authentication into my website. However, I have run into an error related to session management that reads as follows: TypeError: req.session.regenerate is not a function at SessionManager.logIn (C:&bso ...

Error encountered when packaging WebAssembly using Rollup

When I use wasm-pack to compile some rust code into webassembly, specifically with the option --target browser (which is the default), these are the files that I see in typescript/deps/ed25519xp: ed25519xp_bg.wasm ed25519xp_bg.d.ts ed25519xp.d.ts ed25519 ...

tRPC asynchronous mutation does not have a return value

Attempting to utilize tRPC for the first time here. I have created a mutation called "add" that takes a URL as input and returns a hardcoded slug. Router export const entryRouter = router({ add: publicProcedure .input(input) .output(output) ...

Incorporating HTML and JavaScript into TypeScript: How to Embed a Shopify Buy Button in a .tsx document

I am currently looking to integrate Shopify with my personal website. My frontend is built using React (NextJS with TypeScript). The embed code for the Shopify buy button consists of an HTML div tag wrapping JavaScript. I am wondering how I can effectivel ...

Replicating entities in TypeScript

I am currently developing an Angular 2 application using TypeScript. In a User Management component, I have implemented a table that displays all the users in my system. When a user is clicked on within the table, a form appears with their complete set of ...

The implementation of CommonJS modules allows for efficient modularization

While using Nx for my Angular workspace, I noticed something that sparked a question in my mind. Why is it necessary to use CommonJS modules in all tsconfig.spec.json files for libs? Looking at Nx examples, I observed that not all libs include it, only app ...

Guide on attaching an onclick event to a button created with a string in Next.js?

<div onClick={(event) => this.addToCart(event)}> <ReactMarkdownWithHtml children={this.props.customButton} allowDangerousHtml /> </div> In my current situation, I am facing an issue with the code above. The button is being rendered ...

Obtaining a JWT token from local storage in a server-side component

Once the user has logged in, the API response contains a JWT token which I save in the local storage. Now, how can I access this token from the server component to make API requests? I am using Next.js 14. I am currently storing the token in a context but ...

Change the variable value within the service simultaneously from various components

There is a service called DisplaysService that contains a variable called moneys. Whenever I make a purchase using the buy button on the buy component, I update the value of this variable in the service from the component. However, the updated value does n ...

When utilizing the Angular 2 Stack, the Typescript Reflect.getMetadata('design:type'...) method may return an Object instead of a Date

When running the code sample below, it outputs "[Function: Date]", which is as expected. import 'reflect-metadata' function logType(target : any, key : string) { var t = Reflect.getMetadata("design:type", target, key); console.log(`${k ...

A step-by-step guide to showcasing dates in HTML with Angular

I have set up two datepickers in my HTML file using bootstrap and I am attempting to display a message that shows the period between the first selected date and the second selected date. The typescript class is as follows: export class Datepicker { ...