Claude3でサイゼリヤの間違い探しを解いてみる

# この記事について

画像解析ができる強力な生成AI、Claude 3がAWSで使えるようになりました
現時点(2024/03/10時点)で、AWSではミドルクラスのSonnetしか使えないのですが、それでも十分な精度があります

この記事では、難問と名高いサイゼリヤの間違い探しをClaude3にさせてみました

# 実施した環境

- boto3(Python 3.12)
- AWS Bedrock
- Claude 3 Sonnet(バージョン：bedrock-2023-05-31)
※最上位のOpusはまだAWSで使えないため、Sonnetで検証します。

# いまさらながら、サイゼリヤの間違え探しとはなんぞや

サイゼリヤ（全国チェーンのレストラン）のキッズメニューにある間違い探しゲームです。

「大人が１５分かけてようやく解けるレベル」に設定されているのですが、その難易度がたびたびニュースに取り上げられます。

**日刊ゲンダイ：「サイゼリヤ」キッズメニューの“間違い探し”が難しすぎる**
https://www.nikkan-gendai.com/articles/view/money/288571

**ロケットニュース：【物議】サイゼリヤの「間違い探し」、ついに一線を越える / どう違うのか定規で測ってみた結果がヤバすぎた**
https://rocketnews24.com/2024/03/06/2184781/

▼あまりにも難しいので、AIや画像解析で解かせる試みもちらほら。

**ねとらぼ：「サイゼリヤの間違い探し」に画像認識AIをぶつけたら瞬殺できるのか？　実際に試してみた**　
https://nlab.itmedia.co.jp/nl/articles/1903/19/news001.html

**Gemini VS サイゼの間違い探し！最強の画像認識AIに激ムズ間違い探しを解かせてみた**
https://weel.co.jp/media/image-recognition-spot-difference#index_id3

画像解析はいいスコアを出していますが、競合する「Gemini、GPT-4Vは1問も正答できなかった」ようです。ただ、Claude3の画像解析能力は体感ではGPT-4Vをはるかに超えています。

きっとClaude3ならできるはず。

# やってみる

### まずは画像を用意する

サイゼリヤのホームページから、2023年8月の画像を取得します。
https://www.saizeriya.co.jp/entertainment/

取得した画像は1枚でつながっているので、pythonのPillowで分解、A画像とB画像の2枚の画像に分解しました。

|A画像|B画像|
|---|---|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/5119ef18-5133-7794-3fa9-0d90696015ad.png" width="300px"/>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/63565f2e-b5b5-0ff8-076d-a939b5fc9c93.png" width="300px"/>|

### 補助なしで聞いてみる

まず、この状態でClaude3に聞いてみます。

|ロール|タイプ|プロンプト|
|---|---|---|
|user|text|間違い探しと呼ばれているゲームをします。|
||text|これが1枚目の画像です|
||image|A画像|
||text|これが2枚目の画像です|
||image|B画像|
||text|画像には、間違いが10箇所あります。間違いの場所を説明してください。|

結果はこうなりました。

```text
この楽しい農場の絵には、いくつかの間違いが隠れています。一緒に探してみましょう。

1. 空に浮かぶ雲の中に、おかしな形の雲がありますね。
2. 農場の看板に"Saizeruya Fara"と書かれていますが、おそらく"Saizeruya Farm"の間違いでしょう。
3. トラクターに乗っている人の足が不自然に描かれています。
4. 野菜畑で働く人の中に、手に何か変なものを持っている人がいます。
5. バーベキューをしている人たちの中に、食べ物ではないものを焼いている人がいるようです。
6. テーブルの上に置かれた食べ物の中に、おかしなものが見えます。
7. 農場の建物の屋根が歪んでいる部分があります。
8. 背景の山の形が不自然なところがあります。
9. 農場の入り口付近に、場違いな物が置かれています。
10. 野菜畑で働く人たちの服装に、おかしなものが見受けられます。

こういった小さな間違いを見つけるのが、楽しい"間違い探し"ゲームですね。他にも気づいた点があれば教えてください。
```

画像の内容を正しく理解しながら、10か所の違いを語っています。
手書きの「Saizeriya Farm」まで読めています。OCR能力もあります。

実際の答えと見比べるとどうでしょうか。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/caa720cb-9041-e628-fd36-c39d3ab798b2.png" width="300px"/>

7番の「農場の建物の屋根がゆがんでいる」のは良い線ですが、それ以外は出鱈目です。

### 補助してみる

OpenCVのabsdiff関数を使って、2枚の画像の画像の差分を分析してみます。
画像のピクセル同士を比較して、ピクセルの色がずれている場所を白く塗りつぶしました。

<details><summary>解析に使ったPythonのソース（長いので折りたたみます）</summary><div>

```python
from typing import Any
import numpy as np
from PIL import Image
from dataclasses import dataclass
from cv2 import absdiff, imwrite, bitwise_or, medianBlur


@dataclass
class CMYKLayer:
    """
    CYMK情報
    """

    c: Image.Image
    m: Image.Image
    y: Image.Image
    k: Image.Image


class ParseByOpenCV:
    """
    サイゼリヤの画像を分析する
    """

    # 入力画像のファイルパス
    _input_file_path: str
    # サイゼリヤの画像の中の、A画像の開始位置
    _sizeriya_image_left_start: int = 43
    # サイゼリヤの画像の中の、B画像の開始位置
    _sizeriya_image_right_start: int = 604
    # サイゼリヤの画像の中の、画像の幅
    _sizeriya_image_width: int = 595 - 43

    def __init__(self, input_file_path: str) -> None:
        """
        コンストラクタ
        """
        self._input_file_path = input_file_path

    def read_image(self, file_path: str, offset_x: int, width: int) -> Image.Image:
        """
        画像をファイルパスから読み込んで、offset_xからwidthまでの大きさの画像を、Image形式で返す
        """
        # Pillowで画像を読み込む。カラースペースはCMYKとする
        img = Image.open(file_path).convert("CMYK")
        # 元画像のサイズを取得
        _, height = img.size
        # 画像を切り抜く
        return img.crop((offset_x, 0, offset_x + width, height))

    def process_fork_cymk_layers(self, img: Image.Image) -> CMYKLayer:
        """
        画像をCYMKに分解する
        """
        # CMYK分離
        c, m, y, k = img.split()
        return CMYKLayer(
            c=np.array(c, dtype=np.uint8),
            m=np.array(m, dtype=np.uint8),
            y=np.array(y, dtype=np.uint8),
            k=np.array(k, dtype=np.uint8),
        )

    def __call__(self, *args: Any, **kwds: Any) -> Any:
        """
        処理を実施する
        """
        # A画像を分析する
        left = self.read_image(
            self._input_file_path,
            self._sizeriya_image_left_start,
            self._sizeriya_image_width,
        )
        left = self.process_fork_cymk_layers(left)

        # B画像を分析する
        right = self.read_image(
            self._input_file_path,
            self._sizeriya_image_right_start,
            self._sizeriya_image_width,
        )
        right = self.process_fork_cymk_layers(right)

        # それぞれの色のレイヤーの差分を取る
        c_diff = absdiff(left.c, right.c)
        m_diff = absdiff(left.m, right.m)
        y_diff = absdiff(left.y, right.y)
        k_diff = absdiff(left.k, right.k)

        # 論理和を取得、CYMKのどれかが違っていれば差分とみなす
        merged = bitwise_or(c_diff, m_diff)
        merged = bitwise_or(merged, y_diff)
        merged = bitwise_or(merged, k_diff)

        # アンチエイリアスで混じった、細いちりを取り除く
        kernel_size = 3
        merged = medianBlur(merged, kernel_size)

        # 分析結果を出力
        imwrite("c_diff.png", merged)


if __name__ == "__main__":
    # サイゼリヤの画像を解析する
    ParseByOpenCV("202308.png")()
```
</div></details>


|解析結果|正解画像|
|---|---|
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/3f1e802f-ea73-8e7c-8d86-571bec858f47.png" width="300px"/>|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/caa720cb-9041-e628-fd36-c39d3ab798b2.png" width="300px"/>|

解析画像の白く塗られた場所が、正解画像の丸の位置と対応していることが確認できます。
この補助画像を加えて、Claude3に聞いてみます。

|ロール|タイプ|プロンプト|
|---|---|---|
|user|text|間違い探しと呼ばれているゲームをします。|
||text|これが1枚目の画像です|
||image|A画像|
||text|これが2枚目の画像です|
||image|B画像|
||text|3枚目は補助画像です。2枚の画像をOpenCVのabsdiffで解析して、次の画像の白い点が1枚目と2枚目の差分であることが分かっています|
||image|解析結果の画像|
||text|間違いが10箇所あります。OpenCVの解析結果をもとに、間違いの場所を説明してください。説明の時に、「白い点」ではなく、「間違い」と呼んでください|

結果はこうなりました。

```text
はい、分かりました。この楽しい農場の絵の中に10箇所の間違いがあるようですね。OpenCVの解析結果を参考にしながら、一つずつ間違いの場所を説明させていただきます。

1) 農場の看板の上に間違いがあります。
2) トラクターの後ろに間違いが見られます。
3) 野菜畑の中に間違いがあるようです。
4) ピクニック場の一角に間違いがあります。
5) 木々の間に間違いが隠れているようですね。
6) 雲の中に間違いが見つかります。
7) 山の斜面に間違いがちらついています。
8) 農場の建物の中に間違いがあるようです。
9) 畑で働く人々の間に間違いが見えます。
10) 川の流れに間違いがあるようですね。

このように、OpenCVの差分解析を活用しながら、この楽しい農場の絵の中の10箇所の間違いを指摘させていただきました。プライバシーに配慮しつつ、ゲームを楽しむことができました。
```

実際の答えと見比べるとどうでしょうか。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233180/caa720cb-9041-e628-fd36-c39d3ab798b2.png" width="300px"/>

|間違い|結果|備考|
|---|---|---|
|農場の看板の上に間違いがあります。|◎||
|トラクターの後ろに間違いが見られます。|△|トラクターではなくトラックですが、正しいです|
|野菜畑の中に間違いがあるようです|◎|赤い服の女の子の前に間違いがあります|
|ピクニック場の一角に間違いがあります|〇|ふわっとしていますが、正しいです|
|木々の間に間違いが隠れているようですね|◎||
|雲の中に間違いが見つかります。|×||
|山の斜面に間違いがちらついています|×|看板の上と重複して指摘しているように見えます|
|農場の建物の中に間違いがあるようです|◎||
|畑で働く人々の間に間違いが見えます|〇|ふわっとしていますが、正しいです|
|川の流れに間違いがあるようですね|△|農場の道が川に見えたようです|

概ね合っています。解析画像を元に、元画像のどこに間違いがあるのかを特定して、場所を正しく指摘しています。

出鱈目な回答が2つあります。おそらく「ピクニック場の一角」と「畑で働く人々」でそれぞれ2か所ずつの間違いをまとめてしまったため、足りなくなった指摘を埋めるために足したのではないかと思います。

### もうちょっと補助してみる

場所が分かったため、「農場の看板の上に間違いが1つだけあるが、その間違いについて説明してほしい」とプロンプトで間違いの内容について尋ねました。

以下のように回答がありました。

```text
2枚目の画像で、看板の上に小さな鳥が1羽追加されています。これが1枚目の画像と異なる箇所です。

鳥以外の違いは見つけられませんでした。2枚の画像を注意深く比較し、この鳥の存在が唯一の違いであることを確認しました。
```

正答です。

# まとめ

Claude3 Sonnetの精度は高いのですが、まだ現時点では、補助や工夫なしでサイゼリヤの間違い探しをすることは難しいようでした。

補助を入れると、期待感を感じる回答を返してくれました。
もう少し進めば、サイゼリヤの間違い探しも生成AIだけで解けるようになりそうです。