0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gemini 3 Flash の Agentic Vision を使うと Image -> Code処理 -> Image になることができた

0
Posted at

概要

この間 Gemini 3 Flash に Agentic Vision という機能が追加されました。

非常に簡単に説明すると、昔には、

インプット:画像 → アウトプット:画像
もしくは
インプット:画像 → アウトプット:テキスト

のパータンが多いですが、今回だと、

インプット:画像 → 中間生成物:コード&モデル内で実行 → アウトプット:画像とコード

これだけにはわからないかもしれないので、ここから3つの例をピックアップして説明しますが、公式からの検証には Agentic Vision なしの結果と比べて全ベンチマークも上回って、最大20%ぐらい改善があります。

例の1:ズームしてデティールを抽出

よくあるのはインプットの画像にはテキスト・数字の量が多すぎてLLMが処理しきれないケースがあるけど、この例では画像の一部だけズームして抽出することによって、抽出の精度が上がって、またはテキスト量が多い画像には分割して処理することができます。

インプット画像

01.png

コード

from google.colab import userdata

from google import genai
from google.genai import types

client = genai.Client(api_key=userdata.get('GEMINI_API_KEY'))

with open('01.png', 'rb') as f:
  image_bytes = f.read()

image = types.Part.from_bytes(
    data=image_bytes,
    mime_type="image/png",
)

response = client.models.generate_content(
    model="gemini-3-flash-preview",
    contents=[image, "国庫短期証券と社債だけの画像を切り抜いてください"],
    config=types.GenerateContentConfig(
        tools=[types.Tool(code_execution=types.ToolCodeExecution)]
    ),
)

print(response.candidates[0].content)

アウトプット

response には、コード・画像・(生成された)説明文も含んでいます。

アウトプットテキスト
parts=[Part(
  executable_code=ExecutableCode(
    code="""import PIL.Image
import PIL.ImageDraw

# Load the image to get its dimensions
img = PIL.Image.open('input_file_0.png')
width, height = img.size

# Since the request is to "crop an image of only Treasury Discount Bills and Corporate Bonds",
# and they are far apart, I'll identify their regions.
# I will output the coordinates in the comment for reference.

# Treasury Discount Bills (国庫短期証券) is roughly around y=240 to y=400
# Corporate Bonds (社債等) is roughly around y=770 to y=800

# Let's perform a more precise detection.
# I'll create a single crop if it's reasonable, but they are far.
# Maybe the user wants a combined image. I'll crop two parts and stack them.

# Treasury Discount Bills region (including header and values)
# Top: row starts at ~245
# Sub-items: Underwriting, Outright Purchases, Outright Sales, Net Outright Sales to Gov.
# Bottom of this section: ~400
tdb_box = [245, 25, 395, 975] # [ymin, xmin, ymax, xmax] in normalized? No, let's use pixels later.

# Corporate Bonds region
# Row starts at ~772
# Bottom of this row: ~797
cb_box = [772, 25, 797, 975]

# I will use detection to find them more accurately.
# Let's use the provided information and standard layout.
# [{box_2d: [244, 26, 397, 973], label: "treasury_discount_bills"},
#  {box_2d: [773, 26, 798, 973], label: "corporate_bonds"}]

# Let's just crop them and stack them.
tdb_crop = img.crop((int(26 * width / 1000), int(244 * height / 1000), int(973 * width / 1000), int(397 * height / 1000)))
cb_crop = img.crop((int(26 * width / 1000), int(773 * height / 1000), int(973 * width / 1000), int(798 * height / 1000)))

# Combine them vertically
combined = PIL.Image.new('RGB', (tdb_crop.width, tdb_crop.height + cb_crop.height + 10), (255, 255, 255))
combined.paste(tdb_crop, (0, 0))
combined.paste(cb_crop, (0, tdb_crop.height + 10))

combined.save('combined_crop.png')

# Also, if they just wanted one crop containing both:
full_crop = img.crop((int(26 * width / 1000), int(244 * height / 1000), int(973 * width / 1000), int(798 * height / 1000)))
full_crop.save('full_range_crop.png')

# I will just provide the 'combined_crop.png' as it matches "only ... and ..." better.
print(f"Combined size: {combined.size}")
""",
    language=<Language.PYTHON: 'PYTHON'>
  ),
  thought_signature=b'\x12\xc3\x15\n\xc0\x15\x01\xbe>\xf6\xfb\x92\xad.M\x0f\x953\xc8N\xf7o\xb1\xd1QW\x90lP7\x0e\x8d\x90\x1b\x9b\x1e:\xf7\xcd\xe4L\x8bc\xe6\xce\xb0\xed\xf6\xc9\t\xe3~E[\x1e\xe3\xc2\xae\x88Q\xff\xfa\xa3k\x18Ob\xa0L1,\x07G\x9f\x1c\xf4;*;4G>G\xd3\x8d\xda\xd65\xf7D\x06\xf8E8\x86\x05\x16...'
), Part(
  code_execution_result=CodeExecutionResult(
    outcome=<Outcome.OUTCOME_OK: 'OUTCOME_OK'>,
    output="""Combined size: (1395, 350)
"""
  )
), Part(
  inline_data=Blob(
    data=b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x05s\x00\x00\x01^\x08\x02\x00\x00\x00H3\x1b\xaf\x00\x00\xe6\xa8IDATx\x9c\xec\xdde\\\x14]\x1b\x07\xe03\xb3A,\x9d\xd2\x08J\x99\x88"*vaw\xa2\xd8\xdd\x9d\xd8\xdd\xf1\xd8\xdd\xdd\x1d\x18\x84\xdd\xa2`\x00\xd2)\xcd\xf6\xee\xbc\x1f\x00%\x16\x89W\x11...',
    mime_type='image/png'
  )
), Part(
  inline_data=Blob(
    data=b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x05s\x00\x00\x04$\x08\x02\x00\x00\x00\xb0\xb2\x1a\xaf\x00\x01\x00\x00IDATx\x9c\xec\xdde\\\x14]\x1b\x07\xe03\xb3A,\x9d\xd2\x08J\x99\x88"*vaw\xa2\xd8\xdd\x9d\xd8\xdd\xf1\xd8\xdd\xdd\x1d\x18\x84\xdd\xa2`\x00\xd2)\xcd\xf6\xee\xbc\x1f\x00%\x16\x89W\x11...',
    mime_type='image/png'
  )
), Part(
  text="""
"""
), Part(
  text="""ご指定いただいた「国庫短期証券」と「社債等」の項目を切り出し、結合した画像を作成しました。

# 国庫短期証券 (g) からその内訳項目まで
# 社債等 (K)

以上の2つのセクションを抽出しています。""",
  thought_signature=b'\x12\xda\x05\n\xd7\x05\x01\xbe>\xf6\xfb\xce\x8d\x08Dn\x152\x11A\xcbV]\x10\xd5\xf2\x9e\xf2\xf1\xbb\x04\xe8\xf0\x88{\x1d\x0f"P!\xe0\xc0\x86OV\xb8\xc4\xfah\x1f9.JL\x08\x19\xaf\xb6\xb4e\x9be\xdf\x1cj\xdd\xf9\xa9\x1e\xd5\x92\xfe\x13\x18z\xabh\xcaN\xf6"5\xa2\x82\x05\x01\x0e\xf9#(\xe0\x03\xea\xd4\xaf(6...'
)] role='model'

アウトプット画像

image.png

例の2:細かい数字から正確なグラフを作成

上記の例と似ているけど、視点がやや違います。画像作成はあくまで「雰囲気」感で、グラフはわずかの差があっても許さない一方、画像作成でグラフを作成はどうしても100%正しさを追求することが難しいと思います。

この Agentic Vision を使うと、数字を抽出+コードでグラフを作成が一気にできるようになるので、グラフの精度が高くなると思います。

インプット画像

02.png

コード

from google.colab import userdata

from google import genai
from google.genai import types

client = genai.Client(api_key=userdata.get('GEMINI_API_KEY'))

with open('02.png', 'rb') as f:
  image_bytes = f.read()

image = types.Part.from_bytes(
    data=image_bytes,
    mime_type="image/png",
)

response = client.models.generate_content(
    model="gemini-3-flash-preview",
    contents=[image, "地点ごと(X軸)で観測値と日平年値(Y軸)のグラフを作成してください。"],
    config=types.GenerateContentConfig(
        tools=[types.Tool(code_execution=types.ToolCodeExecution)]
    ),
)

print(response.candidates[0].content)

アウトプット

アウトプットテキスト
parts=[Part(
  executable_code=ExecutableCode(
    code="""import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

# Data extraction
data = {
    '地点': ['酸ヶ湯', '守門', '肘折', '十日町', '安塚', '朱鞠内', '津南', '大井沢', '大山', '只見'],
    '観測値': [424, 253, 248, 218, 213, 211, 191, 189, 181, 171],
    '日平年値': [320, 199, 264, 163, 126, 191, 213, 205, 133, 172]
}

df = pd.DataFrame(data)

# Setting up for Japanese font (assuming standard location or using a fallback)
# Often in these environments, we can use a specific font path.
import matplotlib.font_manager as fm
# Let's find a font that supports Japanese.
fonts = [f.name for f in fm.fontManager.ttflist if 'Gothic' in f.name or 'Mincho' in f.name]
# Try to set a font if found
if fonts:
    plt.rcParams['font.family'] = fonts[0]
else:
    # If no font found, we might have issues with labels, but let's try 'DejaVu Sans' as fallback
    # though it doesn't support Japanese. I'll search specifically for Japanese fonts.
    pass

# Check for specific Japanese fonts common in Linux
jp_fonts = [f.fname for f in fm.fontManager.ttflist if 'Takao' in f.name or 'VL' in f.name or 'IPA' in f.name or 'Gothic' in f.name]
if jp_fonts:
    prop = fm.FontProperties(fname=jp_fonts[0])
else:
    prop = None

fig, ax = plt.subplots(figsize=(12, 6))

x = np.arange(len(df['地点']))
width = 0.35

rects1 = ax.bar(x - width/2, df['観測値'], width, label='観測値 (cm)', color='skyblue')
rects2 = ax.bar(x + width/2, df['日平年値'], width, label='日平年値 (cm)', color='orange')

ax.set_ylabel('積雪の深さ (cm)', fontproperties=prop)
ax.set_title('地点別 積雪の深さ(観測値 vs 日平年値)', fontproperties=prop)
ax.set_xticks(x)
ax.set_xticklabels(df['地点'], fontproperties=prop, rotation=45)
ax.legend(prop=prop)

# Add values on top of bars
def autolabel(rects):
    for rect in rects:
        height = rect.get_height()
        ax.annotate('{}'.format(height),
                    xy=(rect.get_x() + rect.get_width() / 2, height),
                    xytext=(0, 3),  # 3 points vertical offset
                    textcoords="offset points",
                    ha='center', va='bottom')

autolabel(rects1)
autolabel(rects2)

plt.tight_layout()
plt.savefig('snow_depth_graph.png')
plt.show()
""",
    language=<Language.PYTHON: 'PYTHON'>
  ),
  thought_signature=b'\x12\xdb\x14\n\xd8\x14\x01\xbe>\xf6\xfb\xfa\xf5D\x03,\xbc\x12\x9d\xae\xae\x8b\x8d_\xfc\xdf\xc7\x7f\x01\nj;&\x16\xb4F\xe5M}\xb2o\xf5\xfe\xab\xd3D-*}\x89\xc3\xd4\xad\x17y\xd6k\x0f:C\xa0\xfd4[\xfa\xa81E\x06\x8c\r\x01\xb9I\xfb\x88\xdd.\xc4+m\x16W\xceQ\x99\xd2{Y\x97K\xaaJ\xc1\x1aD\xea...'
), Part(
  inline_data=Blob(
    data=b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x04\xb0\x00\x00\x02X\x08\x06\x00\x00\x00r\xe6\x1f\x1a\x00\x00\x00:tEXtSoftware\x00Matplotlib version3.10.1, https://matplotlib.or...',
    mime_type='image/png'
  )
), Part(
  inline_data=Blob(
    data=b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x04\xb0\x00\x00\x02X\x08\x06\x00\x00\x00r\xe6\x1f\x1a\x00\x00\x00:tEXtSoftware\x00Matplotlib version3.10.1, https://matplotlib.or...',
    mime_type='image/png'
  )
), Part(
  text="""ご提示いただいた画像からデータを抽出し、各地点における「観測値」と「日平年値」を比較するグラフを作成しました。

抽出したデータは以下の通りです:
*   **酸ヶ湯**: 観測値 424cm / 平年値 320cm
*   **守門**: 観測値 253cm / 平年値 199cm
*   **肘折**: 観測値 248cm / 平年値 264cm
*   **十日町**: 観測値 218cm / 平年値 163cm
*   **安塚**: 観測値 213cm / 平年値 126cm
*   **朱鞠内**: 観測値 211cm / 平年値 191cm
*   **津南**: 観測値 191cm / 平年値 213cm
*   **大井沢**: 観測値 189cm / 平年値 205cm
*   **大山**: 観測値 181cm / 平年値 133cm
*   **只見**: 観測値 171cm / 平年値 172cm""",
  thought_signature=b'\x12\xae\x02\n\xab\x02\x01\xbe>\xf6\xfb\xd1\x15m\x00g\xa9\xc0n\xcf%+M\xd6\xd0?\xae\x81?I% \xda]\x01\xddA\x9d\n@eY\xf6SI\xa5\x17\x1fy\xdc\xb5\xe1}\xd5\r]c\xdf\t\x17\xb6P\x13\x01\xcf*\xe2\xb7\x96\x088\xc7?p\xfa\x0f\x0b\xd5\xeb\xe2\x91\xda\xdc\xd1\x1c\xdc\x85\xdczk\x06<"\x84T\xd5\t...'
)] role='model'

アウトプット画像

image.png

例の3:グラフに注釈を追加

複雑なグラフに注釈を追加するのも現状のモデルには難しいけど、Agentic Vision を使うと正しいところに画像の加工ができるようになります。さらにコードを再利用すると、毎回LLMをコールする必要がなくなるので非常に大きなメリットとも言えます。

インプット画像

03.png

コード

from google.colab import userdata

from google import genai
from google.genai import types

client = genai.Client(api_key=userdata.get('GEMINI_API_KEY'))

with open('03.png', 'rb') as f:
  image_bytes = f.read()

image = types.Part.from_bytes(
    data=image_bytes,
    mime_type="image/png",
)

response = client.models.generate_content(
    model="gemini-3-flash-preview",
    contents=[image, "上昇サポートラインを画像に追加してください。インプット画像は03.pngです。"],
    config=types.GenerateContentConfig(
        tools=[types.Tool(code_execution=types.ToolCodeExecution)]
    ),
)

print(response.candidates[0].content)

アウトプット

アウトプットテキスト
parts=[Part(
  executable_code=ExecutableCode(
    code="""import cv2
import numpy as np

# Load the image
img = cv2.imread('input_file_0.png')
height, width, _ = img.shape
print(f"Image dimensions: {width}x{height}")

# I will define some points for the support line based on visual estimation and then refine.
# Point A: around the low in mid-April 25
# Point B: around the low in mid-January 26
# Looking at the image:
# Apr 25 label is roughly at x=140 (out of 1000)
# Jan 26 label is roughly at x=830 (out of 1000)
# Height: 2% is at bottom, 4% is at top.

# Let's try to detect the blue line to find precise low points.
# Convert to HSV to better isolate the blue line.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_blue = np.array([100, 50, 50])
upper_blue = np.array([130, 255, 255])
mask = cv2.inRange(hsv, lower_blue, upper_blue)

# Find coordinates of the blue line
coords = np.column_stack(np.where(mask > 0))
# coords is (y, x)

# To find a support line, we look for local minima.
# Let's find the lowest y for each x.
min_y_at_x = {}
for y, x in coords:
    if x not in min_y_at_x or y > min_y_at_x[x]: # larger y means lower on the chart
        min_y_at_x[x] = y

# Focus on specific regions to find major lows
# Region 1: around Apr 25 (roughly x=140 in relative coords? let's use actual pixels)
# Let's say Apr 25 is around x=140 if width is 1000.
# Width is 1000, Height is 281 according to typical aspect ratio for this kind of chart, but let's see.
# Wait, I printed dimensions. Let me check the output.
""",
    language=<Language.PYTHON: 'PYTHON'>
  ),
  thought_signature=b'\x12\xf0\x10\n\xed\x10\x01\xbe>\xf6\xfb\xecH4\xad,\xa4>\x1e\x96\x92\x882\x97,\x1d\x9d\x10\x0e\xbdr0Xctz[\xa5a_\x99ib\x9a=~v\xf5T.\xa0\xcdydf\xdc3\xa3u\xfe\x94A\x1d\x1a\x1ds\xed\x0e\xaf#\xec\xf4\\\xc6\x91\xf9\xebG\xe7iD\x15+9T\xd3\xd2\xe7\x9ak\x0c\xb8\x85\x99E0\x8f...'
), Part(
  code_execution_result=CodeExecutionResult(
    outcome=<Outcome.OUTCOME_OK: 'OUTCOME_OK'>,
    output="""Image dimensions: 1265x391
"""
  )
), Part(
  executable_code=ExecutableCode(
    code="""# Identify major lows.
# We want the highest y values (lowest points on chart).
# Let's look for local maxima in the min_y_at_x values (which represent global lows at each x)

sorted_x = sorted(min_y_at_x.keys())
# Find local maxima of y in min_y_at_x
lows = []
for i in range(10, len(sorted_x) - 10):
    x = sorted_x[i]
    y = min_y_at_x[x]
    # Check if it's a local maximum in a window
    window = [min_y_at_x[sorted_x[j]] for j in range(i-10, i+11)]
    if y == max(window):
        lows.append((x, y))

# Filtering major lows
# We want points that can form a support line.
# Visually, the most important lows are:
# 1. The deep dip in April 25.
# 2. A dip around August.
# 3. A dip around November/January.

# Let's just pick two points that look like they'd form a good support line.
# Point 1: near Apr 25 dip
# Point 2: near Jan 26 dip

p1 = (184, 268) # Estimated from image
p2 = (1120, 120) # Estimated from image

# Let's try to refine these using the detected points.
def find_best_low(target_x, search_range=30):
    candidates = [ (x, y) for x, y in lows if abs(x - target_x) < search_range ]
    if not candidates:
        return None
    return max(candidates, key=lambda p: p[1]) # Max y is lowest point

refined_p1 = find_best_low(180)
refined_p2 = find_best_low(1120)

print(f"Refined P1: {refined_p1}")
print(f"Refined P2: {refined_p2}")

# Let's also find a point in between to see if it fits.
refined_p_mid = find_best_low(480) # Near August
print(f"Refined Mid: {refined_p_mid}")

# Let's draw the line.
# Support line usually has a positive slope here.
# Let's use refined_p1 and refined_p2 for the line.

# Calculate line parameters: y = mx + c
if refined_p1 and refined_p2:
    x1, y1 = refined_p1
    x2, y2 = refined_p2
    m = (y2 - y1) / (x2 - x1)
    c = y1 - m * x1

    # Draw the line across the chart
    start_x = 50
    start_y = int(m * start_x + c)
    end_x = 1200
    end_y = int(m * end_x + c)

    # Output image
    output_img = img.copy()
    cv2.line(output_img, (start_x, start_y), (end_x, end_y), (0, 0, 255), 2) # Red line

    cv2.imwrite('output.png', output_img)
else:
    print("Could not find suitable points.")
""",
    language=<Language.PYTHON: 'PYTHON'>
  ),
  thought_signature=b'\x12\xa6\x07\n\xa3\x07\x01\xbe>\xf6\xfb\xc4O\x13\x0f\x1d\x10\x03\xb4\xc0\x1d\xfeM\xd8\xc6\x99\xd7\x1e\xd6d\xa9~\xe3\r\xa1\xe6V\xf1\xa44\xf43&wJ>\xfb\x02\x01\xf8\x04c\xbbHC\x13\xf0\\\xb1\xf74R\xd2\xce\x88l=j\xaa\x90\xb4\x01\xde\x96f\x0bf\xcbm\xbd\xdb\xc6k\xa3O\xa4\xc2\xeb\x15*\xd1I?\xd31Je...'
), Part(
  code_execution_result=CodeExecutionResult(
    outcome=<Outcome.OUTCOME_OK: 'OUTCOME_OK'>,
    output="""Refined P1: (np.int64(188), np.int64(265))
Refined P2: (np.int64(1107), np.int64(91))
Refined Mid: (np.int64(468), np.int64(195))
"""
  )
), Part(
  inline_data=Blob(
    data=b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x04\xf1\x00\x00\x01\x87\x08\x02\x00\x00\x00^|P\xb4\x00\x00 \x00IDATx\x01\xec\xc1\x0ft\x9d\xe7}\x1f\xf6\xef%\xae\xde\xef\xc5\xbf\x0b\x08\x7f,H \xa0+\x8b%%Jr\xe5\x98\xe0dq\x12\x9ax\x89l\xd9M\x98L\xec\xd9z\xea\xb6'T\xa4X\xa9\xb3...',
    mime_type='image/png'
  )
), Part(
  executable_code=ExecutableCode(
    code="""# Finding better points for the support line.
p_nov = find_best_low(850)
print(f"Refined Nov: {p_nov}")

# Let's use (188, 265) and (1107, 120) instead of (1107, 91) to make it more of a "support" line that doesn't cut through much.
# Actually, let's try (188, 265) and (877, 175) based on the "Refined Nov" result which I'll get.
# Let's just use the two most prominent "supports".
# Maybe just lower the whole line a bit.

# Draw a blue line (to match theme?) or just stick with red but make it look more like a support.
# I'll try to find a line that touches (188, 265) and stays below or at the price elsewhere.
# Let's try connecting (188, 265) with (468, 195).
# Slope m1 = (195 - 265) / (468 - 188) = -70 / 280 = -0.25
# Let's try connecting (188, 265) with (1107, 110).
# Slope m2 = (110 - 265) / (1107 - 188) = -155 / 919 = -0.168
# m2 is flatter.

# Let's use (188, 265) and (877, 175) roughly.
# Wait, I'll check refined_nov value first.
""",
    language=<Language.PYTHON: 'PYTHON'>
  ),
  thought_signature=b'\x12\xa6\x0e\n\xa3\x0e\x01\xbe>\xf6\xfb_\xc5\xf9\x11\x8f\xccnH\xaf3\xb7\xb4\xaf\xd2w>\xd8\xef5\xc4\x98x\xdd\xad\xdc\x1d\x86,\xeb\r\xc2O*S\xf4\x9c\x8b\x0f\xe6\xe7\xc4N\x7f\xe8\x15\x97\xadh\x8b\x92\x83g\xf5\x13?3\x011&\x8a\x81\xb91\xd4\xe1\x1d\xa3\xf4aq^v\xfa\xe1a\xf1\xc4\xcd\x12\xb8\xad\xca\rl:\xb3...'
), Part(
  code_execution_result=CodeExecutionResult(
    outcome=<Outcome.OUTCOME_OK: 'OUTCOME_OK'>,
    output="""Refined Nov: (np.int64(855), np.int64(156))
"""
  )
), Part(
  executable_code=ExecutableCode(
    code="""# Final line parameters
x1, y1 = 188, 265
x2, y2 = 855, 156

m = (y2 - y1) / (x2 - x1)
c = y1 - m * x1

# Extend the line
start_x = 30
start_y = int(m * start_x + c)
end_x = 1230
end_y = int(m * end_x + c)

# Draw on original image
final_img = img.copy()
cv2.line(final_img, (start_x, start_y), (end_x, end_y), (0, 0, 255), 3) # Red, thickness 3

cv2.imwrite('support_line.png', final_img)
""",
    language=<Language.PYTHON: 'PYTHON'>
  ),
  thought_signature=b'\x12\xd0\t\n\xcd\t\x01\xbe>\xf6\xfb\xc7\xfaW\xe7\x10a\x9c\x1b\x8d\xd8Q\x85\xdeZ`\xd1l\x8b\x9cVe\r\x10\xb6\x0e\xba\xaa\xda\xce\xe1\xa3\xfb\xb7\xc51\xf8/r\xf6^\xa0\x17\xf2\xe0\xf3\xad\xfa\x88JB\x01\x8e\xbc\xd3\xba\xd29\x8f5_|\xcb\x85\xa0\x0c\xe2\xd0\xf9\xe4C\xd4r\xa8Ii\x96F\xd3\x17\x19\xdeLf\xce\xa5f...'
), Part(
  inline_data=Blob(
    data=b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x04\xf1\x00\x00\x01\x87\x08\x02\x00\x00\x00^|P\xb4\x00\x00 \x00IDATx\x01\xec\xc1\rt\xdd\xe7}\x1f\xf6\xef%.\xef\xf7\xe2\xed\x02\xc2\x8b\x05\t\x04t\x1d\xa9\xa4\xde\xe8\xd21\xc1\xc9\xe2$\xacV\x13\xc5\xb2\x9b0\x99\xd8\xb3\xf5\xd4mO\xa8H\xb1\\g\x9b...',
    mime_type='image/png'
  )
), Part(
  text="""チャートの主要な安値を結ぶ上昇サポートラインを追加しました。4月の安値と11月頃の安値を基準に、全体のトレンドを下支えするラインを赤色で描画しています。

# サポートラインを描画した画像を保存しました
cv2.imwrite('support_line.png', final_img)

加工済みの画像(`support_line.png`)をご確認ください。""",
  thought_signature=b'\x12\xdb\x01\n\xd8\x01\x01\xbe>\xf6\xfb\x9fx\xd19\xb6\xd9\xa0\xc9O}p\xa3\x88D\x910\xc1\xb8\xd1O\xf0\x85,e\xa6S\xb5s5Y\xf8\x1di\xdeI\xe0\xa9\n\xd3P\xebO\x13\xb7\x1d\x8e\xc6}\xfb{\x18@\xcaY.y\x07\xfe\xbc\xebL\x9f\x8a\xdb\xb7+\xb8\xed1\xab\x9ebu\xd09\x8du\x0b\xe7O\xa6\x1ep\xfb\xc3\x0e...'
)] role='model'

アウトプット画像

image.png

感想

なかなか斬新な機能で、ざっくりサマリーすると:

  • テキスト量が多かったり・ディテールが多かったりする場合はツールなしより精度が高い
  • グラフ作成はコードの中に正確な数字を含むので画像生成だけより精度が高い
  • モデルの中にコードを実行するのでコード実行の安全性が担保される
  • アウトプットにはコードも含むので再利用・他のAIにさらに処理は可能

そして上記の例あくまで一部だけで、他の活用方法はまだたくさんあるし、他のLLMやツールを併用すると、さらに新しい画像の処理や分析手法が出てくると思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?