image-classifier-imagenet Svelte Themes

Image Classifier Imagenet

本リポジトリでは、TensorFlow.js と ONNX Runtime を組み合わせた画像分類アプリケーションを提供しています

画像分析アプリケーション

TensorFlow.js と ONNX Runtime を使用した高性能画像分類アプリケーション。課題仕様準拠のシンプルモードと、7種類のモデルを選択できる拡張モードを提供します。

Web UI イメージ

ダッシュボード画面
ダッシュボード画面
画像アップロード画面
画像アップロード画面(デュアルモード対応)
画像詳細画面
画像詳細画面(メタデータ表示)

このリポジトリについて

本リポジトリでは、TensorFlow.js と ONNX Runtime を組み合わせた画像分類アプリケーションを提供しています。

このアプリケーションの開発理由

このアプリケーションは求職中だった ponponusa がソフトウェア企業への選考に進むにあたって技術課題として提示されたものへの解答となります。

実装目標の作業時間として累計16時間が目標、提出期限は2週間でした。

このアプリケーションはの実装にかかっている実質的な作業時間としては約20時間です。

基本機能の実装については、約14時間、renderへのデプロイで約4時間、クライアントでの推論対応で約2時間です。

技術選定について

実装には積極的に生成AIエージェントを活用し、コードの品質向上と効率化を図っています。

TensorFlow.js を用いたNode.jsアプリケーションを選択し、さらにONNX Runtimeも組み合わせることで、より多様なモデルを扱えるように拡張しています。

データベースには軽量でセットアップが容易なSQLiteを採用し、CLIとREST APIの両方を提供することで、様々な利用シーンに対応できるよう設計しました。

Web UIにはAstro + Svelteを採用し、モダンで高速なフロントエンドを実現しています。

TypeScriptを全体の言語として採用し、型安全性と開発効率の向上を図っています。

実装するにあたっての工夫や考慮点

課題ではAIによるブラックボックスとなっているAPI部分を可能な限り独自に解釈し、Strategy Patternを用いて分析器を抽象化することで、将来的な拡張性を確保しつつ課題仕様も満たしながら、独自の機能を提供できるように工夫しています。

AIによるコーディングで懸念されがちな動くだけのコードではなく、AIエージェントをしっかりと活用しコードの品質向上と効率化を実現、ESLintやPrettierによるコード整形、Vitestによるユニットテスト(PlaywrightによるE2Eテストは準備のみ)を導入し、堅牢なコードベースを維持しドキュメントも充実させています。

課題実装における詳細

docs/assignment-api.md に課題APIの詳細な実装内容、テスト内容、仕様との差異、トラブルシューティングを記載しています。合わせてご参照ください。

主な機能

  • 🖼️ 画像分類: TensorFlow.js と ONNX Runtime による高精度な画像分類
  • 💻 クライアント/サーバー実行: ブラウザまたはサーバーで画像分析を選択可能
    • クライアント実行: サーバーメモリ消費ゼロ、ユーザーのリソースを活用
    • サーバー実行: 安定した速度、全モデル対応
  • 🎯 デュアルモード: 課題仕様準拠モードと拡張機能モード
  • 🤖 7種類のモデル: MobileNet、Inception V3、SqueezeNet、ResNet-50など
  • 🧵 ワーカースレッド: C++メモリを確実に解放する高度なメモリ管理
  • 🌐 Web UI: Astro + Svelteによるモダンなフロントエンド
    • 📋 Assignment/Extendedモード切り替え: ドロップダウンで簡単にモード切り替え
    • 🔄 実行環境トグル: クライアント/サーバー実行をワンクリックで切り替え
    • 📊 ai_analysis_logテーブル表示: 課題モードで分析ログを表示
    • 🔝 ページトップに戻るボタン: スムーススクロールで快適なナビゲーション
    • 🔄 自動モード切り替え: 登録時のモードに応じてリスト表示を自動切り替え
    • 🏷️ クラス名リンク: バッジクリックでクラス別画像一覧へ遷移
  • ⌨️ CLI: コマンドラインインターフェース
  • 🔌 REST API: OpenAPI仕様準拠のRESTful API + 課題仕様準拠API
  • 📊 統計情報: クラスごとの画像数・信頼度の集計
  • パフォーマンス追跡: 処理時間・メモリ使用量の記録
  • 🧪 テスト: Vitest、Playwright対応(77テスト)
  • 🏗️ 拡張可能: Strategy Patternによる分析器アーキテクチャ

技術スタック

フロントエンド

  • Astro 4.16.19 - SSRフレームワーク
  • Svelte 4.2.20 - UIコンポーネント
  • TypeScript 5.9.3

バックエンド

開発ツール

セットアップ

🌐 クラウドデプロイ(Render.com)

本番環境として公開されています:

Note:

  • Free Tierは15分間非アクティブでスリープします。初回アクセス時は起動に30秒程度かかります。
  • Free Tierではディスクストレージが利用できないため、データベースとアップロード画像は再デプロイ時にリセットされます。
  • 永続化が必要な場合は Starter プラン($9/月)をご利用ください。

Docker環境(推奨)🐳

すべてのプラットフォーム(Windows/Mac/Linux)で同一の環境を提供します。

必要要件

  • Docker Desktop (Windows/Mac/Linux)
  • Docker Compose(Docker Desktopに含まれています)

クイックスタート

# リポジトリをクローン
git clone <repository-url>
cd image-classifier-imagenet

# Dockerコンテナをビルド・起動(初回は5-10分程度かかります)
docker compose up -d

# ブラウザで http://localhost:4321 を開く

Docker CLI の使用

# CLIコマンドをコンテナ内で実行
./docker-cli.sh register --image test-images/cat.jpg --analyzer mobilenet
./docker-cli.sh list
./docker-cli.sh classes

# または、直接docker composeコマンドを使用
docker compose exec app pnpm cli register --image test-images/cat.jpg
docker compose exec app pnpm test

開発環境と本番環境の違い

項目 開発環境 本番環境
Dockerファイル Dockerfile Dockerfile.prod
Composeファイル docker-compose.yml docker-compose.prod.yml
イメージ名 image-classifier-dev image-classifier-prod
コンテナ名 image-classifier-dev image-classifier-prod
イメージサイズ 約5.2GB 約5.2GB
最適化 ビルドツール保持 ビルドツール削除・キャッシュクリア
ホットリロード ✅ 対応(開発モード) ❌ 非対応(プレビューモード)
デバッグツール ✅ 含まれる ✅ 含まれる
起動モード pnpm dev pnpm preview
用途 ローカル開発・テスト 本番デプロイ・ステージング確認
ボリューム ホストマウント 専用ボリューム(永続化)
データベース ./db (ホスト) db-data ボリューム
アップロード画像 ./public/uploads uploads-data ボリューム

Note:

  • 本番環境は開発環境と同じ依存関係を保持しつつ、ビルド後に不要なツール(Python、make、g++など)を削除してクリーンアップしています。
  • 本番環境ではアップロードされた画像を専用ボリュームで管理し、public/uploadsdist/client/uploadsの両方にマウントすることで、Astroのビルド済み環境でも正しく配信されます。
  • データベースはmain.dbを使用し、環境変数DATABASE_PATHで指定されています。
  • データベース自動初期化: 両環境ともエントリーポイントスクリプト(docker-entrypoint.sh)により、初回起動時に自動的にデータベースが作成されます。手動でのpnpm db:init実行は不要です。

簡単起動スクリプト

プロジェクトには便利な起動スクリプトが用意されています:

# 開発環境を起動
./run-dev.sh                # バックグラウンドで起動
./run-dev.sh --build        # 再ビルドして起動
./run-dev.sh --no-detach    # フォアグラウンドで起動(ログ表示)

# 本番環境を起動
./run-prod.sh               # バックグラウンドで起動
./run-prod.sh --build       # 再ビルドして起動
./run-prod.sh --no-detach   # フォアグラウンドで起動(ログ表示)

# 環境のクリーンアップ
./docker-cleanup.sh                    # 開発環境のみクリーンアップ
./docker-cleanup.sh --target prod      # 本番環境のみクリーンアップ
./docker-cleanup.sh --target both      # 両方クリーンアップ
./docker-cleanup.sh --target dev -v    # ボリュームも含めて削除

Docker コマンド一覧

開発環境(docker-compose.yml)
# コンテナをビルド・起動
docker compose up -d

# ビルドから実行
docker compose up -d --build

# ログを表示(リアルタイム)
docker compose logs -f

# コンテナを停止
docker compose down

# コンテナを完全削除(データベース・ボリューム含む)
docker compose down -v

# コンテナ内でシェルを実行
docker compose exec app sh

# データベースを再初期化(通常は不要、初回起動時に自動作成される)
docker compose exec app pnpm db:init

# AI分析ログにサンプルデータを挿入(課題モードのテスト用)
docker compose exec app pnpm db:seed-ai-log
本番環境(docker-compose.prod.yml)
# 本番イメージをビルド・起動
docker compose -f docker-compose.prod.yml up -d --build

# 起動のみ(イメージビルド済みの場合)
docker compose -f docker-compose.prod.yml up -d

# ログを表示
docker compose -f docker-compose.prod.yml logs -f

# コンテナを停止
docker compose -f docker-compose.prod.yml down

# コンテナを完全削除(データベース含む)
docker compose -f docker-compose.prod.yml down -v

# コンテナの状態確認
docker compose -f docker-compose.prod.yml ps

# イメージサイズを確認
docker images image-classifier

Docker環境の利点

  • ✅ Node.js、pnpm、ビルドツールなどの環境構築が不要
  • ✅ Windows/Mac/Linuxで完全に同一の環境
  • ✅ ネイティブモジュール(better-sqlite3, TensorFlow.js)が自動ビルド
  • データベース自動初期化(初回起動時に自動作成)
  • ✅ ホットリロード対応(ソースコード変更が即座に反映)
  • ✅ ポータブル(コンテナごと移動可能)
  • ✅ クリーンな環境(ホストマシンを汚さない)

ローカル環境(Docker不使用)

Docker を使用しない場合のセットアップ方法は、プラットフォーム別ドキュメントを参照してください:

注意: ローカル環境はネイティブモジュールのビルドが複雑なため、Docker環境を推奨します。

クライアント実行モデルのセットアップ

クライアント側で画像分析を実行する場合、ONNXモデルファイルが必要です:

# モデルファイルを自動ダウンロード
pnpm run download:models

詳細は クライアントモデルセットアップガイド を参照してください。

使用方法

Web UI

# 開発サーバーを起動
pnpm dev

# ブラウザで http://localhost:4321 を開く

主な機能:

  • 📋 課題モード/拡張モード切り替え: ドロップダウンで簡単に表示を切り替え
    • 課題モード: ai_analysis_logテーブルのデータを表示(課題モードの分析ログ)
    • 拡張モード: image_analysisテーブルのデータを表示(拡張モードの分析結果)
  • 💻 クライアント/サーバー実行切り替え: 画像アップロード時に実行環境を選択
    • クライアント実行: ブラウザでモデルを実行(サーバーメモリ消費なし)
    • サーバー実行: サーバーでモデルを実行(安定した速度)
  • 🔄 自動モード切り替え: 登録時のモードに応じてリスト表示を自動切り替え
  • 🔝 ページトップに戻るボタン: スムーススクロールで快適なナビゲーション
  • 📋 課題モード: 仕様準拠のシンプルな画像分析API
  • 🎛️ 拡張モード: 7種類のモデルから選択可能
  • 画像のアップロードと分析
  • 登録済み画像の一覧表示(テーブル/サムネイル表示切り替え)
  • クラスごとの統計情報表示
  • パフォーマンスメトリクス表示(処理時間・メモリ使用量)
  • 画像の削除

モード切り替え

Web UIでは2つのモードを切り替えられます:

  • 課題モード 📋

    • 課題仕様準拠の /api/assignment/upload を使用
    • MockAnalyzer による高速レスポンス
    • シンプルな結果表示(クラスID + 信頼度)
    • ファイル選択不要(エラーテストも可能)
  • 拡張モード 🎛️

    • 7種類のモデルから選択可能
    • 詳細なメタデータ表示
    • パフォーマンストラッキング
    • TensorFlow / ONNX Runtime 対応

CLI

# 画像を登録(デフォルト: mobilenet)
pnpm cli register --image path/to/image.jpg

# 特定のアナライザーを指定して登録
pnpm cli register --image path/to/image.jpg --analyzer squeezenet-onnx

# 利用可能なアナライザー一覧を表示
pnpm cli analyzers

# 画像一覧を表示
pnpm cli list

# 特定画像の詳細を表示(メタデータ含む)
pnpm cli get <id>

# クラス統計を表示
pnpm cli classes

# 特定クラスの画像を表示
pnpm cli images-by-class <className>

# 画像を削除
pnpm cli delete <id>

# すべての画像を削除
pnpm cli delete-all --yes

詳細は docs/cli-manual.md を参照。

REST API

# APIサーバーを起動
pnpm dev

# APIエンドポイント: http://localhost:4321/api

エンドポイント:

拡張API(Extended API)

  • POST /api/upload?analyzer=<type> - 画像をアップロード・分析(モデル選択可能)
  • GET /api/images - 画像一覧を取得
  • GET /api/images/:id - 特定画像の詳細を取得(メタデータ含む)
  • DELETE /api/images/:id - 画像を削除
  • GET /api/classes - クラス統計を取得
  • GET /api/classes/:className/images - 特定クラスの画像一覧を取得
  • GET /api/analyzers - 利用可能なアナライザー一覧を取得

課題API(Assignment API)

  • POST /api/assignment/upload - 課題仕様準拠の画像分析API
    • リクエスト: image_path (FormData)
    • レスポンス: {success: boolean, message: string, estimated_data: {class: number, confidence: number}}
    • ログ記録: ai_analysis_log テーブルに記録

詳細は docs/openapi.yaml を参照。

画像分析アーキテクチャ

本プロジェクトは、Strategy Patternを採用した拡張可能な分析器アーキテクチャを実装しています。

メモリ管理の仕組み

ワーカースレッドによるC++メモリ解放

ONNX RuntimeやTensorFlow.jsはC++ネイティブモジュールを使用しており、Node.jsのガベージコレクション(GC)では解放できないメモリを確保します。本プロジェクトでは、以下の手法でメモリを確実に解放:

  1. ワーカースレッド実行: 画像分析を別スレッドで実行
  2. 即座に終了: 分析完了後、worker.terminate() で強制終了
  3. OS回収: ワーカープロセスのメモリ(C++メモリ含む)をOSが強制回収
// src/lib/worker-helper.ts
const worker = new Worker(workerPath, { workerData: { imagePath, analyzerType } });
worker.on('message', (result) => {
  worker.terminate(); // メモリ解放のため即座に終了
  resolve(result.data);
});

ビルドプロセス

  • 開発環境: tsx でTypeScriptを直接実行
  • 本番環境: esbuild で事前ビルド(dist/worker/image-analyzer-worker.js

利用可能な分析器

TensorFlow.js ランタイム

MockAnalyzer(開発/テスト用)

  • 特徴: ハッシュベースの決定論的分類器
  • 速度: ⚡ 超高速(<1ms/画像)
  • 精度: 低(テスト用)
  • 依存: なし(TensorFlow不要)
  • 用途: 開発環境、テスト、CI/CD、課題API

MobileNet V3 Analyzer

  • 特徴: ImageNet事前学習済みMobileNet v2
  • 速度: ⚡ 高速(100-300ms/画像)
  • 精度: 🎯 中精度(1000クラス対応)
  • メモリ: 50-100MB
  • 用途: 本番環境、バランス重視

MobileNet V2 Analyzer

  • 特徴: 軽量版MobileNet
  • 速度: ⚡ 高速(80-250ms/画像)
  • 精度: 🎯 中精度
  • メモリ: 40-80MB
  • 用途: リソース制約環境

Inception V3 Analyzer

  • 特徴: Google Inception V3モデル
  • 速度: ⚖️ 中速(300-500ms/画像)
  • 精度: 🎯 高精度
  • メモリ: 100-150MB
  • 用途: 精度重視

ONNX Runtime

SqueezeNet ONNX Analyzer

  • 特徴: 超軽量モデル(ONNX形式)
  • 速度: ⚡ 超高速(50-150ms/画像)
  • 精度: 🎯 中精度
  • メモリ: 5-10MB
  • モデルサイズ: 5MB
  • 用途: 高速処理、リソース最小化
  • 自動ダウンロード: 初回実行時にONNX Model Zooから取得

MobileNet V2 ONNX Analyzer

  • 特徴: MobileNetのONNX版
  • 速度: ⚡ 高速(80-200ms/画像)
  • 精度: 🎯 中精度
  • メモリ: 15-30MB
  • モデルサイズ: 14MB
  • 用途: バランス重視、ONNX Runtime活用

ResNet-50 ONNX Analyzer

  • 特徴: 深層残差ネットワーク(ONNX形式)
  • 速度: 🐢 低速(500-1000ms/画像)
  • 精度: 🎯 最高精度
  • メモリ: 100-200MB
  • モデルサイズ: 98MB
  • 用途: 最高精度要求時

詳細は docs/analyzer-architecture.md を参照。

開発

ビルド

# 本番ビルド
pnpm build

# ビルド結果のプレビュー
pnpm preview

テスト

# すべてのテストを実行
pnpm test

# ウォッチモード
pnpm test:watch

# カバレッジ
pnpm test:coverage

# E2Eテスト(未実装)
pnpm test:e2e

テスト構成:

  • ユニットテスト: 77テスト
    • データベース: 4テスト
    • APIクライアント: 11テスト
    • 画像分析: 9テスト
    • APIエンドポイント: 20テスト
    • CLIコマンド: 24テスト
    • Assignment API: 9テスト

コード品質

# Linter
pnpm lint

# Formatter(チェック)
pnpm format:check

# Formatter(自動修正)
pnpm format

プロジェクト構成

image-classifier-imagenet/
├── db/                      # データベースファイル
│   ├── schema.sql           # SQLスキーマ定義
│   └── main.db              # SQLiteデータベース
├── docs/                    # ドキュメント
│   ├── analyzer-architecture.md  # 分析器アーキテクチャ
│   ├── cli-manual.md        # CLIマニュアル
│   └── openapi.yaml         # API仕様
├── models/                  # ONNXモデル(自動ダウンロード)
│   ├── squeezenet1.1-7.onnx
│   ├── mobilenetv2-7.onnx
│   └── resnet50-v1-7.onnx
├── public/                  # 静的ファイル
│   ├── images/              # 静的画像
│   └── uploads/             # アップロードされた画像
├── src/
│   ├── components/          # Svelteコンポーネント
│   │   ├── ImageUploadModal.svelte  # モード切り替え対応
│   │   ├── ImageList.svelte
│   │   └── ClassStats.svelte
│   ├── layouts/             # Astroレイアウト
│   ├── lib/                 # ライブラリコード
│   │   ├── analyzers/       # 画像分析器
│   │   │   ├── base-analyzer.ts
│   │   │   ├── onnx-base-analyzer.ts
│   │   │   ├── mock-analyzer.ts
│   │   │   ├── mobilenet-analyzer.ts
│   │   │   ├── mobilenet-v2-analyzer.ts
│   │   │   ├── inception-v3-analyzer.ts
│   │   │   ├── squeezenet-onnx-analyzer.ts
│   │   │   ├── mobilenet-v2-onnx-analyzer.ts
│   │   │   ├── resnet50-onnx-analyzer.ts
│   │   │   ├── imagenet-labels.json
│   │   │   └── factory.ts
│   │   ├── types/
│   │   │   └── database.ts  # DB型定義(ai_analysis_log含む)
│   │   ├── api-client.ts    # APIクライアント
│   │   ├── database.ts      # データベース接続
│   │   ├── repository.ts    # データアクセス層
│   │   └── utils/           # ユーティリティ
│   ├── pages/               # Astroページ
│   │   ├── api/             # APIエンドポイント
│   │   │   ├── upload.ts    # Extended API
│   │   │   ├── analyzers.ts # アナライザー一覧
│   │   │   └── assignment/
│   │   │       └── upload.ts  # Assignment API
│   │   └── *.astro          # Webページ
│   ├── scripts/             # CLIスクリプト
│   │   ├── cli.ts           # CLIエントリーポイント
│   │   └── commands/        # CLIコマンド実装
│   │       ├── analyzers.ts # アナライザー一覧
│   │       └── register.ts  # -a オプション対応
│   └── styles/              # スタイルシート
├── tests/                   # テストコード
│   ├── unit/                # ユニットテスト
│   │   ├── assignment-api.test.ts  # Assignment APIテスト
│   │   └── ...
│   ├── e2e/                 # E2Eテスト
│   └── fixtures/            # テストフィクスチャ
└── test-images/             # テスト用画像

パフォーマンス

画像分析速度

分析器 ランタイム 初期化 1画像 10画像 メモリ
Mock - <1ms <1ms 5ms <1MB
MobileNet V3 TensorFlow 3.2s 250ms 2.5s 50-100MB
MobileNet V2 TensorFlow 2.8s 200ms 2.0s 40-80MB
Inception V3 TensorFlow 4.5s 400ms 4.0s 100-150MB
SqueezeNet ONNX Runtime 1.0s 100ms 1.0s 5-10MB
MobileNet V2 (ONNX) ONNX Runtime 1.5s 150ms 1.5s 15-30MB
ResNet-50 (ONNX) ONNX Runtime 2.5s 700ms 7.0s 100-200MB

モデルサイズ

  • SqueezeNet (ONNX): 5MB
  • MobileNet V2 (ONNX): 14MB
  • ResNet-50 (ONNX): 98MB
  • TensorFlowモデル: メモリ上で展開(ダウンロード不要)

ONNX Runtime の利点

  • ✅ モデルの自動ダウンロード(初回のみ)
  • ✅ 高速な推論実行
  • ✅ 低メモリフットプリント
  • ✅ クロスプラットフォーム対応
  • ✅ GPU加速対応(環境により)

トラブルシューティング

本番環境で画像が404エラーになる

本番環境(docker-compose.prod.yml)でアップロードした画像が表示されない場合、ボリューム設定を確認してください。

原因: Astroのビルド済み環境では静的ファイルがdist/client/から配信されるため、実行時にアップロードされた画像が見つからない。

解決策: uploads-dataボリュームをpublic/uploadsdist/client/uploadsの両方にマウント(v2.0以降で対応済み)

volumes:
  - uploads-data:/app/public/uploads
  - uploads-data:/app/dist/client/uploads

統計情報APIが空のデータを返す

/api/classes{"classes":[]}を返す場合、以下を確認してください:

  1. データベースパスの確認: 環境変数DATABASE_PATHが正しく設定されているか

    # docker-compose.prod.yml
    environment:
      - DATABASE_PATH=/app/db/main.db  # main.db を使用
    
  2. APIエンドポイントの動的レンダリング: prerender = falseが設定されているか

    // src/pages/api/classes.ts
    export const prerender = false;
    
  3. データベース自動初期化の確認: 初回起動時にログを確認

    # 起動ログでDB初期化メッセージを確認
    docker compose -f docker-compose.prod.yml logs app | grep -i database
    
    # 出力例:
    # Database not found at /app/db/main.db. Initializing database...
    # Database initialized successfully.
    

    Note: 通常は自動初期化されるため手動でのpnpm db:init実行は不要です。問題がある場合のみ手動で実行してください。

Windows PowerShellでの日本語文字化け

CLIコマンドの出力が文字化けする場合、UTF-8エンコーディングを設定してください:

一時的な対処(現在のセッションのみ):

[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$env:PYTHONIOENCODING = "utf-8"
chcp 65001

恒久的な対処(推奨):

PowerShellプロファイルに追加:

# プロファイルを開く
notepad $PROFILE

# 以下を追加して保存
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8

または、Windows Terminalを使用すると自動的にUTF-8が適用されます(推奨)。

TensorFlow.jsのインストールエラー

# ネイティブモジュールの再ビルド
npm rebuild @tensorflow/tfjs-node --build-from-source

メモリ不足

# Node.jsヒープサイズを増やす
export NODE_OPTIONS="--max-old-space-size=4096"

# または軽量なMockAnalyzerを使用
export ANALYZER_TYPE=mock

分析が遅い

MobileNetの設定を調整:

// src/lib/analyzers/mobilenet-analyzer.ts
this.model = await mobilenet.load({
  version: 1, // version 2より高速
  alpha: 0.5, // モデルサイズを削減
});

ライセンス

MIT

貢献

プルリクエストを歓迎します。大きな変更の場合は、まずissueを開いて変更内容を議論してください。

参考資料

Top categories

Loading Svelte Themes