📸

【GCP×LINE Bot】結婚式で「笑顔写真コンテスト」アプリを自作したら50人から150枚の写真が集まった

に公開

TL;DR

  • 結婚式の余興として 「笑顔写真コンテスト」 をLINE Bot + GCPで実装
  • Cloud Vision API で笑顔検出、Gemini で自然さを評価、Average Hash で類似画像を検出
  • 開発期間 約2週間、費用 実質無料(GCP無料枠内) で本番運用に成功
  • 当日は 50人のゲストから150枚以上の写真 が投稿され、大盛況!

はじめに

結婚式、エンジニアだったら何か作りたくなりますよね?

せっかくの結婚式なので、エンジニアとしての技術力を活かして何か面白いことができないかと考えていました。

そこで作ったのが 「笑顔写真コンテスト」 アプリです。

ゲストがLINE Botに写真を送ると、AIが笑顔を採点してリアルタイムでランキング表示 される仕組みです。
披露宴会場のスクリーンにランキングを映し出し、ゲスト参加型のイベントとして盛り上げました。

全体ランキング

結果として 50人のゲストから150枚以上の写真 が集まり、想像以上の盛り上がりに。本記事では、このシステムの設計から当日の運用まで詳しく紹介します!

この記事でわかること

  • LINE Bot × GCP でリアルタイムスコアリングシステムを作る方法
  • Cloud Vision API と Gemini を組み合わせた「笑顔採点」の実装
  • 類似画像検出(Average Hash)でスパム対策する方法
  • 結婚式という「一発勝負」の本番を乗り切るための工夫

システム全体像

アーキテクチャ図

技術スタック

コンポーネント 技術 役割
ユーザーI/F LINE Messaging API ゲストが写真を投稿
Webhook Cloud Functions LINEからのメッセージ受信・画像保存
スコアリング Cloud Functions 画像分析・スコア計算
笑顔検出 Cloud Vision API 顔検出 + 笑顔度判定
テーマ評価 Vertex AI (Gemini 2.5 Flash) 自然さ・幸福度をAI評価
画像保存 Cloud Storage 画像ストレージ
DB Firestore ユーザー・スコア・ランキング管理
フロントエンド Firebase Hosting + Vanilla JS リアルタイムランキング表示
IaC Terraform インフラ管理

なぜこの構成?

サーバーレス を選んだ理由は結婚式は一度きりのイベントなので、常時サーバーを稼働させるのはもったいない。
Cloud Functions なら従量課金でイベント終了後のランニングコストはほぼゼロだからです。

LINE Bot を選んだ理由は、ゲストのほぼ全員がLINEを使っていて参加のハードルが低いからです。

Vanilla JS でフロントエンドを実装したのは、ランキング表示程度の機能にReactやNext.jsは過剰だと判断したからです。
Firestoreのリアルタイムリスナーがあればフレームワークなしで十分でした。

スコアリングアルゴリズム

このシステムの核となる部分です。単に笑顔を検出するだけでなく、「グループ写真を促進する」 という設計思想を込めています。

計算式

総合スコア = (笑顔スコア × AI評価スコア ÷ 100) × 類似ペナルティ

1. 笑顔スコア(Cloud Vision API)

Cloud Vision API の顔検出機能で、画像内のすべての顔に対して「笑顔の度合い」を数値化します。

ポイントは、検出された笑顔の「合計値」をスコアにしていること。

ただし、以下の調整が入ります:

  • 笑顔判定の閾値: LIKELY(75点相当)以上の笑顔のみカウント
  • 顔サイズ調整: 顔の大きさに応じて 0.5〜1.2 倍の係数を掛ける

顔サイズ調整は、遠くの人混みが高得点になるのを防ぐためです。近くで撮影するほど高スコアになります。

1人の満面の笑み(クローズアップ) → 約95点 × 1.2 = 約114点
5人の笑顔グループ(中距離) → 約95点 × 5 × 0.7 = 約333点

つまり、グループで撮影すると高得点になりやすいが、顔が小さすぎると減点される というバランスです。
結婚式でゲスト同士の交流を促進しつつ、適度な距離感で撮影することを促します。

2. AI評価スコア(Vertex AI / Gemini)

笑顔スコアだけだと、極端な話、満面の作り笑いでも高得点になってしまいます。
そこで、Gemini にマルチモーダルで画像を送り、以下の観点で0〜100点の評価をしてもらいます。

  • 自然さ(30点): 作り笑いではなく自然な表情か
  • 幸福度(40点): 目が笑っているか、純粋な喜びが表現されているか
  • 周囲との調和(30点): グループ全体で統一感のある雰囲気が出ているか

AIはスコアと一緒に コメントも生成 します。
「素敵な笑顔ですね!」といったフィードバックをLINE Botからユーザーに返信することで、投稿のモチベーションを高めます。

3. 類似ペナルティ(Average Hash)

連写した写真を何枚もアップロードして上位を独占されるのを防ぐため、Average Hash による類似画像検出を実装しています。

仕組みはシンプル:

  1. 画像を8×8ピクセルに縮小
  2. 各ピクセルが平均より明るいか暗いかで64ビットのハッシュ値を生成
  3. 2つの画像のハッシュを比較し、異なるビット数(ハミング距離)が閾値以下なら「類似」と判定

類似画像と判定された場合、スコアは 1/3 に減点 されます。
これで連写スパムは意味がなくなり、バリエーション豊かな写真の投稿を促すようにしました。

スコア例

シチュエーション 笑顔スコア AI評価 類似 総合スコア
5人グループの自然な笑顔(中距離) 333 85 なし 283点
1人のクローズアップ笑顔 114 90 なし 103点
1人の笑顔(類似画像あり) 114 90 あり 34点
料理の写真 0 0 - 0点

工夫したポイント

Claude Codeで開発を完結

今回の開発は、設計からコーディング、デバッグまで すべてClaude Code で行いました。
Terraformの設定、Cloud Functionsのコード、フロントエンドのHTML/CSS/JS、さらにはこの記事の下書きまで、ほぼすべての作業をClaude Codeとの対話で進めました。
この程度の規模なら、適切なプロンプト設計とレビューで高品質なコードが得られることを実感しました。
この記事もClaude Codeにドラフトを書かせ、私が編集・加筆しています。

レスポンス速度の最適化

写真を投稿してからスコアが返ってくるまでの時間は、ユーザー体験に直結します。
以下の工夫で 3〜5秒 に収めました。

  • Vision API と Vertex AI への問い合わせを 並列実行
  • Gemini のモデルをモジュールレベルで初期化し、毎回の初期化オーバーヘッドを削減
  • 画像データを Base64 ではなく バイナリ でそのまま処理

Rate Limiting 対策

披露宴のピーク時には、短時間に多くの写真が投稿されます。
Vertex AI にはレート制限があるため、指数バックオフによるリトライ を実装しました。
結果としてRate Limitに引っかかることはありませんでしたが、念のための保険として有効だと考えています。

当日の運用

ゲスト側の体験

  1. 会場に設置したQRコードを読み取り、LINE公式アカウントを友だち追加
  2. 最初にテキストで名前を送信して登録
  3. あとは写真を送るだけ!数秒でスコアとAIコメントが返ってきます

line response

会場スクリーン

  • ランキングモード: 直近15枚の中から上位3枚を大きく表示

ランキングモード

  • スライドショーモード: 直近30枚をランダムに表示し、新しい投稿があれば自動で追加

スライドショーモード

Firestoreのリアルタイムリスナーを使用しているため、新しい写真が投稿されると即座にランキングに反映されます。

  • 管理者ダッシュボード: 投稿状況や総スコアランキングをリアルタイムで確認可能

管理者ダッシュボード

披露宴中はスマホでこのページを監視して状況を把握していました。

結果

  • 50人のゲスト から 150枚以上の写真 が投稿
  • 複数人で写ったグループ写真が多く、交流促進に成功

開発期間・コスト

開発期間

約2週間(平日夜と週末に作業)

Claude Codeのおかげでかなり効率的に進められました。

費用

サービス 使用量 実際の支払い
Vertex AI(Gemini) ¥175 ¥0
Cloud Run Functions ¥11 ¥0
その他(Storage, Firestore等) ¥10 ¥0
合計 約¥200 ¥0

GCPの無料枠で全額カバーされ、実質無料 で運用できました。
イベント終了後はリソースを削除すれば、ランニングコストもゼロになります。

反省点と学び

最終結果発表の操作

最終結果発表画面への切り替えは、PCから手動で操作する必要があり、披露宴中は新郎新婦は忙しいため友人に操作をお願いし、負担をかけてしまいました。
改善案: 今思えばマウスだけ新郎新婦テーブルに置いて私が操作すればよかったです。なぜ気づかなかったのか…。

テスト不足

本番と同じ条件(数十人の同時アップロード)でのテストが十分にできず、当日まで不安が残りました。
Cloud Functions のオートスケールを信じるしかなかったですが、結果的には問題ありませんでした。

学び: 今回のようなスケジュール厳しい中での個人開発では「本番同等のテスト」は現実的に難しいです。
代わりに、フェイルセーフの設計(エラー時にゲストに不快な思いをさせない)に注力するのが正解だと感じました。

まとめ

結婚式という大切なイベントで自作システムを本番稼働させるのは、かなり緊張しました。
「もし動かなかったら…」という不安は最後まで消えませんでしたが、結果的に大きなトラブルなく運用でき、多くのゲストに楽しんでもらえました。

エンジニアとして培ってきた技術を、自分の人生の大切な瞬間に活かせたのは良い経験になりました。

同じようなことをやってみたい方へ:

  1. シンプルに作る - 複雑な機能は不要。投稿→スコア表示の基本フローを確実に
  2. フェイルセーフを意識する - エラー時にもゲストに不快な思いをさせない設計
  3. 当日の操作は最小限に - 新郎新婦は当日は忙しいので、できる限り自動化しよう

参考

https://www.m3tech.blog/entry/wedding-line-bot

Discussion