Sinds camera’s enorm zijn verbeterd in de afgelopen jaren, is realtime objectdetectie een steeds gewilde functionaliteit geworden. Deze technologie wordt in veel situaties gebruikt, van zelfrijdende auto’s en slimme bewakingssystemen tot augmented reality.

Computer vision, een mooie term voor de technologie die camera’s met computers gebruikt om bewerkingen uit te voeren zoals hierboven genoemd, is een uitgebreid en ingewikkeld vakgebied. Maar waarschijnlijk weet je niet dat je heel eenvoudig vanuit je browser aan de slag kunt met realtime objectdetectie!

Dit artikel legt uit hoe je een app voor realtime objectdetectie kunt bouwen met React en hoe je de app kunt deployen op Kinsta. Deze app voor realtime objectdetectie maakt gebruik van de webcamfeed van de gebruiker.

Vereisten

Hier volgt een overzicht van de belangrijkste technologieën die in deze handleiding worden gebruikt:

  • React: React wordt gebruikt om de gebruikersinterface (UI) van de applicatie te bouwen. React blinkt uit in het renderen van dynamische inhoud en zal nuttig zijn bij het presenteren van de webcam feed en gedetecteerde objecten in de browser.
  • TensorFlow.js: TensorFlow.js is een JavaScript bibliotheek die de kracht van machine learning naar de browser brengt. Hiermee kun je voorgetrainde modellen voor objectdetectie laden en direct in de browser uitvoeren, zodat er geen complexe server-side verwerking nodig is.
  • Coco SSD: De applicatie gebruikt een voorgetraind objectdetectiemodel genaamd Coco SSD, een lichtgewicht model dat in staat is om een groot aantal alledaagse objecten in realtime te herkennen. Hoewel Coco SSD een krachtig hulpmiddel is, is het belangrijk op te merken dat het is getraind op een algemene dataset van objecten. Als je specifieke detectiebehoeften hebt, kun je een aangepast model trainen met TensorFlow.js door deze handleiding te volgen.

Een nieuw React project opzetten

  1. Maak een nieuw React project. Doe dit door het volgende commando uit te voeren:
    npm create vite@latest kinsta-object-detection --template react

    Dit zal een basislijn React project voor je scaffold met behulp van vite.

  2. Installeer vervolgens de TensorFlow en Coco SSD bibliotheken door de volgende commando’s in het project uit te voeren:
    npm i @tensorflow-models/coco-ssd @tensorflow/tfjs

Nu ben je klaar om te beginnen met het ontwikkelen van je app.

De app configureren

Laten we, voordat we de code voor de objectdetectielogica schrijven, eerst begrijpen wat er in deze handleiding wordt ontwikkeld. Zo ziet de UI van de app eruit:

Een screenshot van de voltooide app met de header en een knop om webcamtoegang in te schakelen.
UI-ontwerp van de app.

Wanneer een gebruiker op de knop Start Webcam klikt, wordt hij gevraagd om de app toestemming te geven voor toegang tot de webcam feed. Als die toestemming is gegeven, begint de app met het tonen van de webcam feed en detecteert objecten in de feed. De app maakt dan een kader om de gedetecteerde objecten op de live feed weer te geven en voegt er ook een label aan toe.

Maak om te beginnen de UI voor de app door de volgende code in het bestand App.jsx te plakken:

import ObjectDetection from './ObjectDetection';
function App() {
  return (
    <div className="app">
      <h1>Image Object Detection</h1>
        <ObjectDetection />
    </div>
  );
}

export default App;

Dit codefragment specificeert een header voor de pagina en importeert een custom component met de naam ObjectDetection. Dit component bevat de logica voor het vastleggen van de webcamfeed en het detecteren van objecten in realtime.

Om dit component te maken, maak je een nieuw bestand met de naam ObjectDetection.jsx in je src directory en plak je de volgende code erin:

import { useEffect, useRef, useState } from 'react';

const ObjectDetection = () => {
  const videoRef = useRef(null);
  const [isWebcamStarted, setIsWebcamStarted] = useState(false)

  const startWebcam = async () => {
    // TODO
  };

  const stopWebcam = () => {
     // TODO
  };

  return (
    <div className="object-detection">
      <div className="buttons">
        <button onClick={isWebcamStarted ? stopWebcam : startWebcam}>{isWebcamStarted ? "Stop" : "Start"} Webcam</button>
      </div>
      <div className="feed">
        {isWebcamStarted ? <video ref={videoRef} autoPlay muted /> : <div />}
      </div>
    </div>
  );
};

export default ObjectDetection;

De bovenstaande code definieert een HTML structuur met een knop om de webcamfeed te starten en te stoppen en een <video> element dat wordt gebruikt om de gebruiker zijn webcamfeed te laten zien zodra deze actief is. Een statuscontainer isWebcamStarted wordt gebruikt om de status van de webcam feed op te slaan. Twee functies, startWebcam en stopWebcam worden gebruikt om de webcam feed te starten en te stoppen. Laten we ze definiëren:

Hier is de code voor de functie startWebcam:

const startWebcam = async () => {
    try {
      setIsWebcamStarted(true)
      const stream = await navigator.mediaDevices.getUserMedia({ video: true });

      if (videoRef.current) {
        videoRef.current.srcObject = stream;
      }
    } catch (error) {
      setIsWebcamStarted(false)
      console.error('Error accessing webcam:', error);
    }
  };

Deze functie zorgt voor het verzoek aan de gebruiker om webcamtoegang te verlenen, en zodra de toestemming is verleend, stelt het de <video> in om de live webcamfeed aan de gebruiker te tonen.

Als de code er niet in slaagt om toegang te krijgen tot de webcam feed (mogelijk omdat er geen webcam is op het huidige apparaat of omdat de gebruiker geen toestemming krijgt), dan zal de functie een bericht printen naar de console. Je kunt een error blok gebruiken om de reden van de mislukking aan de gebruiker weer te geven.

Vervang vervolgens de functie stopWebcam door de volgende code:

const stopWebcam = () => {
    const video = videoRef.current;

    if (video) {
      const stream = video.srcObject;
      const tracks = stream.getTracks();

      tracks.forEach((track) => {
        track.stop();
      });

      video.srcObject = null;
      setPredictions([])
      setIsWebcamStarted(false)
    }
  };

Deze code controleert de lopende videostreamtracks die worden benaderd door het <video> object en stopt elk van hen. Ten slotte wordt de status isWebcamStarted ingesteld op false.

Probeer nu de app uit te voeren om te controleren of je de webcam feed kunt openen en bekijken.

Zorg ervoor dat je de volgende code in het bestand index.css plakt om ervoor te zorgen dat de app er hetzelfde uitziet als de preview die je eerder zag:

#root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;
  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;
  min-width: 100vw;
  min-height: 100vh;
  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  font-weight: 500;
  color: #646cff;
  text-decoration: inherit;
}

a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  display: flex;
  place-items: center;
  min-width: 100vw;
  min-height: 100vh;
}

h1 {
  font-size: 3.2em;
  line-height: 1.1;
}

button {
  border-radius: 8px;
  border: 1px solid transparent;
  padding: 0.6em 1.2em;
  font-size: 1em;
  font-weight: 500;
  font-family: inherit;
  background-color: #1a1a1a;
  cursor: pointer;
  transition: border-color 0.25s;
}

button:hover {
  border-color: #646cff;
}

button:focus,

button:focus-visible {
  outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }

  a:hover {
    color: #747bff;
  }

  button {
    background-color: #f9f9f9;
  }
}

.app {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.object-detection {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;

  .buttons {
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: row;

    button {
      margin: 2px;
    }
  }

  div {
    margin: 4px;
  }
}

Verwijder ook het App.css bestand om de stijlen van de componenten niet te verknoeien. Nu ben je klaar om de logica te schrijven voor het integreren van real-time objectdetectie in je app.

Realtime objectdetectie instellen

  1. Begin met het toevoegen van de imports voor Tensorflow en Coco SSD bovenaan ObjectDetection.jsx:
    import * as cocoSsd from '@tensorflow-models/coco-ssd';
    
    import '@tensorflow/tfjs';
  2. Maak vervolgens een state aan in de component ObjectDetection om de array van predictions op te slaan die zijn gegenereerd door het Coco SSD model:
    const [predictions, setPredictions] = useState([]);
  3. Maak vervolgens een functie die het Coco SSD model laadt, de video feed verzamelt en de voorspellingen genereert:
    const predictObject = async () => {
        const model = await cocoSsd.load();
    
        model.detect(videoRef.current).then((predictions) => {
          setPredictions(predictions);
        })
    
          .catch(err => {
            console.error(err)
          });
      };

    Deze functie gebruikt de videofeed en genereert voorspellingen voor objecten die aanwezig zijn in de feed. Het zal je voorzien van een matrix van voorspelde objecten, elk met een label, een betrouwbaarheidspercentage en een set coördinaten die de locatie van het object in het videoframe weergeven.

    Je moet deze functie continu callen om videoframes te verwerken wanneer ze binnenkomen en dan de voorspellingen gebruiken die zijn opgeslagen in de predictions status om vakjes en labels te tonen voor elk geïdentificeerd object op de live videofeed.

  4. Gebruik vervolgens de functie setInterval om de functie continu te callen. Je moet ook voorkomen dat deze functie wordt gecalld nadat de gebruiker de webcam feed heeft gestopt. Om dat te doen, gebruik je de clearInterval functie van JavaScript.Voeg de volgende state container en de useEffect hook toe in het ObjectDetection component om in te stellen dat de predictObject functie continu wordt gecalld als de webcam is ingeschakeld en wordt verwijderd als de webcam is uitgeschakeld:
    const [detectionInterval, setDetectionInterval] = useState()
    
      useEffect(() => {
        if (isWebcamStarted) {
          setDetectionInterval(setInterval(predictObject, 500))
        } else {
          if (detectionInterval) {
            clearInterval(detectionInterval)
            setDetectionInterval(null)
          }
        }
      }, [isWebcamStarted])

    Dit stelt de app in om elke 500 milliseconden de objecten te detecteren die voor de webcam aanwezig zijn. Je kunt overwegen om deze waarde te veranderen, afhankelijk van hoe snel je de objectdetectie wilt hebben, maar houd er wel rekening mee dat als je het te vaak doet, dit tot gevolg kan hebben dat je app veel geheugen in de browser gebruikt.

  5. Nu je de voorspellingsgegevens in de prediction state container hebt, kun je deze gebruiken om een label en een kader rond het object weer te geven in de live video feed. Om dat te doen, werk je de return declaration van de ObjectDetection bij zodat deze het volgende weergeeft:
    return (
        <div className="object-detection">
          <div className="buttons">
            <button onClick={isWebcamStarted ? stopWebcam : startWebcam}>{isWebcamStarted ? "Stop" : "Start"} Webcam</button>
          </div>
          <div className="feed">
            {isWebcamStarted ? <video ref={videoRef} autoPlay muted /> : <div />}
            {/* Add the tags below to show a label using the p element and a box using the div element */}
            {predictions.length > 0 && (
              predictions.map(prediction => {
                return <>
                  <p style={{
                    left: `${prediction.bbox[0]}px`, 
                    top: `${prediction.bbox[1]}px`,
                    width: `${prediction.bbox[2] - 100}px`
                }}>{prediction.class  + ' - with ' 
                + Math.round(parseFloat(prediction.score) * 100) 
                + '% confidence.'}</p>
                <div className={"marker"} style={{
                  left: `${prediction.bbox[0]}px`,
                  top: `${prediction.bbox[1]}px`,
                  width: `${prediction.bbox[2]}px`,
                  height: `${prediction.bbox[3]}px`
                }} />
                </>
              })
            )}
          </div>
          {/* Add the tags below to show a list of predictions to user */}
          {predictions.length > 0 && (
            <div>
              <h3>Predictions:</h3>
              <ul>
                {predictions.map((prediction, index) => (
                  <li key={index}>
                    {`${prediction.class} (${(prediction.score * 100).toFixed(2)}%)`}
                  </li>
                ))}
              </ul>
            </div>
          )}
    
        </div>
      );

    Dit zal een lijst met voorspellingen direct onder de webcam feed renderen en een box tekenen rond het voorspelde object met behulp van de coördinaten van Coco SSD samen met een label bovenaan de boxen.

  6. Om de boxen en het label correct te stijlen, voeg je de volgende code toe aan het bestand index.css:
    .feed {
      position: relative;
    
      p {
        position: absolute;
        padding: 5px;
        background-color: rgba(255, 111, 0, 0.85);
        color: #FFF;
        border: 1px dashed rgba(255, 255, 255, 0.7);
        z-index: 2;
        font-size: 12px;
        margin: 0;
      }
    
      .marker {
        background: rgba(0, 255, 0, 0.25);
        border: 1px dashed #fff;
        z-index: 1;
        position: absolute;
      }
    
    }

    Hiermee is de ontwikkeling van de app voltooid. Je kunt nu de dev server herstarten om de applicatie te testen. Hier zie je hoe de app eruit zou moeten zien als hij klaar is:

    Een GIF die laat zien hoe de gebruiker de app uitvoert, de camera toegang geeft tot de app en de app vervolgens vakjes en labels laat zien rond gedetecteerde objecten in de feed.
    Demo van de realtime objectdetectie met webcam

Je kunt de volledige code vinden in deze GitHub repository.

De voltooide app deployen naar Kinsta

De laatste stap is het deployen van de app op Kinsta om hem beschikbaar te maken voor je gebruikers. Om dat te doen, kun je bij Kinsta gratis tot 100 statische websites hosten, rechtstreeks vanaf de Git provider van jouw voorkeur (Bitbucket, GitHub of GitLab).

Als je git repository klaar is, volg dan deze stappen om je objectdetectie app te deployen naar Kinsta:

  1. Log in of maak een account aan om je MyKinsta dashboard te bekijken.
  2. Autoriseer Kinsta met je Git provider.
  3. Klik op Statische  sites op de linker zijbalk en klik dan op Site toevoegen.
  4. Selecteer de repository en de branch waarvan je wilt deployen.
  5. Geef je site een unieke naam.
  6. Voeg de bouwinstellingen toe in het volgende formaat:
    • Build commando: yarn build of npm run build
    • Node versie: 20.2.0
    • Publish directory: dist
  7. Klik ten slotte op Maak site.

Zodra de app is gedeployd, kun je op Bezoek site klikken in het dashboard om toegang te krijgen tot de app. Je kunt nu proberen de app op verschillende apparaten met camera’s uit te voeren om te zien hoe hij presteert.

Als alternatief voor Statische Site Hosting kun je je statische site implementeren met Kinsta’s Applicatie Hosting, dat meer flexibiliteit biedt bij het hosten, een breder scala aan voordelen en toegang tot robuustere features. Bijvoorbeeld schaalbaarheid, custom deployments met behulp van een Dockerfile en uitgebreide analytics met real-time en historische gegevens.

Samenvatting

Je hebt met succes een realtime objectdetectie applicatie gebouwd met React, TensorFlow.js en Kinsta. Dit stelt je in staat om de spannende wereld van computer vision te verkennen en interactieve ervaringen direct in de browser van de gebruiker te creëren.

Onthoud dat het Coco SSD model dat we hebben gebruikt slechts een startpunt is. Met verdere verkenning kun je je verdiepen in aangepaste objectdetectie met TensorFlow.js, waardoor je de app kunt aanpassen om specifieke objecten te identificeren die relevant zijn voor jouw behoeften.

De mogelijkheden zijn enorm! Deze app dient als basis om meer gedetailleerde applicaties te bouwen, zoals augmented reality ervaringen of slimme bewakingssystemen. Door je app te deployen op het betrouwbare platform van Kinsta, kun je je creatie delen met de wereld en de kracht van computervisie tot leven zien komen.

Wat is een probleem dat jij bent tegengekomen en waarvan je denkt dat realtime objectdetectie het kan oplossen? Laat het ons weten in de comments hieronder!

Kumar Harsh

Kumar is a software developer and a technical author based in India. He specializes in JavaScript and DevOps. You can learn more about his work on his website.