ジャコ Lab

プログラミング関連のメモ帳的ブログです

diffusers v0.15.0 の Multi-ControlNet でバツポーズのオジサンを美少女にしてみる

github.com

こちらのリリースノートにある ControlNet を複数使う Mutil-ControlNet で、 バッテンポーズをする美少女を作りたいと思います。

使用するモデル

モデルは Meina/MeinaMix_V11 を使用させていただきます。

元画像

元画像は 写真ACバツのポーズをするサラリーマン の画像を使わせていただきます。

ControlNet で使用する画像を準備する

OpenPose

zako-lab929.hatenablog.com

OpenPose の ControlNet は以前に上記の記事で試しています。ご参考まで。


from diffusers.utils import load_image
from controlnet_aux import OpenposeDetector

# 投入画像の準備
init_image_url = "./753215_s.jpg"
init_image = load_image(init_image_url).resize((512, 512))

# コントロールイメージ作成
processor = OpenposeDetector.from_pretrained('lllyasviel/ControlNet')
control_image = processor(init_image)
control_image

元画像が 640 x 480 でしたが、アスペクト比気にせず 512x512 にリサイズしておきます。

というか、そのまま使ってみたら、なんか肩幅が広い気がする美少女ができてしまった!

OpenPoseDetector で検出した画像

OpenPose は無加工で使うので、そのまま変数に保持してても、ファイル出力しても良いかなと思います。
※本記事では1回ダウンロードしておきます。(Canny の方は加工するためにダウンロードするので手順を合わせるため)

変数に保持しとく場合は変数名変えたほうがいいかも!

Canny

zako-lab929.hatenablog.com

Canny の ControlNet は以前に上記の記事で試しています。ご参考まで。


import cv2
from diffusers.utils import load_image
import numpy as np
from PIL import Image

# 投入画像の準備
init_image_url = "./753215_s.jpg"
init_image = load_image(init_image_url).resize((512, 512))

# コントロールイメージを作成するメソッド
def make_canny_condition(image, low_threshold, high_threshold):
    image = np.array(image)
    image = cv2.Canny(image, low_threshold, high_threshold)
    image = image[:, :, None]
    image = np.concatenate([image, image, image], axis=2)
    return Image.fromarray(image)

control_image = make_canny_condition(init_image, 100, 200)
control_image

Canny エッジを検出したオジサン

加工するためにダウンロードします。

Canny の方は加工します

加工後

適当に線を消しただけ!

ControlNet を使用する

事前準備

OpenPose のコントロール画像、及び、Canny のコントロール画像を Google Colab アップロードします。
それぞれ control_image_openpose.png , control_image_canny.png とします。

まずは OpenPose の ControlNet のみで

import torch
from diffusers import ControlNetModel, StableDiffusionControlNetPipeline, DPMSolverMultistepScheduler

# OpenControlNet の準備
controlnet = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_openpose",
    torch_dtype=torch.float16
)
# Pipeline の準備
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "Meina/MeinaMix_V11",
    torch_dtype=torch.float16,
    controlnet=controlnet
).to("cuda")
pipe.enable_model_cpu_offload()

# スケジューラーの設定
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config, use_karras_sigmas=True)
from diffusers.utils import load_image

# コントロール画像をロードする
openpose_control_image = load_image("./openpose_control_image.png")

# パイプライン実行
prompt = "good fingers, good hands, best quality, masterpiece, super fine illustration, anime style, 1 girl, cute, school uniform, standing"
negative_prompt = "bad fingers, bad hands, missing fingers, worst quality,ugly,bad anatomy,jpeg artifacts"
image = pipe(
    prompt,
    negative_prompt=negative_prompt,
    guidance_scale=7,
    num_inference_steps=30,
    image=openpose_control_image
).images[0]
image
プロンプト
good fingers, good hands (綺麗な指が出力されるように)
best quality, masterpiece (品質)
super fine illustration, anime style (スタイル)
1 girl, cute, school uniform (女の子)
standing (寝転がってる画像がよく出てしまったので)
ネガティブプロンプト
bad fingers, bad hands, missing fingers (変な指が出力されないように)
worst quality,ugly,bad anatomy,jpeg artifacts (低品質が出力されないように)

プロンプトには細かいポーズは指定していません。

(左) OpenPose | (右) ControlNet で生成した画像

なかなか良き。
あとは手がバッテンポーズになって欲しいです。

Seed ガチャは何度か回しました!

いよいよ Multi-ControlNet

import torch
from diffusers import ControlNetModel, StableDiffusionControlNetPipeline, DPMSolverMultistepScheduler

# OpenPose の ControlNet の準備
controlnet_openpose = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_openpose",
    torch_dtype=torch.float16
)

# Canny の ControlNet の準備
controlnet_canny = ControlNetModel.from_pretrained(
    "lllyasviel/control_v11p_sd15_canny",
    torch_dtype=torch.float16
)

# Pipeline の準備
pipe = StableDiffusionControlNetPipeline.from_pretrained(
    "Meina/MeinaMix_V11",
    torch_dtype=torch.float16,
    controlnet=[controlnet_openpose, controlnet_canny]
).to("cuda")
pipe.enable_model_cpu_offload()

# スケジューラーの設定
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config, use_karras_sigmas=True)
from diffusers.utils import load_image

# コントロール画像をロードする
control_image_openpose = load_image("./openpose_control_image.png")
control_image_canny = load_image("./canny_control_image.png")

# パイプラインを実行
prompt = "good fingers, good hands, best quality, masterpiece, super fine illustration, anime style, 1 girl, cute, school uniform, standing"
negative_prompt = "bad fingers, bad hands, missing fingers, worst quality,ugly,bad anatomy,jpeg artifacts"
image = pipe(
    prompt,
    negative_prompt=negative_prompt,
    guidance_scale=7,
    num_inference_steps=30,
    generator=generator,
    image=[control_image_openpose, control_image_canny]
).images[0]
image

(左) OpenPose | (中) Canny | (右) 生成された画像

手が、、、オジサン、、、 (でかい)

しかし、期待通りのポーズを生成することができました。

まとめ

結構、狙い通りのポーズを生成することができました。

手の品質については、Canny の線画が雑なのであのような感じなのでしょう。
もっと、詳細に綺麗な手の線画を用意すればもっと綺麗な絵が出来上がりそうです。