見出し画像

初心者向けGeminiで画像セグメンテーションする方法

はじめに

GoogleのAIモデル Gemini 2.5 Flash には、画像セグメンテーション(領域分割) という便利な機能があります。

これは、ただ単に「画像に何が写っているか」を答えるだけでなく、「画像のどこに写っているか」を物の形に合わせて正確に特定して切り分けてくれる機能です。

この機能を使いたくて、Google公式のサンプルコードをGoogle Colabで実行してみました。

しかし、そのままコピペしただけでは、私のやりたいこと(Google Drive内の複数の画像ファイルを処理するなど)ができず、いくつかのエラーにも遭遇しました。

今回は、ClaudeSonnet4.5thinkingの解析力を借りながら、公式コードをカスタマイズし、発生したエラーを解決していった過程をまとめました。

Python初心者が公式コードを実用化する際につまづきがちなポイントを整理したので、同じようにGemini APIを活用したい方の参考になれば嬉しいです。

開発環境

  • Google Colab

  • Python 3.10

  • Google Gen AI SDK (google-genai)

  • Model: gemini-2.5-flash


Google公式コードの課題

Googleが提供している公式コード(抜粋)は、以下のようなシンプルなものでした。


# Google公式コード(抜粋)
def extract_segmentation_masks(image_path: str, output_dir: str = "segmentation_outputs"):
  # ... (省略) ...
  # Parse JSON response
  items = json.loads(parse_json(response.text))
  # ... (省略) ...

if __name__ == "__main__":
  extract_segmentation_masks("path/to/image.png")

これをそのまま使うには、以下の課題がありました。

  1. 単一ファイルしか処理できない: 1枚ずつパスを書き換えるのは現実的ではありません。

  2. Google Driveとの連携: Drive内の画像を直接扱いたい。

  3. JSONパースの不安定さ: モデルの出力によってはエラーになることがありました。

エラー解決とカスタマイズの道のり

対応1:複数ファイルの一括処理と出力整理

まずやりたかったのは、「Google Driveにある画像フォルダを丸ごと読み込んで、自動で全部処理する」ことでした。勿論、GoogleColabのContentフォルダに手動で画像をアップロードしてもよいのですが、公式コードの if __name__ == "__main__": ブロックを修正し、glob を使ってフォルダ内の画像を順番に処理するように変更しました。また、結果が混ざらないよう、画像ごとにフォルダを分けるようにしました。

# 修正前(単一ファイル)
if __name__ == "__main__":
  extract_segmentation_masks("path/to/image.png")

# 修正後(複数ファイル・フォルダ自動生成)
if __name__ == "__main__":
  import glob
  
  # 複数拡張子に対応してファイルを取得
  image_files = glob.glob(os.path.join(IMAGE_DIR, "*.jpg")) + \
                glob.glob(os.path.join(IMAGE_DIR, "*.png"))
  
  for image_path in image_files:
    # 画像名でサブディレクトリを作成
    image_name = os.path.splitext(os.path.basename(image_path))[0]
    output_subdir = os.path.join(OUTPUT_DIR, image_name)
    
    # 出力先を指定して実行
    extract_segmentation_masks(image_path, output_dir=output_subdir)

これで一括処理ができるようになりました。

対応2:JSONDecodeError の解決

実行中、モデルからのレスポンスを処理する箇所で json.decoder.JSONDecodeError が発生しました。

JSONDecodeError: Expecting value: line 1 column 1 (char 0)

原因の特定

ClaudeSonnet4.5thinkingに解析してもらったところ、公式コードの parse_json 関数に問題がある可能性が浮上しました。モデルがマークダウンのコードブロック(```json ... ```)を含めて返してくる際、正しくJSON部分だけを抜き出せていないケースがありました。

解決策

parse_json 関数をより堅牢なロジックに修正しました。

# 修正前(公式コード)
def parse_json(json_output: str):
  # Parsing out the markdown fencing
  lines = json_output.splitlines()
  for i, line in enumerate(lines):
    if line == "```json":
      json_output = "\n".join(lines[i+1:])  # Remove everything before "```json"
      output = json_output.split("```")[0]  # Remove everything after the closing "```"
      break  # Exit the loop once "```json" is found
  return json_output

# 修正後
def parse_json(json_output: str):
  lines = json_output.splitlines()
  extracted_json = ""
  for i, line in enumerate(lines):
    if line == "```json":
      # ```json の後ろから次の ``` の前までを取得
      content_after_fence = "\n".join(lines[i+1:])
      extracted_json = content_after_fence.split("```")[0]
      break
  return extracted_json.strip() # 空白除去を追加

これにより、モデルの出力に多少のブレがあっても安定して動作するようになりました。

対応3:FileNotFoundError の解決(結果表示)

処理自体は成功したのですが、最後に生成されたマスク画像を表示しようとしたところでエラーが発生しました。

FileNotFoundError: [Errno 2] No such file or directory: '/content/segmentation_outputs'

原因の特定

これは「対応1」で行ったカスタマイズ(出力先ディレクトリの変更と階層化)が、結果表示用のコードに反映されていなかったためです。元のコードは /content/segmentation_outputs という固定パスを探しに行っていました。

解決策

表示用コードを、新しいディレクトリ構造(/content/output/[画像名]/)に合わせて修正しました。

# 修正前
actual_output_dir = os.path.join(os.getcwd(), "segmentation_outputs")
output_files = os.listdir(actual_output_dir)
# ...

# 修正後
actual_output_dir = "/content/output"

# サブディレクトリを走査するように変更
if os.path.exists(actual_output_dir):
    subdirs = [d for d in os.listdir(actual_output_dir) if os.path.isdir(os.path.join(actual_output_dir, d))]
    for subdir in subdirs:
        # 各フォルダ内のオーバーレイ画像を探して表示
        # ... (表示処理) ...

これで、処理した全画像の結果をColab上で綺麗に確認できるようになりました。

実装したGoogleColabのコード全文は、以下のリンクからダウンロードできます。

画像はお好みのものを使用してみてください。


まとめ

今回の開発では、以下の3ステップでGeminiによるセグメンテーション自動化を実現しました。

  1. 複数ファイル対応: glob とループ処理で一括自動化。

  2. 堅牢性の向上: 公式コードのJSONパース処理を改善し、エラーを回避。

  3. パスの整合性: 処理ロジックの変更に合わせて、表示側のパス処理も追従させる。

GoogleAntigravity(ClaudeSonnet4.5thinking)にコード解析と修正案を出してもらうことで、Python初心者の私でも短時間で実用的なツールに仕上げることができました。

いいなと思ったら応援しよう!