Posted on

Tensorflow.js的coco-ssd對象檢測

官方教程

教程網址: https://github.com/tensorflow/tfjs-models/tree/master/coco-ssd

目標檢測模型,旨在定位和識別單個圖像中的多個目標。

該模型是 COCO-SSD 模型的 TensorFlow.js 移植。有關 Tensorflow 對象檢測 API 的更多信息,請查看 tensorflow/object_detection中的自述文件。

該模型檢測 COCO 數據集中定義的對象,這是一個大規模的對象檢測、分割和字幕數據集。您可以在這裡找到更多信息。該模型能夠檢測80 類物體。(SSD 代表單次多盒檢測)。

此 TensorFlow.js 模型不需要您了解機器學習。它可以將輸入作為任何基於瀏覽器的圖像元素(例如<img><video><canvas> 元素),並返回帶有類名稱和置信度的邊界框數組。

<!-- Load TensorFlow.js. This is required to use coco-ssd model. -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"> </script>
<!-- Load the coco-ssd model. -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd"> </script>

<!-- Replace this with your image. Make sure CORS settings allow reading the image! -->
<img id="img" src="cat.jpg"/>

<!-- Place your code in the script tag below. You can also use an external .js file -->
<script>
  // Notice there is no 'import' statement. 'cocoSsd' and 'tf' is
  // available on the index-page because of the script tag above.

  const img = document.getElementById('img');

  // Load the model.
  cocoSsd.load().then(model => {
    // detect objects in the image.
    model.detect(img).then(predictions => {
      console.log('Predictions: ', predictions);
    });
  });
</script>

讀取攝像機

以下為一個簡單讀取攝像機並且作物件偵測的程式範例

<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <title>Test</title>
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"> </script>
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd"> </script>
</head>

<body>
  <div class="select">
    <label for="videoSource">Video source: </label><select id="videoSource"></select>
  </div>
  <button id="showVideo">Open camera</button>
  <br />
  <!-- Video element to capture camera input -->
  <video id="video" autoplay playsinline style="position: absolute;z-index: -999;"></video><canvas id="canvas"
    width="100%"></canvas>
  <div id="message"></div>
  <script>
    const video = document.getElementById('video');
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');
    document.querySelector('#showVideo').addEventListener('click', e => init(e));
    const videoSelect = document.querySelector('select#videoSource');
    function gotDevices(deviceInfos) {
      console.log(deviceInfos)
      deviceInfos.forEach(deviceInfo => {
        if (deviceInfo.kind == "videoinput") {
          const option = document.createElement('option');
          option.value = deviceInfo.deviceId;
          option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`;
          videoSelect.appendChild(option);
        }
      });
    }
    function gotStream(stream) {
      window.stream = stream; // make stream available to console
      videoElement.srcObject = stream;
      // Refresh button list in case labels have become available
      return navigator.mediaDevices.enumerateDevices();
    }
    window.onload = () => {

      navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
      const constraints = {
        audio: { deviceId: audioSource ? { exact: audioSource } : undefined },
        video: { deviceId: videoSource ? { exact: videoSource } : undefined }
      };
      navigator.mediaDevices.getUserMedia(constraints).then(gotStream).then(gotDevices).catch(handleError);
    }


    function getRandomColor() {
      const randomColor = Math.floor(Math.random() * 16777215).toString(16);
      return "#" + ("000000" + randomColor).slice(-6);
    }
    function handleSuccess(stream) {
      const videoTracks = stream.getVideoTracks();
      var predictions = [];
      video.srcObject = stream;
      // When the video is playing, draw it on the canvas
      video.addEventListener('play', () => {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        // Continuously draw the video frames on the canvas
        function drawFrame() {
          // Draw the video frame on the canvas
          ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
          const scaleX = canvas.width / video.videoWidth;
          const scaleY = canvas.height / video.videoHeight;

          // Draw the bounding boxes on the canvas
          predictions.forEach(prediction => {
            const x = prediction.bbox[0] * scaleX;
            const y = prediction.bbox[1] * scaleY;
            const width = prediction.bbox[2] * scaleX;
            const height = prediction.bbox[3] * scaleY;

            ctx.strokeStyle = 'blue';
            ctx.lineWidth = 2;
            ctx.strokeRect(x, y, width, height);

            ctx.fillStyle = 'blue';
            ctx.font = '18px Arial';
            ctx.fillText(prediction.class, x, y);
          });
          // Call the drawFrame function again to continuously update the canvas
          requestAnimationFrame(drawFrame);
        }

        // Start drawing video frames
        drawFrame();
        // Load the model.
        cocoSsd.load().then(model => {
          function detectFrame() {
            // detect objects in the image.
            model.detect(video).then(preds => {
              predictions = preds
              setTimeout(detectFrame, 100);
            });
          }
          detectFrame()
        });
      });
    }

    function handleError(error) {
      if (error.name === 'OverconstrainedError') {
        const v = constraints.video;
        alert(`The resolution ${v.width.exact}x${v.height.exact} px is not supported by your device.`);
      } else if (error.name === 'NotAllowedError') {
        alert('Permissions have not been granted to use your camera and ' +
          'microphone, you need to allow the page access to your devices in ' +
          'order for the demo to work.');
      }
      alert(`getUserMedia error: ${error.name}`, error);
    }

    async function init(e) {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ audio: false, video: { deviceId: videoSelect.value ? { exact: videoSelect.value } : undefined } });
        handleSuccess(stream);
        e.target.disabled = true;
      } catch (e) {
        handleError(e);
      }
    }
  </script>
</body>

</html>

執行成果

以下為範例執行程式: https://claire-chang.com/wp-content/uploads/2023/08/sample.html

要開啟鏡頭權限需要至網站設定開啟權限