ブラウザだけで熊を検出する。TensorFlow.jsが教えてくれた「軽さ」と「重さ」
最初に検出されたのは、画面に映った熊のぬいぐるみでした。
信頼度87.3%。青いボックスが熊の周りを囲み、ラベルには「bear」と表示されています。カメラを動かすと、ボックスも一緒に追いかけてきます。リアルタイムで。
これまで物体検出というと、サーバーに画像を送って、結果を待つ。そんなイメージでした。でも、このシステムは違います。ブラウザの中で、TensorFlow.jsが直接AIモデルを動かし、Webカメラの映像から熊を検出します。このシステムは熊検出に特化していますが、設定画面で「bear」以外の動物クラス(dog、cat、birdなど)も選択可能です。すべてがクライアント側で完結します。
処理速度は8 FPS。1秒間に8回、画面を解析して物体を見つけ出します。これは、人間が瞬きする間にも複数回の検出が行われる速度です。最初は「遅いのでは」と不安でしたが、実際に使ってみると、この速度で十分に実用的だと分かりました。
この記事では、TensorFlow.jsを使ったブラウザ単体での熊検出特化システムを作った記録を、技術的な試行錯誤とともに紹介します。
対象読者
- TensorFlow.js(ブラウザ上で機械学習を実行するライブラリ)で物体検出を実装したいが、サーバー側の処理を避けたいフロントエンド開発者
- リアルタイム物体検出の精度と速度のバランスに悩んでいる人
- 検出履歴の保存や重複検出の軽減機能を実装したい制作者
- ブラウザだけで完結するAI(人工知能)アプリケーションの可能性を探っている人
記事に書いてあること
- TensorFlow.js(ブラウザ上で機械学習を実行するライブラリ)でYOLO11xモデル(物体検出用のAIモデル)を読み込み、ブラウザ単体で熊を検出する方法(熊検出特化システム、設定で他の動物クラスも検出可能)
- 検出速度8 FPS(1秒間に8回の検出)を実現するためのフレーム制御とパフォーマンス最適化
- IndexedDB(ブラウザ内のデータベース)を使った検出履歴の保存と、重複検出を軽減するIoU(重なり具合の指標)+ 色ヒストグラム + サイズの判定ロジック
- 信頼度閾値15%の調整過程と、処理速度(高速/標準/高精度)の3モード実装
- Webカメラ、動画URL、画面共有の3つの入力ソースに対応した柔軟な設計
- 検出エリア指定(ROI:Region of Interest、関心領域)機能の実装詳細
作ったもの
AI(人工知能)熊検出特化システム。TensorFlow.js(ブラウザ上で機械学習を実行するライブラリ)を使用したブラウザ単体でのリアルタイム熊検出です。YOLO11xモデル(物体検出用のAIモデル)は80種類の物体クラスに対応しており、このシステムは熊(bear)の検出に特化しています。設定画面では、他の動物クラス(dog、cat、birdなど)も選択可能です。検出履歴の保存・読み込み機能、重複検出の軽減機能を搭載しています。
CTA: 検出フローを全画面で確認する
最初に作ったのは、検出結果を表示するだけのシンプルな画面でした。けれど実際に使ってみると、同じ物体を連続で検出してしまい、履歴が膨らんでいく問題に直面しました。そこで、重複検出を軽減する機能を追加しました。
前提知識のメモ
- TensorFlow.js(ブラウザ上で機械学習を実行するライブラリ)の基本的な使い方(モデルの読み込み、推論の実行)
- WebカメラAPI(
getUserMedia、ブラウザからカメラにアクセスする仕組み)の基本的な知識
- IndexedDB(ブラウザ内のデータベース)を使ったデータ保存の経験(任意)
- Canvas API(HTML5の描画機能)での画像描画と座標変換の理解
YOLOモデル(物体検出用のAIモデル)や物体検出のアルゴリズムについて詳しく知らなくても、この記事を読み進められます。必要に応じて、専門用語は「翻訳」しながら説明します。
TensorFlow.jsでYOLO11xモデルを読み込む
物体検出の核心は、AI(人工知能)モデルの読み込みです。このシステムでは、YOLO11x(物体検出用の高精度なAIモデル)という高精度なモデルを使用しています。
モデルの読み込みは、予測処理を行うコンポーネントの中で行われます。最初は、モデルのパスを直接指定していましたが、実際に動かしてみると、ネットワークエラーやキャッシュの問題で読み込みに失敗することがありました。
そこで、リトライ機能を実装しました。最大3回まで再試行し、失敗した場合はキャッシュを無効化して再読み込みします。
async loadModel() {
const modelPath = '/ai_models/yolo11x_web_model/model.json';
const maxRetries = 3;
let lastError = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
if (attempt > 1) {
console.log(`🔄 モデル読み込み再試行中... (${attempt}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
} else {
console.log('🔄 AIモデル読み込み中...(YOLO11x - 高精度モデル)');
}
// キャッシュを無効化するためにタイムスタンプを追加(リトライ時のみ)
const cacheBuster = attempt > 1 ? `?t=${Date.now()}` : '';
const pathWithCacheBuster = modelPath + cacheBuster;
this.model = await tf.loadGraphModel(pathWithCacheBuster, {
requestInit: {
cache: attempt > 1 ? 'reload' : 'default'
}
});
console.log('✅ AIモデル準備完了(YOLO11x)');
return;
} catch (error) {
lastError = error;
console.warn(`⚠️ モデル読み込みエラー (試行 ${attempt}/${maxRetries}):`, error.message);
// キャッシュエラーの場合は、キャッシュをクリアして再試行
if (error.message.includes('CACHE') || error.message.includes('Failed to fetch')) {
console.log('💡 キャッシュエラーを検出。再試行します...');
continue;
}
if (attempt < maxRetries) {
continue;
}
}
}
console.error('❌ モデル読み込みに失敗しました(全試行失敗):', lastError);
throw lastError;
}
GraphModel形式のモデルを読み込む処理で、YOLO11x(物体検出用の高精度なAIモデル)を読み込みます。YOLO11xは、80種類の物体クラスに対応した高精度モデルで、640×640ピクセルの画像を入力として受け取ります。
最初は、YOLO11n(物体検出用の軽量なAIモデル)という軽量モデルを使っていました。しかし、熊の検出精度が低く、誤検出が多かったです。そこで、YOLO11x(物体検出用の高精度なAIモデル)に変更しました。精度は向上しましたが、モデルサイズが大きくなり、読み込み時間も長くなりました。
モデルの読み込み時間は、初回で約5秒、2回目以降はキャッシュにより約1秒に短縮されました。ユーザーが待つ時間としては長いと感じたので、読み込み中の表示を追加しました。これにより、ユーザーは「今、何が起きているか」を理解できるようになりました。
モデルの読み込みが完了すると、検出処理を開始できます。ただし、YOLO11xモデルのサイズは約12MB。初回読み込み時には、このサイズのデータをダウンロードする必要があります。YOLO11nの約6MBと比べると2倍ですが、精度の向上を考えると許容範囲だと判断しました。
8 FPSを実現するフレーム制御
リアルタイム検出の速度は、フレーム制御によって決まります。このシステムでは、8 FPS(1秒間に8回の検出)を目標としました。
最初は、アニメーションフレームごとに検出処理を実行していました。しかし、これでは処理が重く、ブラウザがフリーズしてしまいました。YOLO11x(物体検出用の高精度なAIモデル)の推論には平均150ミリ秒かかるため、60 FPS(約16ミリ秒間隔)では処理が追いつきません。
そこで、フレーム制御を追加しました。検出処理を行うメソッドの中で、前回の検出から一定時間が経過していない場合は、検出処理をスキップします。
async predictFrame() {
if (!this.isPredicting) return;
const now = performance.now();
if (this.lastFrameTime && now - this.lastFrameTime < 1000 / this.fps) {
requestAnimationFrame(() => this.predictFrame());
return;
}
this.lastFrameTime = now;
// ... 検出処理 ...
}
1秒をFPSで割った値が、検出間隔になります。8 FPSの場合、125ミリ秒(0.125秒)ごとに検出処理を実行します。
最初は、FPSを10に設定していました。しかし、実際に動かしてみると、処理が追いつかず、フレームがスキップされてしまいました。YOLO11xの推論時間が150ミリ秒程度かかるため、100ミリ秒間隔(10 FPS)では処理が間に合いません。
そこで、FPSを8に下げました。125ミリ秒間隔にすることで、推論処理が完了する余裕が生まれ、処理が安定しました。さらに、FPSを6に下げることも試しましたが、検出結果の滑らかさが損なわれたため、8 FPSに落ち着きました。
FPSの調整は、処理速度の設定(高速/標準/高精度)によっても変わります。高速モードでは15 FPS、標準モードでは8 FPS、高精度モードでは4 FPSに設定されています。ただし、YOLO11xの推論時間を考慮すると、高速モードでも実際には8-10 FPS程度になることが多いです。
検出処理の核心:画像の前処理と推論
検出処理の流れは、以下の通りです。
1. ビデオフレームをCanvasに描画
2. Canvasの画像を640×640ピクセルにリサイズ
3. 画像をテンソルに変換し、正規化(0-1の範囲に変換)
4. モデルに渡して推論を実行
5. 検出結果を後処理(NMS: Non-Maximum Suppression)でフィルタリング
6. バウンディングボックスを元の画像サイズにスケール
この流れの中で、最も時間がかかるのは推論処理です。YOLO11xモデルの推論には、平均150ミリ秒かかります。これは、8 FPS(125ミリ秒間隔)の検出サイクルに収まる範囲です。ただし、推論時間が150ミリ秒を超える場合、次のフレームの検出が遅れる可能性があります。
画像の前処理では、Canvasの画像をテンソル(多次元配列)に変換します。その後、640×640ピクセルにリサイズし、0-1の範囲に正規化します。
const input = tf.tidy(() => {
return tf.browser.fromPixels(tempCanvas)
.resizeBilinear([640, 640])
.div(255.0)
.expandDims(0);
});
テンソル(多次元配列)のメモリ管理を自動化する処理により、処理が終わると、使用したテンソルを自動的に解放してくれます。これにより、メモリリークを防ぎます。
最初は、テンソル(多次元配列)のメモリ管理を自動化する処理を使わずに実装していました。結果、メモリ使用量が増加し、ブラウザがクラッシュしてしまいました。特に、推論処理で生成される大量のテンソルが解放されず、メモリ使用量が1GBを超えることもありました。メモリ管理を自動化する処理を追加することで、メモリ使用量が安定し、200-300MB程度に収まるようになりました。
NMS(Non-Maximum Suppression、非極大値抑制)処理では、重複する検出結果を除去します。非極大値抑制の処理を使い、最大500個の検出結果から、IoU(重なり具合の指標)閾値0.45、スコア閾値0.2でフィルタリングします。これらの値は、実際の検出結果を見ながら調整しました。
最初は、IoU閾値を0.5に設定していました。しかし、これでは同じ物体が複数のボックスで検出されてしまいました。そこで、IoU閾値を0.45に下げました。これにより、重複検出が減りましたが、近接した別の物体が1つにまとめられてしまう問題も発生しました。最終的に、0.45という値に落ち着きました。
信頼度閾値15%の調整過程
検出結果の信頼度は、0から1の範囲で表されます。1に近いほど、検出の確信度が高いことを意味します。
このシステムでは、信頼度閾値を15%(0.15)に設定しています。これは、検出結果のうち、信頼度が15%以上のものだけを表示するという意味です。
最初は、信頼度閾値を50%に設定していました。しかし、これでは熊の検出漏れが多く、特に遠くの熊や、一部が隠れている熊を検出できませんでした。そこで、閾値を30%に下げました。これにより、検出率は向上しましたが、誤検出も増えました。
さらに、閾値を15%に下げました。これにより、検出率は大幅に向上しましたが、誤検出も増加しました。ただし、熊検出に特化したシステムであることを考えると、検出漏れを減らすことが優先されました。誤検出は、重複検出の軽減機能で対処します。
信頼度閾値は、ユーザーが設定画面で調整できます。10%から95%まで、5%刻みで変更可能です。低い値に設定すると、多くの物体を検出できますが、誤検出も増えます。高い値に設定すると、検出精度は上がりますが、検出漏れが増えます。
実際に使ってみると、15%という値は、検出率と誤検出のバランスが取れた値だと感じています。ただし、環境やカメラの条件によって最適な値は変わるため、ユーザーが調整できるようにしました。
重複検出を軽減するIoU + 色ヒストグラム + サイズ判定
同じ物体を連続で検出してしまう問題を解決するため、重複検出の軽減機能を実装しました。
判定には、3つの指標を使います。
1. IoU(Intersection over Union、重なり具合の指標): バウンディングボックスの重なり具合を表す指標。0から1の範囲で、1に近いほど重なりが大きい。
2. 色ヒストグラムの類似度: 検出領域の色分布の類似度。バタチャリヤ距離(統計的な距離の指標)を使って計算。
3. サイズの類似度: バウンディングボックスのサイズの類似度。面積の比率で計算。
最初は、IoUだけを使っていました。しかし、これでは位置が少しずれただけで重複と判定されてしまい、検出結果が不安定になりました。特に、カメラが動いたり、被写体が動いたりすると、同じ物体でも位置がずれてしまい、重複と判定されませんでした。
そこで、色ヒストグラムの類似度も追加しました。これにより、同じ物体かどうかの判定がより正確になりました。ただし、色が似ている別の物体(例えば、同じ色の熊のぬいぐるみが2つある場合)を区別できませんでした。
そこで、サイズの類似度も追加しました。これにより、同じ物体かどうかの判定がさらに正確になりました。
async evaluateSimilarity(currentDetection, previousDetection) {
// IoUの計算
let maxIoU = 0;
for (const currentBox of currentDetection.boxes) {
for (const previousBox of previousDetection.boxes) {
const iou = this.calculateIoU(currentBox, previousBox);
maxIoU = Math.max(maxIoU, iou);
}
}
// 色ヒストグラムの類似度を計算
const currentHist = await this.calculateColorHistogram(currentDetection.imageData);
const previousHist = await this.calculateColorHistogram(previousDetection.imageData);
const colorSimilarity = this.calculateHistogramSimilarity(currentHist, previousHist);
// サイズの類似度を計算
const sizeSimilarity = currentDetection.boxes.map((currentBox, i) => {
const [_, __, y2_1, x2_1] = currentBox;
const [___, ____, y2_2, x2_2] = previousDetection.boxes[i] || currentBox;
const currentArea = y2_1 * x2_1;
const previousArea = y2_2 * x2_2;
const ratio = Math.min(currentArea, previousArea) / Math.max(currentArea, previousArea);
return ratio;
}).reduce((acc, val) => acc + val, 0) / currentDetection.boxes.length;
// 総合的な類似度スコアを計算
const weights = {
iou: 0.4,
color: 0.3,
size: 0.3
};
const combinedSimilarity =
weights.iou * maxIoU +
weights.color * colorSimilarity +
weights.size * sizeSimilarity;
return {
isSimilar: combinedSimilarity > this.similarityThreshold.combined,
scores: {
iou: maxIoU,
color: colorSimilarity,
size: sizeSimilarity,
combined: combinedSimilarity
}
};
}
IoU、色ヒストグラム、サイズの類似度を組み合わせることで、重複検出の判定がより正確になりました。IoUの重みを0.4、色ヒストグラムの重みを0.3、サイズの重みを0.3に設定しています。
最初は、IoUの重みを0.6、色ヒストグラムの重みを0.4に設定していました。しかし、これでは位置の重なりを重視しすぎてしまい、動いている物体を正しく判定できませんでした。そこで、各指標の重みを均等に近づけ、IoU 0.4、色0.3、サイズ0.3に調整しました。これにより、動いている物体でも正しく判定できるようになりました。
重複検出の閾値は、similarityThresholdオブジェクトで管理しています。IoUの閾値は0.7、色ヒストグラムの閾値は0.85、総合的な閾値は0.75に設定しています。これらの値は、実際の検出結果を見ながら調整しました。
色ヒストグラムの計算では、画像を32×32ピクセルにリサイズしてから計算します。これにより、計算時間を短縮しつつ、十分な精度を保てます。最初は、元の画像サイズで計算していましたが、計算時間が長すぎました。32×32ピクセルにリサイズすることで、計算時間を約10分の1に短縮できました。
IndexedDBで検出履歴を保存
検出結果を保存するため、IndexedDB(ブラウザ内のデータベース)を使いました。IndexedDBは、ブラウザにデータを永続的に保存できるAPI(アプリケーション・プログラミング・インターフェース)です。
最初は、検出結果を配列に保存していました。しかし、これではページをリロードすると、データが消えてしまいます。そこで、IndexedDBに保存するように変更しました。
async initDB() {
if (this.db) return this.db;
try {
this.db = await new Promise((resolve, reject) => {
const request = indexedDB.open('detectionDB', 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('detections')) {
const store = db.createObjectStore('detections', {
keyPath: 'id',
autoIncrement: true
});
store.createIndex('timestamp', 'timestamp', { unique: false });
}
};
});
return this.db;
} catch (error) {
throw error;
}
}
データベースの初期化は、アップグレードが必要な時に発生するイベントで行われます。検出結果を保存するオブジェクトストアを作成し、タイムスタンプでインデックスを張ります。これにより、時刻で検索できるようになります。
検出結果の保存は、検出が完了したタイミングで行われます。検出結果には、バウンディングボックス、信頼度、クラス名、タイムスタンプ、画像データが含まれます。
最初は、すべての検出結果を保存していました。しかし、これではIndexedDBの容量がすぐにいっぱいになってしまいました。特に、画像データを含めると、1件あたり数百KBになることもありました。
そこで、重複検出の軽減機能を実装し、類似度が高い検出結果は保存しないようにしました。これにより、保存されるデータ量を大幅に削減できました。
さらに、履歴の上限を500件に設定しました。これにより、メモリ使用量を抑制し、ブラウザがクラッシュすることを防ぎます。
検出履歴の読み込みは、検出履歴を管理するコンポーネントで行われます。ページネーション機能を実装し、12件ずつ表示するようにしました。これにより、大量の検出結果でも、快適に閲覧できます。
3つの入力ソースに対応
このシステムは、3つの入力ソースに対応しています。
1. Webカメラ: getUserMedia(ブラウザからカメラにアクセスする仕組み)でカメラにアクセス
2. 動画URL: YouTubeや直リンクの動画を読み込み
3. 画面共有: getDisplayMedia(ブラウザから画面をキャプチャする仕組み)で画面をキャプチャ
最初は、Webカメラのみに対応していました。しかし、実際に使ってみると、「動画を分析したい」という要望が多かったです。そこで、動画URLと画面共有の機能を追加しました。
動画URLの読み込みでは、YouTubeのURLを解析し、埋め込みプレーヤーに変換します。直リンクの動画の場合は、<video>要素で直接読み込みます。
function getYouTubeEmbedUrl(url) {
const videoIdMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([^&\?\/]+)/);
if (videoIdMatch && videoIdMatch[1]) {
return `https://www.youtube.com/embed/${videoIdMatch[1]}`;
}
return null;
}
YouTubeのURL解析では、正規表現を使って動画IDを抽出します。youtube.com/watch?v=、youtu.be/、youtube.com/embed/の3つの形式に対応しています。
画面共有では、getDisplayMediaで画面をキャプチャし、<video>要素に表示します。これにより、YouTube動画やその他の動画を分析できます。
screenStream = await navigator.mediaDevices.getDisplayMedia({
video: { cursor: 'never' },
audio: false
});
cursor: 'never'を指定することで、マウスカーソルが画面共有に含まれないようにします。これにより、検出結果がより正確になります。
3つの入力ソースは、同じprediction-componentで処理されます。setVideoStream()メソッドで、ビデオストリームを設定します。これにより、入力ソースが変わっても、検出処理のコードは変更する必要がありません。
検出エリア指定(ROI)機能の実装
特定の領域のみを検出対象にするため、ROI(Region of Interest、関心領域)機能を実装しました。
ROIの設定は、ROIを設定するメソッドで行います。選択した領域は、正規化座標(0から1の範囲)で保存されます。
最初は、ROI機能を実装していませんでした。しかし、実際に使ってみると、「特定の領域だけを検出したい」という要望が多かったです。例えば、画面の中央部分だけを検出対象にしたい場合などです。
そこで、ROI機能を追加しました。ROI(関心領域)の適用は、検出処理の後に行われます。検出結果のバウンディングボックスの中心点が、ROIの範囲内にあるかどうかを判定し、範囲外の検出結果は除外します。
setROI(roiArea) {
this.roiArea = roiArea;
console.log('ROIエリアを設定しました:', roiArea);
}
// 検出処理でROIを適用
if (this.roiArea) {
const roiX = this.roiArea.x * videoWidth;
const roiY = this.roiArea.y * videoHeight;
const roiW = this.roiArea.width * videoWidth;
const roiH = this.roiArea.height * videoHeight;
// バウンディングボックスの中心点がROI内にあるかチェック
const boxCenterX = (x1 + x2) / 2;
const boxCenterY = (y1 + y2) / 2;
if (boxCenterX < roiX || boxCenterX > roiX + roiW ||
boxCenterY < roiY || boxCenterY > roiY + roiH) {
continue; // ROI外の検出はスキップ
}
}
ROI機能により、不要な領域の検出を除外できるようになりました。これにより、検出精度が向上し、処理速度も改善しました。特に、画面の端に映る不要な物体を除外できるため、検出結果がより正確になります。
ROIエリアは、Canvas上にオレンジ色の点線で表示されます。これにより、ユーザーは検出対象の領域を視覚的に確認できます。
パフォーマンス測定と最適化
パフォーマンスの測定は、高精度なタイマー機能を使って行いました。
推論処理の時間は、平均150ミリ秒でした。これは、8 FPS(125ミリ秒間隔)の検出サイクルに収まる範囲です。ただし、推論時間が150ミリ秒を超える場合、次のフレームの検出が遅れる可能性があります。
メモリ使用量は、テンソル(多次元配列)のメモリ管理を自動化する処理を使うことで安定しました。テンソルの自動解放により、メモリリークを防ぎます。メモリ使用量は、200-300MB程度に収まります。
処理速度の最適化では、以下の点を改善しました。
1. 画像のリサイズ: 検出処理用の画像を640×640ピクセルにリサイズすることで、処理時間を短縮
2. テンソルの自動解放: テンソル(多次元配列)のメモリ管理を自動化する処理でメモリ管理を自動化
3. フレーム制御: 一定間隔でのみ検出処理を実行し、不要な処理をスキップ
4. 色ヒストグラムの計算: 画像を32×32ピクセルにリサイズしてから計算することで、計算時間を短縮
これらの最適化により、8 FPSを安定して実現できるようになりました。
実際に、複数のデバイスでテストしました。デスクトップPC(Intel Core i7、16GB RAM)では、8 FPSを安定して実現できました。ノートPC(Intel Core i5、8GB RAM)では、6-7 FPS程度になりました。スマートフォン(iPhone 12)では、3-4 FPS程度になりました。デバイスの性能によって、処理速度は大きく変わります。
使用しているAIモデルの紹介とライセンス
このシステムでは、Ultralytics社が開発したYOLO11シリーズのモデルを使用しています。
YOLO11x(高精度モデル)
このシステムのメインモデルとして使用しているYOLO11xは、YOLO11シリーズの中で最も高精度なモデルです。高い検出精度と複雑な物体検出タスクへの対応力が特徴で、熊検出のような特定の物体を高精度で検出する用途に適しています。
モデルサイズは約12MBで、640×640ピクセルの画像を入力として受け取り、80種類の物体クラスを検出できます。推論時間は平均150ミリ秒程度で、8 FPSのリアルタイム検出を実現しています。
YOLO11n(軽量モデル)
システムではYOLO11xを使用していますが、プロジェクト内にはYOLO11nモデルも含まれています。YOLO11nは、YOLO11シリーズの中で最も軽量なモデルで、モデルサイズは約6MBです。エッジデバイスやリソースが限られた環境での利用に適しており、高速な推論速度と低い計算コストが特徴です。
ライセンス情報
YOLO11xおよびYOLO11nモデルは、AGPL-3.0(GNU Affero General Public License 3.0)ライセンスの下で提供されています。
重要なポイント:
- オープンソースライセンス: AGPL-3.0は、オープンソースのコピーレフトライセンスです
- ソースコード公開義務: このライセンスの下でモデルを使用する場合、ネットワーク経由での利用も含め、ソースコードの公開義務が課されます
- 商用利用: 商用利用を検討する場合、Ultralytics社とのエンタープライズライセンス契約が必要です
- 非商用利用: 非商用の研究・教育目的での利用は、ライセンス条件に従って利用可能です
詳細なライセンス条件や商用利用に関する情報は、Ultralytics社の公式ライセンスページをご参照ください。
このシステムは、非商用の研究・教育目的でYOLO11xモデルを使用しています。商用利用を検討される場合は、適切なライセンス契約を締結してください。
使ってみて
実際に生成されたシステムを、旧サイトで公開しています。Webカメラにアクセスを許可すると、リアルタイムで熊検出が始まります。
カメラに熊のぬいぐるみや熊の画像を映すと、青いボックスが表示されます。信頼度が高い検出結果ほど、ボックスの色が濃くなります。検出履歴は、右側のサイドバーで確認できます。
設定画面では、信頼度閾値や処理速度を調整できます。高速モードでは15 FPS、標準モードでは8 FPS、高精度モードでは4 FPSで動作します。用途に応じて、最適な設定を選んでください。
検出対象の設定では、「bear」(熊)がデフォルトですが、他の動物クラス(dog、cat、birdなど)も選択可能です。「animal」を選択すると、すべての動物クラスを検出できます。
まとめ
- TensorFlow.js(ブラウザ上で機械学習を実行するライブラリ)でYOLO11xモデル(物体検出用のAIモデル)を読み込み、ブラウザ単体で熊を検出できるシステムを実装(熊検出特化、設定で他の動物クラスも検出可能)
- 8 FPS(1秒間に8回の検出)の検出速度を実現するため、フレーム制御とパフォーマンス最適化を行った
- 信頼度閾値15%の調整により、検出率を優先しつつ誤検出を抑制
- IoU(重なり具合の指標)+ 色ヒストグラム + サイズの判定により、重複検出を軽減(重み: IoU 0.4、色0.3、サイズ0.3)
- IndexedDB(ブラウザ内のデータベース)で検出履歴を保存し、ページネーション機能を実装(上限500件)
- Webカメラ、動画URL、画面共有の3つの入力ソースに対応
- ROI(Region of Interest、関心領域)機能を追加し、特定の領域のみを検出対象にできるようにした
さらに深く学ぶには
この記事で興味を持った方におすすめのリンク:
最後まで読んでくださり、ありがとうございました。ブラウザだけで熊を検出できるシステムを作る過程で、TensorFlow.jsの可能性を感じました。このシステムは熊検出に特化していますが、設定で他の動物クラスも選択できます。あなたも、ブラウザだけで完結するAIアプリケーションを作ってみませんか?