EmacsからOpenAI APIを使って画像を生成する

しむどん 2024-10-26

近年AIの進化に伴い、さまざまなツールやサービスが登場している。それをEmacsから利用する一環として、以前OpenAI APIを使って文章を生成するEmacs拡張を実装した。それは今も愛用しているが、画像などの生成はサポートしていなかった。しかし、OpenAI API自体には、画像生成のためのモデルとAPIも用意されている。今回はそれを使用し、更にGNU Emacsでの生活を充実させる。

画像生成APIを確認する

OpenAIの画像生成APIは、与えられた文章から画像を生成する。今回は「DALL-E 3」というモデルを使用する。手始めに、画像生成APIの動きを確認する。

OpenAIの画像生成エンドポイントを呼び出すためのリクエストの例を示す。

:ORIGIN = https://api.openai.com
:API_KEY := openai-api-key

POST :ORIGIN/v1/images/generations
Content-Type: application/json
Authorization: Bearer :API_KEY

{
  "model": "dall-e-3",
  "prompt": "pretty cat",
  "n": 1,
  "size": "1024x1024"
}
{
  "created": 1729945494,
  "data": [
    {
      "revised_prompt": "Create an image",
      "url": "https://example.com/DUMMY/img-NIXf.png?st=DUMMY&se=DUMMY&sp=r&sv=DUMMY&sr=DUMMY&rscd=inline&rsct=image/png&skoid=DUMMY&sktid=DUMMY&skt=DUMMY&ske=DUMMY&sks=DUMMY&skv=DUMMY&sig=DUMMY"
    }
  ]
}

// POST DUMMY/v1/images/generations
// HTTP/1.1 200 OK
// Date: Sat, 26 Oct 2024 12:24:54 GMT
// Content-Type: application/json
// Content-Length: 1140
// Connection: keep-alive
// openai-version: 2020-10-01
// access-control-allow-origin: *

このリクエストを送信すると、APIは生成された画像のURLを含むJSONレスポンスを返す。このdata.urlが生成された画像のURLとなっており、アクセスすると画像をダウンロードできる。

Emacsから利用する

このAPIを用いてGNU Emacsから画像を生成するEmacs Lispを書く。

;;; openai-image --- OpenAI API Imaging Utility -*- lexical-binding: t -*-

;; Copyright (C) 2024 TakesxiSximada

;; Author: TakesxiSximada <[email protected]>
;; Maintainer: TakesxiSximada <[email protected]>
;; Repository:
;; Version: 1
;; Package-Version: 20241027.0000
;; Package-Requires: ((emacs "28.0")
;; Date: 2024-10-27

;; This file is not part of GNU Emacs.

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;; https://platform.openai.com/docs/api-reference/images/create
;;
;; Request
;;
;; curl https://api.openai.com/v1/images/generations \
;;   -H "Content-Type: application/json" \
;;   -H "Authorization: Bearer $OPENAI_API_KEY" \
;;   -d '{
;;     "model": "dall-e-3",
;;     "prompt": "A cute baby sea otter",
;;     "n": 1,
;;     "size": "1024x1024"
;;   }'
;;
;; Response
;;
;; {
;;   "created": 1589478378,
;;   "data": [
;;     {
;;       "url": "https://..."
;;     },
;;     {
;;       "url": "https://..."
;;     }
;;   ]
;; }

;;; Code:

(defvar openai-image-previous-ai-prompt "")

;;;###autoload
(defun openai-image-create-and-view (ai-prompt)
  (interactive
   (list (string-trim (read-string-from-buffer "Open AI Image"
					       openai-image-previous-ai-prompt))))
  (when (not (string-empty-p ai-prompt))
    (setq openai-image-previous-ai-prompt ai-prompt)
    (make-process :name "*Open AI Image*"
		  :buffer (generate-new-buffer "*Open AI Image*")
		  :command `("curl" "https://api.openai.com/v1/images/generations"
			     "-X" "POST"
			     "-H" "Content-Type: application/json"
			     "-H" ,(format "Authorization: Bearer %s" openai-api-key)
			     "-d" ,(json-encode `(:model "dall-e-3" :n 1 :size "1024x1024" :prompt ,ai-prompt)))
		  :sentinel (lambda (process event)
			      (when (string-equal event "finished\n")
				(let ((resp (with-current-buffer (process-buffer process)
					      (goto-char 0)
					      (json-parse-buffer))))
				  (eww (gethash "url" (aref (gethash "data" resp) 0)))))))))

(provide 'openai-image)
;; openai-image.el ends here

openai-image.el

このコードブロックでは、OpenAI APIを通じて画像を生成し、レスポンスに含まれる画像のURLにewwを使用してアクセスしている。

EmacsにはHTTPリクエストを送信する方法がいくつかある1。今回は、make-processを使ってcurlをサブプロセスとして呼び出す方法を用いた。とても原始的なやり方だけれど、Emacsの基本的な機能しか使っていない分、Emacs Lispの迷路に迷い込む事が少ない。

プロンプト(AIに渡す文字列)は、何度も修正を加えて画像の生成をやり直す事を想定し、前回のプロンプトが初期値として設定されるようにした。プロンプトに文言を加えながら、生成される画像を調整できるだろう。

まとめ

OpenAIの画像生成モデル「DALL-E 3」を使用し、GNU Emacsから画像生成を行った。具体的には、curlを用いてAPIを呼び出し、生成された画像のURLを表示するEmacs Lispを実装した。プロンプトの初期設定や、生成画像の調整のための工夫も取り入れた。これにより様々な画像を作成するタスクが捗る。またひとつ僕のGNU Emacsが強くなってしまった。


1

url-retrieveやrequest.elやplzなど。ただしrequest.elとplzは標準ライブラリではない。