照片变故事:用生成式 AI + Google API 自动生成博客的 Colab 实战

作者:API传播员 · 2025-11-17 · 阅读时间:7分钟

上传相册 → 提取 EXIF → 反向地理编码 → Vertex AI 成文,全程只需 15 分钟。文末 5 颗 AI 提示词彩蛋,让“缓存、重试、文档”全程自动化,效率 x10!🚀


一、项目概览:4 步把照片变成博客

步骤 目标 工具/接口
① 预处理 提取 EXIF、调尺寸、转 JPG Pillow、piexif
② 地理增强 反向地理编码 + 附近地标 Google Maps API
③ 视觉描述 图像→标题 Vertex AI ImageText 模型
④ 故事成文 标题+地理+时间→博客 Vertex AI PaLM + 提示工程

懒人包:直接跑 Colab,改 3 行配置即可生成你的旅行故事。


二、照片预处理:统一格式、瘦身、提坐标

from PIL import Image
import piexif, os, pathlib

SRC_DIR   = "photos"
DST_DIR   = "photos_converted"
MAX_EDGE  = 800          # 最长边≤800px,减少流量
JPG_QUAL  = 85

pathlib.Path(DST_DIR).mkdir(exist_ok=True)

for img_path in pathlib.Path(SRC_DIR).iterdir():
    img = Image.open(img_path)
    img.thumbnail((MAX_EDGE, MAX_EDGE), Image.LANCZOS)

    # 保存为JPG,复制EXIF
    new_path = pathlib.Path(DST_DIR) / f"{img_path.stem}.jpg"
    exif_dict = piexif.load(img.info.get("exif", b""))
    exif_bytes = piexif.dump(exif_dict)
    img.save(new_path, format="JPEG", quality=JPG_QUAL, exif=exif_bytes)

提取关键字段:

from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
import datetime, json

def exif_to_decimal(degrees, minutes, seconds, direction):
    deg = degrees[0] / degrees[1]
    min = minutes[0] / minutes[1]
    sec = seconds[0] / seconds[1]
    decimal = deg + min/60 + sec/3600
    return -decimal if direction in ['S', 'W'] else decimal

def get_lat_lng(exif):
    gps = exif.get("GPSInfo")
    if not gps: return None, None
    lat = exif_to_decimal(*gps[2], gps[1])
    lng = exif_to_decimal(*gps[4], gps[3])
    return lat, lng

def extract_meta(img_path):
    img = Image.open(img_path)
    exif = {TAGS.get(k, k): v for k, v in img._getexif().items()}
    lat, lng = get_lat_lng(exif)
    date_str = exif.get("DateTime", "")
    return {"lat": lat, "lng": lng, "date": date_str}

三、Google Maps API 增强:把坐标变地名

import googlemaps, os

MAPS_API_KEY = os.getenv("MAPS_API_KEY")
gmaps = googlemaps.Client(key=MAPS_API_KEY)

def enrich_location(lat, lng, radius=1000):
    if lat is None or lng is None:
        return {"address": None, "nearby": []}
    address = gmaps.reverse_geocode((lat, lng), language="en")[0]["formatted_address"]
    nearby  = gmaps.places_nearby(location=(lat, lng), radius=radius, language="en")["results"][:3]
    return {"address": address, "nearby": [{"name": p["name"], "type": p.get("types", [])] for p in nearby]}

四、Vertex AI:图像→标题→故事

1. 初始化模型

import vertexai
from vertexai.vision_models import ImageTextModel, Image

PROJECT_ID = "your-gcp-project"
vertexai.init(project=PROJECT_ID)
model = ImageTextModel.from_pretrained("imagetext")

2. 生成标题

def caption_image(img_path):
    img = Image.load_from_file(img_path)
    captions = model.get_captions(
        image=img,
        number_of_results=1,
        language="en"
    )
    return captions[0] if captions else "A memorable moment"

3. 提示模板(Few-Shot)

album_context = """I flew to Los Angeles for a short trip,
and the album contains the photos from the day I arrived there.
The man in those photos is myself."""

few_shot_example = """
Photo 1: Sunset at Santa Monica Pier
Photo 2: Street food on Venice Beach
Blog:
📍 Santa Monica, CA — Day 1
I landed in LA just in time for golden hour. The pier stretched into the Pacific, carnival lights flickering...
(insert Photo 1 here)
After sunset, I wandered to Venice Beach for the legendary tacos...
(insert Photo 2 here)
"""

prompt_template = f"""
{album_context}
{few_shot_example}
Now generate a blog post in the same style for the following photos:
{{photos_info}}
"""

4. 调用 PaLM 生成正文

from vertexai.language_models import TextGenerationModel

palm = TextGenerationModel.from_pretrained("text-bison@001")
response = palm.predict(prompt=prompt_template, max_output_tokens=1024)
blog_md = response.text

五、完整 Colab 调用示例

# 1️⃣ 批量处理相册
meta_list = [extract_meta(p) for p in Path("photos_converted").glob("*.jpg")]

# 2️⃣ 增强地理信息
for m in meta_list:
    m.update(enrich_location(m["lat"], m["lng"]))

# 3️⃣ 生成标题
for m in meta_list:
    m["caption"] = caption_image(m["path"])

# 4️⃣ 组装 Few-Shot Prompt
photos_info = "\n".join([f"Photo {i+1}: {m['caption']} ({m['address']})"
                         for i, m in enumerate(meta_list)])
final_prompt = prompt_template.format(photos_info=photos_info)

# 5️⃣ 生成博客
blog_md = palm.predict(final_prompt, max_output_tokens=2048).text

六、提示工程小技巧

技术 作用 示例
Few-Shot 减少“跑题” 提供 1-2 段输出样板
角色扮演 统一口吻 “你是一名旅行博主”
占位符 方便后期替换 (Photo 1 here) → 正则替换为 ![Photo 1](path)

想系统优化 Prompt?用 代码审查助手 扫描模板,AI 会提示“缺少输出长度限制”或“示例与真实字段不一致”等潜在坑。🔍


七、性能与成本优化

  1. 图片缓存:先上传 Cloud Storage,返回公共 URL,避免重复 Base64
  2. Token 复用:Maps 反向地理编码结果缓存 24 h,节省 \$0.005/次
  3. 并发生成:使用 ThreadPoolExecutor 批量调用 ImageText,10 张照片 <15 秒
  4. PaLM 输出缓存:相同上下文+标题组合 MD5 做 Key,命中缓存直接返回,节省 50% 费用

缓存策略写不好?对 代码优化 说“为 Maps 反向地理编码加 LRU 缓存”,秒出带 TTL 的 Python 装饰器。⚡


八、一键部署与监控

  • GitHub Actions:推送即自动构建 Colab 环境并跑示例
  • Prometheus Exporter:记录生成耗时、Token 用量、缓存命中率
  • 预算告警:月度 Vertex AI 费用 > \$20 立即飞书机器人通知

监控指标想量化?用 开发任务管理系统 KPI 输入“平均生成耗时 ≤ 20 秒,缓存命中率 ≥ 60%”,AI 自动拆成每日可追踪的北极星指标。📈


九、总结

通过“EXIF 提取 → Maps 增强 → 视觉 caption → Few-Shot 提示工程”四连击,我们把一堆静态照片变成了可读、可分享、可 SEO 的博客故事。

省流版

  1. 跑通 Colab → 2. 换自己的相册 → 3. 一键生成 Markdown → 4. 发布到博客/公众号

生成式 AI 不只是聊天,它也能成为你最会讲故事的“旅行伙伴”。✈️

原文链接: https://medium.com/google-developer-experts/photo-storytelling-leveraging-generative-ai-and-google-apis-to-compose-posts-from-your-photo-cce8e30f4d57