所要時間: 約35分 | 難易度: ★★☆☆☆
この記事で作るもの
- ローカルPC内の画像フォルダをスキャンし、AI(Llama-3.2-Vision)がその内容を解析。
- 解析結果をJSON形式で保存し、検索やデータベース登録が可能な「構造化データ」を自動生成するPythonスクリプト。
- クラウドAPI(GPT-4o等)を一切使わず、完全オフラインで1枚あたり約1〜2秒(RTX 4090環境)で処理を完結させます。
📦 この記事に関連する商品(楽天メインで価格確認)
RTX 4060 Ti 16GBVRAM 16GB搭載で、Vision系LLMを安価かつ安定して動かせる現時点の最適解。
※アフィリエイトリンクを含みます
前提知識として、Pythonの基本的な文法(変数、ループ、pipによるライブラリ追加)が理解できていることを想定しています。
先に確認するスペック・料金
ローカルLLM、特にVision系(画像認識)を動かすには、通常のLLMよりもVRAM(ビデオメモリ)の消費が激しい傾向にあります。 画像データはテキストよりもトークン消費量が多く、モデルによっては画像1枚で数千トークンを占有するためです。
最低でも VRAM 12GB以上 を搭載したNVIDIA製GPU(RTX 3060 12GB / 4060 Ti 16GBなど)を推奨します。 VRAMが足りないと、処理が極端に遅くなる(CPU推論に切り替わる)か、メモリ不足(OOM)でクラッシュします。 Macユーザーであれば、メモリ16GB以上のApple Silicon(M2/M3/M4)搭載モデルで、かつUnified Memoryを活かせる構成が必須です。
ランニングコストは電気代のみです。 GPT-4oなどのAPIを使うと、画像1,000枚の解析で数千円から1万円程度の費用が発生しますが、ローカル環境なら完全無料です。 検証機を新調する場合、現時点ではRTX 4060 Ti 16GBが「最も安価に実用レベルのVLMを動かせる選択肢」となります。
なぜこの方法を選ぶのか
現在、マルチモーダルモデル(VLM)をローカルで動かす手段は、Llama.cpp、AutoGPTQ、Ollamaなど複数あります。 その中でも「Ollama」を選ぶ理由は、環境構築の圧倒的な速さと、モデルの重み管理の容易さです。
InternVL2やQwen2-VLといった高性能なモデルも登場していますが、Llama-3.2-Visionは「精度のバランス」と「推論速度」において、2026年現在のローカル環境で最も安定しています。
また、Pythonライブラリとしての ollama が非常に洗練されており、APIを叩く感覚でローカル推論を実装できるため、業務システムへの組み込みが容易だからです。
Step 1: 環境を整える
まずは推論エンジンとなるOllamaをインストールし、必要なモデルをローカルに落とします。
# Ollamaのインストール(公式サイトからインストーラーを実行後、コマンドラインで以下を叩く)
ollama pull llama3.2-vision
# Pythonライブラリのインストール
pip install ollama pillow
ollama pull llama3.2-vision は、約11B(またはその軽量版)のマルチモーダルモデルをダウンロードします。
pillow は、Pythonで画像サイズを最適化し、AIに渡す前の前処理を行うために使用します。
⚠️ 落とし穴:
Ollamaのバージョンが古いと、Visionモデルの画像入力に対応していない場合があります。
必ず ollama serve を最新版にアップデートしてから実行してください。
また、GPUドライバ(CUDA)が古いと速度が10倍以上遅くなるため、最新のGame Ready/Studioドライバへの更新を強く推奨します。
Step 2: 基本の設定
Pythonからモデルを呼び出すための初期設定を書きます。 ここでは、単に画像を渡すだけでなく、AIが「何を出力すべきか」を定義するシステムプロンプトが重要になります。
import ollama
import os
import json
from PIL import Image
import io
# 使用するモデル名
MODEL_NAME = "llama3.2-vision"
def get_image_analysis(image_path, prompt):
# 画像を開いてバイト列に変換
with open(image_path, "rb") as f:
image_bytes = f.read()
# Ollama APIの呼び出し
response = ollama.generate(
model=MODEL_NAME,
prompt=prompt,
images=[image_bytes],
format="json", # 出力をJSONに強制する
options={"temperature": 0.1} # 出力の安定性を高めるために低めに設定
)
return response['response']
format="json" を指定するのが実務上のポイントです。
これにより、AIが余計な解説文(「はい、この画像は〜」など)を出力せず、純粋なJSONデータのみを返すようになります。
また、temperature は 0.1 に設定します。画像解析において「創造性」は不要であり、事実に基づいた一貫性のある回答が必要だからです。
Step 3: 動かしてみる
まずは1枚の画像で、正しくJSONが返ってくるかテストします。
カレントディレクトリに sample.jpg を用意して実行してください。
test_prompt = """
この画像を解析して、以下のJSONフォーマットで返してください。
{
"object_detected": ["物体名1", "物体名2"],
"scene": "場所の説明",
"dominant_color": "主要な色",
"summary": "1行での説明"
}
"""
try:
result = get_image_analysis("sample.jpg", test_prompt)
print(result)
# JSONとしてパースできるか確認
parsed_data = json.loads(result)
print(f"解析成功: {parsed_data['summary']}")
except Exception as e:
print(f"エラーが発生しました: {e}")
期待される出力
{
"object_detected": ["laptop", "coffee cup", "notebook"],
"scene": "office desk",
"dominant_color": "white and brown",
"summary": "A laptop and a coffee cup on a wooden desk in a bright office."
}
結果が返ってこない場合、VRAM不足でモデルがロードできていない可能性があります。
その場合は ollama list でモデルが正常に存在するか確認し、他のメモリを消費しているアプリ(ブラウザや別のAIツール)を閉じてください。
Step 4: 実用レベルにする
実務では1枚ずつ手動で実行することはありません。 フォルダ内の全画像を自動走査し、リトライ処理と進行状況の保存機能を備えた「バッチ処理スクリプト」へと拡張します。 特に、大きな画像はAIが処理できない場合があるため、リサイズ処理を挟むのがプロの技です。
def process_folder(input_folder, output_file):
supported_exts = (".jpg", ".jpeg", ".png")
results = []
# 既存の結果があれば読み込む(途中から再開用)
if os.path.exists(output_file):
with open(output_file, "r", encoding="utf-8") as f:
results = json.load(f)
processed_files = {r['file_name'] for r in results}
for filename in os.listdir(input_folder):
if filename.lower().endswith(supported_exts) and filename not in processed_files:
path = os.path.join(input_folder, filename)
print(f"Processing: {filename}...")
try:
# 画像をリサイズ(VRAM節約と処理高速化のため)
with Image.open(path) as img:
img.thumbnail((1024, 1024))
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format='JPEG')
img_bytes = img_byte_arr.getvalue()
response = ollama.generate(
model=MODEL_NAME,
prompt="Describe this image in detail for a searchable database.",
images=[img_bytes],
format="json"
)
analysis = json.loads(response['response'])
results.append({
"file_name": filename,
"analysis": analysis
})
# 1枚ごとに保存(クラッシュ対策)
with open(output_file, "w", encoding="utf-8") as f:
json.dump(results, f, indent=2, ensure_ascii=False)
except Exception as e:
print(f"Skip {filename} due to error: {e}")
# 実行
process_folder("./my_images", "analysis_results.json")
このスクリプトの肝は img.thumbnail((1024, 1024)) です。
最近のスマホ写真は4K以上の解像度がありますが、VLM内部では固定解像度にリサイズされて処理されます。
事前にPython側でリサイズしてバイト列を小さくしておくことで、Ollamaへの転送負荷を減らし、推論の安定性を劇的に向上させることができます。
よくあるトラブルと解決法
| エラー内容 | 原因 | 解決策 |
|---|---|---|
ollama.ResponseError: model not found | モデルのダウンロード失敗 | ollama pull llama3.2-vision を再実行する。 |
JSONDecodeError | AIがJSON以外の文字を出力した | システムプロンプトで「JSONのみ返せ」と念押しし、format="json" を設定する。 |
| 推論が異様に遅い(数分かかる) | CPU推論になっている | VRAM不足。より小さいモデル(Moondream2等)を試すか、GPUのメモリを空ける。 |
| 画像が認識されない | 非対応形式または破損 | Pillowで一度開き、RGB形式のJPEGに変換してからAIに渡す。 |
次のステップ
このスクリプトが動いたら、次は「検索システム」への応用を考えてみてください。 生成されたJSONを ChromaDB や Qdrant などのベクトルデータベースに投入すれば、自然言語で「赤い車が写っている写真を探して」と指示してローカル画像を検索する「自作Googleフォト」が構築できます。
また、2026年現在のトレンドとしては、InternVL2-Llama3-76B などの巨大なVLMを、今回紹介したコードの MODEL_NAME を書き換えるだけで試すことができます。
精度に満足がいかない場合は、よりパラメータ数の多いモデルへスイッチしてみてください。
その際、RTX 4090 2枚挿しによるNVLink環境があれば、さらに高精度な解析を高速に回すことが可能になります。
よくある質問
Q1: Ollama以外のライブラリ(PyTorch等)で直接動かすのと何が違いますか?
Ollamaは内部で量子化(GGUF形式)を最適に行っているため、VRAM消費を抑えつつ高速に動作します。生のリポジトリからPyTorchで動かすと、VRAMを2倍以上消費することが多く、設定も複雑です。
Q2: 1枚の画像に複数の質問をしたい場合は?
ollama.chat を使い、対話履歴を維持しながら質問を重ねる形式に書き換えてください。ただし、バッチ処理で構造化データを作る目的なら、1回のプロンプトですべての項目をJSONで答えさせる方が効率的です。
Q3: 日本語のプロンプトでも正しく動きますか?
Llama-3.2-Visionは日本語をある程度理解しますが、解析精度は英語プロンプトの方が一段高いです。英語で出力させ、後から別のLLM(Llama-3.1等)で日本語訳するか、Deepl API等と連携させるのが実務での鉄板構成です。


