OpenCV.jpを試す。「ブラウザでOpenCVを使い画像処理」

コンピュータ

OpenCV.jsは、画像処理・コンピュータビジョンライブラリとして広く使われているOpenCVを、Webブラウザ上で動作させるためのJavaScriptバインディングです。

ライセンス

OpenCV.jsのライセンスは、Apache License, Version 2.0です。商用利用を含め、無料で自由に使えます。

OpenCV.js is licensed under the Apache License, Version 2.0.

詳細はApache License 2.0の原文をご確認ください。

導入方法

公式ビルドを利用する場合

<script async src="https://docs.opencv.org/4.x/opencv.js"></script>

動作確認

OpenCV.js 動作確認(グレースケール)

OpenCV.js を読み込み中…

入力


出力(Gray)

※実際動作します。

プライバシーについて
このデモはブラウザ内(ローカル)で画像を処理します。選択した画像がサーバーへアップロードされることはありません。

ソースコード

HTML

  <p id="cv-status" style="margin:.25rem 0 1rem;">OpenCV.js を読み込み中…</p>

  <label style="display:inline-block;margin:.25rem 0 .5rem;">
    画像を選択: <input type="file" id="cv-file" accept="image/*">
  </label>

  <div style="display:flex;gap:12px;align-items:flex-start;flex-wrap:wrap;">
    <div>
      <div style="font-size:.9em;margin-bottom:.25rem;">入力</div>
      <img id="cv-input" alt="" style="display:none;" />
      <canvas id="cv-input-canvas" width="0" height="0" style="border:1px solid #ddd;"></canvas>
    </div>
    <div>
      <div style="font-size:.9em;margin-bottom:.25rem;">出力(Gray)</div>
      <canvas id="cv-output" width="0" height="0" style="border:1px solid #ddd;"></canvas>
    </div>
  </div>

JavaScript

(function(){
  // このページにデモ要素がなければ何もしない
  const root = document.querySelector('#opencvjs-demo');
  if (!root) return;

  const OPENCV_SRC = 'https://docs.opencv.org/4.x/opencv.js';
  const $ = (s)=>document.querySelector(s);
  const statusEl = $('#cv-status');
  const fileEl   = $('#cv-file');
  const imgEl    = $('#cv-input');
  const inCv     = $('#cv-input-canvas');
  const outCv    = $('#cv-output');

  // 読み込み状態表示
  function setStatus(text){ if(statusEl) statusEl.textContent = text; }

  // スクリプト動的読み込み
  function loadScript(src){
    return new Promise((resolve, reject) => {
      // 既に読み込み済みならスキップ
      if (document.querySelector('script[data-opencv-js="1"]')) return resolve();
      const s = document.createElement('script');
      s.src = src;
      s.async = true;
      s.defer = true;
      s.dataset.opencvJs = '1';
      s.onload = resolve;
      s.onerror = () => reject(new Error('OpenCV.js が読み込めませんでした'));
      document.head.appendChild(s);
    });
  }

  // OpenCV 初期化待ち(cv.Mat が使えるまで)
  function waitForOpenCV(timeoutMs = 15000){
    return new Promise((resolve, reject) => {
      const start = Date.now();

      // すでに準備OK
      if (window.cv && (cv.Mat || cv['matFromImageData'])) return resolve();

      // onRuntimeInitialized を捕まえる
      if (window.cv && cv.onRuntimeInitialized) {
        cv.onRuntimeInitialized = () => resolve();
      }

      // 保険としてポーリング
      const id = setInterval(() => {
        if (window.cv && (cv.Mat || cv['matFromImageData'])) {
          clearInterval(id);
          resolve();
        } else if (Date.now() - start > timeoutMs) {
          clearInterval(id);
          reject(new Error('OpenCV.js の初期化待ちがタイムアウトしました'));
        }
      }, 120);
    });
  }

  // 画像を入力キャンバスに収めて描画
  function drawToCanvas(img, canvas){
    const maxW = 480;                        // 横幅の簡易制限
    const ratio = img.naturalWidth > maxW ? maxW / img.naturalWidth : 1;
    canvas.width  = Math.round(img.naturalWidth  * ratio);
    canvas.height = Math.round(img.naturalHeight * ratio);
    canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
  }

  // グレースケール処理(最小デモ)
  function processGray(){
    try {
      const src = cv.imread(inCv);
      const dst = new cv.Mat();
      cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
      cv.imshow(outCv, dst);
      src.delete(); dst.delete();
      setStatus('処理完了:出力にグレースケールを表示しました。');
    } catch (err) {
      console.error(err);
      setStatus('処理中にエラーが発生しました(コンソール参照)。');
    }
  }

  // ファイル選択イベント
  let objectUrl = null;
  function onFileChange(e){
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    if (objectUrl) { URL.revokeObjectURL(objectUrl); objectUrl = null; }
    objectUrl = URL.createObjectURL(file);
    imgEl.src = objectUrl;
  }
  imgEl.addEventListener('load', () => { drawToCanvas(imgEl, inCv); processGray(); });
  fileEl.addEventListener('change', onFileChange);
  window.addEventListener('beforeunload', () => { if (objectUrl) URL.revokeObjectURL(objectUrl); });

  // 実行シーケンス
  (async () => {
    try {
      setStatus('OpenCV.js を読み込み中…');
      await loadScript(OPENCV_SRC);
      setStatus('OpenCV.js を初期化中…');
      await waitForOpenCV();
      setStatus('OpenCV.js の準備ができました。画像を選択してください。');
    } catch (err) {
      console.error(err);
      setStatus(err.message || 'OpenCV.js の準備に失敗しました');
    }
  })();
})();

コメント