【個人開発記録】画像生成機能を実装する【しりとり画像ジェネレーター】
はじめに
開発中アプリの概要
しりとりの結果をプロンプトとして画像生成を行い、どんな画像ができるかを楽しみながらローマ字タイピングの練習ができるアプリケーションです。
画像生成機能が子供の興味・動機づけとなり、生成プロンプトとなるしりとりをタイピング形式で行うことで結果的に「ローマ字タイピングの練習につながる」といった狙いがあります。
主要機能である画像生成に関する実装を行う上で前回はプロンプトを英単語に翻訳する翻訳APIを自作する方法を紹介しました。
今回は実際に画像生成を行う Stable Diffusion のAPIを導入する簡単な手順について紹介します。
最後にRailsにおける導入例も掲載しますので参考になれば幸いです。
(Python に関する記事しかないんだもん。。。なのでRails使いの方の力になれたらうれしいいいいぃぃぃ)
生成AI系のAPIを導入する
アプリケーションに画像生成機能を実装する上で必要な画像生成API。
DALL-E3 や Midjourney などの高品質な有料APIを使いたいところですが、
初めてのオリアプ開発でどれだけ費用がかさむのか見通しが読めないのでなるべくなら無料のオープンソースを使いたい。。。
と、いうことで Stable Diffusion のAPIを導入することにしました。
Stable DiffusionのAPI取得方法
方法論は色々ありますが、私はWeb APIを呼び出す方法を取ります。
Stable Diffusion のページから直接APIの提供はしておらず、
Hugging Face (Stable Diffusion のAPIを管理しているGitHubみたいな仕組み)を通じて画像生成系AIの公開モデルのエンドポイントの情報(Web APIのURL)を拾いに行きます。
(1)アクセストークン(APIキー)の作成
図解手順
- まずはユーザー登録を行います。(ユーザー登録方法は省略)
 - 右上のメニューアイコンを展開してSettingをクリック
 - 
New tokenでAPIキーを作成します。
 
 
 - このAPIキーを 
環境変数として使用します。(環境変数の設定は本題ではないので省略します) 
(2)モデルの選定
図解手順
- 
Hugging Faceのトップページのヘッダーかフッターにある(画面サイズによって位置が変わる)ModelsからText-To-Imageのフィルターがかかった画面を表示します。
 - いずれかのモデルページを表示し 
Deploy→InferenceAPI(serverless)の順にクリック
 
Stable Diffusion は Python で使うことがほとんどのようでコード例などはほぼほぼ Python で掲載されてます。
のちほど Railsアプリ で扱う際のコード例を掲載しますのでとりあえず赤枠の部分だけコピーします。

RailsでAPIリクエストのロジックを定義
- 
GemfileにHTTPartyをインストールする 
gem 'httparty'
bundle install
- APIの呼び出しに関するロジックをサービスクラスとして定義する
app/sevices/stable_diffusion_service.rbファイルを作成 
require 'httparty'
require 'base64'
class StableDiffusionService
  API_URL = "https://#{ここに先ほどコピーしたHugging FaceのAPIエンドポイントURLを記載}"
  HEADERS = {
    "Authorization" => ENV['STABLE_DIFFUSION_API_KEY'] # 作成したAPIキーは環境変数化して取得する
  }
  TIMEOUT_SECONDS = 60
  def self.query(prompt, negative_prompt = nil)
    payload = if negative_prompt # negative_promptが指定されていれば、それを含むリクエストペイロードを作成。
                { inputs: prompt, parameters: { negative_prompt: negative_prompt } }
              else
                { inputs: prompt }
              end
    response = HTTParty.post(API_URL, body: payload.to_json, headers: HEADERS, timeout: TIMEOUT_SECONDS) # HTTPartyを使ってPOSTリクエストを送信し、レスポンスを取得。
    case response.code # HTTPレスポンスコードによる処理の分岐。
    when 200
      return response.body # ステータスコード200の場合、レスポンスボディを返す。
    when 404
      raise StandardError, "リソースが見つかりません。"
    when 500
      raise StandardError, "サーバー内部エラーが発生しました。"
    else
      raise StandardError, "API呼び出しで予期しないエラーが発生しました。ステータスコード: #{response.code}" # その他のステータスコードの場合
    end
  rescue Net::ReadTimeout => e
    raise StandardError, "リクエストがタイムアウトしました: #{e.message}"
  rescue HTTParty::Error => e
    raise StandardError, "HTTPartyでエラーが発生しました: #{e.message}"
  rescue StandardError => e
    raise StandardError, "API呼び出しでエラーが発生しました: #{e.message}"
  end
end
- コントローラーから呼び出す
最後にrender json:でJavaScriptへデータを返却しています。 
# 〜省略〜
  def create
    words = params[:words].join(", ")
    translated_words = TranslationService.translate(words) # 前回記事で紹介したAPIの呼び出し
    image_bytes = StableDiffusionService.query(translated_words, negative_prompt)
    image_data = Base64.encode64(image_bytes)
    render json: { image: image_data }
  end
さいごに
モデルの選定にこだわり始めるとキリがないですが、こだわらないと不適切な画像がバンバンでてきてしまうので看過できるレベルの出力ができるモデルを選択しました。
(アプリのターゲットが小学校低学年の子なので)
今日現在で使用しているモデルはこちらです。
もしオススメのモデルがあれば教えてください^_^(切実)
Discussion