壁紙アプリを個人で開発・運営していると、ある時点で必ず直面する問題があります。
「画像が増えすぎて、カテゴリ分類が追いつかない」
わたしが運営する壁紙アプリには、現在3000枚以上の壁紙画像が登録されています。最初は手動で分類していましたが、月に数百枚ペースで追加していると、分類作業だけで週に数時間を奪われるようになりました。
そこで試したのがGemini Vision APIを使った自動分類です。結論から言うと、精度90%超・コスト1000枚あたり数十円という結果が出ました。ここでは設計から実装、実際に運用してわかったことまで書きます。
課題の整理: なぜ手動分類は限界なのか
壁紙アプリのカテゴリ分類は、一見単純な作業に見えます。「これは自然風景」「これはミニマリスト」「これはダークテーマ」と判断するだけです。
しかし実際にやってみると、3つの問題が出てきます。
問題1: 判断のブレ
同じ画像でも、疲れているときと集中しているときで分類が変わることがあります。ユーザーが「このカテゴリ変じゃない?」とレビューで指摘してくれることが何度かありました。
問題2: スケーラビリティ
100枚なら手動でもできます。1000枚になると1日がかりです。3000枚を超えると、追加のたびに分類が遅れてリリースが詰まります。
問題3: 多言語対応
アプリが多言語対応している場合、カテゴリ名の翻訳作業も伴います。「Natural Scenery」「Natürliche Landschaft」…これも全部手動でやっていました。
Gemini Vision APIを選んだ理由
画像分類のAPIは他にもあります。Google Cloud Vision APIやAWSのRekognitionも候補でした。
Gemini Vision APIを選んだ理由は3つです。
理由1: カスタムカテゴリに対応できる
既存の分類APIは「山」「空」「人物」のような汎用カテゴリを返します。でも壁紙アプリには独自のカテゴリがあります。「ローファイ・チルアウト」「グラデーション」「タイポグラフィ」など。Geminiは自然言語でカテゴリを定義して渡せるため、アプリ独自の分類体系に完全対応できます。
理由2: 説明文の同時生成
カテゴリ分類と同時に、「この壁紙の雰囲気の説明文」を生成させることができます。App Store掲載文や多言語タグの自動生成にも使えます。
理由3: コストの予測しやすさ
Gemini Flash(低コストモデル)であれば、1000枚の画像処理が数十円程度で収まります。月次コストの見通しが立てやすい点が個人開発者に向いています。
実装: 分類パイプラインの設計
全体のフローはシンプルです。
画像ファイル一覧取得 → Gemini APIに画像+カテゴリリストを送信
→ レスポンス解析 → DBに分類結果を保存 → 信頼度が低い場合は手動確認キューへ
import google.generativeai as genai
import json
from pathlib import Path
genai.configure(api_key="YOUR_GEMINI_API_KEY")
model = genai.GenerativeModel("gemini-2.0-flash")
# アプリ独自の30カテゴリを定義
CATEGORIES = [
"自然風景", "ミニマリスト", "ダークテーマ", "グラデーション",
"抽象アート", "都市景観", "サンセット", "オーシャン",
"フォレスト", "スペース・宇宙", "フラワー・植物", "アニマル",
"テクスチャ", "パターン", "タイポグラフィ", "建築",
"食べ物", "スポーツ", "テクノロジー", "ゲーム",
"アニメ・イラスト", "水彩・アート", "モノクロ", "ヴィンテージ",
"ネオン・サイバーパンク", "和風・日本", "北欧スタイル", "ローファイ",
"クリスマス・季節", "その他"
]
def classify_wallpaper(image_path: str) -> dict:
"""壁紙画像を分類して結果を返す"""
image_file = genai.upload_file(image_path)
prompt = f"""以下の壁紙画像を分析し、最も適切なカテゴリを選んでください。
カテゴリ一覧:
{json.dumps(CATEGORIES, ensure_ascii=False)}
以下のJSON形式で回答してください:
{{
"primary_category": "メインカテゴリ名",
"secondary_category": "サブカテゴリ名(オプション)",
"confidence": 0.0〜1.0の数値,
"description_ja": "この壁紙の雰囲気を30文字以内で",
"description_en": "30-char atmosphere description in English",
"tags": ["タグ1", "タグ2", "タグ3"]
}}
JSONのみ返してください。"""
response = model.generate_content([image_file, prompt])
try:
result = json.loads(response.text.strip())
return result
except json.JSONDecodeError:
# JSON解析失敗の場合は手動確認キューへ
return {"primary_category": "その他", "confidence": 0.0, "error": response.text}バッチ処理: 3000枚を効率よく処理する
1枚ずつ処理すると、Gemini APIのレート制限にぶつかります。適切な間隔を設けながらバッチ処理します。
import time
import asyncio
from pathlib import Path
async def classify_batch(image_dir: str, output_file: str):
"""ディレクトリ内の全画像を分類してJSONに保存"""
images = list(Path(image_dir).glob("*.jpg")) + list(Path(image_dir).glob("*.png"))
results = []
manual_review_queue = []
print(f"処理対象: {len(images)}枚")
for i, img_path in enumerate(images):
result = classify_wallpaper(str(img_path))
result["filename"] = img_path.name
results.append(result)
# 信頼度0.7未満は手動確認へ
if result.get("confidence", 0) < 0.7:
manual_review_queue.append(img_path.name)
# 進捗表示
if (i + 1) % 50 == 0:
print(f"処理済み: {i+1}/{len(images)} ({len(manual_review_queue)}件が手動確認待ち)")
# レート制限対策: 0.5秒待機
time.sleep(0.5)
# 結果を保存
with open(output_file, "w", encoding="utf-8") as f:
json.dump({
"total": len(images),
"manual_review_count": len(manual_review_queue),
"manual_review_queue": manual_review_queue,
"results": results
}, f, ensure_ascii=False, indent=2)
print(f"完了。手動確認が必要: {len(manual_review_queue)}枚")実際に動かして気づいたこと
実際に3000枚を処理してみた結果を正直に書きます。
精度について
全体の約93%が「信頼度0.7以上」の自動分類になりました。残り7%(約210枚)が手動確認キューに入りましたが、そのほとんどは「自然風景+ミニマリスト」のように複数カテゴリにまたがる画像でした。
コストについて
Gemini 2.0 Flashを使い、3000枚の処理費用は合計で約450円(画像入力トークン込み)でした。手動で同じ作業をした場合の時間コストと比べると、非常に割安です。
失敗パターン
- 真っ黒・真っ白に近い画像:「モノクロ」か「ミニマリスト」か迷う
- 複合的なアート作品:分類が安定しない
- 解像度が低すぎる画像:詳細が読み取れず精度が落ちる
これらはプロンプトの改良(「迷った場合はその他を選んでください」等の追記)で精度が向上しました。
今後の改善: 分類結果をフィードバックとして活用する
現在、ユーザーのアプリ内行動データ(よく見られる壁紙・お気に入り登録数)をGeminiの分類精度向上に活かす実験を進めています。
「ユーザーが気に入る画像の特徴」をGeminiに学習させると、カテゴリ分類の精度がさらに上がるはずです。これはまだ実験段階ですが、個人開発者がデータを活かしてAIを育てていく良い例になると思っています。
手動作業をAIに任せて浮いた時間は、新しいコンテンツの制作や機能改善に使えます。小さな自動化の積み重ねが、個人開発の持続可能性を高めます。