FLASH

FLASHFront-camera Live Auto Shutter by palm Hand)は、セミナー受付における出席確認を自動化するスマートフォン向けWebアプリケーションである。運営側が用意したAndroidスマートフォンをキオスク端末として設置し、参加者がフロントカメラに手のひらをかざすだけで、カウントダウン撮影が自動実行される。撮影された顔写真はサーバーへ自動アップロードされ、次の参加者の撮影待機に即座に移行する。

手のひら検出にはGoogleのMediaPipe Handsを採用しており、すべてのML推論はブラウザ内で完結する。映像フレームが外部サーバーに送信されることはなく、送信されるのは撮影確定後のJPEG画像のみである。ネイティブアプリのインストールは不要で、URLアクセスだけで利用を開始できる。参加者はカメラの前で手のひらをかざすだけでよく、画面に一切触れる必要がない。

概要

FLASHは、セミナー等の受付における出席確認用の顔写真撮影を自動化するWebアプリケーションである。運営側が用意したAndroidスマートフォンのブラウザ上で動作し、URLにアクセスするだけで利用を開始できる。参加者がフロントカメラの前で手のひらをかざすと、MediaPipe Handsが手のひらの開いた状態を検出し、カウントダウン撮影を自動実行する。撮影画像は設定されたサーバーへ自動アップロードされ、次の参加者の撮影に即座に移行する。受付スタッフと参加者双方の操作負荷を最小化し、スムーズな出席確認フローを実現することを目的としている。

背景と課題

特徴

システム構成

FLASHはクライアントサイドで撮影・手のひら検出を完結し、サーバー側は画像受信・保存のみを担当する構成をとる。Webアプリ本体(HTML/CSS/JS/MediaPipe Handsモデル等の静的ファイル)およびPHP受信スクリプトは同一のXサーバー上に設置される。

Android端末(キオスク) 📷 フロントカメラ映像 getUserMedia (facingMode: "user") 🤚 手のひら検出 MediaPipe Hands(21ランドマーク) ⏱ カウントダウン → 📸 Canvas撮影 canvas.toBlob() → JPEG生成 ⚙️ localStorage 🔒 Wake Lock PWA — Service Worker + Manifest HTTP POST (JPEG + APIキー) Xサーバー 📄 静的ファイル HTML / CSS / JS / MLモデル 🔧 PHP受信スクリプト APIキー認証 + 画像保存 📁 日付フォルダ保存 例: /20260214/20260214120000_a1b2c3.jpg

クライアントサイド

カメラ映像取得にはMediaDevices API(getUserMedia)を使用し、facingMode: "user" の指定によりフロントカメラを選択する。手のひら検出にはMediaPipe Handsを使用し、ブラウザ上で21個のランドマーク検出を行って手のひらが開いた状態を判定する。撮影処理は <video> 要素のフレームを <canvas> に描画し、canvas.toBlob() によりJPEG画像を生成する。解像度は1920×1080程度の高品質である。画面のスリープ防止にはWake Lock APIを使用し、設定値の永続化にはlocalStorageを使用する。画面の向きはCSS・JavaScriptにより縦向き固定とされる。

サーバーサイド

PHPスクリプトがPOSTリクエストで画像データとAPIキーを受信する。APIキーの照合を行い、不一致の場合はリクエストを拒否する(HTTP 403)。受信日に応じた日付フォルダ(例:20260214)をサーバー上に自動作成し、クライアントから送信されたファイル名で画像ファイルを保存する。接続テスト用のリクエストにも応答する。

利用フロー

運営側の準備

  1. Android端末のブラウザ(Chrome推奨)でFLASHのURLにアクセスする
  2. 初回アクセス時はPINコード設定画面が自動表示されるので、4桁の数字PINを設定する
  3. PIN設定完了後、設定画面に遷移する。アップロード先URL、APIキー、セミナー名、ガイドメッセージ等を入力する
  4. 「接続テスト」ボタンでサーバーとの疎通を確認する
  5. 設定完了後、撮影待機画面に遷移する
  6. 端末を受付に縦置きで設置する
💡
初回セットアップのポイント — PINコード以外の設定項目にはすべてデフォルト値が設定されているため、最低限PINコードの設定のみでも撮影を開始できる。ただし、保存モードが「サーバーアップロード」の場合、アップロード先URLが未設定だと撮影待機画面に警告が表示される。

参加者の利用

  1. 受付に設置された端末の前に立つ
  2. 画面にはカメラプレビュー、セミナー名、ガイドメッセージ(例:「手のひらをかざしてください」)が表示されている
  3. フロントカメラに向かって手のひらをかざす
  4. カウントダウンが開始され、画面中央に大きな数字が表示される。画面の縁が点滅する
  5. カウントダウン終了時にフラッシュ演出とともに撮影が実行される
  6. 撮影後、設定されたプレビュー秒数の間プレビューが表示される
  7. 自動的に撮影待機画面に戻り、次の参加者の撮影が可能となる
🤚
参加者は一切の操作が不要 — 画面タッチやボタン操作は一切必要ない。手のひらをかざすだけで、カウントダウン→撮影→アップロード→待機復帰がすべて自動で完了する。

撮影待機画面

撮影待機画面は、参加者が最初に目にする画面であり、アプリの主画面である。カメラプレビューが全画面に近いレイアウトで表示され、手のひら検出が有効な場合はリアルタイムに検出処理が実行されている。

画面構成要素

要素 位置 説明
カメラプレビュー 全画面 フロントカメラの映像をリアルタイムで表示
セミナー名 上部 設定画面で入力したセミナー名を表示(設定されている場合のみ)
ガイドメッセージ プレビュー上 「手のひらをかざしてください」等のガイド文言を表示
歯車アイコン 画面隅 設定画面へのアクセス用。タップするとPINコード入力画面を表示
撮影カウント 歯車アイコンの下 現在のセッションにおける撮影枚数を数字のみで表示
手のひらマーク 画面内 手のひらタイマーが有効な場合に、検出可能であることを示すアイコン
警告メッセージ 画面上 アップロード先URL未設定時に「アップロード先が未設定です」と表示
12:00 📶 🔋
第12回 AI活用セミナー
👤
🤚 手のひらをかざしてください
⚙️
12
撮影待機画面のイメージ(縦向き)

手のひら検出とトリガー

検出の仕組み

FLASHではMediaPipe Handsを使用してリアルタイムに手のひらを検出する。MediaPipe HandsはGoogleが開発したブラウザ上で動作する手検出用機械学習モデルであり、手のひらの21個のランドマーク(関節点)をリアルタイムで検出できる。検出処理はすべてブラウザ内で完結し、映像フレームが外部サーバーへ送信されることはない。

モデルおよび必要なアセットの読み込みが完了するまではローディング表示が行われ、読み込み完了後に検出が開始される。Service Workerによるキャッシュにより、2回目以降のアクセスではモデルの読み込みが高速化される。

項目 仕様
検出モデル MediaPipe Hands
ランドマーク数 21個(手の関節点)
判定条件 21個のランドマークの位置関係から「手のひらが開いた状態」を判定
信頼度閾値 0.8以上(palm_conf_threshold
推論実行間隔 100ms目安(inference_interval_ms、動的調整あり)
推論実行場所 すべてブラウザ内(端末内完結)

トリガー条件

手のひら検出が信頼度閾値以上で成立しても、単発の検出ではトリガーは成立しない。連続フレームでの検出成立(デフォルト5フレーム連続)が要求され、単発の誤検出を低減する。トリガー成立後、即座にカウントダウンが開始される。

パラメータ 説明
palm_conf_threshold 0.8 手のひら検出の信頼度閾値
consecutive_frames_required 5 トリガー成立に必要な連続検出フレーム数
🔬
誤検出の低減 — 連続フレーム検出の要求により、背景の模様や一瞬映り込んだ物体による誤トリガーが低減される。また、ML推論が重い場合は推論頻度が動的に調整(フレームスキップ等)され、カメラプレビューの滑らかさが維持される。

カウントダウンと撮影

カウントダウン演出

トリガー成立後、画面中央に大きな数字のオーバーレイが表示されカウントダウンが開始される。カウントダウン中は画面の縁を点滅させる演出が行われ、参加者にカウントダウン進行中であることを視覚的に伝える。

項目 仕様
タイマー秒数 3秒 / 5秒 / 10秒(設定画面で選択)
カウントダウン表示 画面中央に大きな数字のオーバーレイ
縁点滅演出 カウントダウンに同期して画面の縁を点滅
中断・キャンセル 不可。一度開始されたカウントダウンは必ず最後まで進行する
3
カウントダウン中
2
縁が点滅
1
準備!
📸
フラッシュ!
カウントダウン → 撮影フラッシュ演出の流れ(3秒タイマーの場合)

撮影実行

カウントダウン終了時に画面フラッシュ演出が行われ、<canvas> キャプチャにより撮影が実行される。canvas.toBlob() によりJPEG画像データが生成される。ファイル名は {yyyyMMddHHmmss}_{ランダム英数字6文字}.jpg の形式でクライアントサイドで生成される。

項目 仕様
画像フォーマット JPEG
解像度 1920×1080程度
品質パラメータ 0.92(image_quality
ファイル名形式 20260214120000_a1b2c3.jpg

プレビュー表示

撮影完了後、撮影画像がプレビュー表示される。プレビュー表示秒数は設定画面で変更可能であり、1秒/3秒/5秒/10秒から選択できる(デフォルト3秒)。プレビュー表示中にバックグラウンドでサーバーへの画像アップロードが開始される。

画像アップロード

アップロード処理

撮影した画像は、設定画面で指定されたアップロード先URLにHTTP POSTで送信される。POSTリクエストにはAPIキーが含まれ、サーバー側での認証に使用される。プレビュー表示中にアップロードが完了した場合、プレビュー表示終了後に撮影待機画面に自動遷移する。

項目 仕様
送信方式 HTTP POST
送信内容 JPEG画像データ、APIキー、ファイル名
タイムアウト 10秒(upload_timeout_ms: 10000)
最大リトライ回数 3回(upload_max_retries

リトライとエラー処理

アップロード失敗時は自動的にリトライが実行され、最大3回まで試行される。プレビュー表示中にアップロードが完了しなかった場合、プレビュー終了後にローディング表示に切り替わり、アップロード完了まで待機する。3回リトライしてもすべて失敗した場合は、エラーメッセージが表示され「スキップ」ボタンが提供される。スキップボタン押下で撮影待機画面に戻る。スキップした場合、撮影カウントはカウントアップされない。

📸 撮影完了
🖼 プレビュー表示
+ アップロード開始
プレビュー終了
✅ アップロード完了
撮影待機に復帰
カウント+1
⏳ アップロード未完了
ローディング表示
リトライ自動実行
成功
待機復帰
3回失敗
エラー表示
+ スキップボタン
アップロード処理の分岐フロー

ローカル保存モード

設定画面で保存モードを「ローカル保存」に切り替えることができる。ローカル保存モードでは、撮影画像をダウンロードリンク(<a download>)により端末にダウンロード保存する。サーバーへのアップロードは行われない。ネットワーク障害時の代替手段として利用できる。

設定画面

設定画面は、撮影待機画面の隅に常時表示されている歯車アイコンからアクセスする。歯車アイコンをタップするとPINコード入力画面が表示され、正しいPINコードを入力した場合のみ設定画面に遷移する。

設定項目一覧

設定項目 説明 選択肢・形式 デフォルト
撮影設定
手のひらタイマー ON/OFF 手のひら検出によるタイマー撮影機能の有効/無効 ON / OFF ON
タイマー秒数 カウントダウンの秒数 3秒 / 5秒 / 10秒 3秒
プレビュー秒数 撮影後のプレビュー表示時間 1秒 / 3秒 / 5秒 / 10秒 3秒
保存・アップロード設定
保存モード サーバーアップロード / ローカル保存の切り替え サーバーアップロード / ローカル保存 サーバーアップロード
アップロード先URL PHP受信スクリプトのURL テキスト入力 空欄
APIキー サーバー認証用のAPIキー テキスト入力 空欄
接続テストボタン サーバーとの疎通確認 ボタン
表示設定
セミナー名 撮影待機画面上部に表示するセミナー名 テキスト入力 空欄(非表示)
ガイドメッセージ 撮影待機画面に表示するガイド文言 テキスト入力 手のひらをかざしてください
管理
PINコード変更 管理用PINコードの変更 数字4桁入力 初回設定時に指定
カウントリセット 撮影カウントをゼロに戻す ボタン

接続テスト

設定画面の「接続テスト」ボタンを押すと、設定されたアップロード先URLとAPIキーを使ってサーバーとの疎通確認が行われる。サーバー側でAPIキーの照合が成功すればテスト成功、認証失敗の場合はエラーが表示される。本番の撮影運用を開始する前に、必ず接続テストでサーバーとの通信が正常であることを確認することが推奨される。

PINコード管理

設定と変更

PINコードは数字4桁で構成される。初回アクセス時にはPINコード設定画面が自動表示され、PIN設定完了までは撮影画面に進むことができない。PINコードはlocalStorageに保存され、設定画面内で変更することも可能である。

ロックアウト

PINコード入力を10回連続で間違えた場合、10分間PINコード入力がロックアウトされる。ロックアウト中はロックアウト状態であることと残り時間が画面に表示される。

項目 仕様
PIN桁数 数字4桁
ロックアウト条件 10回連続失敗
ロックアウト時間 10分間
ロックアウト中の表示 ロックアウト状態 + 残り時間
保存場所 localStorage

PINコードの回復

⚠️
PINコードを忘れた場合 — PINコードの回復手段は、ブラウザのサイトデータクリアによる全設定初期化のみである。サイトデータをクリアすると、PINコードを含むすべての設定値が消去され、初回セットアップ状態に戻る。この手順は設定画面内にも注意書きとして記載されている。

状態遷移

FLASHの画面状態は以下のように遷移する。主要な正常フローは S0 → S1 → S2 → S3 → S4 → S5 → S6 → S7 → S8 → S9 → S4(ループ)である。

状態 名称 説明 遷移先
S0 初回セットアップ 初回アクセス時。PINコード設定を行う S1
S1 設定画面 各種設定の変更が可能 S2
S2 カメラ権限要求 カメラアクセス権限を要求 許可 → S3 / 拒否 → S-E
S3 モデル読み込み中 MediaPipe Handsのモデルを読み込み。ローディング表示 完了 → S4 / 失敗 → S-E2
S4 撮影待機(検出待機) フレームごとに手のひら検出を実行 検出成立 → S5 / 歯車タップ → PIN入力
S5 トリガー成立 連続Nフレームで手のひら検出が成立 S6
S6 カウントダウン中 視覚的カウントダウンと縁点滅。中断不可 S7
S7 撮影実行 Canvasキャプチャにより画像生成。フラッシュ演出 S8
S8 プレビュー表示・アップロード中 プレビュー表示 + バックグラウンドアップロード 完了 → S9 / 未完了 → S8-L
S8-L アップロード待機 ローディング表示。リトライ自動実行 完了 → S9 / 3回失敗 → S8-E
S8-E アップロード失敗 エラー表示 + スキップボタン スキップ → S9(カウントアップなし)
S9 クールダウン 再トリガーを2秒間抑止 S4
S-E カメラエラー カメラ権限拒否。権限許可の案内を表示
S-E2 モデル読み込み失敗 リトライUI。手動シャッターのみで利用可能 S4(機能制限付き)
S0 初回設定 S1 設定画面 S2 カメラ権限 S3 モデル読込 S4 撮影待機 🤚 手のひら検出中 S5 トリガー成立 S6 カウントダウン ⏱ 中断不可 S7 📸 撮影実行 S8 プレビュー + アップロード中 完了 S9 クールダウン 2秒間 ループ(次の参加者) 未完了 S8-L ローディング 3回失敗 S8-E エラー S-E カメラエラー S-E2 モデル失敗
FLASHの状態遷移図(正常フローは緑のループで循環する)

エラー処理

コード エラー 対処
E-01 カメラ権限拒否 機能が利用できない旨を表示し、ブラウザ設定から権限を許可する方法を案内
E-02 getUserMedia非対応ブラウザ 非対応である旨を表示し、推奨ブラウザ(Android Chrome)への案内
E-03 MediaPipe Handsモデル読み込み失敗 リトライUIを提供。手のひらタイマーは無効化し、手動シャッターのみで撮影可能
E-04 WebGL/WebAssembly非対応 手のひらタイマーは無効化し、手動シャッターのみで撮影可能
E-05 アップロード失敗(3回リトライ後) エラーメッセージを表示し、スキップボタンを提供
E-06 カメラストリーム中断 フォアグラウンド復帰時にストリームの再取得を試行
🛟
フォールバック設計 — MediaPipe Handsの読み込みに失敗した場合やWebGL/WebAssembly非対応の端末でも、手動シャッターのみの機能制限付きで撮影は継続可能である。手のひら検出は利便性を高めるオプション機能として位置づけられており、撮影の基本機能がブロックされることはない。

パラメータ一覧

以下はFLASHで調整可能なパラメータの一覧である。「設定画面」列が「可」のものは管理者が設定画面から変更できる。「不可」のものはコード内で定義されており、変更にはソースコードの編集が必要である。

パラメータ名 説明 既定値 設定画面
検出・トリガー
palm_conf_threshold 手のひら検出信頼度閾値 float 0.8 不可
consecutive_frames_required トリガー成立に必要な連続検出フレーム数 int 5 不可
palm_timer_enabled 手のひらタイマー ON/OFF bool ON
inference_interval_ms ML推論の実行間隔(ミリ秒) int 100 不可(動的調整)
撮影
timer_seconds タイマー秒数 int 3 可(3/5/10)
cooldown_seconds クールダウン秒数 float 2.0 不可
image_quality JPEG画像品質(0.0〜1.0) float 0.92 不可
preview_seconds プレビュー表示秒数 int 3 可(1/3/5/10)
アップロード
save_mode 保存モード enum SERVER
upload_url アップロード先URL string 空欄
api_key サーバー認証用APIキー string 空欄
upload_timeout_ms アップロード1回あたりのタイムアウト int 10000 不可
upload_max_retries アップロード最大リトライ回数 int 3 不可
表示・管理
seminar_name セミナー名表示 string 空欄
guide_message ガイドメッセージ string 手のひらをかざしてください
pin_code 管理用PINコード string(4桁) 初回設定時に指定

サーバーサイド仕様

サーバーサイドはXサーバー上に設置されるPHPスクリプトで構成される。画像アップロード用と接続テスト用の2つの機能を、いずれもPOSTリクエストで提供する。

画像アップロード処理

項目 仕様
リクエスト方式 HTTP POST
受信データ APIキー、JPEG画像データ、ファイル名
認証 APIキー照合。不一致の場合はHTTP 403
保存先 受信日の日付フォルダ(例:20260214)に自動作成・保存
ファイル名 クライアントから送信されたファイル名をそのまま使用
成功レスポンス HTTP 200

接続テスト処理

項目 仕様
リクエスト方式 HTTP POST
受信データ APIキー
認証成功 HTTP 200(テスト成功レスポンス)
認証失敗 HTTP 403(認証エラーレスポンス)

フォルダ構造の例

upload/
├── 20260214/
│   ├── 20260214091523_a3f8k2.jpg
│   ├── 20260214091601_m9x2p7.jpg
│   └── 20260214091648_q1w4r8.jpg
└── 20260215/
    ├── 20260215100012_b7c3d9.jpg
    └── ...

PWA対応

FLASHはPWA(Progressive Web App)として動作する。Web App Manifestを提供し、ホーム画面への追加時にアプリ名・アイコン・テーマカラーが適切に表示される。Service Workerを導入し、アプリシェル(HTML/CSS/JS/MediaPipe Handsモデル)をキャッシュすることで、2回目以降の起動が高速化される。

項目 仕様
マニフェスト アプリ名、アイコン、テーマカラー、表示モードを定義
Service Worker HTML/CSS/JS/MediaPipe Handsモデルをキャッシュ
オフライン時 ローカル保存モードでの撮影は可能。サーバーアップロードモードではエラー表示
ホーム画面追加 アプリライクに起動。ブラウザUIなしで全画面表示可能

トラブルシューティング

カメラが起動しない

手のひらが検出されない

誤検出が頻発する

アップロードに失敗する

画面がスリープする

設定画面に入れない

モデルの読み込みが遅い・失敗する

「アップロード先が未設定です」の警告が表示される

注意事項

⚠️
使用上の注意
  • 対象ブラウザはモバイル版Chrome(Android)である。iOS Safari、デスクトップブラウザは対象外
  • カメラはフロントカメラのみ対応。背面カメラでの撮影は非対応
  • 端末は必ず縦向きに設置すること。横向きにするとレイアウトが崩れる
  • カウントダウンは一度開始されると必ず最後まで進行する。中断・キャンセルの手段はない
  • 手のひら検出を含むすべての映像処理はブラウザ内で完結し、映像フレームが外部サーバーに送信されることはない。送信されるのは撮影確定後のJPEG画像のみ
  • localStorageには設定値およびPINコードのみが保存され、撮影画像データは永続保存されない
  • 低性能なAndroid端末ではML推論によりプレビューのフレームレートが低下する場合がある。推論頻度は動的に調整されるが、著しく低性能な端末では手のひらタイマーをOFFにし手動シャッターでの運用を検討すること
  • 長時間の連続運用(100名以上の撮影等)では、端末の発熱やバッテリー消耗に注意すること。充電ケーブルを接続した状態での運用を推奨
  • PINコードを忘れた場合の回復手段はブラウザのサイトデータクリア(全設定初期化)のみ。PINコードは安全な場所に控えておくこと
  • アプリが使用するサーバーへのアップロード先は管理者が設定画面で明示的に指定したURLに限定される。意図しない送信先へのアップロードは行われない
  • 本アプリは顔認証処理を行わない。撮影された画像の照合・認証は別システムで行う必要がある