メインコンテンツに移動
デベロッパーのためのクラウド活用方法

生成 AI で物体検出 ! Amazon Nova でバウンディングボックスを描画しちゃおう

2025-06-03 | Author : 小林 大樹

はじめに

こんにちは ! ソリューションアーキテクトのこば D です。この記事では、Amazon Bedrock で利用できる基盤モデル「Amazon Nova」を使って、テキストで指示するだけで画像に映る物体を検出し、バウンディングボックス (Bounding box) を描画する方法をご紹介します。


X ポスト » | Facebook シェア » | はてブ »

ご注意

本記事で紹介する AWS サービスを起動する際には、料金がかかります。builders.flash メールメンバー特典の、クラウドレシピ向けクレジットコードプレゼントの入手をお勧めします。

*ハンズオン記事およびソースコードにおける免責事項 »

builders.flash メールメンバー登録

builders.flash メールメンバー登録で、毎月の最新アップデート情報とともに、AWS を無料でお試しいただけるクレジットコードを受け取ることができます。

今すぐ登録 »

1. 基盤モデルによる物体検出とは、従来の物体検出との違い

画像から特定の部品を検出したい、小売店の棚に陳列された商品の位置と種類を把握したい・・・など、ビジネスの現場では、画像の中から特定の「モノ」を見つけ出し、さらにその「場所」や「範囲」まで正確に把握したいという「物体検出」のニーズが数多く存在します。

物体検出とは

物体検出とは、画像や動画の中から特定のオブジェクト (物体) を自動的に識別し、その位置を検出する技術です。多くの場合、検出された物体は矩形 (バウンディングボックス) で囲まれ、「何が」「どこに」あるのかを視覚的に表示します。

これまでの物体検出手法とその課題

これまでの物体検出では、YOLO (You Only Look Once) などの専門的な機械学習アルゴリズムが使われてきました。これらのモデルは画像や動画の中から特定のクラス (例えば「人」「車」「ワンちゃん」など) の物体を自動で見つけ出すことができます。AWS では Amazon Rekognition が同様の機能 をマネージドに提供しています。

YOLO や Amazon Rekognition などの物体検出は、大量の教師データで事前学習されたモデルを活用しています。これらのシステムは学習済みのカテゴリー (ラベル) に対しては高い精度で高速に物体検出が可能です。一方で、大きな制約もあります。それは、事前に学習されていなかったり、定義されていなかったりするラベルは認識できないという点です。

例えば、「dog (ワンちゃん) 」という一般的なカテゴリは学習済みであるため検出できますが、「白いワンちゃん」だったり「おもちゃのにんじん」のような特定の色の指定や特殊なオブジェクトについては、それらが学習データセットに含まれていない場合、個別にラベルを検出することはできません。これは機械学習の世界では「Long tail 問題」として知られています。一般的なオブジェクト (人や車など) はデータが豊富で検出精度も高いですが、まれにしか出現しないオブジェクトや特殊な状況下のオブジェクトは、学習データが少ないため検出精度が下がりがちなのです。

そのため、このような詳細なカテゴリや特殊なアイテムを認識するためには、具体的なラベルを含む追加データでモデルを再学習させるか、特定用途向けにファインチューニングを行うという追加の作業が必要でした。この課題に対応するため、AWS では Amazon Rekognition Custom Labels という機能も提供しており、自分で用意したデータセットでモデルのトレーニングを行うことが可能です。一方で、自分で画像データを収集・ラベル付けし、モデルをトレーニングするという作業が必要となっています。

これに対し、Amazon Nova のようなマルチモーダル基盤モデルによる物体検出は、アプローチが異なります。Amazon Nova は、他の基盤モデル同様に、ユーザーがテキストによるプロンプト (指示) を与えることでその出力をコントロールすることができますが、言語理解と画像認識能力にも優れており、ユーザーの指示を元に画像に映っている物体の座標を特定することができます。Amazon Nova の公式ドキュメントの「Vision understanding prompting techniques」の中でも、「Bounding box detection」として、この機能が明確に紹介されています。

Missing alt text value

Amazon Rekognition Custom Labels

そのため、このような詳細なカテゴリや特殊なアイテムを認識するためには、具体的なラベルを含む追加データでモデルを再学習させるか、特定用途向けにファインチューニングを行うという追加の作業が必要でした。この課題に対応するため、AWS では Amazon Rekognition Custom Labels という機能も提供しており、自分で用意したデータセットでモデルのトレーニングを行うことが可能です。一方で、自分で画像データを収集・ラベル付けし、モデルをトレーニングするという作業が必要となっています。

Missing alt text value
If you need to identify bounding box coordinates for an object, you can utilize the Amazon Nova model to output bounding boxes on a scale of [0, 1000]. After you have obtained these coordinates, you can then resize them based on the image dimensions as a post-processing step.

Amazon Nova の公式ドキュメント

(日本語訳)

(日本語訳)
「オブジェクトのバウンディングボックス座標を特定する必要がある場合、Amazon Nova モデルを利用して、[0, 1000] のスケールでバウンディングボックスを出力させることができます。これらの座標を取得した後、後処理ステップとして画像の寸法に基づいてリサイズすることができます。」

プロンプトの例

以下はバウンディングボックス検出を実現するためのプロンプトの例です。

json
Detect bounding box of objects in the image, only detect {item_name} category objects with high confidence, output in a list of bounding box format.

 

Output example:

[

    {"{item_name}": [x1, y1, x2, y2]},

    ...

]

 

Result:

このプロンプトは「画像の中から、『{item_name}』で指定したモノ (例えば「白いワンちゃん」や「おもちゃのにんじん」) を見つけ出して、その位置を四角で囲むための座標を教えてください。結果はこういうフォーマットでお願いします」という指示を行っています。このプロンプトエンジニアリングにより、Amazon Nova は以下のようなアウトプットを返します。

アウトプット

json
[ {"white dog": [114, 29, 395, 312]}, {"Toy carrot": [88, 132, 321, 233]} ]  

Bounding Box を描画した図

最後に、この出力された座標情報に基づいて、画像上に四角形などの図形を描画処理すれば、Amazon Nova が画像の中の「白いワンちゃん」や「おもちゃのにんじん」を検出してくれたかのような結果を得ることができます。

Missing alt text value

基盤モデルによる物体検出

この手法の何がすごいかというと、プロンプトを変更するだけで、検出したい対象を柔軟に変えられる点です。従来の物体検出のように、特定の対象物を見つけるためにモデルの再学習をしたり、ファインチューニングをしたり・・・といった追加の作業やコストが不要になります。

「白いワンちゃん」を見つけたい時はプロンプトに "white dog" と記述し、「おもちゃのにんじん」を見つけたい時は "Toy carrot" と記述するだけです。さらには、「座っている白いワンちゃん」など、より具体的で状況に依存した対象物の検出も、プロンプトの工夫次第で実現できる可能性があります。

この「基盤モデルによる物体検出」というアプローチは、従来の物体検出技術が持つ「定義済みのカテゴリに対する高速・高精度な検出」という強みとは異なる、「プロンプトに基づく柔軟性と適応性の高さ」という新たな価値を提供します。これにより、今まで専門的な知識や追加の学習コストが必要だった多様な物体検出タスクをよりお手軽に利用することができるのです。

2. Amazon Nova で物体検出を試してみよう

それでは実際に、Amazon Nova を使って画像内の物体を検知するプロセスを、サンプルコードと一緒に解説していきます。

2-1. 必要なライブラリのインストール

まず、コードを実行するために必要なライブラリを準備します。
ここでは、AWS SDK である boto3 と、画像処理ライブラリの Pillow をインストールします。

※ Python が動作する環境の用意および、Amazon Bedrock API を利用するための認証情報の設定 (AWS CLI など) は別途必要です

boto3 と Pillow をインストール

bash
pip install boto3 Pillow

2-2. ライブラリのインポートと基本設定

まず、必要なモジュールをインポートします。

モジュールをインポート

python
import base64
import boto3
import io
import json
import re # 正規表現を扱うライブラリ

from PIL import Image, ImageDraw, ImageFont # Pillow ライブラリから必要なモジュールをインポート

Bedrock Runtime クライアントの初期化

Amazon Bedrock の API を呼び出すためのクライアントを初期化します。modelId には、利用する Amazon Nova のモデル ID を指定してください。この記事の例では、Nova Pro を想定していますが、必要に応じて Nova Lite や Nova Premier のモデル ID に変更可能です。

※ Amazon Nova Micro は、画像入力に対応していません

Amazon Bedrock の API を呼び出す

python
modelId = "us.amazon.nova-pro-v1:0" # 使用する Nova モデルの ID
accept = "application/json"
contentType = "application/json"
bedrock_rt = boto3.client("bedrock-runtime", region_name="us-east-1") # Bedrock Runtimeクライアントの作成 (リージョンは適宜変更)

2-3. モデルの出力を安全に扱うためのヘルパー関数の定義

Amazon Nova モデルからの出力は JSON 形式で返されることが期待されますが、プログラムで安全に処理するため、そして意図した形式で結果を得るために、いくつかのヘルパー関数を定義しておきましょう。

safe_json_load 関数

まずは、モデルからの JSON 文字列を安全にパースするための関数 safe_json_load です。モデルの出力が完全な JSON 形式でない場合や、微調整が必要な場合を考慮し、正規表現などを使って整形処理を行っています。

python
def safe_json_load(json_string):
    try:
        json_string = re.sub(r"\s", "", json_string) # 空白文字の除去
        json_string = re.sub(r"\(", "[", json_string) # 丸括弧を角括弧に置換
        json_string = re.sub(r"\)", "]", json_string) # 丸括弧を角括弧に置換
        bbox_set = {}
        # モデルの出力が途中で切れている場合や、不正な形式になっている場合の補正処理
        for b in re.finditer(r"\[\d+,\d+,\d+,\d+\]", json_string):
            if b.group(0) in bbox_set:
                json_string = json_string[:bbox_set[b.group(0)][1]] + "}]"
                break
            bbox_set[b.group(0)] = (b.start(), b.end())
        else:
            json_string = json_string[:bbox_set[b.group(0)][1]] + "}]"
        json_string = re.sub(r"\]\},\]$", "]}]", json_string) # 末尾の余分な文字の修正
        json_string = re.sub(r"\]\],\[\"", "]},{\"", json_string) # 区切り文字の修正
        json_string = re.sub(r"\]\],\[\{\"", "]},{\"", json_string) # 区切り文字の修正
        return json.loads(json_string)
    except Exception as e:
        print(e)
        return []

get_font 関数

次に、検出結果を画像上に描画する際に、物体のラベルを表示するためのフォントを取得する関数 get_font を定義します。 Arial フォントをデフォルトで利用し、Arial が見つからない場合はデフォルトフォントを使用するようにしています。

python
def get_font(height):
    try:
        # 画像の高さに基づいてフォントサイズを調整
        font = ImageFont.truetype("Arial", size=height // 20)
    except (OSError, IOError):
        # Arialフォントが見つからない場合はデフォルトフォントを試みる
        try:
            font = ImageFont.load_default()
            print("Using default font as Arial font not found")
        except Exception as e:
            print(f"Error loading default font: {e}")
            # デフォルトフォントも読み込めない場合はエラーを発生させる
            raise

2-4. 物体検出を実行するメイン関数 detection

いよいよ、画像を入力し、指定した物体を Amazon Nova に検出させ、結果を描画するまでの一連の処理を行うメイン関数 detection を定義します。この関数が、この記事の核となる処理を担います。

detection 関数

python
def detection(image_path, category_list, image_short_size=360):
    image_pil = Image.open(image_path)
    width, height = image_pil.size

    ratio = image_short_size/ min(width, height) # 画像の短辺が image_short_size になるようにリサイズ比率を計算
    width = round(ratio * width)
    height = round(ratio * height)

    image_pil = image_pil.resize((width, height), resample=Image.Resampling.LANCZOS)
    
    buffer = io.BytesIO()
    image_pil.save(buffer, format="webp", quality=90) # リサイズされた画像をメモリ上のバッファに webp 形式で保存
    image_data = buffer.getvalue()

    category_str = ",".join([f'"{category}"' for category in category_list])# プロンプト内で使用するために、カテゴリリストをカンマ区切りの文字列に変換
    
    # Amazon Novaモデルへの指示 (プロンプト) を作成
    prompts = """
Detect bounding box of objects in the image, only detect %s category objects with high confidence, output in a list of bounding box format.
Output example:
[
    {"%s": [x1, y1, x2, y2]},
    ...
]
""" % (category_str, category_list[0]) # category_list の最初の要素を例として使用

    # モデルの出力形式を誘導するための prefill を定義して、JSON 形式の出力に誘導
    prefill="""[
    {"
"""

    print("Input Prompt:\n", prompts)

    messages = [
        {
            "role": "user",
            "content": [
                {
                    "image": {
                        "format": 'webp',
                        "source": {
                            "bytes": image_data,
                        }
                    }
                },
                {
                    "text": prompts
                },
            ],
        },
        {
            "role": "assistant",
            "content": [
                {
                    "text": prefill
                },
            ],
        }
    ]

    # Amazon Bedrock Runtime クライアントを使用して、Amazon Nova モデルにリクエストを送信
    response = bedrock_rt.converse(
        modelId=modelId,       
        messages=messages,     
        inferenceConfig={      
            "temperature": 0.0,  # 出力のランダム性を制御
            "maxTokens": 1024,  
        },
    )

    output = prefill + response.get('output')["message"]["content"][0]["text"]

    result = safe_json_load(output)

    color_list = [
        'blue',
        'green',
        'yellow',
        'red',
        'orange',
        'pink',
        'purple',
    ]

    font = get_font(height) 


    print("Result:\n")

    for idx, item in enumerate(result):
        label = next(iter(item))
        bbox = item[label]
        x1, y1, x2, y2 = bbox
        if x1 >= x2 or y1 >= y2:# 座標値が不正 (例: x1 が x2 以上) でないか簡単なチェック
            print(f"Skipping invalid bbox for {label}: {bbox}")
            continue

        # Amazon Nova が出力する座標は[0, 1000] のスケールなので、リサイズされた画像 (image_pil) の寸法に合わせてリスケールする。
        w, h = image_pil.size 
        x1 = x1 / 1000 * w
        x2 = x2 / 1000 * w
        y1 = y1 / 1000 * h
        y2 = y2 / 1000 * h
        bbox = (x1, y1, x2, y2)
        bbox = list(map(round, bbox))
        print(f"Detect <{label}> in {bbox}")
        # draw bounding box
        draw = ImageDraw.Draw(image_pil)
        color = color_list[idx % len(color_list)]
        draw.rectangle(bbox, outline=color, width=2)
        draw.text((x1 + 4, y1 + 2), label, fill=color, font=font)
    return image_pil

この detection 関数の大まかな流れは、以下のとおりです。

  • 画像の前処理
    入力された画像を Pillow で開き、モデルが処理しやすいように一定のサイズ (ここでは短辺が image_short_size になるように) にリサイズし、webp 形式のバイトデータに変換します。
  • プロンプトの作成
    ユーザーが指定した category_list (検出したい物体名をリストにしたもの) を、Amazon Nova に入力するプロンプトに代入し、最終的なプロンプトを作成します。
  • prefill による出力形式の誘導
    Nova が生成するアウトプットを JSON 形式に誘導するため、モデルの返答の最初の文字を prefill で定義しています。この実装では、モデルが { から出力を始めるよう強制することで、JSON 形式でアウトプットが返ってくるようになっています。
  • Amazon Nova へのリクエスト
    Converse API を通じて、画像データとプロンプトを含むリクエストを Amazon Bedrock に送信します。ここでは、パラメータの temperature に 0.0 を設定し、回答のぶれを少なくしています
  • バウンディングボックスの描画
    リスケールされた座標を元に、Pillow を使って、検出された物体のラベルと、バウンディングボックスを描画します。

 

それでは、実際に画像を指定してこの detection 関数を呼び出し、その結果を見ていきましょう。

2-5. 実際に物体検出を試す

では、3 つの異なるシナリオで物体検出を試していきましょう。テスト用の画像として、愛犬のポメ太郎の写真を使っていきます。

1) 同じラベルで複数の物体を検出する

この画像をコードと同じディレクトリに ./media/pometaro_01.png として保存し、detection 処理を実行しましょう。以下のように、検出対象の画像のパスと、検出したいラベルを設定して実行します。

Missing alt text value

検出したいラベル

python
detection(
        "./media/pometaro_01.png", # 検出対象の画像
        category_list=["toy"], # 検出したいラベル
    )

カテゴリを矩形で囲んで表示

このコードを実行すると、./media/pometaro_01.png という画像の中から「toy(おもちゃ)」というカテゴリの物体を探し出し、その位置を矩形で囲んで表示します。

 

どうでしょうか、「Toy (おもちゃ) 」というざっくりとした指示にも関わらず、くまさんとにんじんを Bounding box で検出することができています。矩形の範囲もかなり正確ですね。 (ちなみに、「おもちゃ」と日本語でラベルを指定しても動作しますが、体感として英語のほうが精度が出やすいため、この記事では英語を利用しています) 

Missing alt text value

2) 複数の異なるラベルを検出する

次はこちらの画像を ./media/pometaro_02.png として保存しましょう。ここでは、一枚の画像から「エビ天(tempura shrimp)」「くまさん(bear)」「ベッド(bed)」という複数のカテゴリの物体を同時に検出するよう指示してみます。

Missing alt text value

複数のカテゴリ検出

python
detection(
    "./media/pometaro_02.png", # 検出対象の画像
    category_list=["tempura shrimp","bear", "Bed"], # 検出したいラベル
)

検出成功 !

見事検出することができました !見切れているベッドや、エビ天という難しい概念もきちんと理解できていますね。(ちなみに tempura だけだとにんじんのおもちゃも検出されてしまったので、tempura shrimp とより具体的に指定しています) 

Missing alt text value

3) より詳細な指示を行い、物体を検出する

この例では、「ワンちゃん (dog) 」という概念を更に細分化して、「ブラウン色のワンちゃん(brown dog)」「白色のワンちゃん(white dog)」というラベルを指定してみます。こちらの画像を ./media/pometaro_03.png として保存し、以下の指示を実行しましょう。

Missing alt text value

細分化指示

python
detection(
    "./media/pometaro_03.png", # 検出対象の画像
    category_list=["brown dog","white dog"], # 検出したいラベル
)

検出成功 !

どうでしょうか。きちんとワンちゃんの毛色を理解して検出してくれています!

Missing alt text value

2-6. 異なるモデルやパラメータでの検出を試す

ポメ太郎のかわいさがみなさんに伝わったところで、より難易度の高いタスクにも挑戦してみます。


こちらはポメ太郎の朝ごはんですが、このうちより濃い茶色のほうのごはんを検出してみましょう。

 

今回は上記画像を dogfood.png として保存し、「より濃い茶色のほうのごはん (The darker brown feed) 」を検出してね、と無茶振りしていきます。従来の物体検出モデルであれば、「“より” ってなにと比較してるんだよ !」と突っ込まれそうなタスクです。

Missing alt text value

検出したいラベル ["The darker brown feed"]

今回は上記画像を dogfood.png として保存し、「より濃い茶色のほうのごはん (The darker brown feed) 」を検出してね、と無茶振りしていきます。従来の物体検出モデルであれば、「“より” ってなにと比較してるんだよ !」と突っ込まれそうなタスクです。

python
detection(
    "./media/dogfood.jpg",  # 検出対象の画像
    category_list=["The darker brown feed"],  # 検出したいラベル
)

今回のタスクは難易度が高いため、せっかくなので Nova Lite / Pro / Premier モデルそれぞれを利用し、パラメータ (Temperature) の値も変えながら実験してみましょう。

Nova Lite

Missing alt text value まずはマルチモーダル対応の Nova シリーズで最も軽量な Nova Lite ですが、うまく検出できていませんね。Temperature も高くなればなるほど検出結果がぶれているように見えます。

Nova Pro

Missing alt text value 続いて Nova Pro です。こちらは Lite に比べるとある程度「より濃い茶色のごはん」を理解して検出できているように見えます。Temperature = 1.0 だと検出精度が下がっているように見えますね。

Nova Premier

Missing alt text value 最後に、シリーズ最新モデルである Nova Premier です。Temperature = 0 の場合、かなり正確に「より濃い茶色のごはん」が検出できていることがわかります。さすがに画面端の見切れたごはんの検出は難しかったようですが、それでもすごいですね。また、Temperature が高くなると精度が下がるように見えます。この傾向はすべての Nova モデルで共通して見られるため、物体検出ユースケースでは Temperature = 0 と設定することが推奨されそうですね。(なお、実験に使用したごはんはポメ太郎がおいしくいただきました)

実験のまとめ

これらの実行結果からわかるように、Amazon Nova はプロンプトで指定されたカテゴリの物体を画像中から見つけ出し、その座標を抽出することができます。特に、従来の物体検出モデルでは再学習やファインチューニングが必要だったような、より詳細な条件 (例:特定の色) や、文脈に依存する物体 (例:おもちゃの種類) の検出も、プロンプトを工夫するだけで対応できる可能性を秘めている点が、基盤モデルによる物体検出の魅力です。

さらに、基盤モデルによる処理を組み合わせることでより活用の幅を広げることもできます。例えば、今回の物体検出では、検出を行う物体のラベル (category_list) を人間が考えていました。ですが、このラベル生成自体も基盤モデルに任せることもできそうです。例えば、以下のフローのように、入力された画像を元に Claude 3.7 Sonnet がラベルを自動的に生成し、Amazon Nova が生成されたラベルをもとに物体検出を行う、といったワークフローを組むこともできます。この自由度の高さが基盤モデルを活用する魅力のひとつですね。

基盤モデルフロー

Missing alt text value

3. おわりに

この記事では、Amazon Bedrock で利用可能なマルチモーダル基盤モデル「Amazon Nova」と、その強力な物体検出能力について、サンプルコードを交えながらご紹介しました。

従来の物体検出技術が持つ強みとは異なる、「プロンプトに基づく柔軟性と適応力の高さ」という新たな価値により、多様な物体検出タスクを、簡単にアプリケーションに組み込むことができるようになりました。

とはいえ、従来の Amazon Rekognition のような物体検出モデルが不要になるかというと、そういうわけではありません。Amazon Nova と Amazon Rekognition は、それぞれ異なる強みと用途を持つ相補的なサービスです。

Amazon Rekognition は、「人物がいるか」「車が写っているか」といった一般的な物体の検出を高速かつ正確に行うのが得意です。さらに、学習データを用意する手間はかかりますが、Custom Labels を利用することで独自のユースケースにも対応することが可能です。

一方の Nova は、「白いポメラニアンのしっぽ」など、より具体的で柔軟な検出指示に対応できる強みがありますが、回答 (座標) の生成が完了するまでのレイテンシーが発生します。そのため、実際の開発においては両者を組み合わせるアプローチも効果的です。

例えば、Rekognition で大まかな物体検出を高速に行い、必要に応じて Nova でより詳細な分析を行うといった使い方です。こうすることで、高速かつ高い柔軟性を発揮できるアプリケーションが実現できます。どちらが優れているというよりも、要件に合わせて最適なツールを選ぶ、あるいは組み合わせることが大切になってきます。

本記事でご紹介したサンプルコードは Amazon Nova のサンプルコードを集約した GitHub リポジトリ「amazon-nova-samples」 内の「multimodal-understanding/repeatable-patterns/13-image-grounding/」ディレクトリに Jupyter Notebook 形式で公開されています。ぜひ、実際に手を動かして、Amazon Nova の力を体験してみてください。

筆者プロフィール

小林 大樹
アマゾン ウェブ サービス ジャパン合同会社
ソリューションアーキテクト

生成 AI とポメ太郎 (愛犬) をこよなく愛するソリューションアーキテクト。普段は業種業界問わず、様々なお客様の生成 AI x AWS 活用をサポートしています。最近の趣味は AI コーディングエージェントで Vibe Coding する傍ら愛犬と遊ぶことです。

Missing alt text value