1 はじめに
CX 事業本部 delivery部の平内(SIN)です。
Amazon Bedrockを使用することで、生成AIを軽易に利用することが可能ですが、カテゴリ「Image」に分類される、Stability AI の Stable Diffusion XL(Preview) では、画像の生成が可能になっています。
生成AIで画像作成する場合、入力するプロンプトが、非常に大きな影響を与えますが、思った通りの出力を得ることは、比較的難しい作業となります。このため、ビジネスのシーン等で、即戦力として活用するのは、まだ、ちょっとハードルの高いものになっているかも知れません。
そこで、今回は、イラストなどの作成時に、簡単な下絵を書いて、そこから、「アイデアを膨らませるために、たくさんのサンプルを生成する」というイメージで作業してみました。
2 フリーハンド
最初にフリーハンドで書いた絵で試してます。(絵心のカケラもない私が、頑張って書いたイラストです)
設定したプロンプトは、以下のとおりです。
prompt = "cute puppy,light smile, In front of the doghouse with the red roof,illustration"
3 パワーポイント
次は、パワーポイントで頑張って書いた絵で試しました。
プロンプトは、以下のとおりです。
prompt = "sticker illustration of cute puppy,light smile, In front of the doghouse with the red roof,vectorized"
4 作業手順
作業は、実装した create_sample_illustration.pyと、下絵を配置して行います。
% tree .
.
├── create_sample_illustration.py
└── dog.png
実行すると、6枚の画像が生成されます。
% python3 create_sample_illustration.py
20231009_021923.png
20231009_021930.png
20231009_021935.png
20231009_021940.png
20231009_021945.png
20231009_021950.png
.
├── create_sample_illustration.py
├── data
│ ├── 20231009_021923.json
│ ├── 20231009_021923.png
│ ├── 20231009_021930.json
│ ├── 20231009_021930.png
│ ├── 20231009_021935.json
│ ├── 20231009_021935.png
│ ├── 20231009_021940.json
│ ├── 20231009_021940.png
│ ├── 20231009_021945.json
│ ├── 20231009_021945.png
│ ├── 20231009_021950.json
│ ├── 20231009_021950.png
│ └── sample_20231009_021950.png
└── dog.png
xxxxxx_xxxxxx.pngが、サイズ(512*512)の生成された画像で、xxxxxx_xxxxxx.jsonが、その諸元です。また、最後に、sample_xxxxxx_xxxxxx.pngが出力されますが、こちらは、作成された6枚の絵を一覧できるように合成した画像となります。
5 create_sample_illustration.py
コードは、大きくは、「イラストを生成するクラス(IllustrationGenerator)」と、最後に「サンプル画像を生成するクラス(Sample_Images)」の2つで構成されています。
Stable Diffusion XLを使用してい、boto3で画像生成しているのは、前者のIllustrationGeneratorです。
import io
import math
import os
import json
import datetime
import random
import base64
from PIL import Image, ImageDraw, ImageFont
import boto3
# サンプル画像を生成するクラス
class Sample_Images:
def __init__(self, size):
self.index = 0
self.size = size
self.create_max = 6
font_path = "/System/Library/Fonts/ヒラギノ角ゴシック W5.ttc"
self.font_size = 30
height = math.floor(self.size * (self.create_max / 3))
width = math.floor(self.size * (self.create_max / 2))
self.base_image = Image.new("RGB", (width, height))
self.draw = ImageDraw.Draw(self.base_image)
self.font = ImageFont.truetype(font_path, size=self.font_size)
def append(self, image, name):
x = int(self.index % (self.create_max / 2) * self.size)
y = int(math.floor(self.index / (self.create_max / 2)) * self.size)
self.base_image.paste(image, (x, y))
self.draw.text(
(x, y + self.size - self.font_size - 5),
name,
fill=(0, 0, 0),
font=self.font,
)
self.index += 1
def save(self, filename):
self.base_image = self.base_image.resize((900, 600))
self.base_image.save(filename)
# イラストを生成するクラス
class IllustrationGenerator:
def __init__(self, size, prompt, data_path, input_image):
self.boto3_bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
self.model_id = "stability.stable-diffusion-xl"
self.size = size
self.data_path = data_path
# パラメータ
self.config = {
"filename": "",
"seed": 0,
"prompt": prompt,
"size": self.size,
"steps": 50,
"cfg_scale": 30,
"start_schedule": 0.6,
"style_preset": "anime",
"input_image": input_image,
}
# 入力画像
self.input_image_b64 = self.__image_to_base64(
Image.open(input_image).resize((self.size, self.size))
)
def __image_to_base64(self, image):
buffer = io.BytesIO()
image.save(buffer, format="PNG")
return base64.b64encode(buffer.getvalue()).decode("utf-8")
def __base64_to_image(self, base64_str):
return Image.open(io.BytesIO(base64.decodebytes(bytes(base64_str, "utf-8"))))
def create(self):
# 日時文字列をファイル名に使用する
basename = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
# 毎回変化する設定値
self.config["filename"] = basename + ".png"
self.config["seed"] = random.randint(0, 100000)
# 入力パラメータ
body = json.dumps(
{
"text_prompts": [{"text": self.config["prompt"], "weight": 1.0}],
"cfg_scale": self.config["cfg_scale"],
"seed": self.config["seed"],
"start_schedule": self.config["start_schedule"],
"steps": self.config["steps"],
"style_preset": self.config["style_preset"],
"init_image": self.input_image_b64,
}
)
# 推論
response = self.boto3_bedrock.invoke_model(body=body, modelId=self.model_id)
# 推論結果の画像取得
response_body = json.loads(response.get("body").read())
response_image = self.__base64_to_image(
response_body["artifacts"][0].get("base64")
)
# レスポンス画像の保存
response_image.save("{}/{}.png".format(self.data_path, basename))
# 設定値の保存
with open("{}/{}.json".format(self.data_path, basename), "w") as f:
json.dump(self.config, f, ensure_ascii=False)
# 生成された画像と名前を返す
return (response_image, basename)
def main():
size = 512
data_path = "./data"
if not os.path.exists(data_path):
os.makedirs(data_path)
# 元画像
input_image = "./dog.png"
# 作成するイラストの説明
# prompt = "cute puppy,light smile, In front of the doghouse with the red roof,illustration"
prompt = "sticker illustration of cute puppy,light smile, In front of the doghouse with the red roof,vectorized"
# サンプル画像をまとめるクラス
sample_images = Sample_Images(size)
# イラスト生成クラス
illustration_generator = IllustrationGenerator(size, prompt, data_path, input_image)
for i in range(6):
(image, basename) = illustration_generator.create()
sample_images.append(image, "{}.png".format(basename))
print("{}.png".format(basename))
sample_images.save("{}/sample_{}.png".format(data_path, basename))
if __name__ == "__main__":
main()
6 最後に
今回は、アイデア出しのために、簡単な下絵から、たくさんのイラストを生成する作業をやってみました。
オンデマンドで使用する Stable Diffusion XL のコストは、1枚、$0.018です。
(サイズ 512*512 以下 ステップ 51以下の場合の料金です。上記のコードは、この範囲内で動作してます)
これぐらいのコストであれば、多くのサンプルを出力して、スピーディーにより良いアイデアが得られれば、費用対効果は大きいのではと思いました。
※「フリーハンド」と「パワーポイント」の違いは、微妙ですが、一応、差が出ているような気がしてます。