Depuis l’amélioration des caméras, la détection d’objets en temps réel est devenue une fonctionnalité de plus en plus recherchée. Des voitures autonomes aux systèmes de surveillance intelligents en passant par les applications de réalité augmentée, cette technologie est utilisée dans de nombreuses situations.

La vision par ordinateur, terme sophistiqué désignant la technologie qui utilise des caméras avec des ordinateurs pour effectuer des opérations telles que celles mentionnées ci-dessus, est un domaine vaste et complexe. Cependant, vous ne savez peut-être pas que vous pouvez vous lancer dans la détection d’objets en temps réel très facilement depuis le confort de votre navigateur.

Cet article explique comment créer une application de détection d’objets en temps réel à l’aide de React et la déployer sur Kinsta. L’application de détection d’objets en temps réel exploite le flux de la webcam de l’utilisateur.

Pré-requis

Voici un aperçu des technologies clés utilisées dans ce guide :

  • React: React est utilisé pour construire l’interface utilisateur de l’application. React excelle dans le rendu de contenu dynamique et sera utile pour présenter le flux de la webcam et les objets détectés dans le navigateur.
  • TensorFlow.js : TensorFlow.js est une bibliothèque JavaScript qui apporte la puissance de l’apprentissage automatique au navigateur. Elle vous permet de charger des modèles pré-entraînés pour la détection d’objets et de les exécuter directement dans le navigateur, éliminant ainsi le besoin d’un traitement complexe côté serveur.
  • Coco SSD : L’application utilise un modèle de détection d’objets pré-entraîné appelé Coco SSD, un modèle léger capable de reconnaître un large éventail d’objets quotidiens en temps réel. Bien que Coco SSD soit un outil puissant, il est important de noter qu’il a été formé sur un ensemble de données générales d’objets. Si vous avez des besoins de détection spécifiques, vous pouvez vous entraîner sur un modèle personnalisé à l’aide de TensorFlow.js en suivant ce guide.

Créer un nouveau projet React

  1. Créez un nouveau projet React. Pour cela, exécutez la commande suivante :
    npm create vite@latest kinsta-object-detection --template react

    Cela vous permettra d’échafauder un projet React de base en utilisant vite.

  2. Ensuite, installez les bibliothèques TensorFlow et Coco SSD en exécutant les commandes suivantes dans le projet :
    npm i @tensorflow-models/coco-ssd @tensorflow/tfjs

Vous êtes maintenant prêt à développer votre application.

Configuration de l’application

Avant d’écrire le code de la logique de détection des objets, il convient de comprendre ce qui est développé dans ce guide. Voici à quoi ressemblerait l’interface utilisateur de l’application :

Conception de l'interface utilisateur de l'application.
Conception de l’interface utilisateur de l’application.

Lorsqu’un utilisateur clique sur le bouton Start Webcam, il est invité à autoriser l’application à accéder au flux de la webcam. Une fois l’autorisation accordée, l’application commence à afficher le flux de la webcam et détecte les objets présents dans le flux. Elle affiche alors une boîte pour montrer les objets détectés sur le flux en direct et y ajoute une étiquette.

Pour commencer, créez l’interface utilisateur de l’application en collant le code suivant dans le fichier App.jsx:

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

export default App;

Cet extrait de code spécifie un en-tête pour la page et importe un composant personnalisé nommé ObjectDetection. Ce composant contient la logique permettant de capturer le flux de la webcam et de détecter les objets en temps réel.

Pour créer ce composant, créez un nouveau fichier nommé ObjectDetection.jsx dans votre répertoire src et collez-y le code suivant :

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;

Le code ci-dessus définit une structure HTML avec un bouton pour démarrer et arrêter le flux de la webcam et un élément <video> qui sera utilisé pour montrer à l’utilisateur le flux de sa webcam une fois qu’il est actif. Un conteneur d’état isWebcamStarted est utilisé pour stocker l’état du flux de la webcam. Deux fonctions, startWebcam et stopWebcam, sont utilisées pour démarrer et arrêter le flux de la webcam. Définissons-les :

Voici le code de la fonction 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);
    }
  };

Cette fonction se charge de demander à l’utilisateur d’autoriser l’accès à la webcam et, une fois l’autorisation accordée, elle configure le site <video> pour qu’il affiche le flux de la webcam en direct à l’utilisateur.

Si le code ne parvient pas à accéder au flux de la webcam (peut-être parce qu’il n’y a pas de webcam sur l’appareil actuel ou parce que l’utilisateur n’a pas l’autorisation), la fonction imprime un message sur la console. Vous pouvez utiliser un bloc d’erreur pour afficher la raison de l’échec à l’utilisateur.

Remplacez ensuite la fonction stopWebcam par le code suivant :

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)
    }
  };

Ce code vérifie les pistes de flux vidéo en cours d’exécution auxquelles l’objet <video> accède et arrête chacune d’entre elles. Enfin, il définit l’état de isWebcamStarted en false.

À ce stade, essayez d’exécuter l’application pour vérifier si vous pouvez accéder au flux de la webcam et le visualiser.

Veillez à coller le code suivant dans le fichier index.css pour vous assurer que l’application a la même apparence que l’aperçu que vous avez vu plus tôt :

#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;
  }
}

Supprimez également le fichier App.css pour éviter de perturber les styles des composants. Vous êtes maintenant prêt à écrire la logique d’intégration de la détection d’objets en temps réel dans votre application.

Mise en place de la détection d’objets en temps réel

  1. Commencez par ajouter les importations pour Tensorflow et Coco SSD en haut de ObjectDetection.jsx:
    import * as cocoSsd from '@tensorflow-models/coco-ssd';
    
    import '@tensorflow/tfjs';
  2. Ensuite, créez un état dans le composant ObjectDetection pour stocker le tableau des prédictions générées par le modèle Coco SSD :
    const [predictions, setPredictions] = useState([]);
  3. Créez ensuite une fonction qui charge le modèle Coco SSD, collecte le flux vidéo et génère les prédictions :
    const predictObject = async () => {
        const model = await cocoSsd.load();
    
        model.detect(videoRef.current).then((predictions) => {
          setPredictions(predictions);
        })
    
          .catch(err => {
            console.error(err)
          });
      };

    Cette fonction utilise le flux vidéo et génère des prédictions pour les objets présents dans le flux. Elle vous fournira un tableau d’objets prédits, chacun contenant une étiquette, un pourcentage de confiance et un ensemble de coordonnées indiquant l’emplacement de l’objet dans l’image vidéo.

    Vous devez appeler cette fonction en permanence pour traiter les images vidéo au fur et à mesure qu’elles arrivent, puis utiliser les prédictions stockées dans l’état predictions pour afficher les cases et les étiquettes de chaque objet identifié sur le flux vidéo en direct.

  4. Ensuite, utilisez la fonction setInterval pour appeler la fonction en continu. Vous devez également empêcher cette fonction d’être appelée après que l’utilisateur a arrêté le flux de la webcam. clearInterval Ajoutez le conteneur d’état suivant et le crochet useEffect dans le composant ObjectDetection pour configurer la fonction predictObject afin qu’elle soit appelée en continu lorsque la webcam est activée et qu’elle soit supprimée lorsque la webcam est désactivée :
    const [detectionInterval, setDetectionInterval] = useState()
    
      useEffect(() => {
        if (isWebcamStarted) {
          setDetectionInterval(setInterval(predictObject, 500))
        } else {
          if (detectionInterval) {
            clearInterval(detectionInterval)
            setDetectionInterval(null)
          }
        }
      }, [isWebcamStarted])

    Cela permet à l’application de détecter les objets présents devant la webcam toutes les 500 millisecondes. Vous pouvez envisager de modifier cette valeur en fonction de la vitesse à laquelle vous souhaitez que la détection des objets soit effectuée, tout en gardant à l’esprit que si vous le faites trop souvent, votre application risque d’utiliser beaucoup de mémoire dans le navigateur.

  5. Maintenant que vous disposez des données de prédiction dans le conteneur d’état prediction, vous pouvez les utiliser pour afficher une étiquette et un cadre autour de l’objet dans le flux vidéo en direct. Pour cela, mettez à jour l’instruction return de l’instruction ObjectDetection pour qu’elle renvoie ce qui suit :
    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>
      );

    Cela rendra une liste de prédictions juste en dessous du flux de la webcam et dessinera une boîte autour de l’objet prédit en utilisant les coordonnées de Coco SSD ainsi qu’une étiquette en haut des boîtes.

  6. Pour styliser correctement les boîtes et l’étiquette, ajoutez le code suivant au fichier 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;
      }
    
    }

    Ceci termine le développement de l’application. Vous pouvez maintenant redémarrer le serveur de développement pour tester l’application. Voici à quoi elle devrait ressembler une fois terminée :

    Démonstration de la détection d'objets en temps réel à l'aide d'une webcam
    Démonstration de la détection d’objets en temps réel à l’aide d’une webcam

Vous pouvez trouver le code complet dans ce dépôt GitHub.

Déployer l’application terminée sur Kinsta

La dernière étape consiste à déployer l’application sur Kinsta pour la mettre à la disposition de vos utilisateurs. Pour ce faire, Kinsta vous permet d’héberger gratuitement jusqu’à 100 sites web statiques directement depuis votre fournisseur Git préféré (Bitbucket, GitHub ou GitLab).

Une fois que votre dépôt git est prêt, suivez les étapes suivantes pour déployer votre application de détection d’objets sur Kinsta :

  1. Connectez-vous ou créez un compte pour afficher votre tableau de bord MyKinsta.
  2. Autorisez Kinsta avec votre fournisseur Git.
  3. Cliquez sur Sites statiques dans la colonne latérale de gauche, puis sur Ajouter un site.
  4. Sélectionnez le dépôt et la branche à partir desquels vous souhaitez effectuer le déploiement.
  5. Attribuez un nom unique à votre site.
  6. Ajoutez les paramètres de construction dans le format suivant :
    • Commande de construction : yarn build ou npm run build
    • Version Node : 20.2.0
    • Répertoire de publication : dist
  7. Enfin, cliquez sur Créer un site.

Une fois l’application déployée, vous pouvez cliquer sur Visiter le site dans le tableau de bord pour accéder à l’application. Vous pouvez maintenant essayer d’exécuter l’application sur différents appareils équipés de caméras pour voir comment elle fonctionne.

Comme alternative à l’hébergement de site statique, vous pouvez déployer votre site statique avec l’hébergement d’application de Kinsta, qui offre une plus grande flexibilité d’hébergement, une plus large gamme d’avantages et l’accès à des fonctionnalités plus robustes. Par exemple, l’évolutivité, le déploiement personnalisé à l’aide d’un fichier Docker et des analyses complètes englobant des données en temps réel et historiques.

Résumé

Vous avez réussi à construire une application de détection d’objets en temps réel à l’aide de React, TensorFlow.js et Kinsta. Cela vous permet d’explorer le monde passionnant de la vision par ordinateur et de créer des expériences interactives directement dans le navigateur de l’utilisateur.

N’oubliez pas que le modèle Coco SSD que nous avons utilisé n’est qu’un point de départ. En explorant davantage, vous pouvez approfondir la détection d’objets personnalisés à l’aide de TensorFlow.js, ce qui vous permet d’adapter l’application à l’identification d’objets spécifiques répondant à vos besoins.

Les possibilités sont vastes ! Cette application sert de base à la création d’applications plus détaillées, comme des expériences de réalité augmentée ou des systèmes de surveillance intelligents. En déployant votre application sur la plateforme fiable de Kinsta, vous pouvez partager votre création avec le monde entier et voir la puissance de la vision par ordinateur prendre vie.

Quel est le problème que vous avez rencontré et que vous pensez que la détection d’objets en temps réel peut résoudre ? Faites-le nous savoir dans les commentaires ci-dessous !

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.