画像に何が写っているかを自動で判定するアプリを作ろうとしたとき、Google Cloud Vision API の認証設定で1時間以上つまずいた経験はありませんか。
公式ドキュメントを読んでも「サービスアカウントキーをどこに置けばいいか」「GOOGLE_APPLICATION_CREDENTIALS をどう設定するか」「Antigravity でどう環境変数を管理すればいいか」という情報がバラバラで、まとまった入門記事が少ないと感じています。
ここでは Antigravity IDE を使って Google Cloud Vision API の Python 統合を、実際に動くコードを軸に解説します。認証の落とし穴、Antigravity でのプロジェクト設定、FastAPI エンドポイントの構築まで、一通り体験できる内容にしました。
Google Cloud Vision API でできること
Vision API は画像を送るだけで以下の情報を返してくれます。
- ラベル検出: 画像に写っているもの(「犬」「公園」「晴れた空」など)を自動分類
- テキスト認識(OCR): 看板や書類・名刺の文字を読み取る
- 顔検出: 顔の位置・感情(喜び、驚きなど)を検出
- ランドマーク検出: 有名な場所を識別
- セーフサーチ: 不適切なコンテンツのフィルタリング
個人開発でよく使うのはラベル検出と OCR です。「ユーザーが投稿した写真のカテゴリを自動で分類する」「名刺や領収書をスキャンしてデータ化する」といった用途に向いています。月1,000ユニットは無料枠があるので、プロトタイプを動かすうちはコストを気にしなくて済む点も助かります。
Antigravity でのプロジェクト設定
.rules ファイルで AI の提案品質を上げる
Antigravity には .rules ファイルを使って AI アシスタントへの指示を事前に定義できます。Vision API プロジェクトではこんな設定が役立ちます。
# .rules(プロジェクトルート)
You are working on a Python project using Google Cloud Vision API.
- Always use google-cloud-vision >= 3.7 package
- Store API credentials in .env file, never hardcode
- Use GOOGLE_APPLICATION_CREDENTIALS for service account auth
- Error handling is required for all API calls
- Use Python 3.11+この数行を置いておくだけで、Antigravity の AI が Vision API のコードを提案するときに、認証の扱いやエラーハンドリングを自動で考慮してくれるようになります。「Vision API で画像のラベルを取得するコードを書いて」と頼むだけで、適切な実装が得られる感覚は、試してみると驚くほど変わります。
環境変数の安全な管理
サービスアカウントキーの設定でよくある間違いは、JSON ファイルのパスをコードに直接書いてしまうことです。
# ❌ やってはいけない(パスがコードに露出する)
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "/Users/masaki/keys/service-account.json".env ファイルで管理するのが正しい方法です。
# .env(.gitignore に必ず追加する)
GOOGLE_APPLICATION_CREDENTIALS=./credentials/service-account.json
GOOGLE_CLOUD_PROJECT=your-project-id# アプリ側
from dotenv import load_dotenv
load_dotenv()
# GOOGLE_APPLICATION_CREDENTIALS は google-cloud ライブラリが自動で読み込むAntigravity 上での環境変数管理についてはこちらの記事も参考になります:Antigravity の環境変数・シークレット管理ガイド
Python で Vision API を呼び出す
ラベル検出の基本実装
まず実際に動くコードから見てみましょう。
# vision_client.py
import os
from google.cloud import vision
from dotenv import load_dotenv
load_dotenv()
def analyze_image_labels(image_path: str) -> list[dict]:
"""
画像ファイルを分析してラベルのリストを返す
Returns:
list[dict]: [{"description": "Dog", "score": 0.98}, ...]
Raises:
RuntimeError: Vision API がエラーレスポンスを返した場合
FileNotFoundError: 指定ファイルが存在しない場合
"""
client = vision.ImageAnnotatorClient()
with open(image_path, "rb") as f:
content = f.read()
image = vision.Image(content=content)
response = client.label_detection(image=image)
# ステータスが 200 でもレスポンス内にエラーが含まれることがある
if response.error.message:
raise RuntimeError(
f"Vision API エラー: {response.error.message}\n"
"詳細: https://cloud.google.com/apis/design/errors"
)
return [
{
"description": label.description,
"score": round(label.score, 4),
"topicality": round(label.topicality, 4),
}
for label in response.label_annotations
]
if __name__ == "__main__":
labels = analyze_image_labels("sample.jpg")
for label in labels:
print(f"{label['description']}: {label['score']:.1%}")
# 出力例:
# Dog: 98.6%
# Puppy: 95.2%
# Carnivore: 93.8%response.error.message のチェックがポイントです。Vision API は HTTP ステータスが 200 でも、レスポンス内にエラーが含まれることがあります。これを見落とすと「成功したと思ったのに実は失敗していた」という状況になりがちです。
URL から直接分析する
ローカルファイルではなく Web 上の URL を直接分析することもできます。
def analyze_image_from_url(image_url: str) -> list[dict]:
"""
画像 URL を指定してラベル分析を行う
Note:
パブリックアクセス可能な URL のみ動作。
認証が必要な URL や内部 URL は使用不可。
Raises:
RuntimeError: Vision API エラー時
"""
client = vision.ImageAnnotatorClient()
image = vision.Image()
image.source.image_uri = image_url
response = client.label_detection(image=image)
if response.error.message:
raise RuntimeError(f"Vision API エラー: {response.error.message}")
return [
{"description": label.description, "score": round(label.score, 4)}
for label in response.label_annotations
]ユーザーが画像 URL を投稿するアプリではこちらの方が便利ですが、Google のサーバーが直接アクセスしに行くため、認証が必要な URL では動作しない点に注意が必要です。
FastAPI エンドポイントへの統合
実用的な Web API として仕上げるため、FastAPI と組み合わせてみます。Antigravity のインラインチャット(Cmd+I)でこのように依頼すると、.rules の内容を踏まえたコードを生成してくれます。
この vision_client.py を FastAPI エンドポイントに統合して。
POST /analyze でJSONの画像URLを受け取り、ラベルのリストを返す。
エラー時は適切なHTTPステータスコードを使って。
生成されるコードはおおむねこのようなものになります。
# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, HttpUrl
from vision_client import analyze_image_from_url
app = FastAPI(title="Vision API Service")
class ImageAnalysisRequest(BaseModel):
url: HttpUrl
class LabelResult(BaseModel):
description: str
score: float
@app.post("/analyze", response_model=list[LabelResult])
async def analyze_image(request: ImageAnalysisRequest):
"""画像 URL を受け取ってラベルのリストを返す"""
try:
labels = analyze_image_from_url(str(request.url))
return labels
except RuntimeError as e:
# Vision API 側のエラーは 502 で返す
raise HTTPException(status_code=502, detail=f"Vision API エラー: {e}")
except Exception as e:
raise HTTPException(status_code=500, detail=f"内部エラー: {e}")
@app.get("/health")
async def health_check():
return {"status": "ok"}FastAPI のプロジェクト構成についてはAntigravity × FastAPI バックエンド開発ガイドも参考にしてみてください。
よくあるつまずきポイント
「Could not automatically determine credentials」
サービスアカウントの JSON ファイルのパスが間違っているか、GOOGLE_APPLICATION_CREDENTIALS が正しく設定されていないときに発生します。
# Antigravity のターミナルで確認
echo $GOOGLE_APPLICATION_CREDENTIALS
python -c "from google.auth import default; creds, project = default(); print(f'Project: {project}')"load_dotenv() を呼んでいても、新しいシェルを開くと .env が反映されていないことがあります。Antigravity のターミナルで新しいセッションを開き直してから試すと解決することが多いです。
「Request had insufficient authentication scopes」
サービスアカウントに必要なロールが付与されていないときのエラーです。Google Cloud Console でサービスアカウントに「Cloud Vision API ユーザー」ロールを追加してください。
割り当て超過(quota exceeded)
Vision API の無料枠は月1,000ユニットです。開発中に大量のテスト画像を送ると予想より早く上限に達することがあります。同じ画像 URL をテストで使い回すか、開発環境では画像の送信回数に上限をつけるコードを添えておくと安心です。
# 開発時の簡易レート制限
import time
_request_count = 0
_MAX_DEV_REQUESTS = 50
def analyze_with_limit(image_url: str) -> list[dict]:
global _request_count
if _request_count >= _MAX_DEV_REQUESTS:
raise RuntimeError("開発用リクエスト上限に達しました")
_request_count += 1
time.sleep(0.1) # 連続リクエストを少し抑制
return analyze_image_from_url(image_url)Vision API をさらに活用するために
ラベル検出に慣れてきたら、次は OCR(テキスト認識) を試してみることをおすすめします。client.text_detection() に切り替えるだけで使えます。名刺や領収書のデジタル化など、業務でも使いやすい機能です。
さらに、Vision API で取得した情報を Gemini API に渡して自然言語で要約・分析するという組み合わせも面白いです。Antigravity × Gemini API 高度な統合ガイドでその実装例を確認できます。
まずは今日、analyze_image_labels() 関数だけを手元で動かしてみてください。認証が通って最初のラベルが返ってきた瞬間、次に作りたいものが自然と見えてきます。