5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ModalでStable-Diffusion-webUI + ControlNetをうごかそう!

Last updated at Posted at 2023-05-31

Google ColabでStable-Diffusion-webUIが使いづらくなってしまっていよいよPCを買わねばなるまいか・・・と思っていたところ、Modalというのがいいらしいよ・・・という噂をきいて試してみました。

しーぴーさん:
【無料】GPUがなくてもStable Diffusionで遊びたい!
https://zenn.dev/cp20/articles/stable-diffusion-webui-with-modal

箱庭のボブさん:
Modalを使ってStable Diffusion web UI +ControlNetで遊ぶ
https://note.com/light_impala137/n/n99b014389a69

お二人の記事を参考に、使えるようになりました。ありがとうございます!

しーぴーさん、箱庭のボブさんのコードを踏襲しつつ、一度ダウンロードしたモデルはダウンロードしないように変更を加えましたので取り急ぎご紹介です。
より良い方法があればぜひ教えてください〜

以下のコードを[stable-diffusion-webui_controlNet-aria2.py]として保存します。

stable-diffusion-webui_controlNet-aria2.py
from colorama import Fore
from pathlib import Path

import modal
import shutil
import subprocess
import sys
import shlex
import os

# modal系の変数の定義
stub = modal.Stub("stable-diffusion-webui")
volume_main = modal.SharedVolume().persist("stable-diffusion-webui-main")

# パスの定義
webui_dir = "/content/stable-diffusion-webui"
webui_model_dir = webui_dir + "/models/Stable-diffusion/"

# ControlNetのパスを定義
webui_controlNet_dir = webui_dir + "/extensions/sd-webui-controlnet"
webui_controlNet_model_dir = webui_dir + "/extensions/sd-webui-controlnet/models/"

# モデルのID
model_ids = [
    {
        "repo_id": "hakurei/waifu-diffusion-v1-4",
        "model_path": "wd-1-4-anime_e1.ckpt",
        "config_file_path": "wd-1-4-anime_e1.yaml",
    },
 ]

@stub.function(

    image=modal.Image.from_dockerhub("python:3.10-slim")
    .apt_install(
        "git", "libgl1-mesa-dev", "libglib2.0-0", "libsm6", "libxrender1", "libxext6", "gcc", "libcairo2-dev", "aria2",
    )
    .run_commands(
        "pip install -U -e git+https://github.com/CompVis/taming-transformers.git@master#egg=taming-transformers"
        
    )
    .pip_install(
        "blendmodes==2022",
        "transformers==4.25.1",
        "accelerate==0.12.0",
        "basicsr==1.4.2",
        "gfpgan==1.3.8",
        "gradio==3.16.2",
        "numpy==1.23.3",
        "Pillow==9.4.0",
        "realesrgan==0.3.0",
        "torch",
        "omegaconf==2.2.3",
        "pytorch_lightning==1.7.6",
        "scikit-image==0.19.2",
        "fonts",
        "font-roboto",
        "timm==0.6.7",
        "piexif==1.1.3",
        "einops==0.4.1",
        "jsonmerge==1.8.0",
        "clean-fid==0.1.29",
        "resize-right==0.0.2",
        "torchdiffeq==0.2.3",
        "kornia==0.6.7",
        "lark==1.1.2",
        "inflection==0.5.1",
        "GitPython==3.1.27",
        "torchsde==0.2.5",
        "safetensors==0.2.7",
        "httpcore<=0.15",
        "tensorboard==2.9.1",
        "taming-transformers==0.0.1",
        "clip",
        "xformers",
        "test-tube",
        "diffusers",
        "invisible-watermark",
        "pyngrok",
        "xformers==0.0.16rc425",
        "gdown",
        "huggingface_hub",
        "colorama",
    )
    .pip_install("git+https://github.com/mlfoundations/open_clip.git@bb6e834e9c70d9c27d0dc3ecedeebeaeb1ffad6b"),
    secret=modal.Secret.from_name("my-huggingface-secret"),
    shared_volumes={webui_dir: volume_main},
    
    #ここでGPUを指定
    gpu="a10g",
    # gpu=modal.gpu.A10G(count=2),
    # gpu=modal.gpu.T4(count=2),
    timeout=6000,
)
async def run_stable_diffusion_webui():
    print(Fore.CYAN + "\n---------- セットアップ開始 ----------\n")

    webui_dir_path = Path(webui_model_dir)
    if not webui_dir_path.exists():
        
        subprocess.run(f"git clone -b v2.1 https://github.com/camenduru/stable-diffusion-webui {webui_dir}", shell=True)

    #controlNetをgit clone
    controlNet_dir_path = Path(webui_controlNet_dir)
    if not controlNet_dir_path.exists():
        subprocess.run(f"git clone https://github.com/Mikubill/sd-webui-controlnet {webui_controlNet_dir}", shell=True)

    def download_hf_file(repo_id, filename):
        from huggingface_hub import hf_hub_download

        download_dir = hf_hub_download(repo_id=repo_id, filename=filename)
        return download_dir

    for model_id in model_ids:
        print(Fore.GREEN + model_id["repo_id"] + "のセットアップを開始します...")

        if not Path(webui_model_dir + model_id["model_path"]).exists():
            model_downloaded_dir = download_hf_file(
                model_id["repo_id"],
                model_id["model_path"],
            )
            shutil.copy(model_downloaded_dir, webui_model_dir + os.path.basename(model_id["model_path"]))

        if "config_file_path" not in model_id:
          continue

        if not Path(webui_model_dir + model_id["config_file_path"]).exists():
            config_downloaded_dir = download_hf_file(
                model_id["repo_id"], model_id["config_file_path"]
            )
            shutil.copy(config_downloaded_dir, webui_model_dir + os.path.basename(model_id["config_file_path"]))

        print(Fore.GREEN + model_id["repo_id"] + "のセットアップが完了しました!")

    controlNet_files = [
        "control_v11e_sd15_ip2p_fp16.safetensors",
        "control_v11e_sd15_shuffle_fp16.safetensors",
        "control_v11p_sd15_canny_fp16.safetensors",
        "control_v11f1p_sd15_depth_fp16.safetensors",
        "control_v11p_sd15_inpaint_fp16.safetensors",
        "control_v11p_sd15_lineart_fp16.safetensors",
        "control_v11p_sd15_mlsd_fp16.safetensors",
        "control_v11p_sd15_normalbae_fp16.safetensors",
        "control_v11p_sd15_openpose_fp16.safetensors",
        "control_v11p_sd15_scribble_fp16.safetensors",
        "control_v11p_sd15_seg_fp16.safetensors",
        "control_v11p_sd15_softedge_fp16.safetensors",
        "control_v11p_sd15s2_lineart_anime_fp16.safetensors",
        "control_v11f1e_sd15_tile_fp16.safetensors",
        "control_v11e_sd15_ip2p_fp16.yaml",
        "control_v11e_sd15_shuffle_fp16.yaml",
        "control_v11p_sd15_canny_fp16.yaml",
        "control_v11f1p_sd15_depth_fp16.yaml",
        "control_v11p_sd15_inpaint_fp16.yaml",
        "control_v11p_sd15_lineart_fp16.yaml",
        "control_v11p_sd15_mlsd_fp16.yaml",
        "control_v11p_sd15_normalbae_fp16.yaml",
        "control_v11p_sd15_openpose_fp16.yaml",
        "control_v11p_sd15_scribble_fp16.yaml",
        "control_v11p_sd15_seg_fp16.yaml",
        "control_v11p_sd15_softedge_fp16.yaml",
        "control_v11p_sd15s2_lineart_anime_fp16.yaml",
        "control_v11f1e_sd15_tile_fp16.yaml",
        "t2iadapter_style_sd14v1.pth",
        "t2iadapter_sketch_sd14v1.pth",
        "t2iadapter_seg_sd14v1.pth",
        "t2iadapter_openpose_sd14v1.pth",
        "t2iadapter_keypose_sd14v1.pth",
        "t2iadapter_depth_sd14v1.pth",
        "t2iadapter_color_sd14v1.pth",
        "t2iadapter_canny_sd14v1.pth",
        "t2iadapter_canny_sd15v2.pth",
        "t2iadapter_depth_sd15v2.pth",
        "t2iadapter_sketch_sd15v2.pth",
        "t2iadapter_zoedepth_sd15v1.pth"
    ]

    for file_name in controlNet_files:
        if not Path(webui_controlNet_model_dir + file_name).exists():
            if file_name.endswith('.pth') or file_name.endswith('.safetensors'):
                controlNet_url = "https://huggingface.co/ckpt/ControlNet-v1-1/resolve/main/" + file_name
            elif file_name.endswith('.yaml'):
                controlNet_url = "https://huggingface.co/ckpt/ControlNet-v1-1/raw/main/" + file_name
            subprocess.run(["aria2c", "--console-log-level=error", "-c", "-x", "16", "-s", "16", "-k", "1M", controlNet_url, "-d", webui_controlNet_model_dir, "-o", file_name])

        if not Path(webui_model_dir + "BloodNightOrangeMix.vae.pt").exists():
            vae_url = "https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt"
            subprocess.run(["aria2c", "--console-log-level=error", "-c", "-x", "16", "-s", "16", "-k", "1M", vae_url, "-d", webui_model_dir, "-o", "BloodNightOrangeMix.vae.pt"])

    print(Fore.CYAN + "\n---------- セットアップ完了 ----------\n")

    # WebUIを起動
    sys.path.append(webui_dir)
    sys.argv += shlex.split("--skip-install --xformers")
    os.chdir(webui_dir)
    from launch import start, prepare_environment

    prepare_environment()
    # 拡張機能が使えるように--enable-insecure-extension-accessを追加
    sys.argv = shlex.split("--a --gradio-debug --share --xformers --enable-insecure-extension-access")
    start()
    
    
@stub.local_entrypoint()
def main():
    run_stable_diffusion_webui.call()

modal run stable-diffusion-webui_controlNet-aria2.py
とすると、しばらくしてwebuiのurlが表示されます。

Running on local URL:  http://127.0.0.1:7860
Running on public URL: https://xxxxxxxx-xxxx-xxxx.gradio.live

public URLのほうにブラウザでアクセスすると、見慣れたStable-diffusion-webUIが表示されます!

Modal環境へのファイル転送

Modal環境へのファイル(Loraや、モデルなど)転送は、以下のコマンドで実行できます。

modal volume put <ボリューム名> <ローカルディレクトリ> <リモートディレクトリ>
[stable-diffusion-webui_controlNet-aria2.py]では、<ボリューム名>は、[stable-diffusion-webui-main]です。

例:

$ modal volume put stable-diffusion-webui-main lora /models/Lora

ファイルを送る際には、リモートディレクトリの後に[/]をつけます。
例:

$ modal volume put stable-diffusion-webui-main /Users/mydir/lora.safetensors /models/Lora/

$ modal volume put stable-diffusion-webui-main /Users/mydir/breakdomainrealistic_R2333.safetensors /models/Stable-diffusion/

$ modal volume put stable-diffusion-webui-main /Users/mydir/badhandv4.pt /embeddings/

転送が完了すると、WebUIから認識できるようになります。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?