画像と問合せをPOSTする
前回に引き続き、いよいよ画像のPOSTまでたどり着きました。前回画像をPOSTしたClaude3では公式サンプルがわかりやすいんですが、GPTのAPIリファレンスには画像をPOSTできるサンプルが見当たりませんでした。ネットに上がっていたPythonを実装してみて、LotusScriptに移植してみます。
前回の記事を見ていない方は合わせてご参照下さい。
コイツ…動くぞ!Nomad Mobileでも動くGPT-4oを使ったテキストファイル、画像問合せ(前編) – Notes開発者のためのXPagesデザインレシピ (enjoyxpages.sakura.ne.jp)
前回と同じくフォームを作ります。基本的にテキストファイルの選択と同じ感じです。
【フォーム】
画像をPOSTするには、Claude3でやったように画像をバイナリで読み込み、Base64でエンコードしてテキストとして扱うので、JSONの属性が変わるだけで難しいことはありません。下記JSONの{base64_image}をエンコードした画像のテキストに置き換えます。
【JSON】
{
"model": "gpt-4o-2024-05-13",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "何が描かれていますか?詳しく日本語で教えて下さい。"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,{base64_image}"
}
}
]
}
]
}
LotusScriptのコードもテキストファイルの読み込み部分とそんなに変わりません。NotesStreamクラスでバイナリを読み込み、浜さんに教えて貰った裏コードBase64にエンコードします。
浜さんのブログには@式関係、LotusScript関係など多彩なテクニックが掲載されていますので、Notes系ブログがどんどんなくなっていく中、貴重な存在です。
出直し!! ドミノ塾 (denaoshidomino.com)
【LotusScript】
Sub Click(Source As Button)
'---------- ---------- ---------- ---------- ----------
'OpenAI APIから取得したレスポンスを書込み
'
'---------- ---------- ---------- ---------- ----------
Const APIURL = "https://api.openai.com/v1/chat/completions"
Const APIKEY = "(取得したAPIKEY)"
Const MODEL = "gpt-4o-2024-05-13"
'クラス・変数宣言
Dim ws As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Dim doc As NotesDocument
Dim session As New NotesSession
Dim http As NotesHTTPRequest
Dim jsonNav As NotesJSONNavigator
Dim stream As NotesStream '画像ファイル読み込み
Dim sRequest As String '入力した質問
Dim sBodyJson As String '送信するJSON文字列
Dim sContent As String '回答本文
Dim vResHeader As Variant 'httpレスポンスヘッダー全体
Dim vResCode As Variant 'httpレスポンスヘッダー0行目リスト
Dim iResCode As Integer 'httpレスポンスコード
Dim sImagePath As String '画像ファイルパス
Dim sBase64Str As String 'Base64変換後ファイル
Dim sMediaType As String 'メディアタイプ(POSTで使用)
'クラス・変数セット
Set http = session.CreateHTTPRequest()
Set uidoc = ws.CurrentDocument
Set doc = uidoc.Document
'入力チェック
sRequest = doc.Request(0)
If sRequest = "" Then
Msgbox "質問を入力して下さい。"
Exit Sub
End If
sImagePath = doc.ImagePath(0)
If sImagePath = "" Then
Msgbox "画像ファイルパスを入力して下さい。"
Exit Sub
End If
'画像ファイルの読み込みとBase64エンコード
Set stream = session.CreateStream()
Call stream.Open(sImagePath, "binary")
sBase64Str = Replace(stream.ReadEncoded(ENC_BASE64, 76), Chr(13) & Chr(10) , "") '裏コード
'画像ファイルのMediaType決定
sMediaType = GetMediaType(sImagePath)
'Getリクエストを実行し、結果を取得
Call http.SetHeaderField("Content-Type", "application/json")
Call http.SetHeaderField("Authorization", "Bearer " & APIKEY)
http.PreferJSONNavigator = True
'特殊文字対策
sRequest = ReplaceSpecialCharacters(sRequest)
'リクエスト送信
sBodyJson = |{"model":"| & MODEL & |", "messages":[| _
& |{"role":"user", "content":[| _
& |{"type":"text","text":"| & sRequest & |"},| _
& |{"type":"image_url","image_url": {| _
& |"url": "data:| & sMediaType & |;base64,|& sBase64Str & |"| _
& |} }| _
& |] }| _
& |] }|
Set jsonNav = http.post(APIURL, sBodyJson)
'エラーレスポンスチェック
vResHeader = http.GetResponseHeaders()
vResCode = Split(vResHeader(0)," ")
iResCode = Cint(vResCode(1))
If iResCode <> 200 Then
Msgbox sContent , 16 , "Error : " & Cstr(iResCode)
Exit Sub
End If
'レスポンスから回答を抽出
sContent = jsonNav.GetElementByPointer("/choices/0/message/content").Value
'回答をフォームに書込み
doc.HttpStatus = iResCode
doc.Response = sContent
Exit Sub
ErrRtn: 'エラールーチン
Msgbox Error() & Chr(13) & "Line: " & Erl(), 16, "Error"
Print Error(), "ErrorCode: " & Err(), "Line: " & Erl()
Print "モジュール: 送信でエラーが発生しました。"
End
End Sub
Function GetMediaType(sFilePath As String) As String
'---------- ---------- ---------- ---------- ----------
' ファイルの拡張子からMediaTypeを決定する
' 引数:
' 戻り値:MediaType(image/jpeg、image/png、image/gif、image/webp
'---------- ---------- ---------- ---------- ----------
Dim sExt As String '拡張子
Dim sMediaType As String 'メディアタイプ
sExt = Lcase(Strrightback(sFilePath , "."))
Select Case sExt
Case "jpg","jpeg":
sMediaType = "image/jpeg"
Case "png":
sMediaType = "image/png"
Case "gif":
sMediaType = "image/gif"
Case "webp":
sMediaType = "image/webp"
Case Else
sMediaType = "Unknown"
End Select
GetMediaType = sMediaType
End Function
'<<<<<<<<<< ReplaceSpecialCharacters関数は省略 >>>>>>>>>>
実行結果と活用シーン
実行結果は下記のようになります。あまり大きな画像をPOSTすると、一気にチャージ金額を使い切るのでご注意下さい。(スマホ画像を直接POSTするなど)
【質問とPOST画像1】
・添付画像に記載された文字をすべて読み取って下さい。回答は読み取った文字だけにして下さい。
【質問とPOST画像2】
・添付の画像を見て、あなたは何を感じますか?できるだけ詳しく教えて下さい。
最後に
簡易OCRとしても使えるかもしれませんし、JSONに「system」属性を追加すれば、あなたの気持ちを唯一わかってくれるチャットAIになるかもしれません。
e-mailやLINE、messengerでのやりとりが当たり前になっている現代、そのアイコンの向こうにいるのは本当にその人ですか?あなたが嬉しくなったり、悲しくなったりするのにその人は必要ですか?もしかしたら生成AIの方が、あなたの意見を否定せず、優しく寄り添ってくれる存在かもしれませんよ。